Skip to content

Commit 62df65a

Browse files
bbc2Saurabh Kumar
authored andcommitted
Fix unicode/str inconsistency in Python 2 (theskumar#177)
This encodes environment keys and values for `os.environ` in Python 2 and fixes the following issues: * Values loaded from `.env` being `unicode` whereas regular values being `str` (i.e. `bytes`) in Python 2. * Error when loading `.env` with UTF-8 characters in Python 2. * `# type: ignore` hiding the issue from the type checker. `sys.getfilesystemencoding()` is used because it seems to match the encoding used for environment variables with Python 2. Since it can apparently return `None`, we fall back to a default encoding (UTF-8 here) if that's the case. I tested this on Windows where `"mbcs"` is the file system encoding. Hopefully, this will also work on other systems.
1 parent c50c6f7 commit 62df65a

File tree

4 files changed

+30
-13
lines changed

4 files changed

+30
-13
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,10 @@ Unreleased
300300
-----
301301

302302
- Add type hints and expose them to users ([@qnighy])([#172])
303-
- `load_dotenv` and `dotenv_values` now accepts `encoding` paramater, defaults to `None` ([@theskumar])([@earlbread]) (#161)
303+
- `load_dotenv` and `dotenv_values` now accept an `encoding` parameter, defaults to `None`
304+
([@theskumar])([@earlbread])([#161])
305+
- Fix `str`/`unicode` inconsistency in Python 2: values are always `str` now. ([@bbc2])([#121])
306+
- Fix Unicode error in Python 2, introduced in 0.10.0. ([@bbc2])([#176])
304307

305308
0.10.1
306309
-----
@@ -417,6 +420,8 @@ Unreleased
417420
[#148]: https://github.com/theskumar/python-dotenv/issues/148
418421
[#158]: https://github.com/theskumar/python-dotenv/issues/158
419422
[#172]: https://github.com/theskumar/python-dotenv/issues/172
423+
[#121]: https://github.com/theskumar/python-dotenv/issues/121
424+
[#176]: https://github.com/theskumar/python-dotenv/issues/176
420425

421426
[@asyncee]: https://github.com/asyncee
422427
[@greyli]: https://github.com/greyli

src/dotenv/compat.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
from typing import Text, Type
21
import sys
2+
33
if sys.version_info >= (3, 0):
44
from io import StringIO # noqa
55
else:
66
from StringIO import StringIO # noqa
77

88
PY2 = sys.version_info[0] == 2 # type: bool
9-
WIN = sys.platform.startswith('win') # type: bool
10-
text_type = Text # type: Type[Text]

src/dotenv/main.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from collections import OrderedDict
1616
from contextlib import contextmanager
1717

18-
from .compat import StringIO, PY2, WIN, text_type
18+
from .compat import StringIO, PY2
1919

2020
if TYPE_CHECKING: # pragma: no cover
2121
if sys.version_info >= (3, 6):
@@ -109,6 +109,17 @@ def parse_stream(stream):
109109
yield binding
110110

111111

112+
def to_env(text):
113+
# type: (Text) -> str
114+
"""
115+
Encode a string the same way whether it comes from the environment or a `.env` file.
116+
"""
117+
if PY2:
118+
return text.encode(sys.getfilesystemencoding() or "utf-8")
119+
else:
120+
return text
121+
122+
112123
class DotEnv():
113124

114125
def __init__(self, dotenv_path, verbose=False, encoding=None):
@@ -156,13 +167,7 @@ def set_as_environment_variables(self, override=False):
156167
for k, v in self.dict().items():
157168
if k in os.environ and not override:
158169
continue
159-
# With Python2 on Windows, force environment variables to str to avoid
160-
# "TypeError: environment can only contain strings" in Python's subprocess.py.
161-
if PY2 and WIN:
162-
if isinstance(k, text_type) or isinstance(v, text_type):
163-
k = k.encode('ascii')
164-
v = v.encode('ascii')
165-
os.environ[k] = v # type: ignore
170+
os.environ[to_env(k)] = to_env(v)
166171

167172
return True
168173

tests/test_core.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from IPython.terminal.embed import InteractiveShellEmbed
1313

1414
from dotenv import dotenv_values, find_dotenv, load_dotenv, set_key
15-
from dotenv.compat import StringIO
15+
from dotenv.compat import PY2, StringIO
1616
from dotenv.main import Binding, parse_stream
1717

1818

@@ -229,6 +229,15 @@ def test_dotenv_values_export():
229229
assert os.environ['foo'] == 'bar'
230230

231231

232+
def test_dotenv_values_utf_8():
233+
stream = StringIO(u"a=à\n")
234+
load_dotenv(stream=stream)
235+
if PY2:
236+
assert os.environ["a"] == u"à".encode(sys.getfilesystemencoding())
237+
else:
238+
assert os.environ["a"] == "à"
239+
240+
232241
def test_dotenv_empty_selfreferential_interpolation():
233242
stream = StringIO(u'some_path="${some_path}:a/b/c"\n')
234243
stream.seek(0)

0 commit comments

Comments
 (0)