Skip to content

Commit 46c1ddb

Browse files
committed
Merge branch 'remote-master'
2 parents c65a86a + 36516a7 commit 46c1ddb

File tree

5 files changed

+64
-26
lines changed

5 files changed

+64
-26
lines changed

CHANGELOG.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ 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-
- The `dotenv_path` argument of `set_key` and `unset_key` now has a type of `Union[str,
13-
os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]).
8+
## [0.19.0] - 2021-07-24
149

1510
### Changed
1611

1712
- Require Python 3.5 or a later version. Python 2 and 3.4 are no longer supported. (#341
1813
by [@bbc2]).
1914

15+
### Added
16+
17+
- The `dotenv_path` argument of `set_key` and `unset_key` now has a type of `Union[str,
18+
os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]).
19+
- The `stream` argument of `load_dotenv` and `dotenv_values` can now be a text stream
20+
(`IO[str]`), which includes values like `io.StringIO("foo")` and `open("file.env",
21+
"r")` (#348 by [@bbc2]).
22+
2023
## [0.18.0] - 2021-06-20
2124

2225
### Changed
@@ -218,13 +221,13 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
218221
## 0.6.2
219222

220223
- Fix dotenv list command ([@ticosax](https://github.com/ticosax))
221-
- Add iPython Suport
224+
- Add iPython Support
222225
([@tillahoffmann](https://github.com/tillahoffmann))
223226

224227
## 0.6.0
225228

226229
- Drop support for Python 2.6
227-
- Handle escaped charaters and newlines in quoted values. (Thanks
230+
- Handle escaped characters and newlines in quoted values. (Thanks
228231
[@iameugenejo](https://github.com/iameugenejo))
229232
- Remove any spaces around unquoted key/value. (Thanks
230233
[@paulochf](https://github.com/paulochf))
@@ -282,7 +285,8 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
282285
[@yannham]: https://github.com/yannham
283286
[@zueve]: https://github.com/zueve
284287

285-
[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v0.18.0...HEAD
288+
[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v0.19.0...HEAD
289+
[0.19.0]: https://github.com/theskumar/python-dotenv/compare/v0.18.0...v0.19.0
286290
[0.18.0]: https://github.com/theskumar/python-dotenv/compare/v0.17.1...v0.18.0
287291
[0.17.1]: https://github.com/theskumar/python-dotenv/compare/v0.17.0...v0.17.1
288292
[0.17.0]: https://github.com/theskumar/python-dotenv/compare/v0.16.0...v0.17.0

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.18.0
2+
current_version = 0.19.0
33
commit = True
44
tag = True
55

src/dotenv/main.py

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding
3333
class DotEnv():
3434
def __init__(
3535
self,
36-
dotenv_path: Union[str, _PathLike, io.StringIO],
36+
dotenv_path: Optional[Union[str, _PathLike]],
37+
stream: Optional[IO[str]] = None,
3738
verbose: bool = False,
3839
encoding: Union[None, str] = None,
3940
interpolate: bool = True,
4041
override: bool = True,
4142
) -> None:
42-
self.dotenv_path = dotenv_path # type: Union[str,_PathLike, io.StringIO]
43+
self.dotenv_path = dotenv_path # type: Optional[Union[str, _PathLike]]
44+
self.stream = stream # type: Optional[IO[str]]
4345
self._dict = None # type: Optional[Dict[str, Optional[str]]]
4446
self.verbose = verbose # type: bool
4547
self.encoding = encoding # type: Union[None, str]
@@ -48,14 +50,17 @@ def __init__(
4850

4951
@contextmanager
5052
def _get_stream(self) -> Iterator[IO[str]]:
51-
if isinstance(self.dotenv_path, io.StringIO):
52-
yield self.dotenv_path
53-
elif os.path.isfile(self.dotenv_path):
53+
if self.dotenv_path and os.path.isfile(self.dotenv_path):
5454
with io.open(self.dotenv_path, encoding=self.encoding) as stream:
5555
yield stream
56+
elif self.stream is not None:
57+
yield self.stream
5658
else:
5759
if self.verbose:
58-
logger.info("Python-dotenv could not find configuration file %s.", self.dotenv_path or '.env')
60+
logger.info(
61+
"Python-dotenv could not find configuration file %s.",
62+
self.dotenv_path or '.env',
63+
)
5964
yield io.StringIO('')
6065

6166
def dict(self) -> Dict[str, Optional[str]]:
@@ -304,7 +309,7 @@ def _is_interactive():
304309

305310
def load_dotenv(
306311
dotenv_path: Union[str, _PathLike, None] = None,
307-
stream: Optional[io.StringIO] = None,
312+
stream: Optional[IO[str]] = None,
308313
verbose: bool = False,
309314
override: bool = False,
310315
interpolate: bool = True,
@@ -313,7 +318,8 @@ def load_dotenv(
313318
"""Parse a .env file and then load all the variables found as environment variables.
314319
315320
- *dotenv_path*: absolute or relative path to .env file.
316-
- *stream*: `StringIO` object with .env content, used if `dotenv_path` is `None`.
321+
- *stream*: Text stream (such as `io.StringIO`) with .env content, used if
322+
`dotenv_path` is `None`.
317323
- *verbose*: whether to output a warning the .env file is missing. Defaults to
318324
`False`.
319325
- *override*: whether to override the system environment variables with the variables
@@ -322,9 +328,12 @@ def load_dotenv(
322328
323329
If both `dotenv_path` and `stream`, `find_dotenv()` is used to find the .env file.
324330
"""
325-
f = dotenv_path or stream or find_dotenv()
331+
if dotenv_path is None and stream is None:
332+
dotenv_path = find_dotenv()
333+
326334
dotenv = DotEnv(
327-
f,
335+
dotenv_path=dotenv_path,
336+
stream=stream,
328337
verbose=verbose,
329338
interpolate=interpolate,
330339
override=override,
@@ -335,7 +344,7 @@ def load_dotenv(
335344

336345
def dotenv_values(
337346
dotenv_path: Union[str, _PathLike, None] = None,
338-
stream: Optional[io.StringIO] = None,
347+
stream: Optional[IO[str]] = None,
339348
verbose: bool = False,
340349
interpolate: bool = True,
341350
encoding: Optional[str] = "utf-8",
@@ -352,9 +361,12 @@ def dotenv_values(
352361
353362
If both `dotenv_path` and `stream`, `find_dotenv()` is used to find the .env file.
354363
"""
355-
f = dotenv_path or stream or find_dotenv()
364+
if dotenv_path is None and stream is None:
365+
dotenv_path = find_dotenv()
366+
356367
return DotEnv(
357-
f,
368+
dotenv_path=dotenv_path,
369+
stream=stream,
358370
verbose=verbose,
359371
interpolate=interpolate,
360372
override=True,

src/dotenv/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.18.0"
1+
__version__ = "0.19.0"

tests/test_main.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def test_load_dotenv_redefine_var_used_in_file_with_override(dotenv_file):
277277

278278

279279
@mock.patch.dict(os.environ, {}, clear=True)
280-
def test_load_dotenv_utf_8():
280+
def test_load_dotenv_string_io_utf_8():
281281
stream = io.StringIO("a=à")
282282

283283
result = dotenv.load_dotenv(stream=stream)
@@ -286,6 +286,18 @@ def test_load_dotenv_utf_8():
286286
assert os.environ == {"a": "à"}
287287

288288

289+
@mock.patch.dict(os.environ, {}, clear=True)
290+
def test_load_dotenv_file_stream(dotenv_file):
291+
with open(dotenv_file, "w") as f:
292+
f.write("a=b")
293+
294+
with open(dotenv_file, "r") as f:
295+
result = dotenv.load_dotenv(stream=f)
296+
297+
assert result is True
298+
assert os.environ == {"a": "b"}
299+
300+
289301
def test_load_dotenv_in_current_dir(tmp_path):
290302
dotenv_path = tmp_path / '.env'
291303
dotenv_path.write_bytes(b'a=b')
@@ -353,7 +365,7 @@ def test_dotenv_values_file(dotenv_file):
353365
({}, "a=b\nc=${a}\nd=e\nc=${d}", True, {"a": "b", "c": "e", "d": "e"}),
354366
],
355367
)
356-
def test_dotenv_values_stream(env, string, interpolate, expected):
368+
def test_dotenv_values_string_io(env, string, interpolate, expected):
357369
with mock.patch.dict(os.environ, env, clear=True):
358370
stream = io.StringIO(string)
359371
stream.seek(0)
@@ -363,6 +375,16 @@ def test_dotenv_values_stream(env, string, interpolate, expected):
363375
assert result == expected
364376

365377

378+
def test_dotenv_values_file_stream(dotenv_file):
379+
with open(dotenv_file, "w") as f:
380+
f.write("a=b")
381+
382+
with open(dotenv_file, "r") as f:
383+
result = dotenv.dotenv_values(stream=f)
384+
385+
assert result == {"a": "b"}
386+
387+
366388
@pytest.mark.parametrize(
367389
"before,env_dict,after",
368390
[

0 commit comments

Comments
 (0)