Skip to content

Commit 8b767d5

Browse files
authored
Merge branch 'main' into lo/support-fifo-files
2 parents 8c74db5 + 09d7cee commit 8b767d5

File tree

12 files changed

+234
-101
lines changed

12 files changed

+234
-101
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
contents: write
1717

1818
steps:
19-
- uses: actions/checkout@v5
19+
- uses: actions/checkout@v6
2020

2121
- name: Set up Python
2222
uses: actions/setup-python@v6

.github/workflows/test.yml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
name: Run Tests
22

3-
on: [push, pull_request]
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
412

513
jobs:
614
test:
715
runs-on: ${{ matrix.os }}
16+
timeout-minutes: 15
817

918
strategy:
1019
fail-fast: false
@@ -13,10 +22,16 @@ jobs:
1322
os:
1423
- ubuntu-latest
1524
python-version:
16-
["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", pypy3.9, pypy3.10]
25+
["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", pypy3.11]
26+
include:
27+
# Windows: Test lowest and highest supported Python versions
28+
- os: windows-latest
29+
python-version: "3.10"
30+
- os: windows-latest
31+
python-version: "3.14"
1732

1833
steps:
19-
- uses: actions/checkout@v5
34+
- uses: actions/checkout@v6
2035

2136
- name: Set up Python ${{ matrix.python-version }}
2237
uses: actions/setup-python@v6

README.md

Lines changed: 71 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
[![Build Status][build_status_badge]][build_status_link]
44
[![PyPI version][pypi_badge]][pypi_link]
55

6-
python-dotenv reads key-value pairs from a `.env` file and can set them as environment
7-
variables. It helps in the development of applications following the
6+
python-dotenv reads key-value pairs from a `.env` file and can set them as
7+
environment variables. It helps in the development of applications following the
88
[12-factor](https://12factor.net/) principles.
99

1010
- [Getting Started](#getting-started)
1111
- [Other Use Cases](#other-use-cases)
12-
* [Load configuration without altering the environment](#load-configuration-without-altering-the-environment)
13-
* [Parse configuration as a stream](#parse-configuration-as-a-stream)
14-
* [Load .env files in IPython](#load-env-files-in-ipython)
12+
- [Load configuration without altering the environment](#load-configuration-without-altering-the-environment)
13+
- [Parse configuration as a stream](#parse-configuration-as-a-stream)
14+
- [Load .env files in IPython](#load-env-files-in-ipython)
1515
- [Command-line Interface](#command-line-interface)
1616
- [File format](#file-format)
17-
* [Multiline values](#multiline-values)
18-
* [Variable expansion](#variable-expansion)
17+
- [Multiline values](#multiline-values)
18+
- [Variable expansion](#variable-expansion)
1919
- [Related Projects](#related-projects)
2020
- [Acknowledgements](#acknowledgements)
2121

@@ -25,13 +25,13 @@ variables. It helps in the development of applications following the
2525
pip install python-dotenv
2626
```
2727

28-
If your application takes its configuration from environment variables, like a 12-factor
29-
application, launching it in development is not very practical because you have to set
30-
those environment variables yourself.
28+
If your application takes its configuration from environment variables, like a
29+
12-factor application, launching it in development is not very practical because
30+
you have to set those environment variables yourself.
3131

32-
To help you with that, you can add python-dotenv to your application to make it load the
33-
configuration from a `.env` file when it is present (e.g. in development) while remaining
34-
configurable via the environment:
32+
To help you with that, you can add python-dotenv to your application to make it
33+
load the configuration from a `.env` file when it is present (e.g. in
34+
development) while remaining configurable via the environment:
3535

3636
```python
3737
from dotenv import load_dotenv
@@ -46,18 +46,19 @@ By default, `load_dotenv()` will:
4646

4747
- Look for a `.env` file in the same directory as the Python script (or higher up the directory tree).
4848
- Read each key-value pair and add it to `os.environ`.
49-
- **Not override** an environment variable that is already set, unless you explicitly pass `override=True`.
49+
- **Not override** existing environment variables (`override=False`). Pass `override=True` to override existing variables.
5050

51-
To configure the development environment, add a `.env` in the root directory of your
52-
project:
51+
To configure the development environment, add a `.env` in the root directory of
52+
your project:
5353

5454
```
5555
.
5656
├── .env
5757
└── foo.py
5858
```
5959

60-
The syntax of `.env` files supported by python-dotenv is similar to that of Bash:
60+
The syntax of `.env` files supported by python-dotenv is similar to that of
61+
Bash:
6162

6263
```bash
6364
# Development settings
@@ -66,22 +67,21 @@ ADMIN_EMAIL=admin@${DOMAIN}
6667
ROOT_URL=${DOMAIN}/app
6768
```
6869

69-
If you use variables in values, ensure they are surrounded with `{` and `}`, like
70-
`${DOMAIN}`, as bare variables such as `$DOMAIN` are not expanded.
70+
If you use variables in values, ensure they are surrounded with `{` and `}`,
71+
like `${DOMAIN}`, as bare variables such as `$DOMAIN` are not expanded.
7172

72-
You will probably want to add `.env` to your `.gitignore`, especially if it contains
73-
secrets like a password.
73+
You will probably want to add `.env` to your `.gitignore`, especially if it
74+
contains secrets like a password.
7475

75-
See the section "File format" below for more information about what you can write in a
76-
`.env` file.
76+
See the section "[File format](#file-format)" below for more information about what you can write in a `.env` file.
7777

7878
## Other Use Cases
7979

8080
### Load configuration without altering the environment
8181

82-
The function `dotenv_values` works more or less the same way as `load_dotenv`, except it
83-
doesn't touch the environment, it just returns a `dict` with the values parsed from the
84-
`.env` file.
82+
The function `dotenv_values` works more or less the same way as `load_dotenv`,
83+
except it doesn't touch the environment, it just returns a `dict` with the
84+
values parsed from the `.env` file.
8585

8686
```python
8787
from dotenv import dotenv_values
@@ -104,9 +104,9 @@ config = {
104104

105105
### Parse configuration as a stream
106106

107-
`load_dotenv` and `dotenv_values` accept [streams][python_streams] via their `stream`
108-
argument. It is thus possible to load the variables from sources other than the
109-
filesystem (e.g. the network).
107+
`load_dotenv` and `dotenv_values` accept [streams][python_streams] via their
108+
`stream` argument. It is thus possible to load the variables from sources other
109+
than the filesystem (e.g. the network).
110110

111111
```python
112112
from io import StringIO
@@ -119,7 +119,7 @@ load_dotenv(stream=config)
119119

120120
### Load .env files in IPython
121121

122-
You can use dotenv in IPython. By default, it will use `find_dotenv` to search for a
122+
You can use dotenv in IPython. By default, it will use `find_dotenv` to search for a
123123
`.env` file:
124124

125125
```python
@@ -140,12 +140,14 @@ Optional flags:
140140

141141
### Disable load_dotenv
142142

143-
Set `PYTHON_DOTENV_DISABLED=1` to disable `load_dotenv()` from loading .env files or streams. Useful when you can't modify third-party package calls or in production.
143+
Set `PYTHON_DOTENV_DISABLED=1` to disable `load_dotenv()` from loading .env
144+
files or streams. Useful when you can't modify third-party package calls or in
145+
production.
144146

145147
## Command-line Interface
146148

147-
A CLI interface `dotenv` is also included, which helps you manipulate the `.env` file
148-
without manually opening it.
149+
A CLI interface `dotenv` is also included, which helps you manipulate the `.env`
150+
file without manually opening it.
149151

150152
```shell
151153
$ pip install "python-dotenv[cli]"
@@ -166,13 +168,14 @@ Run `dotenv --help` for more information about the options and subcommands.
166168

167169
## File format
168170

169-
The format is not formally specified and still improves over time. That being said,
170-
`.env` files should mostly look like Bash files.
171+
The format is not formally specified and still improves over time. That being
172+
said, `.env` files should mostly look like Bash files. Reading from FIFOs (named
173+
pipes) on Unix systems is also supported.
171174

172-
Keys can be unquoted or single-quoted. Values can be unquoted, single- or double-quoted.
173-
Spaces before and after keys, equal signs, and values are ignored. Values can be followed
174-
by a comment. Lines can start with the `export` directive, which does not affect their
175-
interpretation.
175+
Keys can be unquoted or single-quoted. Values can be unquoted, single- or
176+
double-quoted. Spaces before and after keys, equal signs, and values are
177+
ignored. Values can be followed by a comment. Lines can start with the `export`
178+
directive, which does not affect their interpretation.
176179

177180
Allowed escape sequences:
178181

@@ -181,8 +184,8 @@ Allowed escape sequences:
181184

182185
### Multiline values
183186

184-
It is possible for single- or double-quoted values to span multiple lines. The following
185-
examples are equivalent:
187+
It is possible for single- or double-quoted values to span multiple lines. The
188+
following examples are equivalent:
186189

187190
```bash
188191
FOO="first line
@@ -201,26 +204,27 @@ A variable can have no value:
201204
FOO
202205
```
203206

204-
It results in `dotenv_values` associating that variable name with the value `None` (e.g.
205-
`{"FOO": None}`. `load_dotenv`, on the other hand, simply ignores such variables.
207+
It results in `dotenv_values` associating that variable name with the value
208+
`None` (e.g. `{"FOO": None}`. `load_dotenv`, on the other hand, simply ignores
209+
such variables.
206210

207-
This shouldn't be confused with `FOO=`, in which case the variable is associated with the
208-
empty string.
211+
This shouldn't be confused with `FOO=`, in which case the variable is associated
212+
with the empty string.
209213

210214
### Variable expansion
211215

212216
python-dotenv can interpolate variables using POSIX variable expansion.
213217

214-
With `load_dotenv(override=True)` or `dotenv_values()`, the value of a variable is the
215-
first of the values defined in the following list:
218+
With `load_dotenv(override=True)` or `dotenv_values()`, the value of a variable
219+
is the first of the values defined in the following list:
216220

217221
- Value of that variable in the `.env` file.
218222
- Value of that variable in the environment.
219223
- Default value, if provided.
220224
- Empty string.
221225

222-
With `load_dotenv(override=False)`, the value of a variable is the first of the values
223-
defined in the following list:
226+
With `load_dotenv(override=False)`, the value of a variable is the first of the
227+
values defined in the following list:
224228

225229
- Value of that variable in the environment.
226230
- Value of that variable in the `.env` file.
@@ -229,27 +233,27 @@ defined in the following list:
229233

230234
## Related Projects
231235

232-
- [Honcho](https://github.com/nickstenning/honcho) - For managing
233-
Procfile-based applications.
234-
- [django-dotenv](https://github.com/jpadilla/django-dotenv)
235-
- [django-environ](https://github.com/joke2k/django-environ)
236-
- [django-environ-2](https://github.com/sergeyklay/django-environ-2)
237-
- [django-configuration](https://github.com/jezdez/django-configurations)
238-
- [dump-env](https://github.com/sobolevn/dump-env)
239-
- [environs](https://github.com/sloria/environs)
240-
- [dynaconf](https://github.com/rochacbruno/dynaconf)
241-
- [parse_it](https://github.com/naorlivne/parse_it)
242-
- [python-decouple](https://github.com/HBNetwork/python-decouple)
236+
- [environs](https://github.com/sloria/environs)
237+
- [Honcho](https://github.com/nickstenning/honcho)
238+
- [dump-env](https://github.com/sobolevn/dump-env)
239+
- [dynaconf](https://github.com/dynaconf/dynaconf)
240+
- [parse_it](https://github.com/naorlivne/parse_it)
241+
- [django-dotenv](https://github.com/jpadilla/django-dotenv)
242+
- [django-environ](https://github.com/joke2k/django-environ)
243+
- [python-decouple](https://github.com/HBNetwork/python-decouple)
244+
- [django-configuration](https://github.com/jezdez/django-configurations)
243245

244246
## Acknowledgements
245247

246-
This project is currently maintained by [Saurabh Kumar](https://saurabh-kumar.com) and
247-
[Bertrand Bonnefoy-Claudet](https://github.com/bbc2) and would not have been possible
248-
without the support of these [awesome
249-
people](https://github.com/theskumar/python-dotenv/graphs/contributors).
248+
This project is currently maintained by [Saurabh Kumar][saurabh-homepage] and
249+
[Bertrand Bonnefoy-Claudet][gh-bbc2] and would not have been possible without
250+
the support of these [awesome people][contributors].
250251

251-
[build_status_badge]: https://github.com/theskumar/python-dotenv/actions/workflows/test.yml/badge.svg
252-
[build_status_link]: https://github.com/theskumar/python-dotenv/actions/workflows/test.yml
253-
[pypi_badge]: https://badge.fury.io/py/python-dotenv.svg
252+
[gh-bbc2]: https://github.com/bbc2
253+
[saurabh-homepage]: https://saurabh-kumar.com
254254
[pypi_link]: https://badge.fury.io/py/python-dotenv
255+
[pypi_badge]: https://badge.fury.io/py/python-dotenv.svg
255256
[python_streams]: https://docs.python.org/3/library/io.html
257+
[contributors]: https://github.com/theskumar/python-dotenv/graphs/contributors
258+
[build_status_link]: https://github.com/theskumar/python-dotenv/actions/workflows/test.yml
259+
[build_status_badge]: https://github.com/theskumar/python-dotenv/actions/workflows/test.yml/badge.svg

mkdocs.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ markdown_extensions:
1313
- mdx_truly_sane_lists
1414

1515
plugins:
16-
- mkdocstrings
16+
- mkdocstrings:
17+
handlers:
18+
python:
19+
options:
20+
separate_signature: true
21+
show_root_heading: true
22+
show_symbol_type_heading: true
23+
show_symbol_type_toc: true
1724
- search
1825
nav:
1926
- Home: index.md

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description = "Read key-value pairs from a .env file and set them as environment
88
authors = [
99
{name = "Saurabh Kumar", email = "[email protected]"},
1010
]
11-
license = "BSD-3-Clause"
11+
license = { text = "BSD-3-Clause" }
1212
keywords = [
1313
"environment variables",
1414
"deployments",
@@ -22,11 +22,11 @@ classifiers = [
2222
"Development Status :: 5 - Production/Stable",
2323
"Programming Language :: Python",
2424
"Programming Language :: Python :: 3",
25-
"Programming Language :: Python :: 3.9",
2625
"Programming Language :: Python :: 3.10",
2726
"Programming Language :: Python :: 3.11",
2827
"Programming Language :: Python :: 3.12",
2928
"Programming Language :: Python :: 3.13",
29+
"Programming Language :: Python :: 3.14",
3030
"Programming Language :: Python :: Implementation :: PyPy",
3131
"Intended Audience :: Developers",
3232
"Intended Audience :: System Administrators",
@@ -36,7 +36,7 @@ classifiers = [
3636
"Environment :: Web Environment",
3737
]
3838

39-
requires-python = ">=3.9"
39+
requires-python = ">=3.10"
4040

4141
dynamic = ["version", "readme"]
4242

src/dotenv/cli.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,21 @@ def unset(ctx: click.Context, key: Any) -> None:
156156
sys.exit(1)
157157

158158

159-
@cli.command(context_settings={"ignore_unknown_options": True})
159+
@cli.command(
160+
context_settings={
161+
"allow_extra_args": True,
162+
"allow_interspersed_args": False,
163+
"ignore_unknown_options": True,
164+
}
165+
)
160166
@click.pass_context
161167
@click.option(
162168
"--override/--no-override",
163169
default=True,
164170
help="Override variables from the environment file with those from the .env file.",
165171
)
166172
@click.argument("commandline", nargs=-1, type=click.UNPROCESSED)
167-
def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
173+
def run(ctx: click.Context, override: bool, commandline: tuple[str, ...]) -> None:
168174
"""Run command with environment variables present."""
169175
file = ctx.obj["FILE"]
170176
if not os.path.isfile(file):
@@ -180,7 +186,8 @@ def run(ctx: click.Context, override: bool, commandline: List[str]) -> None:
180186
if not commandline:
181187
click.echo("No command given.")
182188
sys.exit(1)
183-
run_command(commandline, dotenv_as_dict)
189+
190+
run_command([*commandline, *ctx.args], dotenv_as_dict)
184191

185192

186193
def run_command(command: List[str], env: Dict[str, str]) -> None:

0 commit comments

Comments
 (0)