From cedd36d395b772adb556ba4635ab42d59ad27567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=82=E1=B4=80=C9=AA=E1=B4=8D=20=E4=B9=87=CA=9Cs?= =?UTF-8?q?=E1=B4=80=C9=B4?= Date: Sun, 18 Sep 2022 16:48:21 +0500 Subject: [PATCH 01/22] Fixed minor spelling mistake (#426) --- src/dotenv/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 05d377a9..83ea3239 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -323,7 +323,7 @@ def load_dotenv( from the `.env` file. encoding: Encoding to be used to read the file. Returns: - Bool: True if atleast one environment variable is set elese False + Bool: True if atleast one environment variable is set else False If both `dotenv_path` and `stream` are `None`, `find_dotenv()` is used to find the .env file. From 718307b8a2fbaca7f511248c23e591bd5e58760a Mon Sep 17 00:00:00 2001 From: Praveensenpai <71966071+Praveensenpai@users.noreply.github.com> Date: Tue, 8 Nov 2022 20:20:45 +0530 Subject: [PATCH 02/22] Update __init__.py fstring --- src/dotenv/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotenv/__init__.py b/src/dotenv/__init__.py index 3512d101..9dbd2543 100644 --- a/src/dotenv/__init__.py +++ b/src/dotenv/__init__.py @@ -23,9 +23,9 @@ def get_cli_string( """ command = ['dotenv'] if quote: - command.append('-q %s' % quote) + command.append(f'-q {quote}') if path: - command.append('-f %s' % path) + command.append(f'-f {path}') if action: command.append(action) if key: From 1ecb57dd7dd516d3ecedf7e58ba84520281fbd00 Mon Sep 17 00:00:00 2001 From: Ben Li-Sauerwine Date: Wed, 27 Jul 2022 03:26:26 -0400 Subject: [PATCH 03/22] Fix out of scope error when "dest" variable is undefined Fixes #413 whereby the NamedTemporaryFile "dest" was out of scope in the error handling portion of rewrite. The problem was initially fixed in #414 but it got reverted because of a linter error. This new commit works around that linter error. --- src/dotenv/main.py | 20 +++++++++----------- tests/test_main.py | 7 +++++++ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 83ea3239..43248819 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -125,19 +125,17 @@ def rewrite( path: Union[str, os.PathLike], encoding: Optional[str], ) -> Iterator[Tuple[IO[str], IO[str]]]: - try: - if not os.path.isfile(path): - with open(path, "w+", encoding=encoding) as source: - source.write("") - with tempfile.NamedTemporaryFile(mode="w+", delete=False, encoding=encoding) as dest: + if not os.path.isfile(path): + with open(path, mode="w", encoding=encoding) as source: + source.write("") + with tempfile.NamedTemporaryFile(mode="w", encoding=encoding, delete=False) as dest: + try: with open(path, encoding=encoding) as source: - yield (source, dest) # type: ignore - except BaseException: - if os.path.isfile(dest.name): + yield (source, dest) + except BaseException: os.unlink(dest.name) - raise - else: - shutil.move(dest.name, path) + raise + shutil.move(dest.name, path) def set_key( diff --git a/tests/test_main.py b/tests/test_main.py index 82c73ba1..9c895851 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -178,6 +178,13 @@ def test_unset_encoding(dotenv_file): assert f.read() == "" +def test_set_key_unauthorized_file(dotenv_file): + os.chmod(dotenv_file, 0o000) + + with pytest.raises(PermissionError): + dotenv.set_key(dotenv_file, "a", "x") + + def test_unset_non_existent_file(tmp_path): nx_file = str(tmp_path / "nx") logger = logging.getLogger("dotenv.main") From 7e199c32db62eeb03a9e5e807f34f730e90216d3 Mon Sep 17 00:00:00 2001 From: Bertrand Bonnefoy-Claudet Date: Fri, 11 Nov 2022 17:27:11 +0100 Subject: [PATCH 04/22] Use 3.11 non-beta in CI --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b7c6d504..15875768 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: matrix: os: - ubuntu-latest - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11.0-beta.4 - 3.11", pypy3.9] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", pypy3.9] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} From 00cc7ae200aa2228167c689bbe8b1d3c5c7a4751 Mon Sep 17 00:00:00 2001 From: momohakarish Date: Thu, 27 Oct 2022 00:41:15 +0300 Subject: [PATCH 05/22] Modernize some code in variables.py --- src/dotenv/variables.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/dotenv/variables.py b/src/dotenv/variables.py index d77b700c..667f2f26 100644 --- a/src/dotenv/variables.py +++ b/src/dotenv/variables.py @@ -1,8 +1,8 @@ import re -from abc import ABCMeta +from abc import ABCMeta, abstractmethod from typing import Iterator, Mapping, Optional, Pattern -_posix_variable = re.compile( +_posix_variable: Pattern[str] = re.compile( r""" \$\{ (?P[^\}:]*) @@ -12,20 +12,18 @@ \} """, re.VERBOSE, -) # type: Pattern[str] +) -class Atom(): - __metaclass__ = ABCMeta - +class Atom(metaclass=ABCMeta): def __ne__(self, other: object) -> bool: result = self.__eq__(other) if result is NotImplemented: return NotImplemented return not result - def resolve(self, env: Mapping[str, Optional[str]]) -> str: - raise NotImplementedError + @abstractmethod + def resolve(self, env: Mapping[str, Optional[str]]) -> str: ... class Literal(Atom): @@ -33,7 +31,7 @@ def __init__(self, value: str) -> None: self.value = value def __repr__(self) -> str: - return "Literal(value={})".format(self.value) + return f"Literal(value={self.value})" def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): @@ -53,7 +51,7 @@ def __init__(self, name: str, default: Optional[str]) -> None: self.default = default def __repr__(self) -> str: - return "Variable(name={}, default={})".format(self.name, self.default) + return f"Variable(name={self.name}, default={self.default})" def __eq__(self, other: object) -> bool: if not isinstance(other, self.__class__): @@ -74,8 +72,8 @@ def parse_variables(value: str) -> Iterator[Atom]: for match in _posix_variable.finditer(value): (start, end) = match.span() - name = match.groupdict()["name"] - default = match.groupdict()["default"] + name = match["name"] + default = match["default"] if start > cursor: yield Literal(value=value[cursor:start]) From cadf4fc60a0c2a6de0a743a225cd9b2c48e1426a Mon Sep 17 00:00:00 2001 From: momohakarish Date: Thu, 27 Oct 2022 22:30:58 +0300 Subject: [PATCH 06/22] Modernize main.py and parser.py code --- src/dotenv/main.py | 28 ++++++++++++++-------------- src/dotenv/parser.py | 29 +++++++++++------------------ 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 43248819..e7b8392a 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -25,23 +25,23 @@ def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding yield mapping -class DotEnv(): +class DotEnv: def __init__( self, dotenv_path: Optional[Union[str, os.PathLike]], stream: Optional[IO[str]] = None, verbose: bool = False, - encoding: Union[None, str] = None, + encoding: Optional[str] = None, interpolate: bool = True, override: bool = True, ) -> None: - self.dotenv_path = dotenv_path # type: Optional[Union[str, os.PathLike]] - self.stream = stream # type: Optional[IO[str]] - self._dict = None # type: Optional[Dict[str, Optional[str]]] - self.verbose = verbose # type: bool - self.encoding = encoding # type: Union[None, str] - self.interpolate = interpolate # type: bool - self.override = override # type: bool + self.dotenv_path: Optional[Union[str, os.PathLike]] = dotenv_path + self.stream: Optional[IO[str]] = stream + self._dict: Optional[Dict[str, Optional[str]]] = None + self.verbose: bool = verbose + self.encoding: Optional[str] = encoding + self.interpolate: bool = interpolate + self.override: bool = override @contextmanager def _get_stream(self) -> Iterator[IO[str]]: @@ -153,7 +153,7 @@ def set_key( an orphan .env somewhere in the filesystem """ if quote_mode not in ("always", "auto", "never"): - raise ValueError("Unknown quote_mode: {}".format(quote_mode)) + raise ValueError(f"Unknown quote_mode: {quote_mode}") quote = ( quote_mode == "always" @@ -165,9 +165,9 @@ def set_key( else: value_out = value_to_set if export: - line_out = 'export {}={}\n'.format(key_to_set, value_out) + line_out = f'export {key_to_set}={value_out}\n' else: - line_out = "{}={}\n".format(key_to_set, value_out) + line_out = f"{key_to_set}={value_out}\n" with rewrite(dotenv_path, encoding=encoding) as (source, dest): replaced = False @@ -222,14 +222,14 @@ def resolve_variables( values: Iterable[Tuple[str, Optional[str]]], override: bool, ) -> Mapping[str, Optional[str]]: - new_values = {} # type: Dict[str, Optional[str]] + new_values: Dict[str, Optional[str]] = {} for (name, value) in values: if value is None: result = None else: atoms = parse_variables(value) - env = {} # type: Dict[str, Optional[str]] + env: Dict[str, Optional[str]] = {} if override: env.update(os.environ) # type: ignore env.update(new_values) diff --git a/src/dotenv/parser.py b/src/dotenv/parser.py index 398bd49a..735f14a3 100644 --- a/src/dotenv/parser.py +++ b/src/dotenv/parser.py @@ -25,23 +25,16 @@ def make_regex(string: str, extra_flags: int = 0) -> Pattern[str]: _single_quote_escapes = make_regex(r"\\[\\']") -Original = NamedTuple( - "Original", - [ - ("string", str), - ("line", int), - ], -) - -Binding = NamedTuple( - "Binding", - [ - ("key", Optional[str]), - ("value", Optional[str]), - ("original", Original), - ("error", bool), - ], -) +class Original(NamedTuple): + string: str + line: int + + +class Binding(NamedTuple): + key: Optional[str] + value: Optional[str] + original: Original + error: bool class Position: @@ -155,7 +148,7 @@ def parse_binding(reader: Reader) -> Binding: reader.read_regex(_whitespace) if reader.peek(1) == "=": reader.read_regex(_equal_sign) - value = parse_value(reader) # type: Optional[str] + value: Optional[str] = parse_value(reader) else: value = None reader.read_regex(_comment) From c22bc509218c981b098df0e028b0a53fbbbb193b Mon Sep 17 00:00:00 2001 From: Bertrand Bonnefoy-Claudet Date: Sat, 12 Nov 2022 14:06:02 +0100 Subject: [PATCH 07/22] Fix IPython test warning about deprecated `magic` IPython would complain about: DeprecationWarning: `magic(...)` is deprecated since IPython 0.13 (warning added in 8.1), use run_line_magic(magic_name, parameter_s). --- tests/test_ipython.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_ipython.py b/tests/test_ipython.py index 921dfd60..f988bd9c 100644 --- a/tests/test_ipython.py +++ b/tests/test_ipython.py @@ -17,8 +17,8 @@ def test_ipython_existing_variable_no_override(tmp_path): os.environ["a"] = "c" ipshell = InteractiveShellEmbed() - ipshell.magic("load_ext dotenv") - ipshell.magic("dotenv") + ipshell.run_line_magic("load_ext", "dotenv") + ipshell.run_line_magic("dotenv", "") assert os.environ == {"a": "c"} @@ -33,8 +33,8 @@ def test_ipython_existing_variable_override(tmp_path): os.environ["a"] = "c" ipshell = InteractiveShellEmbed() - ipshell.magic("load_ext dotenv") - ipshell.magic("dotenv -o") + ipshell.run_line_magic("load_ext", "dotenv") + ipshell.run_line_magic("dotenv", "-o") assert os.environ == {"a": "b"} @@ -48,7 +48,7 @@ def test_ipython_new_variable(tmp_path): os.chdir(str(tmp_path)) ipshell = InteractiveShellEmbed() - ipshell.magic("load_ext dotenv") - ipshell.magic("dotenv") + ipshell.run_line_magic("load_ext", "dotenv") + ipshell.run_line_magic("dotenv", "") assert os.environ == {"a": "b"} From ceca48c8441f381b1edf4772288326b4be6f435c Mon Sep 17 00:00:00 2001 From: momohakarish Date: Sat, 12 Nov 2022 13:43:38 +0200 Subject: [PATCH 08/22] Use f-strings, make some code more concise and change docstrings to double quotes according to PEP257 --- src/dotenv/__init__.py | 2 +- src/dotenv/cli.py | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/dotenv/__init__.py b/src/dotenv/__init__.py index 9dbd2543..7f4c631b 100644 --- a/src/dotenv/__init__.py +++ b/src/dotenv/__init__.py @@ -32,7 +32,7 @@ def get_cli_string( command.append(key) if value: if ' ' in value: - command.append('"%s"' % value) + command.append(f'"{value}"') else: command.append(value) diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index b845b95e..97d6a948 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -29,11 +29,8 @@ @click.version_option(version=__version__) @click.pass_context def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: - '''This script is used to set, get or unset values from a .env file.''' - ctx.obj = {} - ctx.obj['QUOTE'] = quote - ctx.obj['EXPORT'] = export - ctx.obj['FILE'] = file + """This script is used to set, get or unset values from a .env file.""" + ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file} @cli.command() @@ -43,11 +40,11 @@ def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: help="The format in which to display the list. Default format is simple, " "which displays name=value without quotes.") def list(ctx: click.Context, format: bool) -> None: - '''Display all the stored key/value.''' + """Display all the stored key/value.""" file = ctx.obj['FILE'] if not os.path.isfile(file): raise click.BadParameter( - 'Path "%s" does not exist.' % (file), + f'Path "{file}" does not exist.', ctx=ctx ) dotenv_as_dict = dotenv_values(file) @@ -60,7 +57,7 @@ def list(ctx: click.Context, format: bool) -> None: if v is not None: if format in ('export', 'shell'): v = shlex.quote(v) - click.echo('%s%s=%s' % (prefix, k, v)) + click.echo(f'{prefix}{k}={v}') @cli.command() @@ -68,13 +65,13 @@ def list(ctx: click.Context, format: bool) -> None: @click.argument('key', required=True) @click.argument('value', required=True) def set(ctx: click.Context, key: Any, value: Any) -> None: - '''Store the given key/value.''' + """Store the given key/value.""" file = ctx.obj['FILE'] quote = ctx.obj['QUOTE'] export = ctx.obj['EXPORT'] success, key, value = set_key(file, key, value, quote, export) if success: - click.echo('%s=%s' % (key, value)) + click.echo(f'{key}={value}') else: exit(1) @@ -83,11 +80,11 @@ def set(ctx: click.Context, key: Any, value: Any) -> None: @click.pass_context @click.argument('key', required=True) def get(ctx: click.Context, key: Any) -> None: - '''Retrieve the value for the given key.''' + """Retrieve the value for the given key.""" file = ctx.obj['FILE'] if not os.path.isfile(file): raise click.BadParameter( - 'Path "%s" does not exist.' % (file), + f'Path "{file}" does not exist.', ctx=ctx ) stored_value = get_key(file, key) @@ -101,12 +98,12 @@ def get(ctx: click.Context, key: Any) -> None: @click.pass_context @click.argument('key', required=True) def unset(ctx: click.Context, key: Any) -> None: - '''Removes the given key.''' + """Removes the given key.""" file = ctx.obj['FILE'] quote = ctx.obj['QUOTE'] success, key = unset_key(file, key, quote) if success: - click.echo("Successfully removed %s" % key) + click.echo(f"Successfully removed {key}") else: exit(1) @@ -124,7 +121,7 @@ def run(ctx: click.Context, override: bool, commandline: List[str]) -> None: file = ctx.obj['FILE'] if not os.path.isfile(file): raise click.BadParameter( - 'Invalid value for \'-f\' "%s" does not exist.' % (file), + f'Invalid value for \'-f\' "{file}" does not exist.', ctx=ctx ) dotenv_as_dict = { From 025f762241c9a2435cc33cd534d5d998b9d4c64c Mon Sep 17 00:00:00 2001 From: "Michael V. DePalatis" Date: Wed, 23 Nov 2022 08:41:04 -0700 Subject: [PATCH 09/22] Fix typo --- src/dotenv/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotenv/main.py b/src/dotenv/main.py index e7b8392a..36ed94f5 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -321,7 +321,7 @@ def load_dotenv( from the `.env` file. encoding: Encoding to be used to read the file. Returns: - Bool: True if atleast one environment variable is set else False + Bool: True if at least one environment variable is set else False If both `dotenv_path` and `stream` are `None`, `find_dotenv()` is used to find the .env file. From 7dc2492805f6bfc314b4ba4380d5ee5ad499b6c1 Mon Sep 17 00:00:00 2001 From: Bertrand Bonnefoy-Claudet Date: Sat, 12 Nov 2022 14:02:33 +0100 Subject: [PATCH 10/22] Improve error message for `get` and `list` commands The error message would previously be confusing. For example, `dotenv -f . list` would print: Error: Invalid value: Path "." does not exist. Instead, we now print: Error opening env file: [Errno 21] Is a directory: '.' I used this opportunity to slightly refactor the I/O code (e.g. fewer system calls and possible race conditions) for those two subcommands (`get` and `list`). --- src/dotenv/cli.py | 48 ++++++++++++++++++++++++++++++----------------- tests/test_cli.py | 20 +++++++++++++++++--- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index 97d6a948..b490bfaf 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -2,8 +2,9 @@ import os import shlex import sys +from contextlib import contextmanager from subprocess import Popen -from typing import Any, Dict, List +from typing import Any, Dict, IO, Iterator, List try: import click @@ -12,7 +13,7 @@ 'Run pip install "python-dotenv[cli]" to fix this.') sys.exit(1) -from .main import dotenv_values, get_key, set_key, unset_key +from .main import dotenv_values, set_key, unset_key from .version import __version__ @@ -33,6 +34,22 @@ def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file} +@contextmanager +def stream_file(path: os.PathLike) -> Iterator[IO[str]]: + """ + Open a file and yield the corresponding (decoded) stream. + + Exits with error code 2 if the file cannot be opened. + """ + + try: + with open(path) as stream: + yield stream + except OSError as exc: + print(f"Error opening env file: {exc}", file=sys.stderr) + exit(2) + + @cli.command() @click.pass_context @click.option('--format', default='simple', @@ -42,18 +59,16 @@ def cli(ctx: click.Context, file: Any, quote: Any, export: Any) -> None: def list(ctx: click.Context, format: bool) -> None: """Display all the stored key/value.""" file = ctx.obj['FILE'] - if not os.path.isfile(file): - raise click.BadParameter( - f'Path "{file}" does not exist.', - ctx=ctx - ) - dotenv_as_dict = dotenv_values(file) + + with stream_file(file) as stream: + values = dotenv_values(stream=stream) + if format == 'json': - click.echo(json.dumps(dotenv_as_dict, indent=2, sort_keys=True)) + click.echo(json.dumps(values, indent=2, sort_keys=True)) else: prefix = 'export ' if format == 'export' else '' - for k in sorted(dotenv_as_dict): - v = dotenv_as_dict[k] + for k in sorted(values): + v = values[k] if v is not None: if format in ('export', 'shell'): v = shlex.quote(v) @@ -82,12 +97,11 @@ def set(ctx: click.Context, key: Any, value: Any) -> None: def get(ctx: click.Context, key: Any) -> None: """Retrieve the value for the given key.""" file = ctx.obj['FILE'] - if not os.path.isfile(file): - raise click.BadParameter( - f'Path "{file}" does not exist.', - ctx=ctx - ) - stored_value = get_key(file, key) + + with stream_file(file) as stream: + values = dotenv_values(stream=stream) + + stored_value = values.get(key) if stored_value: click.echo(stored_value) else: diff --git a/tests/test_cli.py b/tests/test_cli.py index ca5ba2a1..02dc9d96 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -40,7 +40,14 @@ def test_list_non_existent_file(cli): result = cli.invoke(dotenv_cli, ['--file', 'nx_file', 'list']) assert result.exit_code == 2, result.output - assert "does not exist" in result.output + assert "Error opening env file" in result.output + + +def test_list_not_a_file(cli): + result = cli.invoke(dotenv_cli, ['--file', '.', 'list']) + + assert result.exit_code == 2, result.output + assert "Error opening env file" in result.output def test_list_no_file(cli): @@ -64,11 +71,18 @@ def test_get_non_existent_value(cli, dotenv_file): assert (result.exit_code, result.output) == (1, "") -def test_get_no_file(cli): +def test_get_non_existent_file(cli): result = cli.invoke(dotenv_cli, ['--file', 'nx_file', 'get', 'a']) assert result.exit_code == 2 - assert "does not exist" in result.output + assert "Error opening env file" in result.output + + +def test_get_not_a_file(cli): + result = cli.invoke(dotenv_cli, ['--file', '.', 'get', 'a']) + + assert result.exit_code == 2 + assert "Error opening env file" in result.output def test_unset_existing_value(cli, dotenv_file): From f75103c655f71015fbc0b90a399133149b17f58c Mon Sep 17 00:00:00 2001 From: Lukas Kahwe Smith Date: Thu, 5 Jan 2023 06:41:54 +0100 Subject: [PATCH 11/22] Updated license format to better Align with BSD OSI template (#433) Co-authored-by: Saurabh Kumar Thanks @lsmith77 --- LICENSE | 64 +-------------------------------------------------------- 1 file changed, 1 insertion(+), 63 deletions(-) diff --git a/LICENSE b/LICENSE index 39372fee..acfe8334 100644 --- a/LICENSE +++ b/LICENSE @@ -1,66 +1,4 @@ -python-dotenv -Copyright (c) 2014, Saurabh Kumar - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of python-dotenv nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -django-dotenv-rw -Copyright (c) 2013, Ted Tieken - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of django-dotenv nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Original django-dotenv -Copyright (c) 2013, Jacob Kaplan-Moss - -All rights reserved. +Copyright (c) 2014, Saurabh Kumar (python-dotenv), 2013, Ted Tieken (django-dotenv-rw), 2013, Jacob Kaplan-Moss (django-dotenv) Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: From 09cf4aba6279848e99874ac552660c7108b6ae50 Mon Sep 17 00:00:00 2001 From: Eddie Aftandilian Date: Tue, 10 Jan 2023 21:37:56 -0800 Subject: [PATCH 12/22] Fix type hint for dotenv_path var, add StrPath alias (#432) * Fix type hint for load_dotenv Fixes #431 * Quote type hints to avoid runtime errors in earlier Python versions * Revise type of dotenv_path parameter Based on PR feedback and typeshed's type hint for the built-in open() function: https://github.com/python/typeshed/blob/e2d67bf7034f68c07bd35150247e58e0817725d9/stdlib/builtins.pyi#L1421 * Allow only string paths, not byte paths These paths can flow into `shutil.move`, which does not accept byte paths or (int) file descriptors. See https://github.com/python/typeshed/pull/6832 * Create a type alias for the paths this library accepts And use it consistently in main.py. --- src/dotenv/main.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/dotenv/main.py b/src/dotenv/main.py index 36ed94f5..f40c20ea 100644 --- a/src/dotenv/main.py +++ b/src/dotenv/main.py @@ -12,6 +12,12 @@ from .parser import Binding, parse_stream from .variables import parse_variables +# A type alias for a string path to be used for the paths in this file. +# These paths may flow to `open()` and `shutil.move()`; `shutil.move()` +# only accepts string paths, not byte paths or file descriptors. See +# https://github.com/python/typeshed/pull/6832. +StrPath = Union[str, 'os.PathLike[str]'] + logger = logging.getLogger(__name__) @@ -28,14 +34,14 @@ def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding class DotEnv: def __init__( self, - dotenv_path: Optional[Union[str, os.PathLike]], + dotenv_path: Optional[StrPath], stream: Optional[IO[str]] = None, verbose: bool = False, encoding: Optional[str] = None, interpolate: bool = True, override: bool = True, ) -> None: - self.dotenv_path: Optional[Union[str, os.PathLike]] = dotenv_path + self.dotenv_path: Optional[StrPath] = dotenv_path self.stream: Optional[IO[str]] = stream self._dict: Optional[Dict[str, Optional[str]]] = None self.verbose: bool = verbose @@ -108,7 +114,7 @@ def get(self, key: str) -> Optional[str]: def get_key( - dotenv_path: Union[str, os.PathLike], + dotenv_path: StrPath, key_to_get: str, encoding: Optional[str] = "utf-8", ) -> Optional[str]: @@ -122,7 +128,7 @@ def get_key( @contextmanager def rewrite( - path: Union[str, os.PathLike], + path: StrPath, encoding: Optional[str], ) -> Iterator[Tuple[IO[str], IO[str]]]: if not os.path.isfile(path): @@ -139,7 +145,7 @@ def rewrite( def set_key( - dotenv_path: Union[str, os.PathLike], + dotenv_path: StrPath, key_to_set: str, value_to_set: str, quote_mode: str = "always", @@ -188,7 +194,7 @@ def set_key( def unset_key( - dotenv_path: Union[str, os.PathLike], + dotenv_path: StrPath, key_to_unset: str, quote_mode: str = "always", encoding: Optional[str] = "utf-8", @@ -303,7 +309,7 @@ def _is_interactive(): def load_dotenv( - dotenv_path: Union[str, os.PathLike, None] = None, + dotenv_path: Optional[StrPath] = None, stream: Optional[IO[str]] = None, verbose: bool = False, override: bool = False, @@ -341,7 +347,7 @@ def load_dotenv( def dotenv_values( - dotenv_path: Union[str, os.PathLike, None] = None, + dotenv_path: Optional[StrPath] = None, stream: Optional[IO[str]] = None, verbose: bool = False, interpolate: bool = True, From a18ea18dce89d5f1941710e461c2e72055f590d4 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Sat, 21 Jan 2023 15:31:57 +0530 Subject: [PATCH 13/22] Fix coverage reporting (#445) --- .github/workflows/test.yml | 5 +++++ tox.ini | 24 +++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 15875768..758fbb46 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,21 +5,26 @@ on: [push, pull_request] jobs: test: runs-on: ${{ matrix.os }} + strategy: max-parallel: 8 matrix: os: - ubuntu-latest python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", pypy3.9] + steps: - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Install dependencies run: python -m pip install --upgrade pip pip install tox tox-gh-actions + - name: Test with tox run: tox diff --git a/tox.ini b/tox.ini index cb8a6625..9cca82a4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,23 +1,26 @@ [tox] -envlist = lint,py{35,36,37,38,39,310,311},pypy3,manifest,coverage-report +envlist = lint,py{37,38,39,310,311},pypy3,manifest,coverage-report [gh-actions] python = - 3.7: py37, coverage-report - 3.8: py38, coverage-report - 3.9: py39, coverage-report - 3.10: py310, lint, manifest, coverage-report - 3.11: py311, coverage-report - pypy-3.9: pypy3, coverage-report + 3.7: py37 + 3.8: py38 + 3.9: py39 + 3.10: py310 + 3.11: py311, lint, manifest + pypy-3.9: pypy3 [testenv] deps = pytest - coverage + pytest-cov sh click - py{37,38,39,310,311,py3}: ipython -commands = coverage run --parallel -m pytest {posargs} + py{37,38,39,310,311,pypy3}: ipython +commands = pytest --cov --cov-report=term-missing --cov-config setup.cfg {posargs} +depends = + py{37,38,39,310,311},pypy3: coverage-clean + coverage-report: py{35,36,37,38,39,310,311},pypy3 [testenv:lint] skip_install = true @@ -46,5 +49,4 @@ commands = coverage erase deps = coverage skip_install = true commands = - coverage combine coverage report From b84bccda57fc973e7b8367380194bfae558a3d4b Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Sat, 21 Jan 2023 15:45:30 +0530 Subject: [PATCH 14/22] Add changelog for 0.21.1 --- CHANGELOG.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e53060a5..29e38757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.21.1] - 2022-09-03 + +### Added +* Use Python 3.11 non-beta in CI (#438 by @bbc2) +* Modernize variables code (#434 by @Nougat-Waffle) +* Modernize main.py and parser.py code (#435 by @Nougat-Waffle) +* Improve conciseness of cli.py and __init__.py (#439 by @Nougat-Waffle) +* Improve error message for `get` and `list` commands when env file can't be opened (#441 by @bbc2) +* Updated Licence to align with BSD OSI template (#433 by @lsmith77) + + +### Fixed +* Fix Out-of-scope error when "dest" variable is undefined (#413 by @theGOTOguy) +* Fix IPython test warning about deprecated `magic` (#440 by @bbc2) +* Fix type hint for dotenv_path var, add StrPath alias (#432 by @eaf) + ## [0.21.0] - 2022-09-03 ### Added @@ -16,9 +33,9 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * Drop Python 3.5 and 3.6 and upgrade GA (#393 by @eggplants) * Use `open` instead of `io.open`. (#389 by @rabinadk1) * Improve documentation for variables without a value (#390 by @bbc2) -* Add `parse_it` to Related Projects by (#410 by @naorlivne) -* Update README.md by (#415 by @harveer07) -* Improve documentation with direct use of MkDocs by (#398 by @bbc2) +* Add `parse_it` to Related Projects (#410 by @naorlivne) +* Update README.md (#415 by @harveer07) +* Improve documentation with direct use of MkDocs (#398 by @bbc2) ## [0.20.0] - 2022-03-24 From 5317a560e0943a7311bbc5bfbd9e4b4c13bc2bb6 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Sat, 21 Jan 2023 15:50:46 +0530 Subject: [PATCH 15/22] =?UTF-8?q?Bump=20version:=200.21.0=20=E2=86=92=200.?= =?UTF-8?q?21.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- src/dotenv/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 4d49c291..89b5d30e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.21.0 +current_version = 0.21.1 commit = True tag = True diff --git a/src/dotenv/version.py b/src/dotenv/version.py index 6a726d85..76f24586 100644 --- a/src/dotenv/version.py +++ b/src/dotenv/version.py @@ -1 +1 @@ -__version__ = "0.21.0" +__version__ = "0.21.1" From 291fa669804e0ceaef94a35e7cd6cc90b6189cd5 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Sat, 21 Jan 2023 16:31:07 +0530 Subject: [PATCH 16/22] Update documtation --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++----------------- LICENSE | 18 +++++++++------- docs/license.md | 1 + docs/reference.md | 3 +-- mkdocs.yml | 8 +++++++ 5 files changed, 55 insertions(+), 29 deletions(-) create mode 120000 docs/license.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 29e38757..5845146c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,37 +5,48 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +* + +### Fixed + +* + + ## [0.21.1] - 2022-09-03 ### Added -* Use Python 3.11 non-beta in CI (#438 by @bbc2) -* Modernize variables code (#434 by @Nougat-Waffle) -* Modernize main.py and parser.py code (#435 by @Nougat-Waffle) -* Improve conciseness of cli.py and __init__.py (#439 by @Nougat-Waffle) -* Improve error message for `get` and `list` commands when env file can't be opened (#441 by @bbc2) -* Updated Licence to align with BSD OSI template (#433 by @lsmith77) +* Use Python 3.11 non-beta in CI (#438 by [@bbc2]) +* Modernize variables code (#434 by [@Nougat-Waffle]) +* Modernize main.py and parser.py code (#435 by [@Nougat-Waffle]) +* Improve conciseness of cli.py and __init__.py (#439 by [@Nougat-Waffle]) +* Improve error message for `get` and `list` commands when env file can't be opened (#441 by [@bbc2]) +* Updated License to align with BSD OSI template (#433 by [@lsmith77]) ### Fixed -* Fix Out-of-scope error when "dest" variable is undefined (#413 by @theGOTOguy) -* Fix IPython test warning about deprecated `magic` (#440 by @bbc2) -* Fix type hint for dotenv_path var, add StrPath alias (#432 by @eaf) +* Fix Out-of-scope error when "dest" variable is undefined (#413 by [@theGOTOguy]) +* Fix IPython test warning about deprecated `magic` (#440 by [@bbc2]) +* Fix type hint for dotenv_path var, add StrPath alias (#432 by [@eaf]) ## [0.21.0] - 2022-09-03 ### Added -* CLI: add support for invocations via 'python -m'. (#395 by @theskumar) -* `load_dotenv` function now returns `False`. (#388 by @larsks) -* CLI: add --format= option to list command. (#407 by @sammck) +* CLI: add support for invocations via 'python -m'. (#395 by [@theskumar]) +* `load_dotenv` function now returns `False`. (#388 by [@larsks]) +* CLI: add --format= option to list command. (#407 by [@sammck]) ### Fixed -* Drop Python 3.5 and 3.6 and upgrade GA (#393 by @eggplants) -* Use `open` instead of `io.open`. (#389 by @rabinadk1) -* Improve documentation for variables without a value (#390 by @bbc2) -* Add `parse_it` to Related Projects (#410 by @naorlivne) -* Update README.md (#415 by @harveer07) -* Improve documentation with direct use of MkDocs (#398 by @bbc2) +* Drop Python 3.5 and 3.6 and upgrade GA (#393 by [@eggplants]) +* Use `open` instead of `io.open`. (#389 by [@rabinadk1]) +* Improve documentation for variables without a value (#390 by [@bbc2]) +* Add `parse_it` to Related Projects (#410 by [@naorlivne]) +* Update README.md (#415 by [@harveer07]) +* Improve documentation with direct use of MkDocs (#398 by [@bbc2]) ## [0.20.0] - 2022-03-24 @@ -326,6 +337,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). [@bbc2]: https://github.com/bbc2 [@befeleme]: https://github.com/befeleme [@cjauvin]: https://github.com/cjauvin +[@eaf]: https://github.com/eaf [@earlbread]: https://github.com/earlbread [@eggplants]: https://github.com/@eggplants [@ekohl]: https://github.com/ekohl @@ -337,13 +349,16 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). [@harveer07]: https://github.com/@harveer07 [@jadutter]: https://github.com/jadutter [@larsks]: https://github.com/@larsks +[@lsmith77]: https://github.com/lsmith77 [@mgorny]: https://github.com/mgorny [@naorlivne]: https://github.com/@naorlivne +[@Nougat-Waffle]: https://github.com/Nougat-Waffle [@qnighy]: https://github.com/qnighy [@rabinadk1]: https://github.com/@rabinadk1 [@sammck]: https://github.com/@sammck [@snobu]: https://github.com/snobu [@techalchemy]: https://github.com/techalchemy +[@theGOTOguy]: https://github.com/theGOTOguy [@theskumar]: https://github.com/theskumar [@ulyssessouza]: https://github.com/ulyssessouza [@venthur]: https://github.com/venthur @@ -352,7 +367,8 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). [@zueve]: https://github.com/zueve -[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v0.21.0...HEAD +[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v0.21.1...HEAD +[0.21.1]: https://github.com/theskumar/python-dotenv/compare/v0.21.0...v0.21.1 [0.21.0]: https://github.com/theskumar/python-dotenv/compare/v0.20.0...v0.21.0 [0.20.0]: https://github.com/theskumar/python-dotenv/compare/v0.19.2...v0.20.0 [0.19.2]: https://github.com/theskumar/python-dotenv/compare/v0.19.1...v0.19.2 diff --git a/LICENSE b/LICENSE index acfe8334..3a971190 100644 --- a/LICENSE +++ b/LICENSE @@ -3,14 +3,16 @@ Copyright (c) 2014, Saurabh Kumar (python-dotenv), 2013, Ted Tieken (django-dote Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of django-dotenv nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +- Neither the name of django-dotenv nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT diff --git a/docs/license.md b/docs/license.md new file mode 120000 index 00000000..ea5b6064 --- /dev/null +++ b/docs/license.md @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/docs/reference.md b/docs/reference.md index a126448e..8a3762ad 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -1,3 +1,2 @@ -# Reference +# ::: dotenv -::: dotenv diff --git a/mkdocs.yml b/mkdocs.yml index 27063ca2..65acc0fb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -5,8 +5,15 @@ theme: name: material palette: primary: green + features: + - toc.follow + - navigation.sections + markdown_extensions: - mdx_truly_sane_lists + - toc: + toc_depth: 2 + plugins: - mkdocstrings: handlers: @@ -21,3 +28,4 @@ nav: - Changelog: changelog.md - Contributing: contributing.md - Reference: reference.md + - License: license.md From b904a722c8aea0a41948ea50d0c549ccece97729 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Sat, 21 Jan 2023 19:17:24 +0530 Subject: [PATCH 17/22] update docs - better toc for changelog --- CHANGELOG.md | 66 ++++++++++++++++++++++++++++------------------------ mkdocs.yml | 2 -- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5845146c..d07533b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,19 +7,20 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -### Added +**Added** * -### Fixed +**Fixed** * -## [0.21.1] - 2022-09-03 +## [0.21.1] - 2022-01-21 + +**Added** -### Added * Use Python 3.11 non-beta in CI (#438 by [@bbc2]) * Modernize variables code (#434 by [@Nougat-Waffle]) * Modernize main.py and parser.py code (#435 by [@Nougat-Waffle]) @@ -28,19 +29,22 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * Updated License to align with BSD OSI template (#433 by [@lsmith77]) -### Fixed +**Fixed** + * Fix Out-of-scope error when "dest" variable is undefined (#413 by [@theGOTOguy]) * Fix IPython test warning about deprecated `magic` (#440 by [@bbc2]) * Fix type hint for dotenv_path var, add StrPath alias (#432 by [@eaf]) ## [0.21.0] - 2022-09-03 -### Added +**Added** + * CLI: add support for invocations via 'python -m'. (#395 by [@theskumar]) * `load_dotenv` function now returns `False`. (#388 by [@larsks]) * CLI: add --format= option to list command. (#407 by [@sammck]) -### Fixed +**Fixed** + * Drop Python 3.5 and 3.6 and upgrade GA (#393 by [@eggplants]) * Use `open` instead of `io.open`. (#389 by [@rabinadk1]) * Improve documentation for variables without a value (#390 by [@bbc2]) @@ -50,12 +54,12 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.20.0] - 2022-03-24 -### Added +**Added** - Add `encoding` (`Optional[str]`) parameter to `get_key`, `set_key` and `unset_key`. (#379 by [@bbc2]) -### Fixed +**Fixed** - Use dict to specify the `entry_points` parameter of `setuptools.setup` (#376 by [@mgorny]). @@ -63,25 +67,25 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.19.2] - 2021-11-11 -### Fixed +**Fixed** - In `set_key`, add missing newline character before new entry if necessary. (#361 by [@bbc2]) ## [0.19.1] - 2021-08-09 -### Added +**Added** - Add support for Python 3.10. (#359 by [@theskumar]) ## [0.19.0] - 2021-07-24 -### Changed +**Changed** - Require Python 3.5 or a later version. Python 2 and 3.4 are no longer supported. (#341 by [@bbc2]). -### Added +**Added** - The `dotenv_path` argument of `set_key` and `unset_key` now has a type of `Union[str, os.PathLike]` instead of just `os.PathLike` (#347 by [@bbc2]). @@ -91,7 +95,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.18.0] - 2021-06-20 -### Changed +**Changed** - Raise `ValueError` if `quote_mode` isn't one of `always`, `auto` or `never` in `set_key` (#330 by [@bbc2]). @@ -104,23 +108,23 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.17.1] - 2021-04-29 -### Fixed +**Fixed** - Fixed tests for build environments relying on `PYTHONPATH` (#318 by [@befeleme]). ## [0.17.0] - 2021-04-02 -### Changed +**Changed** - Make `dotenv get ` only show the value, not `key=value` (#313 by [@bbc2]). -### Added +**Added** - Add `--override`/`--no-override` option to `dotenv run` (#312 by [@zueve] and [@bbc2]). ## [0.16.0] - 2021-03-27 -### Changed +**Changed** - The default value of the `encoding` parameter for `load_dotenv` and `dotenv_values` is now `"utf-8"` instead of `None` (#306 by [@bbc2]). @@ -128,17 +132,17 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.15.0] - 2020-10-28 -### Added +**Added** - Add `--export` option to `set` to make it prepend the binding with `export` (#270 by [@jadutter]). -### Changed +**Changed** - Make `set` command create the `.env` file in the current directory if no `.env` file was found (#270 by [@jadutter]). -### Fixed +**Fixed** - Fix potentially empty expanded value for duplicate key (#260 by [@bbc2]). - Fix import error on Python 3.5.0 and 3.5.1 (#267 by [@gongqingkui]). @@ -147,30 +151,30 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.14.0] - 2020-07-03 -### Changed +**Changed** - Privilege definition in file over the environment in variable expansion (#256 by [@elbehery95]). -### Fixed +**Fixed** - Improve error message for when file isn't found (#245 by [@snobu]). - Use HTTPS URL in package meta data (#251 by [@ekohl]). ## [0.13.0] - 2020-04-16 -### Added +**Added** - Add support for a Bash-like default value in variable expansion (#248 by [@bbc2]). ## [0.12.0] - 2020-02-28 -### Changed +**Changed** - Use current working directory to find `.env` when bundled by PyInstaller (#213 by [@gergelyk]). -### Fixed +**Fixed** - Fix escaping of quoted values written by `set_key` (#236 by [@bbc2]). - Fix `dotenv run` crashing on environment variables without values (#237 by [@yannham]). @@ -178,23 +182,23 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.11.0] - 2020-02-07 -### Added +**Added** - Add `interpolate` argument to `load_dotenv` and `dotenv_values` to disable interpolation (#232 by [@ulyssessouza]). -### Changed +**Changed** - Use logging instead of warnings (#231 by [@bbc2]). -### Fixed +**Fixed** - Fix installation in non-UTF-8 environments (#225 by [@altendky]). - Fix PyPI classifiers (#228 by [@bbc2]). ## [0.10.5] - 2020-01-19 -### Fixed +**Fixed** - Fix handling of malformed lines and lines without a value (#222 by [@bbc2]): - Don't print warning when key has no value. @@ -203,7 +207,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.10.4] - 2020-01-17 -### Added +**Added** - Make typing optional (#179 by [@techalchemy]). - Print a warning on malformed line (#211 by [@bbc2]). diff --git a/mkdocs.yml b/mkdocs.yml index 65acc0fb..331965df 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -11,8 +11,6 @@ theme: markdown_extensions: - mdx_truly_sane_lists - - toc: - toc_depth: 2 plugins: - mkdocstrings: From fc19a55de9c8774880b73f19148704f311f517b2 Mon Sep 17 00:00:00 2001 From: jctanner Date: Thu, 23 Feb 2023 01:21:29 -0500 Subject: [PATCH 18/22] Handle situations where the cwd does not exist. (#446) This is seen in some situations with dynaconf where the system under test starts from a src code directory that got moved around or deleted during operation. Signed-off-by: James Tanner --- src/dotenv/cli.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/dotenv/cli.py b/src/dotenv/cli.py index b490bfaf..65ead461 100644 --- a/src/dotenv/cli.py +++ b/src/dotenv/cli.py @@ -17,8 +17,22 @@ from .version import __version__ +def enumerate_env(): + """ + Return a path for the ${pwd}/.env file. + + If pwd does not exist, return None. + """ + try: + cwd = os.getcwd() + except FileNotFoundError: + return None + path = os.path.join(cwd, '.env') + return path + + @click.group() -@click.option('-f', '--file', default=os.path.join(os.getcwd(), '.env'), +@click.option('-f', '--file', default=enumerate_env(), type=click.Path(file_okay=True), help="Location of the .env file, defaults to .env file in current working directory.") @click.option('-q', '--quote', default='always', From 87e5527fcff35b7118818ce8c35c4fd90fe80844 Mon Sep 17 00:00:00 2001 From: "Kenneth C. Arnold" Date: Fri, 24 Feb 2023 01:23:26 -0500 Subject: [PATCH 19/22] Update readme, add python-decouple as a related project (#451) I usually use dotenv, but I learned about this alternative from libhunt. It seems to have similar goals. (I'll probably stick with dotenv.) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 983b7d15..89fc6434 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,7 @@ defined in the following list: - [environs](https://github.com/sloria/environs) - [dynaconf](https://github.com/rochacbruno/dynaconf) - [parse_it](https://github.com/naorlivne/parse_it) +- [python-decouple](https://github.com/HBNetwork/python-decouple) ## Acknowledgements From 06116434822a4d7dd5c4213ff0a54dc6e41a9be6 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Fri, 24 Feb 2023 11:54:32 +0530 Subject: [PATCH 20/22] Drop support for python 3.7, add python 3.12-dev (#449) * fixes for sh 2.* * Drop support for python 3.7 * Add python 3.12 alpha --- .github/workflows/test.yml | 2 +- requirements.txt | 2 +- setup.py | 4 +-- tests/test_cli.py | 66 +++++++++++++++++++------------------- tox.ini | 14 ++++---- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 758fbb46..49e1399f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: matrix: os: - ubuntu-latest - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", pypy3.9] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12-dev", pypy3.9] steps: - uses: actions/checkout@v3 diff --git a/requirements.txt b/requirements.txt index 0206316f..af7e1bc4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ flake8>=2.2.3 ipython pytest-cov pytest>=3.9 -sh>=1.09 +sh>=2 tox twine wheel diff --git a/setup.py b/setup.py index bcf1b0dc..8ceddf92 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ def read_files(files): package_data={ 'dotenv': ['py.typed'], }, - python_requires=">=3.7", + python_requires=">=3.8", extras_require={ 'cli': ['click>=5.0', ], }, @@ -45,11 +45,11 @@ def read_files(files): 'Development Status :: 5 - Production/Stable', 'Programming Language :: Python', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: PyPy', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', diff --git a/tests/test_cli.py b/tests/test_cli.py index 02dc9d96..8afbe59d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -153,61 +153,61 @@ def test_set_no_file(cli): def test_get_default_path(tmp_path): - sh.cd(str(tmp_path)) - with open(str(tmp_path / ".env"), "w") as f: - f.write("a=b") + with sh.pushd(str(tmp_path)): + with open(str(tmp_path / ".env"), "w") as f: + f.write("a=b") - result = sh.dotenv("get", "a") + result = sh.dotenv("get", "a") - assert result == "b\n" + assert result == "b\n" def test_run(tmp_path): - sh.cd(str(tmp_path)) - dotenv_file = str(tmp_path / ".env") - with open(dotenv_file, "w") as f: - f.write("a=b") + with sh.pushd(str(tmp_path)): + dotenv_file = str(tmp_path / ".env") + with open(dotenv_file, "w") as f: + f.write("a=b") - result = sh.dotenv("run", "printenv", "a") + result = sh.dotenv("run", "printenv", "a") - assert result == "b\n" + assert result == "b\n" def test_run_with_existing_variable(tmp_path): - sh.cd(str(tmp_path)) - dotenv_file = str(tmp_path / ".env") - with open(dotenv_file, "w") as f: - f.write("a=b") - env = dict(os.environ) - env.update({"LANG": "en_US.UTF-8", "a": "c"}) + with sh.pushd(str(tmp_path)): + dotenv_file = str(tmp_path / ".env") + with open(dotenv_file, "w") as f: + f.write("a=b") + env = dict(os.environ) + env.update({"LANG": "en_US.UTF-8", "a": "c"}) - result = sh.dotenv("run", "printenv", "a", _env=env) + result = sh.dotenv("run", "printenv", "a", _env=env) - assert result == "b\n" + assert result == "b\n" def test_run_with_existing_variable_not_overridden(tmp_path): - sh.cd(str(tmp_path)) - dotenv_file = str(tmp_path / ".env") - with open(dotenv_file, "w") as f: - f.write("a=b") - env = dict(os.environ) - env.update({"LANG": "en_US.UTF-8", "a": "c"}) + with sh.pushd(str(tmp_path)): + dotenv_file = str(tmp_path / ".env") + with open(dotenv_file, "w") as f: + f.write("a=b") + env = dict(os.environ) + env.update({"LANG": "en_US.UTF-8", "a": "c"}) - result = sh.dotenv("run", "--no-override", "printenv", "a", _env=env) + result = sh.dotenv("run", "--no-override", "printenv", "a", _env=env) - assert result == "c\n" + assert result == "c\n" def test_run_with_none_value(tmp_path): - sh.cd(str(tmp_path)) - dotenv_file = str(tmp_path / ".env") - with open(dotenv_file, "w") as f: - f.write("a=b\nc") + with sh.pushd(str(tmp_path)): + dotenv_file = str(tmp_path / ".env") + with open(dotenv_file, "w") as f: + f.write("a=b\nc") - result = sh.dotenv("run", "printenv", "a") + result = sh.dotenv("run", "printenv", "a") - assert result == "b\n" + assert result == "b\n" def test_run_with_other_env(dotenv_file): diff --git a/tox.ini b/tox.ini index 9cca82a4..fad86f73 100644 --- a/tox.ini +++ b/tox.ini @@ -1,26 +1,26 @@ [tox] -envlist = lint,py{37,38,39,310,311},pypy3,manifest,coverage-report +envlist = lint,py{38,39,310,311,312-dev},pypy3,manifest,coverage-report [gh-actions] python = - 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311, lint, manifest + 3.12-dev: py312-dev pypy-3.9: pypy3 [testenv] deps = pytest pytest-cov - sh + sh >= 2.0.2, <3 click - py{37,38,39,310,311,pypy3}: ipython + py{38,39,310,311,py312-dev,pypy3}: ipython commands = pytest --cov --cov-report=term-missing --cov-config setup.cfg {posargs} depends = - py{37,38,39,310,311},pypy3: coverage-clean - coverage-report: py{35,36,37,38,39,310,311},pypy3 + py{38,39,310,311,312-dev},pypy3: coverage-clean + coverage-report: py{38,39,310,311,312-dev},pypy3 [testenv:lint] skip_install = true @@ -29,11 +29,11 @@ deps = mypy commands = flake8 src tests + mypy --python-version=3.12 src tests mypy --python-version=3.11 src tests mypy --python-version=3.10 src tests mypy --python-version=3.9 src tests mypy --python-version=3.8 src tests - mypy --python-version=3.7 src tests [testenv:manifest] deps = check-manifest From 6c8ddd23752b616aee384693381633187ee07107 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Fri, 24 Feb 2023 12:13:58 +0530 Subject: [PATCH 21/22] Prepare for release 1.0.0 --- CHANGELOG.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d07533b6..2f78c0d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,17 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - -**Added** - -* +## [1.0.0] **Fixed** -* - - +* Drop support for python 3.7, add python 3.12-dev (#449 by [@theskumar]) +* Handle situations where the cwd does not exist. (#446 by [@jctanner]) ## [0.21.1] - 2022-01-21 @@ -352,6 +347,7 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). [@greyli]: https://github.com/greyli [@harveer07]: https://github.com/@harveer07 [@jadutter]: https://github.com/jadutter +[@jctanner]: https://github.com/jctanner [@larsks]: https://github.com/@larsks [@lsmith77]: https://github.com/lsmith77 [@mgorny]: https://github.com/mgorny @@ -371,7 +367,8 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). [@zueve]: https://github.com/zueve -[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v0.21.1...HEAD +[Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/theskumar/python-dotenv/compare/v0.21.0...v1.0.0 [0.21.1]: https://github.com/theskumar/python-dotenv/compare/v0.21.0...v0.21.1 [0.21.0]: https://github.com/theskumar/python-dotenv/compare/v0.20.0...v0.21.0 [0.20.0]: https://github.com/theskumar/python-dotenv/compare/v0.19.2...v0.20.0 From d0684d1c092fb6a9a208a09d43f02e4876ee8196 Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Date: Fri, 24 Feb 2023 12:14:03 +0530 Subject: [PATCH 22/22] =?UTF-8?q?Bump=20version:=200.21.1=20=E2=86=92=201.?= =?UTF-8?q?0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- src/dotenv/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 89b5d30e..3fefd1f0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.21.1 +current_version = 1.0.0 commit = True tag = True diff --git a/src/dotenv/version.py b/src/dotenv/version.py index 76f24586..5becc17c 100644 --- a/src/dotenv/version.py +++ b/src/dotenv/version.py @@ -1 +1 @@ -__version__ = "0.21.1" +__version__ = "1.0.0"