Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support multiple dotenv files #115

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [unreleased]

* support multiple .env files via `-e` or `--dotenv` parameters
* dropped support for Python 3.8

## [3.3.1] -- 2024-07-13
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ the current directory, puts the contents in the environment by either changing
existing- or adding new environment variables, and executes the given command.

`dotenv` supports alternative `.env` files like `.env.development` via the `-e`
or `--dotenv` parameters.
or `--dotenv` parameters. This parameter can be repeated to load multiple
files, the .env files will be loaded in the order they are provided.

With the `--replace` flag, `dotenv` also provides an option to completely
replace the environment variables with the ones from the `.env` file, allowing
Expand Down
3 changes: 2 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ the current directory, puts the contents in the environment by either changing
existing- or adding new environment variables, and executes the given command.

`dotenv` supports alternative `.env` files like `.env.development` via the `-e`
or `--dotenv` parameters.
or `--dotenv` parameters. This parameter can be repeated to load multiple
files, the .env files will be loaded in the order they are provided.

With the `--replace` flag, `dotenv` also provides an option to completely
replace the environment variables with the ones from the `.env` file, allowing
Expand Down
11 changes: 9 additions & 2 deletions dotenv_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ def parse_args(args: list[str] | None = None) -> argparse.Namespace:
parser.add_argument(
"-e",
"--dotenv",
help="alternative .env file",
default=".env",
help=(
"alternative .env file; this parameter can be provided multiple "
"times and the .env files will be evaluated in order"
),
action="append",
default=[".env"],
)

parser.add_argument(
Expand Down Expand Up @@ -78,6 +82,9 @@ def main() -> NoReturn | int:

"""
args = parse_args()
# if alternative .env file is given, remove the default one
if len(args.dotenv) > 1:
args.dotenv = args.dotenv[1:]
if not args.command:
return 0

Expand Down
12 changes: 7 additions & 5 deletions dotenv_cli/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def read_dotenv(filename: str) -> dict[str, str]:


def run_dotenv(
filename: str, command: list[str], replace: bool = False
filenames: list[str], command: list[str], replace: bool = False
) -> NoReturn | int:
"""Run dotenv.

Expand All @@ -83,8 +83,8 @@ def run_dotenv(

Parameters
----------
filename
path to the .env file
filenames
paths to the .env files
command
command to execute
replace_env
Expand All @@ -97,8 +97,10 @@ def run_dotenv(
function does not return normally.

"""
# read dotenv
dotenv = read_dotenv(filename)
# read dotenv files
dotenv = {}
for filename in filenames:
dotenv.update(read_dotenv(filename))

if replace:
# replace env
Expand Down
33 changes: 33 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,39 @@ def test_alternative_dotenv() -> None:
assert b"foo=bar" in proc.stdout


def test_multiple_dotenv() -> None:
"""Test multiple dotenv files."""
with tempfile.NamedTemporaryFile("w", delete=False) as f:
f.write("foo=foo")

with tempfile.NamedTemporaryFile("w", delete=False) as b:
b.write("bar=bar")

proc = run(["dotenv", "-e", f.name, "-e", b.name, "env"], stdout=PIPE)
assert b"foo=foo" in proc.stdout
assert b"bar=bar" in proc.stdout


def test_multiple_dotenv_order() -> None:
"""Test multiple dotenv files are processed in correct order."""
with tempfile.NamedTemporaryFile("w", delete=False) as f1:
f1.write("foo=1")

with tempfile.NamedTemporaryFile("w", delete=False) as f2:
f2.write("foo=2")

proc = run(["dotenv", "-e", f1.name, "-e", f2.name, "env"], stdout=PIPE)
assert b"foo=2" in proc.stdout
assert b"foo=1" not in proc.stdout

proc = run(
["dotenv", "-e", f1.name, "-e", f2.name, "-e", f1.name, "env"],
stdout=PIPE
)
assert b"foo=1" in proc.stdout
assert b"foo=2" not in proc.stdout


def test_nonexisting_dotenv() -> None:
"""Test non-existing dotenv file."""
proc = run(["dotenv", "-e", "/tmp/i.dont.exist", "true"], stderr=PIPE)
Expand Down
Loading