Skip to content

Commit 157282c

Browse files
committed
Add encoding parameter to {get,set,unset}_key
The parameter already exists for `dotenv_values` and `load_dotenv` and has the same meaning.
1 parent ba9408c commit 157282c

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this
66
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
10+
### Added
11+
12+
- Add `encoding` (`Optional[str]`) parameter to `get_key`, `set_key` and `unset_key`.
13+
(#379 by [@bbc2])
14+
815
## [0.19.2] - 2021-11-11
916

1017
### Fixed

src/dotenv/main.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,23 +109,30 @@ def get(self, key: str) -> Optional[str]:
109109
return None
110110

111111

112-
def get_key(dotenv_path: Union[str, _PathLike], key_to_get: str) -> Optional[str]:
112+
def get_key(
113+
dotenv_path: Union[str, _PathLike],
114+
key_to_get: str,
115+
encoding: Optional[str] = "utf-8",
116+
) -> Optional[str]:
113117
"""
114-
Gets the value of a given key from the given .env
118+
Get the value of a given key from the given .env.
115119
116-
If the .env path given doesn't exist, fails
120+
Returns `None` if the key isn't found or doesn't have a value.
117121
"""
118-
return DotEnv(dotenv_path, verbose=True).get(key_to_get)
122+
return DotEnv(dotenv_path, verbose=True, encoding=encoding).get(key_to_get)
119123

120124

121125
@contextmanager
122-
def rewrite(path: Union[str, _PathLike]) -> Iterator[Tuple[IO[str], IO[str]]]:
126+
def rewrite(
127+
path: Union[str, _PathLike],
128+
encoding: Optional[str],
129+
) -> Iterator[Tuple[IO[str], IO[str]]]:
123130
try:
124131
if not os.path.isfile(path):
125-
with io.open(path, "w+") as source:
132+
with io.open(path, "w+", encoding=encoding) as source:
126133
source.write("")
127-
with tempfile.NamedTemporaryFile(mode="w+", delete=False) as dest:
128-
with io.open(path) as source:
134+
with tempfile.NamedTemporaryFile(mode="w+", delete=False, encoding=encoding) as dest:
135+
with io.open(path, encoding=encoding) as source:
129136
yield (source, dest) # type: ignore
130137
except BaseException:
131138
if os.path.isfile(dest.name):
@@ -141,6 +148,7 @@ def set_key(
141148
value_to_set: str,
142149
quote_mode: str = "always",
143150
export: bool = False,
151+
encoding: Optional[str] = "utf-8",
144152
) -> Tuple[Optional[bool], str, str]:
145153
"""
146154
Adds or Updates a key/value to the given .env
@@ -165,7 +173,7 @@ def set_key(
165173
else:
166174
line_out = "{}={}\n".format(key_to_set, value_out)
167175

168-
with rewrite(dotenv_path) as (source, dest):
176+
with rewrite(dotenv_path, encoding=encoding) as (source, dest):
169177
replaced = False
170178
missing_newline = False
171179
for mapping in with_warn_for_invalid_lines(parse_stream(source)):
@@ -187,6 +195,7 @@ def unset_key(
187195
dotenv_path: Union[str, _PathLike],
188196
key_to_unset: str,
189197
quote_mode: str = "always",
198+
encoding: Optional[str] = "utf-8",
190199
) -> Tuple[Optional[bool], str]:
191200
"""
192201
Removes a given key from the given .env
@@ -199,7 +208,7 @@ def unset_key(
199208
return None, key_to_unset
200209

201210
removed = False
202-
with rewrite(dotenv_path) as (source, dest):
211+
with rewrite(dotenv_path, encoding=encoding) as (source, dest):
203212
for mapping in with_warn_for_invalid_lines(parse_stream(source)):
204213
if mapping.key == key_to_unset:
205214
removed = True

tests/test_main.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ def test_set_key(dotenv_file, before, key, value, expected, after):
5353
mock_warning.assert_not_called()
5454

5555

56+
def test_set_key_encoding(dotenv_file):
57+
encoding = "latin-1"
58+
59+
result = dotenv.set_key(dotenv_file, "a", "é", encoding=encoding)
60+
61+
assert result == (True, "a", "é")
62+
assert open(dotenv_file, "r", encoding=encoding).read() == "a='é'\n"
63+
64+
5665
def test_set_key_permission_error(dotenv_file):
5766
os.chmod(dotenv_file, 0o000)
5867

@@ -107,6 +116,16 @@ def test_get_key_ok(dotenv_file):
107116
mock_warning.assert_not_called()
108117

109118

119+
def test_get_key_encoding(dotenv_file):
120+
encoding = "latin-1"
121+
with open(dotenv_file, "w", encoding=encoding) as f:
122+
f.write("é=è")
123+
124+
result = dotenv.get_key(dotenv_file, "é", encoding=encoding)
125+
126+
assert result == "è"
127+
128+
110129
def test_get_key_none(dotenv_file):
111130
logger = logging.getLogger("dotenv.main")
112131
with open(dotenv_file, "w") as f:
@@ -147,6 +166,18 @@ def test_unset_no_value(dotenv_file):
147166
mock_warning.assert_not_called()
148167

149168

169+
def test_unset_encoding(dotenv_file):
170+
encoding = "latin-1"
171+
with open(dotenv_file, "w", encoding=encoding) as f:
172+
f.write("é=x")
173+
174+
result = dotenv.unset_key(dotenv_file, "é", encoding=encoding)
175+
176+
assert result == (True, "é")
177+
with open(dotenv_file, "r", encoding=encoding) as f:
178+
assert f.read() == ""
179+
180+
150181
def test_unset_non_existent_file(tmp_path):
151182
nx_file = str(tmp_path / "nx")
152183
logger = logging.getLogger("dotenv.main")

0 commit comments

Comments
 (0)