python-dotenv
Advanced tools
| repos: | ||
| - repo: https://github.com/astral-sh/ruff-pre-commit | ||
| rev: v0.12.0 | ||
| hooks: | ||
| # Run the linter. | ||
| - id: ruff | ||
| # Run the formatter. | ||
| - id: ruff-format |
| [build-system] | ||
| requires = ["setuptools >= 77.0"] | ||
| build-backend = "setuptools.build_meta" | ||
| [project] | ||
| name = "python-dotenv" | ||
| description = "Read key-value pairs from a .env file and set them as environment variables" | ||
| authors = [ | ||
| {name = "Saurabh Kumar", email = "me+github@saurabh-kumar.com"}, | ||
| ] | ||
| license = "BSD-3-Clause" | ||
| keywords = [ | ||
| "environment variables", | ||
| "deployments", | ||
| "settings", | ||
| "env", | ||
| "dotenv", | ||
| "configurations", | ||
| "python", | ||
| ] | ||
| classifiers = [ | ||
| "Development Status :: 5 - Production/Stable", | ||
| "Programming Language :: Python", | ||
| "Programming Language :: Python :: 3", | ||
| "Programming Language :: Python :: 3.9", | ||
| "Programming Language :: Python :: 3.10", | ||
| "Programming Language :: Python :: 3.11", | ||
| "Programming Language :: Python :: 3.12", | ||
| "Programming Language :: Python :: 3.13", | ||
| "Programming Language :: Python :: Implementation :: PyPy", | ||
| "Intended Audience :: Developers", | ||
| "Intended Audience :: System Administrators", | ||
| "Operating System :: OS Independent", | ||
| "Topic :: System :: Systems Administration", | ||
| "Topic :: Utilities", | ||
| "Environment :: Web Environment", | ||
| ] | ||
| requires-python = ">=3.9" | ||
| dynamic = ["version", "readme"] | ||
| [project.urls] | ||
| Source = "https://github.com/theskumar/python-dotenv" | ||
| [project.optional-dependencies] | ||
| cli = [ | ||
| "click>=5.0", | ||
| ] | ||
| [project.scripts] | ||
| dotenv = "dotenv.__main__:cli" | ||
| [tool.setuptools] | ||
| packages = ["dotenv"] | ||
| package-dir = {"" = "src"} | ||
| package-data = {dotenv = ["py.typed"]} | ||
| [tool.setuptools.dynamic] | ||
| version = {attr = "dotenv.version.__version__"} | ||
| readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"} |
+19
| [lint] | ||
| select = [ | ||
| # pycodestyle | ||
| "E4", | ||
| "E7", | ||
| "E9", | ||
| # Pyflakes | ||
| "F", | ||
| # flake8-bugbear | ||
| "B", | ||
| # iSort | ||
| "I", | ||
| # flake8-builtins | ||
| "A", | ||
| ] |
+21
-9
@@ -8,5 +8,11 @@ # Changelog | ||
| ## [1.2.0] - 2025-10-26 | ||
| - Upgrade build system to use PEP 517 & PEP 518 to use `build` and `pyproject.toml` by [@EpicWink] in [#583] | ||
| - Add support for Python 3.14 by [@23f3001135] in [#579](https://github.com/theskumar/python-dotenv/pull/563) | ||
| - Add support for disabling of `load_dotenv()` using `PYTHON_DOTENV_DISABLED` env var. by [@matthewfranglen] in [#569] | ||
| ## [1.1.1] - 2025-06-24 | ||
| ## Fixed | ||
| ### Fixed | ||
@@ -375,3 +381,11 @@ * CLI: Ensure `find_dotenv` work reliably on python 3.13 by [@theskumar] in [#563](https://github.com/theskumar/python-dotenv/pull/563) | ||
| [#553]: https://github.com/theskumar/python-dotenv/issues/553 | ||
| [#569]: https://github.com/theskumar/python-dotenv/issues/569 | ||
| [#583]: https://github.com/theskumar/python-dotenv/issues/583 | ||
| [@23f3001135]: https://github.com/23f3001135 | ||
| [@EpicWink]: https://github.com/EpicWink | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@alanjds]: https://github.com/alanjds | ||
@@ -391,3 +405,2 @@ [@altendky]: https://github.com/altendky | ||
| [@eumiro]: https://github.com/eumiro | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@freddyaboulton]: https://github.com/freddyaboulton | ||
@@ -403,9 +416,8 @@ [@gergelyk]: https://github.com/gergelyk | ||
| [@lsmith77]: https://github.com/lsmith77 | ||
| [@matthewfranglen]: https://github.com/matthewfranglen | ||
| [@mgorny]: https://github.com/mgorny | ||
| [@naorlivne]: https://github.com/@naorlivne | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@qnighy]: https://github.com/qnighy | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@rabinadk1]: https://github.com/@rabinadk1 | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@sammck]: https://github.com/@sammck | ||
@@ -419,10 +431,10 @@ [@samwyma]: https://github.com/samwyma | ||
| [@venthur]: https://github.com/venthur | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [@x-yuri]: https://github.com/x-yuri | ||
| [@yannham]: https://github.com/yannham | ||
| [@zueve]: https://github.com/zueve | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...HEAD | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...1.1.1 | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.2.0...HEAD | ||
| [1.2.0]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...v1.2.0 | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...v1.1.1 | ||
| [1.1.0]: https://github.com/theskumar/python-dotenv/compare/v1.0.1...v1.1.0 | ||
@@ -429,0 +441,0 @@ [1.0.1]: https://github.com/theskumar/python-dotenv/compare/v1.0.0...v1.0.1 |
+14
-8
@@ -10,6 +10,8 @@ Contributing | ||
| $ pip install -r requirements.txt | ||
| $ pip install -e . | ||
| $ flake8 | ||
| $ pytest | ||
| $ uv venv | ||
| $ uv pip install -r requirements.txt | ||
| $ uv pip install -e . | ||
| $ uv ruff check . | ||
| $ uv format . | ||
| $ uv run pytest | ||
@@ -21,11 +23,15 @@ or with [tox](https://pypi.org/project/tox/) installed: | ||
| Use of pre-commit is recommended: | ||
| $ uv run precommit install | ||
| Documentation is published with [mkdocs](): | ||
| ```shell | ||
| $ pip install -r requirements-docs.txt | ||
| $ pip install -e . | ||
| $ mkdocs serve | ||
| $ uv pip install -r requirements-docs.txt | ||
| $ uv pip install -e . | ||
| $ uv run mkdocs serve | ||
| ``` | ||
| Open http://127.0.0.1:8000/ to view the documentation locally. | ||
+21
-9
@@ -8,5 +8,11 @@ # Changelog | ||
| ## [1.2.0] - 2025-10-26 | ||
| - Upgrade build system to use PEP 517 & PEP 518 to use `build` and `pyproject.toml` by [@EpicWink] in [#583] | ||
| - Add support for Python 3.14 by [@23f3001135] in [#579](https://github.com/theskumar/python-dotenv/pull/563) | ||
| - Add support for disabling of `load_dotenv()` using `PYTHON_DOTENV_DISABLED` env var. by [@matthewfranglen] in [#569] | ||
| ## [1.1.1] - 2025-06-24 | ||
| ## Fixed | ||
| ### Fixed | ||
@@ -375,3 +381,11 @@ * CLI: Ensure `find_dotenv` work reliably on python 3.13 by [@theskumar] in [#563](https://github.com/theskumar/python-dotenv/pull/563) | ||
| [#553]: https://github.com/theskumar/python-dotenv/issues/553 | ||
| [#569]: https://github.com/theskumar/python-dotenv/issues/569 | ||
| [#583]: https://github.com/theskumar/python-dotenv/issues/583 | ||
| [@23f3001135]: https://github.com/23f3001135 | ||
| [@EpicWink]: https://github.com/EpicWink | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@alanjds]: https://github.com/alanjds | ||
@@ -391,3 +405,2 @@ [@altendky]: https://github.com/altendky | ||
| [@eumiro]: https://github.com/eumiro | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@freddyaboulton]: https://github.com/freddyaboulton | ||
@@ -403,9 +416,8 @@ [@gergelyk]: https://github.com/gergelyk | ||
| [@lsmith77]: https://github.com/lsmith77 | ||
| [@matthewfranglen]: https://github.com/matthewfranglen | ||
| [@mgorny]: https://github.com/mgorny | ||
| [@naorlivne]: https://github.com/@naorlivne | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@qnighy]: https://github.com/qnighy | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@rabinadk1]: https://github.com/@rabinadk1 | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@sammck]: https://github.com/@sammck | ||
@@ -419,10 +431,10 @@ [@samwyma]: https://github.com/samwyma | ||
| [@venthur]: https://github.com/venthur | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [@x-yuri]: https://github.com/x-yuri | ||
| [@yannham]: https://github.com/yannham | ||
| [@zueve]: https://github.com/zueve | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...HEAD | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...1.1.1 | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.2.0...HEAD | ||
| [1.2.0]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...v1.2.0 | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...v1.1.1 | ||
| [1.1.0]: https://github.com/theskumar/python-dotenv/compare/v1.0.1...v1.1.0 | ||
@@ -429,0 +441,0 @@ [1.0.1]: https://github.com/theskumar/python-dotenv/compare/v1.0.0...v1.0.1 |
+14
-8
@@ -10,6 +10,8 @@ Contributing | ||
| $ pip install -r requirements.txt | ||
| $ pip install -e . | ||
| $ flake8 | ||
| $ pytest | ||
| $ uv venv | ||
| $ uv pip install -r requirements.txt | ||
| $ uv pip install -e . | ||
| $ uv ruff check . | ||
| $ uv format . | ||
| $ uv run pytest | ||
@@ -21,11 +23,15 @@ or with [tox](https://pypi.org/project/tox/) installed: | ||
| Use of pre-commit is recommended: | ||
| $ uv run precommit install | ||
| Documentation is published with [mkdocs](): | ||
| ```shell | ||
| $ pip install -r requirements-docs.txt | ||
| $ pip install -e . | ||
| $ mkdocs serve | ||
| $ uv pip install -r requirements-docs.txt | ||
| $ uv pip install -e . | ||
| $ uv run mkdocs serve | ||
| ``` | ||
| Open http://127.0.0.1:8000/ to view the documentation locally. | ||
+14
-3
@@ -39,10 +39,17 @@ # python-dotenv | ||
| load_dotenv() # take environment variables | ||
| load_dotenv() # reads variables from a .env file and sets them in os.environ | ||
| ``` | ||
| # Code of your application, which uses environment variables (e.g. from `os.environ` or | ||
| # `os.getenv`) as if they came from the actual environment. | ||
| ``` | ||
| By default, `load_dotenv` doesn't override existing environment variables and looks for a `.env` file in same directory as python script or searches for it incrementally higher up. | ||
| By default, `load_dotenv()` will: | ||
| - Look for a `.env` file in the same directory as the Python script (or higher up the directory tree). | ||
| - Read each key-value pair and add it to `os.environ`. | ||
| - **Not override** an environment variable that is already set, unless you explicitly pass `override=True`. | ||
| To configure the development environment, add a `.env` in the root directory of your | ||
@@ -138,2 +145,6 @@ project: | ||
| ### Disable load_dotenv | ||
| 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. | ||
| ## Command-line Interface | ||
@@ -140,0 +151,0 @@ |
+4
-4
@@ -23,9 +23,9 @@ .PHONY: clean-pyc clean-build test | ||
| sdist: clean | ||
| python setup.py sdist bdist_wheel | ||
| python -m build -o dist . | ||
| ls -l dist | ||
| test: | ||
| pip install -e . | ||
| flake8 . | ||
| py.test tests/ | ||
| uv pip install -e . | ||
| ruff check . | ||
| pytest tests/ | ||
@@ -32,0 +32,0 @@ coverage: |
+1
-1
@@ -1,2 +0,2 @@ | ||
| include LICENSE *.md *.yml *.toml | ||
| include LICENSE *.md *.yml *.yaml *.toml | ||
@@ -3,0 +3,0 @@ include tox.ini |
+39
-29
| Metadata-Version: 2.4 | ||
| Name: python-dotenv | ||
| Version: 1.1.1 | ||
| Version: 1.2.0 | ||
| Summary: Read key-value pairs from a .env file and set them as environment variables | ||
| Home-page: https://github.com/theskumar/python-dotenv | ||
| Author: Saurabh Kumar | ||
| Author-email: me+github@saurabh-kumar.com | ||
| License: BSD-3-Clause | ||
| Author-email: Saurabh Kumar <me+github@saurabh-kumar.com> | ||
| License-Expression: BSD-3-Clause | ||
| Project-URL: Source, https://github.com/theskumar/python-dotenv | ||
| Keywords: environment variables,deployments,settings,env,dotenv,configurations,python | ||
@@ -21,3 +20,2 @@ Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Intended Audience :: System Administrators | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Classifier: Operating System :: OS Independent | ||
@@ -32,14 +30,3 @@ Classifier: Topic :: System :: Systems Administration | ||
| Requires-Dist: click>=5.0; extra == "cli" | ||
| Dynamic: author | ||
| Dynamic: author-email | ||
| Dynamic: classifier | ||
| Dynamic: description | ||
| Dynamic: description-content-type | ||
| Dynamic: home-page | ||
| Dynamic: keywords | ||
| Dynamic: license | ||
| Dynamic: license-file | ||
| Dynamic: provides-extra | ||
| Dynamic: requires-python | ||
| Dynamic: summary | ||
@@ -84,10 +71,17 @@ # python-dotenv | ||
| load_dotenv() # take environment variables | ||
| load_dotenv() # reads variables from a .env file and sets them in os.environ | ||
| ``` | ||
| # Code of your application, which uses environment variables (e.g. from `os.environ` or | ||
| # `os.getenv`) as if they came from the actual environment. | ||
| ``` | ||
| By default, `load_dotenv` doesn't override existing environment variables and looks for a `.env` file in same directory as python script or searches for it incrementally higher up. | ||
| By default, `load_dotenv()` will: | ||
| - Look for a `.env` file in the same directory as the Python script (or higher up the directory tree). | ||
| - Read each key-value pair and add it to `os.environ`. | ||
| - **Not override** an environment variable that is already set, unless you explicitly pass `override=True`. | ||
| To configure the development environment, add a `.env` in the root directory of your | ||
@@ -183,2 +177,6 @@ project: | ||
| ### Disable load_dotenv | ||
| 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. | ||
| ## Command-line Interface | ||
@@ -303,5 +301,11 @@ | ||
| ## [1.2.0] - 2025-10-26 | ||
| - Upgrade build system to use PEP 517 & PEP 518 to use `build` and `pyproject.toml` by [@EpicWink] in [#583] | ||
| - Add support for Python 3.14 by [@23f3001135] in [#579](https://github.com/theskumar/python-dotenv/pull/563) | ||
| - Add support for disabling of `load_dotenv()` using `PYTHON_DOTENV_DISABLED` env var. by [@matthewfranglen] in [#569] | ||
| ## [1.1.1] - 2025-06-24 | ||
| ## Fixed | ||
| ### Fixed | ||
@@ -670,3 +674,11 @@ * CLI: Ensure `find_dotenv` work reliably on python 3.13 by [@theskumar] in [#563](https://github.com/theskumar/python-dotenv/pull/563) | ||
| [#553]: https://github.com/theskumar/python-dotenv/issues/553 | ||
| [#569]: https://github.com/theskumar/python-dotenv/issues/569 | ||
| [#583]: https://github.com/theskumar/python-dotenv/issues/583 | ||
| [@23f3001135]: https://github.com/23f3001135 | ||
| [@EpicWink]: https://github.com/EpicWink | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@alanjds]: https://github.com/alanjds | ||
@@ -686,3 +698,2 @@ [@altendky]: https://github.com/altendky | ||
| [@eumiro]: https://github.com/eumiro | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@freddyaboulton]: https://github.com/freddyaboulton | ||
@@ -698,9 +709,8 @@ [@gergelyk]: https://github.com/gergelyk | ||
| [@lsmith77]: https://github.com/lsmith77 | ||
| [@matthewfranglen]: https://github.com/matthewfranglen | ||
| [@mgorny]: https://github.com/mgorny | ||
| [@naorlivne]: https://github.com/@naorlivne | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@qnighy]: https://github.com/qnighy | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@rabinadk1]: https://github.com/@rabinadk1 | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@sammck]: https://github.com/@sammck | ||
@@ -714,10 +724,10 @@ [@samwyma]: https://github.com/samwyma | ||
| [@venthur]: https://github.com/venthur | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [@x-yuri]: https://github.com/x-yuri | ||
| [@yannham]: https://github.com/yannham | ||
| [@zueve]: https://github.com/zueve | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...HEAD | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...1.1.1 | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.2.0...HEAD | ||
| [1.2.0]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...v1.2.0 | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...v1.1.1 | ||
| [1.1.0]: https://github.com/theskumar/python-dotenv/compare/v1.0.1...v1.1.0 | ||
@@ -724,0 +734,0 @@ [1.0.1]: https://github.com/theskumar/python-dotenv/compare/v1.0.0...v1.0.1 |
+14
-3
@@ -39,10 +39,17 @@ # python-dotenv | ||
| load_dotenv() # take environment variables | ||
| load_dotenv() # reads variables from a .env file and sets them in os.environ | ||
| ``` | ||
| # Code of your application, which uses environment variables (e.g. from `os.environ` or | ||
| # `os.getenv`) as if they came from the actual environment. | ||
| ``` | ||
| By default, `load_dotenv` doesn't override existing environment variables and looks for a `.env` file in same directory as python script or searches for it incrementally higher up. | ||
| By default, `load_dotenv()` will: | ||
| - Look for a `.env` file in the same directory as the Python script (or higher up the directory tree). | ||
| - Read each key-value pair and add it to `os.environ`. | ||
| - **Not override** an environment variable that is already set, unless you explicitly pass `override=True`. | ||
| To configure the development environment, add a `.env` in the root directory of your | ||
@@ -138,2 +145,6 @@ project: | ||
| ### Disable load_dotenv | ||
| 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. | ||
| ## Command-line Interface | ||
@@ -140,0 +151,0 @@ |
+2
-2
@@ -1,5 +0,3 @@ | ||
| black~=22.3.0 | ||
| bumpversion | ||
| click | ||
| flake8>=2.2.3 | ||
| ipython | ||
@@ -12,1 +10,3 @@ pytest-cov | ||
| wheel | ||
| ruff | ||
| pre-commit |
+1
-4
| [bumpversion] | ||
| current_version = 1.1.1 | ||
| current_version = 1.2.0 | ||
| commit = True | ||
@@ -16,5 +16,2 @@ tag = True | ||
| [metadata] | ||
| description_file = README.md | ||
| [tool:pytest] | ||
@@ -21,0 +18,0 @@ testpaths = tests |
+17
-15
| from typing import Any, Optional | ||
| from .main import (dotenv_values, find_dotenv, get_key, load_dotenv, set_key, | ||
| unset_key) | ||
| from .main import dotenv_values, find_dotenv, get_key, load_dotenv, set_key, unset_key | ||
@@ -9,2 +8,3 @@ | ||
| from .ipython import load_ipython_extension | ||
| load_ipython_extension(ipython) | ||
@@ -25,7 +25,7 @@ | ||
| """ | ||
| command = ['dotenv'] | ||
| command = ["dotenv"] | ||
| if quote: | ||
| command.append(f'-q {quote}') | ||
| command.append(f"-q {quote}") | ||
| if path: | ||
| command.append(f'-f {path}') | ||
| command.append(f"-f {path}") | ||
| if action: | ||
@@ -36,3 +36,3 @@ command.append(action) | ||
| if value: | ||
| if ' ' in value: | ||
| if " " in value: | ||
| command.append(f'"{value}"') | ||
@@ -42,12 +42,14 @@ else: | ||
| return ' '.join(command).strip() | ||
| return " ".join(command).strip() | ||
| __all__ = ['get_cli_string', | ||
| 'load_dotenv', | ||
| 'dotenv_values', | ||
| 'get_key', | ||
| 'set_key', | ||
| 'unset_key', | ||
| 'find_dotenv', | ||
| 'load_ipython_extension'] | ||
| __all__ = [ | ||
| "get_cli_string", | ||
| "load_dotenv", | ||
| "dotenv_values", | ||
| "get_key", | ||
| "set_key", | ||
| "unset_key", | ||
| "find_dotenv", | ||
| "load_ipython_extension", | ||
| ] |
+70
-57
@@ -6,5 +6,5 @@ import json | ||
| from contextlib import contextmanager | ||
| from typing import Any, Dict, IO, Iterator, List, Optional | ||
| from typing import IO, Any, Dict, Iterator, List, Optional | ||
| if sys.platform == 'win32': | ||
| if sys.platform == "win32": | ||
| from subprocess import Popen | ||
@@ -15,4 +15,6 @@ | ||
| except ImportError: | ||
| sys.stderr.write('It seems python-dotenv is not installed with cli option. \n' | ||
| 'Run pip install "python-dotenv[cli]" to fix this.') | ||
| sys.stderr.write( | ||
| "It seems python-dotenv is not installed with cli option. \n" | ||
| 'Run pip install "python-dotenv[cli]" to fix this.' | ||
| ) | ||
| sys.exit(1) | ||
@@ -34,3 +36,3 @@ | ||
| return None | ||
| path = os.path.join(cwd, '.env') | ||
| path = os.path.join(cwd, ".env") | ||
| return path | ||
@@ -40,11 +42,23 @@ | ||
| @click.group() | ||
| @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', | ||
| type=click.Choice(['always', 'never', 'auto']), | ||
| help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.") | ||
| @click.option('-e', '--export', default=False, | ||
| type=click.BOOL, | ||
| help="Whether to write the dot file as an executable bash script.") | ||
| @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", | ||
| type=click.Choice(["always", "never", "auto"]), | ||
| help="Whether to quote or not the variable values. Default mode is always. This does not affect parsing.", | ||
| ) | ||
| @click.option( | ||
| "-e", | ||
| "--export", | ||
| default=False, | ||
| type=click.BOOL, | ||
| help="Whether to write the dot file as an executable bash script.", | ||
| ) | ||
| @click.version_option(version=__version__) | ||
@@ -54,3 +68,3 @@ @click.pass_context | ||
| """This script is used to set, get or unset values from a .env file.""" | ||
| ctx.obj = {'QUOTE': quote, 'EXPORT': export, 'FILE': file} | ||
| ctx.obj = {"QUOTE": quote, "EXPORT": export, "FILE": file} | ||
@@ -71,14 +85,18 @@ | ||
| print(f"Error opening env file: {exc}", file=sys.stderr) | ||
| exit(2) | ||
| sys.exit(2) | ||
| @cli.command() | ||
| @cli.command(name="list") | ||
| @click.pass_context | ||
| @click.option('--format', default='simple', | ||
| type=click.Choice(['simple', 'json', 'shell', 'export']), | ||
| 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: | ||
| @click.option( | ||
| "--format", | ||
| "output_format", | ||
| default="simple", | ||
| type=click.Choice(["simple", "json", "shell", "export"]), | ||
| help="The format in which to display the list. Default format is simple, " | ||
| "which displays name=value without quotes.", | ||
| ) | ||
| def list_values(ctx: click.Context, output_format: str) -> None: | ||
| """Display all the stored key/value.""" | ||
| file = ctx.obj['FILE'] | ||
| file = ctx.obj["FILE"] | ||
@@ -88,28 +106,28 @@ with stream_file(file) as stream: | ||
| if format == 'json': | ||
| if output_format == "json": | ||
| click.echo(json.dumps(values, indent=2, sort_keys=True)) | ||
| else: | ||
| prefix = 'export ' if format == 'export' else '' | ||
| prefix = "export " if output_format == "export" else "" | ||
| for k in sorted(values): | ||
| v = values[k] | ||
| if v is not None: | ||
| if format in ('export', 'shell'): | ||
| if output_format in ("export", "shell"): | ||
| v = shlex.quote(v) | ||
| click.echo(f'{prefix}{k}={v}') | ||
| click.echo(f"{prefix}{k}={v}") | ||
| @cli.command() | ||
| @cli.command(name="set") | ||
| @click.pass_context | ||
| @click.argument('key', required=True) | ||
| @click.argument('value', required=True) | ||
| def set(ctx: click.Context, key: Any, value: Any) -> None: | ||
| @click.argument("key", required=True) | ||
| @click.argument("value", required=True) | ||
| def set_value(ctx: click.Context, key: Any, value: Any) -> None: | ||
| """Store the given key/value.""" | ||
| file = ctx.obj['FILE'] | ||
| quote = ctx.obj['QUOTE'] | ||
| export = ctx.obj['EXPORT'] | ||
| 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(f'{key}={value}') | ||
| click.echo(f"{key}={value}") | ||
| else: | ||
| exit(1) | ||
| sys.exit(1) | ||
@@ -119,6 +137,6 @@ | ||
| @click.pass_context | ||
| @click.argument('key', required=True) | ||
| @click.argument("key", required=True) | ||
| def get(ctx: click.Context, key: Any) -> None: | ||
| """Retrieve the value for the given key.""" | ||
| file = ctx.obj['FILE'] | ||
| file = ctx.obj["FILE"] | ||
@@ -132,3 +150,3 @@ with stream_file(file) as stream: | ||
| else: | ||
| exit(1) | ||
| sys.exit(1) | ||
@@ -138,7 +156,7 @@ | ||
| @click.pass_context | ||
| @click.argument('key', required=True) | ||
| @click.argument("key", required=True) | ||
| def unset(ctx: click.Context, key: Any) -> None: | ||
| """Removes the given key.""" | ||
| file = ctx.obj['FILE'] | ||
| quote = ctx.obj['QUOTE'] | ||
| file = ctx.obj["FILE"] | ||
| quote = ctx.obj["QUOTE"] | ||
| success, key = unset_key(file, key, quote) | ||
@@ -148,6 +166,6 @@ if success: | ||
| else: | ||
| exit(1) | ||
| sys.exit(1) | ||
| @cli.command(context_settings={'ignore_unknown_options': True}) | ||
| @cli.command(context_settings={"ignore_unknown_options": True}) | ||
| @click.pass_context | ||
@@ -159,10 +177,9 @@ @click.option( | ||
| ) | ||
| @click.argument('commandline', nargs=-1, type=click.UNPROCESSED) | ||
| @click.argument("commandline", nargs=-1, type=click.UNPROCESSED) | ||
| def run(ctx: click.Context, override: bool, commandline: List[str]) -> None: | ||
| """Run command with environment variables present.""" | ||
| file = ctx.obj['FILE'] | ||
| file = ctx.obj["FILE"] | ||
| if not os.path.isfile(file): | ||
| raise click.BadParameter( | ||
| f'Invalid value for \'-f\' "{file}" does not exist.', | ||
| ctx=ctx | ||
| f"Invalid value for '-f' \"{file}\" does not exist.", ctx=ctx | ||
| ) | ||
@@ -176,4 +193,4 @@ dotenv_as_dict = { | ||
| if not commandline: | ||
| click.echo('No command given.') | ||
| exit(1) | ||
| click.echo("No command given.") | ||
| sys.exit(1) | ||
| run_command(commandline, dotenv_as_dict) | ||
@@ -206,14 +223,10 @@ | ||
| if sys.platform == 'win32': | ||
| if sys.platform == "win32": | ||
| # execvpe on Windows returns control immediately | ||
| # rather than once the command has finished. | ||
| p = Popen(command, | ||
| universal_newlines=True, | ||
| bufsize=0, | ||
| shell=False, | ||
| env=cmd_env) | ||
| p = Popen(command, universal_newlines=True, bufsize=0, shell=False, env=cmd_env) | ||
| _, _ = p.communicate() | ||
| exit(p.returncode) | ||
| sys.exit(p.returncode) | ||
| else: | ||
| os.execvpe(command[0], args=command, env=cmd_env) |
| from IPython.core.magic import Magics, line_magic, magics_class # type: ignore | ||
| from IPython.core.magic_arguments import (argument, magic_arguments, # type: ignore | ||
| parse_argstring) # type: ignore | ||
| from IPython.core.magic_arguments import ( | ||
| argument, | ||
| magic_arguments, | ||
| parse_argstring, | ||
| ) # type: ignore | ||
@@ -10,14 +13,22 @@ from .main import find_dotenv, load_dotenv | ||
| class IPythonDotEnv(Magics): | ||
| @magic_arguments() | ||
| @argument( | ||
| '-o', '--override', action='store_true', | ||
| help="Indicate to override existing variables" | ||
| "-o", | ||
| "--override", | ||
| action="store_true", | ||
| help="Indicate to override existing variables", | ||
| ) | ||
| @argument( | ||
| '-v', '--verbose', action='store_true', | ||
| help="Indicate function calls to be verbose" | ||
| "-v", | ||
| "--verbose", | ||
| action="store_true", | ||
| help="Indicate function calls to be verbose", | ||
| ) | ||
| @argument('dotenv_path', nargs='?', type=str, default='.env', | ||
| help='Search in increasingly higher folders for the `dotenv_path`') | ||
| @argument( | ||
| "dotenv_path", | ||
| nargs="?", | ||
| type=str, | ||
| default=".env", | ||
| help="Search in increasingly higher folders for the `dotenv_path`", | ||
| ) | ||
| @line_magic | ||
@@ -24,0 +35,0 @@ def dotenv(self, line): |
+19
-0
@@ -24,2 +24,12 @@ import io | ||
| def _load_dotenv_disabled() -> bool: | ||
| """ | ||
| Determine if dotenv loading has been disabled. | ||
| """ | ||
| if "PYTHON_DOTENV_DISABLED" not in os.environ: | ||
| return False | ||
| value = os.environ["PYTHON_DOTENV_DISABLED"].casefold() | ||
| return value in {"1", "true", "t", "yes", "y"} | ||
| def with_warn_for_invalid_lines(mappings: Iterator[Binding]) -> Iterator[Binding]: | ||
@@ -353,3 +363,12 @@ for mapping in mappings: | ||
| to this function as `dotenv_path`. | ||
| If the environment variable `PYTHON_DOTENV_DISABLED` is set to a truthy value, | ||
| .env loading is disabled. | ||
| """ | ||
| if _load_dotenv_disabled(): | ||
| logger.debug( | ||
| "python-dotenv: .env loading disabled by PYTHON_DOTENV_DISABLED environment variable" | ||
| ) | ||
| return False | ||
| if dotenv_path is None and stream is None: | ||
@@ -356,0 +375,0 @@ dotenv_path = find_dotenv() |
+18
-11
| import codecs | ||
| import re | ||
| from typing import (IO, Iterator, Match, NamedTuple, Optional, # noqa:F401 | ||
| Pattern, Sequence, Tuple) | ||
| from typing import ( | ||
| IO, | ||
| Iterator, | ||
| Match, | ||
| NamedTuple, | ||
| Optional, | ||
| Pattern, | ||
| Sequence, | ||
| ) | ||
@@ -76,3 +83,3 @@ | ||
| return Original( | ||
| string=self.string[self.mark.chars:self.position.chars], | ||
| string=self.string[self.mark.chars : self.position.chars], | ||
| line=self.mark.line, | ||
@@ -82,6 +89,6 @@ ) | ||
| def peek(self, count: int) -> str: | ||
| return self.string[self.position.chars:self.position.chars + count] | ||
| return self.string[self.position.chars : self.position.chars + count] | ||
| def read(self, count: int) -> str: | ||
| result = self.string[self.position.chars:self.position.chars + count] | ||
| result = self.string[self.position.chars : self.position.chars + count] | ||
| if len(result) < count: | ||
@@ -96,3 +103,3 @@ raise Error("read: End of string") | ||
| raise Error("read_regex: Pattern not found") | ||
| self.position.advance(self.string[match.start():match.end()]) | ||
| self.position.advance(self.string[match.start() : match.end()]) | ||
| return match.groups() | ||
@@ -103,3 +110,3 @@ | ||
| def decode_match(match: Match[str]) -> str: | ||
| return codecs.decode(match.group(0), 'unicode-escape') # type: ignore | ||
| return codecs.decode(match.group(0), "unicode-escape") # type: ignore | ||
@@ -127,10 +134,10 @@ return regex.sub(decode_match, string) | ||
| char = reader.peek(1) | ||
| if char == u"'": | ||
| if char == "'": | ||
| (value,) = reader.read_regex(_single_quoted_value) | ||
| return decode_escapes(_single_quote_escapes, value) | ||
| elif char == u'"': | ||
| elif char == '"': | ||
| (value,) = reader.read_regex(_double_quoted_value) | ||
| return decode_escapes(_double_quote_escapes, value) | ||
| elif char in (u"", u"\n", u"\r"): | ||
| return u"" | ||
| elif char in ("", "\n", "\r"): | ||
| return "" | ||
| else: | ||
@@ -137,0 +144,0 @@ return parse_unquoted_value(reader) |
@@ -1,1 +0,1 @@ | ||
| __version__ = "1.1.1" | ||
| __version__ = "1.2.0" |
| Metadata-Version: 2.4 | ||
| Name: python-dotenv | ||
| Version: 1.1.1 | ||
| Version: 1.2.0 | ||
| Summary: Read key-value pairs from a .env file and set them as environment variables | ||
| Home-page: https://github.com/theskumar/python-dotenv | ||
| Author: Saurabh Kumar | ||
| Author-email: me+github@saurabh-kumar.com | ||
| License: BSD-3-Clause | ||
| Author-email: Saurabh Kumar <me+github@saurabh-kumar.com> | ||
| License-Expression: BSD-3-Clause | ||
| Project-URL: Source, https://github.com/theskumar/python-dotenv | ||
| Keywords: environment variables,deployments,settings,env,dotenv,configurations,python | ||
@@ -21,3 +20,2 @@ Classifier: Development Status :: 5 - Production/Stable | ||
| Classifier: Intended Audience :: System Administrators | ||
| Classifier: License :: OSI Approved :: BSD License | ||
| Classifier: Operating System :: OS Independent | ||
@@ -32,14 +30,3 @@ Classifier: Topic :: System :: Systems Administration | ||
| Requires-Dist: click>=5.0; extra == "cli" | ||
| Dynamic: author | ||
| Dynamic: author-email | ||
| Dynamic: classifier | ||
| Dynamic: description | ||
| Dynamic: description-content-type | ||
| Dynamic: home-page | ||
| Dynamic: keywords | ||
| Dynamic: license | ||
| Dynamic: license-file | ||
| Dynamic: provides-extra | ||
| Dynamic: requires-python | ||
| Dynamic: summary | ||
@@ -84,10 +71,17 @@ # python-dotenv | ||
| load_dotenv() # take environment variables | ||
| load_dotenv() # reads variables from a .env file and sets them in os.environ | ||
| ``` | ||
| # Code of your application, which uses environment variables (e.g. from `os.environ` or | ||
| # `os.getenv`) as if they came from the actual environment. | ||
| ``` | ||
| By default, `load_dotenv` doesn't override existing environment variables and looks for a `.env` file in same directory as python script or searches for it incrementally higher up. | ||
| By default, `load_dotenv()` will: | ||
| - Look for a `.env` file in the same directory as the Python script (or higher up the directory tree). | ||
| - Read each key-value pair and add it to `os.environ`. | ||
| - **Not override** an environment variable that is already set, unless you explicitly pass `override=True`. | ||
| To configure the development environment, add a `.env` in the root directory of your | ||
@@ -183,2 +177,6 @@ project: | ||
| ### Disable load_dotenv | ||
| 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. | ||
| ## Command-line Interface | ||
@@ -303,5 +301,11 @@ | ||
| ## [1.2.0] - 2025-10-26 | ||
| - Upgrade build system to use PEP 517 & PEP 518 to use `build` and `pyproject.toml` by [@EpicWink] in [#583] | ||
| - Add support for Python 3.14 by [@23f3001135] in [#579](https://github.com/theskumar/python-dotenv/pull/563) | ||
| - Add support for disabling of `load_dotenv()` using `PYTHON_DOTENV_DISABLED` env var. by [@matthewfranglen] in [#569] | ||
| ## [1.1.1] - 2025-06-24 | ||
| ## Fixed | ||
| ### Fixed | ||
@@ -670,3 +674,11 @@ * CLI: Ensure `find_dotenv` work reliably on python 3.13 by [@theskumar] in [#563](https://github.com/theskumar/python-dotenv/pull/563) | ||
| [#553]: https://github.com/theskumar/python-dotenv/issues/553 | ||
| [#569]: https://github.com/theskumar/python-dotenv/issues/569 | ||
| [#583]: https://github.com/theskumar/python-dotenv/issues/583 | ||
| [@23f3001135]: https://github.com/23f3001135 | ||
| [@EpicWink]: https://github.com/EpicWink | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@alanjds]: https://github.com/alanjds | ||
@@ -686,3 +698,2 @@ [@altendky]: https://github.com/altendky | ||
| [@eumiro]: https://github.com/eumiro | ||
| [@Flimm]: https://github.com/Flimm | ||
| [@freddyaboulton]: https://github.com/freddyaboulton | ||
@@ -698,9 +709,8 @@ [@gergelyk]: https://github.com/gergelyk | ||
| [@lsmith77]: https://github.com/lsmith77 | ||
| [@matthewfranglen]: https://github.com/matthewfranglen | ||
| [@mgorny]: https://github.com/mgorny | ||
| [@naorlivne]: https://github.com/@naorlivne | ||
| [@Nicals]: https://github.com/Nicals | ||
| [@Nougat-Waffle]: https://github.com/Nougat-Waffle | ||
| [@qnighy]: https://github.com/qnighy | ||
| [@Qwerty-133]: https://github.com/Qwerty-133 | ||
| [@rabinadk1]: https://github.com/@rabinadk1 | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@sammck]: https://github.com/@sammck | ||
@@ -714,10 +724,10 @@ [@samwyma]: https://github.com/samwyma | ||
| [@venthur]: https://github.com/venthur | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [@x-yuri]: https://github.com/x-yuri | ||
| [@yannham]: https://github.com/yannham | ||
| [@zueve]: https://github.com/zueve | ||
| [@randomseed42]: https://github.com/zueve | ||
| [@wrongontheinternet]: https://github.com/wrongontheinternet | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...HEAD | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...1.1.1 | ||
| [Unreleased]: https://github.com/theskumar/python-dotenv/compare/v1.2.0...HEAD | ||
| [1.2.0]: https://github.com/theskumar/python-dotenv/compare/v1.1.1...v1.2.0 | ||
| [1.1.1]: https://github.com/theskumar/python-dotenv/compare/v1.1.0...v1.1.1 | ||
| [1.1.0]: https://github.com/theskumar/python-dotenv/compare/v1.0.1...v1.1.0 | ||
@@ -724,0 +734,0 @@ [1.0.1]: https://github.com/theskumar/python-dotenv/compare/v1.0.0...v1.0.1 |
| .editorconfig | ||
| .pre-commit-config.yaml | ||
| CHANGELOG.md | ||
@@ -9,6 +10,7 @@ CONTRIBUTING.md | ||
| mkdocs.yml | ||
| pyproject.toml | ||
| requirements-docs.txt | ||
| requirements.txt | ||
| ruff.toml | ||
| setup.cfg | ||
| setup.py | ||
| tox.ini | ||
@@ -15,0 +17,0 @@ docs/changelog.md |
@@ -14,4 +14,4 @@ import pytest | ||
| def dotenv_path(tmp_path): | ||
| path = tmp_path / '.env' | ||
| path.write_bytes(b'') | ||
| path = tmp_path / ".env" | ||
| path.write_bytes(b"") | ||
| yield path |
+55
-33
| import os | ||
| import sh | ||
| from pathlib import Path | ||
@@ -7,2 +6,3 @@ from typing import Optional | ||
| import pytest | ||
| import sh | ||
@@ -15,22 +15,24 @@ import dotenv | ||
| @pytest.mark.parametrize( | ||
| "format,content,expected", | ||
| "output_format,content,expected", | ||
| ( | ||
| (None, "x='a b c'", '''x=a b c\n'''), | ||
| ("simple", "x='a b c'", '''x=a b c\n'''), | ||
| ("simple", """x='"a b c"'""", '''x="a b c"\n'''), | ||
| ("simple", '''x="'a b c'"''', '''x='a b c'\n'''), | ||
| ("json", "x='a b c'", '''{\n "x": "a b c"\n}\n'''), | ||
| (None, "x='a b c'", """x=a b c\n"""), | ||
| ("simple", "x='a b c'", """x=a b c\n"""), | ||
| ("simple", """x='"a b c"'""", """x="a b c"\n"""), | ||
| ("simple", '''x="'a b c'"''', """x='a b c'\n"""), | ||
| ("json", "x='a b c'", """{\n "x": "a b c"\n}\n"""), | ||
| ("shell", "x='a b c'", "x='a b c'\n"), | ||
| ("shell", """x='"a b c"'""", '''x='"a b c"'\n'''), | ||
| ("shell", '''x="'a b c'"''', '''x=''"'"'a b c'"'"''\n'''), | ||
| ("shell", """x='"a b c"'""", """x='"a b c"'\n"""), | ||
| ("shell", '''x="'a b c'"''', """x=''"'"'a b c'"'"''\n"""), | ||
| ("shell", "x='a\nb\nc'", "x='a\nb\nc'\n"), | ||
| ("export", "x='a b c'", '''export x='a b c'\n'''), | ||
| ) | ||
| ("export", "x='a b c'", """export x='a b c'\n"""), | ||
| ), | ||
| ) | ||
| def test_list(cli, dotenv_path, format: Optional[str], content: str, expected: str): | ||
| dotenv_path.write_text(content + '\n') | ||
| def test_list( | ||
| cli, dotenv_path, output_format: Optional[str], content: str, expected: str | ||
| ): | ||
| dotenv_path.write_text(content + "\n") | ||
| args = ['--file', dotenv_path, 'list'] | ||
| args = ["--file", dotenv_path, "list"] | ||
| if format is not None: | ||
| args.extend(['--format', format]) | ||
| args.extend(["--format", output_format]) | ||
@@ -43,3 +45,3 @@ result = cli.invoke(dotenv_cli, args) | ||
| def test_list_non_existent_file(cli): | ||
| result = cli.invoke(dotenv_cli, ['--file', 'nx_file', 'list']) | ||
| result = cli.invoke(dotenv_cli, ["--file", "nx_file", "list"]) | ||
@@ -51,3 +53,3 @@ assert result.exit_code == 2, result.output | ||
| def test_list_not_a_file(cli): | ||
| result = cli.invoke(dotenv_cli, ['--file', '.', 'list']) | ||
| result = cli.invoke(dotenv_cli, ["--file", ".", "list"]) | ||
@@ -59,3 +61,3 @@ assert result.exit_code == 2, result.output | ||
| def test_list_no_file(cli): | ||
| result = cli.invoke(dotenv.cli.list, []) | ||
| result = cli.invoke(dotenv.cli.list_values, []) | ||
@@ -68,3 +70,3 @@ assert (result.exit_code, result.output) == (1, "") | ||
| result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'get', 'a']) | ||
| result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "get", "a"]) | ||
@@ -75,3 +77,3 @@ assert (result.exit_code, result.output) == (0, "b\n") | ||
| def test_get_non_existent_value(cli, dotenv_path): | ||
| result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'get', 'a']) | ||
| result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "get", "a"]) | ||
@@ -82,3 +84,3 @@ assert (result.exit_code, result.output) == (1, "") | ||
| def test_get_non_existent_file(cli): | ||
| result = cli.invoke(dotenv_cli, ['--file', 'nx_file', 'get', 'a']) | ||
| result = cli.invoke(dotenv_cli, ["--file", "nx_file", "get", "a"]) | ||
@@ -90,3 +92,3 @@ assert result.exit_code == 2 | ||
| def test_get_not_a_file(cli): | ||
| result = cli.invoke(dotenv_cli, ['--file', '.', 'get', 'a']) | ||
| result = cli.invoke(dotenv_cli, ["--file", ".", "get", "a"]) | ||
@@ -100,3 +102,3 @@ assert result.exit_code == 2 | ||
| result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'unset', 'a']) | ||
| result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "unset", "a"]) | ||
@@ -108,3 +110,3 @@ assert (result.exit_code, result.output) == (0, "Successfully removed a\n") | ||
| def test_unset_non_existent_value(cli, dotenv_path): | ||
| result = cli.invoke(dotenv_cli, ['--file', dotenv_path, 'unset', 'a']) | ||
| result = cli.invoke(dotenv_cli, ["--file", dotenv_path, "unset", "a"]) | ||
@@ -119,7 +121,7 @@ assert (result.exit_code, result.output) == (1, "") | ||
| ("always", "a", "x", "a='x'\n"), | ||
| ("never", "a", "x", 'a=x\n'), | ||
| ("never", "a", "x", "a=x\n"), | ||
| ("auto", "a", "x", "a=x\n"), | ||
| ("auto", "a", "x y", "a='x y'\n"), | ||
| ("auto", "a", "$", "a='$'\n"), | ||
| ) | ||
| ), | ||
| ) | ||
@@ -129,3 +131,13 @@ def test_set_quote_options(cli, dotenv_path, quote_mode, variable, value, expected): | ||
| dotenv_cli, | ||
| ["--file", dotenv_path, "--export", "false", "--quote", quote_mode, "set", variable, value] | ||
| [ | ||
| "--file", | ||
| dotenv_path, | ||
| "--export", | ||
| "false", | ||
| "--quote", | ||
| quote_mode, | ||
| "set", | ||
| variable, | ||
| value, | ||
| ], | ||
| ) | ||
@@ -142,3 +154,3 @@ | ||
| (Path(".nx_file"), "false", "a", "x", "a='x'\n"), | ||
| ) | ||
| ), | ||
| ) | ||
@@ -148,3 +160,13 @@ def test_set_export(cli, dotenv_path, export_mode, variable, value, expected): | ||
| dotenv_cli, | ||
| ["--file", dotenv_path, "--quote", "always", "--export", export_mode, "set", variable, value] | ||
| [ | ||
| "--file", | ||
| dotenv_path, | ||
| "--quote", | ||
| "always", | ||
| "--export", | ||
| export_mode, | ||
| "set", | ||
| variable, | ||
| value, | ||
| ], | ||
| ) | ||
@@ -157,3 +179,3 @@ | ||
| def test_set_non_existent_file(cli): | ||
| result = cli.invoke(dotenv.cli.set, ["a", "b"]) | ||
| result = cli.invoke(dotenv.cli.set_value, ["a", "b"]) | ||
@@ -228,3 +250,3 @@ assert (result.exit_code, result.output) == (1, "") | ||
| def test_run_without_cmd(cli): | ||
| result = cli.invoke(dotenv_cli, ['run']) | ||
| result = cli.invoke(dotenv_cli, ["run"]) | ||
@@ -236,3 +258,3 @@ assert result.exit_code == 2 | ||
| def test_run_with_invalid_cmd(cli): | ||
| result = cli.invoke(dotenv_cli, ['run', 'i_do_not_exist']) | ||
| result = cli.invoke(dotenv_cli, ["run", "i_do_not_exist"]) | ||
@@ -244,5 +266,5 @@ assert result.exit_code == 2 | ||
| def test_run_with_version(cli): | ||
| result = cli.invoke(dotenv_cli, ['--version']) | ||
| result = cli.invoke(dotenv_cli, ["--version"]) | ||
| assert result.exit_code == 0 | ||
| assert result.output.strip().endswith(__version__) |
@@ -6,3 +6,2 @@ import os | ||
| pytest.importorskip("IPython") | ||
@@ -9,0 +8,0 @@ |
@@ -0,4 +1,5 @@ | ||
| import builtins | ||
| import sys | ||
| import builtins | ||
| from unittest import mock | ||
| from dotenv.main import find_dotenv | ||
@@ -178,3 +179,5 @@ | ||
| def test_is_interactive_main_module_with_file_attribute_none(self, tmp_path, monkeypatch): | ||
| def test_is_interactive_main_module_with_file_attribute_none( | ||
| self, tmp_path, monkeypatch | ||
| ): | ||
| """Test _is_interactive when __main__ has __file__ attribute set to None.""" | ||
@@ -199,3 +202,5 @@ self._remove_ps_attributes(monkeypatch) | ||
| def test_is_interactive_no_ps_attributes_and_normal_execution(self, tmp_path, monkeypatch): | ||
| def test_is_interactive_no_ps_attributes_and_normal_execution( | ||
| self, tmp_path, monkeypatch | ||
| ): | ||
| """Test normal script execution scenario where _is_interactive should return False.""" | ||
@@ -202,0 +207,0 @@ self._remove_ps_attributes(monkeypatch) |
+125
-1
@@ -67,3 +67,3 @@ import io | ||
| with pytest.raises(Exception): | ||
| with pytest.raises(PermissionError): | ||
| dotenv.set_key(dotenv_path, "a", "b") | ||
@@ -249,2 +249,126 @@ | ||
| @pytest.mark.parametrize( | ||
| "flag_value", | ||
| [ | ||
| "true", | ||
| "yes", | ||
| "1", | ||
| "t", | ||
| "y", | ||
| "True", | ||
| "Yes", | ||
| "TRUE", | ||
| "YES", | ||
| "T", | ||
| "Y", | ||
| ], | ||
| ) | ||
| def test_load_dotenv_disabled(dotenv_path, flag_value): | ||
| expected_environ = {"PYTHON_DOTENV_DISABLED": flag_value} | ||
| with mock.patch.dict(os.environ, {"PYTHON_DOTENV_DISABLED": flag_value}, clear=True): | ||
| dotenv_path.write_text("a=b") | ||
| result = dotenv.load_dotenv(dotenv_path) | ||
| assert result is False | ||
| assert os.environ == expected_environ | ||
| @pytest.mark.parametrize( | ||
| "flag_value", | ||
| [ | ||
| "true", | ||
| "yes", | ||
| "1", | ||
| "t", | ||
| "y", | ||
| "True", | ||
| "Yes", | ||
| "TRUE", | ||
| "YES", | ||
| "T", | ||
| "Y", | ||
| ], | ||
| ) | ||
| def test_load_dotenv_disabled_notification(dotenv_path, flag_value): | ||
| with mock.patch.dict(os.environ, {"PYTHON_DOTENV_DISABLED": flag_value}, clear=True): | ||
| dotenv_path.write_text("a=b") | ||
| logger = logging.getLogger("dotenv.main") | ||
| with mock.patch.object(logger, "debug") as mock_debug: | ||
| result = dotenv.load_dotenv(dotenv_path) | ||
| assert result is False | ||
| mock_debug.assert_called_once_with( | ||
| "python-dotenv: .env loading disabled by PYTHON_DOTENV_DISABLED environment variable" | ||
| ) | ||
| @pytest.mark.parametrize( | ||
| "flag_value", | ||
| [ | ||
| "", | ||
| "false", | ||
| "no", | ||
| "0", | ||
| "f", | ||
| "n", | ||
| "False", | ||
| "No", | ||
| "FALSE", | ||
| "NO", | ||
| "F", | ||
| "N", | ||
| ], | ||
| ) | ||
| def test_load_dotenv_enabled(dotenv_path, flag_value): | ||
| expected_environ = {"PYTHON_DOTENV_DISABLED": flag_value, "a": "b"} | ||
| with mock.patch.dict(os.environ, {"PYTHON_DOTENV_DISABLED": flag_value}, clear=True): | ||
| dotenv_path.write_text("a=b") | ||
| result = dotenv.load_dotenv(dotenv_path) | ||
| assert result is True | ||
| assert os.environ == expected_environ | ||
| @pytest.mark.parametrize( | ||
| "flag_value", | ||
| [ | ||
| "", | ||
| "false", | ||
| "no", | ||
| "0", | ||
| "f", | ||
| "n", | ||
| "False", | ||
| "No", | ||
| "FALSE", | ||
| "NO", | ||
| "F", | ||
| "N", | ||
| ], | ||
| ) | ||
| def test_load_dotenv_enabled_no_notification(dotenv_path, flag_value): | ||
| with mock.patch.dict(os.environ, {"PYTHON_DOTENV_DISABLED": flag_value}, clear=True): | ||
| dotenv_path.write_text("a=b") | ||
| logger = logging.getLogger("dotenv.main") | ||
| with mock.patch.object(logger, "debug") as mock_debug: | ||
| result = dotenv.load_dotenv(dotenv_path) | ||
| assert result is True | ||
| mock_debug.assert_not_called() | ||
| @mock.patch.dict(os.environ, {}, clear=True) | ||
| def test_load_dotenv_doesnt_disable_itself(dotenv_path): | ||
| dotenv_path.write_text("PYTHON_DOTENV_DISABLED=true") | ||
| result = dotenv.load_dotenv(dotenv_path) | ||
| assert result is True | ||
| assert os.environ == {"PYTHON_DOTENV_DISABLED": "true"} | ||
| def test_load_dotenv_no_file_verbose(): | ||
@@ -251,0 +375,0 @@ logger = logging.getLogger("dotenv.main") |
+542
-160
@@ -8,162 +8,544 @@ import io | ||
| @pytest.mark.parametrize("test_input,expected", [ | ||
| (u"", []), | ||
| (u"a=b", [Binding(key=u"a", value=u"b", original=Original(string=u"a=b", line=1), error=False)]), | ||
| (u"'a'=b", [Binding(key=u"a", value=u"b", original=Original(string=u"'a'=b", line=1), error=False)]), | ||
| (u"[=b", [Binding(key=u"[", value=u"b", original=Original(string=u"[=b", line=1), error=False)]), | ||
| (u" a = b ", [Binding(key=u"a", value=u"b", original=Original(string=u" a = b ", line=1), error=False)]), | ||
| (u"export a=b", [Binding(key=u"a", value=u"b", original=Original(string=u"export a=b", line=1), error=False)]), | ||
| ( | ||
| u" export 'a'=b", | ||
| [Binding(key=u"a", value=u"b", original=Original(string=u" export 'a'=b", line=1), error=False)], | ||
| ), | ||
| (u"# a=b", [Binding(key=None, value=None, original=Original(string=u"# a=b", line=1), error=False)]), | ||
| (u"a=b#c", [Binding(key=u"a", value=u"b#c", original=Original(string=u"a=b#c", line=1), error=False)]), | ||
| ( | ||
| u'a=b #c', | ||
| [Binding(key=u"a", value=u"b", original=Original(string=u"a=b #c", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u'a=b\t#c', | ||
| [Binding(key=u"a", value=u"b", original=Original(string=u"a=b\t#c", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"a=b c", | ||
| [Binding(key=u"a", value=u"b c", original=Original(string=u"a=b c", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"a=b\tc", | ||
| [Binding(key=u"a", value=u"b\tc", original=Original(string=u"a=b\tc", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"a=b c", | ||
| [Binding(key=u"a", value=u"b c", original=Original(string=u"a=b c", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"a=b\u00a0 c", | ||
| [Binding(key=u"a", value=u"b\u00a0 c", original=Original(string=u"a=b\u00a0 c", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"a=b c ", | ||
| [Binding(key=u"a", value=u"b c", original=Original(string=u"a=b c ", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"a='b c '", | ||
| [Binding(key=u"a", value=u"b c ", original=Original(string=u"a='b c '", line=1), error=False)], | ||
| ), | ||
| ( | ||
| u'a="b c "', | ||
| [Binding(key=u"a", value=u"b c ", original=Original(string=u'a="b c "', line=1), error=False)], | ||
| ), | ||
| ( | ||
| u"export export_a=1", | ||
| [ | ||
| Binding(key=u"export_a", value=u"1", original=Original(string=u"export export_a=1", line=1), error=False) | ||
| ], | ||
| ), | ||
| ( | ||
| u"export port=8000", | ||
| [Binding(key=u"port", value=u"8000", original=Original(string=u"export port=8000", line=1), error=False)], | ||
| ), | ||
| (u'a="b\nc"', [Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"', line=1), error=False)]), | ||
| (u"a='b\nc'", [Binding(key=u"a", value=u"b\nc", original=Original(string=u"a='b\nc'", line=1), error=False)]), | ||
| (u'a="b\nc"', [Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"', line=1), error=False)]), | ||
| (u'a="b\\nc"', [Binding(key=u"a", value=u'b\nc', original=Original(string=u'a="b\\nc"', line=1), error=False)]), | ||
| (u"a='b\\nc'", [Binding(key=u"a", value=u'b\\nc', original=Original(string=u"a='b\\nc'", line=1), error=False)]), | ||
| (u'a="b\\"c"', [Binding(key=u"a", value=u'b"c', original=Original(string=u'a="b\\"c"', line=1), error=False)]), | ||
| (u"a='b\\'c'", [Binding(key=u"a", value=u"b'c", original=Original(string=u"a='b\\'c'", line=1), error=False)]), | ||
| (u"a=à", [Binding(key=u"a", value=u"à", original=Original(string=u"a=à", line=1), error=False)]), | ||
| (u'a="à"', [Binding(key=u"a", value=u"à", original=Original(string=u'a="à"', line=1), error=False)]), | ||
| ( | ||
| u'no_value_var', | ||
| [Binding(key=u'no_value_var', value=None, original=Original(string=u"no_value_var", line=1), error=False)], | ||
| ), | ||
| (u'a: b', [Binding(key=None, value=None, original=Original(string=u"a: b", line=1), error=True)]), | ||
| ( | ||
| u"a=b\nc=d", | ||
| [ | ||
| Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1), error=False), | ||
| Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2), error=False), | ||
| ], | ||
| ), | ||
| ( | ||
| u"a=b\rc=d", | ||
| [ | ||
| Binding(key=u"a", value=u"b", original=Original(string=u"a=b\r", line=1), error=False), | ||
| Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2), error=False), | ||
| ], | ||
| ), | ||
| ( | ||
| u"a=b\r\nc=d", | ||
| [ | ||
| Binding(key=u"a", value=u"b", original=Original(string=u"a=b\r\n", line=1), error=False), | ||
| Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2), error=False), | ||
| ], | ||
| ), | ||
| ( | ||
| u'a=\nb=c', | ||
| [ | ||
| Binding(key=u"a", value=u'', original=Original(string=u'a=\n', line=1), error=False), | ||
| Binding(key=u"b", value=u'c', original=Original(string=u"b=c", line=2), error=False), | ||
| ] | ||
| ), | ||
| ( | ||
| u"\n\n", | ||
| [ | ||
| Binding(key=None, value=None, original=Original(string=u"\n\n", line=1), error=False), | ||
| ] | ||
| ), | ||
| ( | ||
| u"a=b\n\n", | ||
| [ | ||
| Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1), error=False), | ||
| Binding(key=None, value=None, original=Original(string=u"\n", line=2), error=False), | ||
| ] | ||
| ), | ||
| ( | ||
| u'a=b\n\nc=d', | ||
| [ | ||
| Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1), error=False), | ||
| Binding(key=u"c", value=u"d", original=Original(string=u"\nc=d", line=2), error=False), | ||
| ] | ||
| ), | ||
| ( | ||
| u'a="\nb=c', | ||
| [ | ||
| Binding(key=None, value=None, original=Original(string=u'a="\n', line=1), error=True), | ||
| Binding(key=u"b", value=u"c", original=Original(string=u"b=c", line=2), error=False), | ||
| ] | ||
| ), | ||
| ( | ||
| u'# comment\na="b\nc"\nd=e\n', | ||
| [ | ||
| Binding(key=None, value=None, original=Original(string=u"# comment\n", line=1), error=False), | ||
| Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"\n', line=2), error=False), | ||
| Binding(key=u"d", value=u"e", original=Original(string=u"d=e\n", line=4), error=False), | ||
| ], | ||
| ), | ||
| ( | ||
| u'a=b\n# comment 1', | ||
| [ | ||
| Binding(key="a", value="b", original=Original(string=u"a=b\n", line=1), error=False), | ||
| Binding(key=None, value=None, original=Original(string=u"# comment 1", line=2), error=False), | ||
| ], | ||
| ), | ||
| ( | ||
| u'# comment 1\n# comment 2', | ||
| [ | ||
| Binding(key=None, value=None, original=Original(string=u"# comment 1\n", line=1), error=False), | ||
| Binding(key=None, value=None, original=Original(string=u"# comment 2", line=2), error=False), | ||
| ], | ||
| ), | ||
| ( | ||
| u'uglyKey[%$=\"S3cr3t_P4ssw#rD\" #\na=b', | ||
| [ | ||
| Binding(key=u'uglyKey[%$', | ||
| value=u'S3cr3t_P4ssw#rD', | ||
| original=Original(string=u"uglyKey[%$=\"S3cr3t_P4ssw#rD\" #\n", line=1), error=False), | ||
| Binding(key=u"a", value=u"b", original=Original(string=u'a=b', line=2), error=False), | ||
| ], | ||
| ), | ||
| ]) | ||
| @pytest.mark.parametrize( | ||
| "test_input,expected", | ||
| [ | ||
| ("", []), | ||
| ( | ||
| "a=b", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "'a'=b", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="'a'=b", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "[=b", | ||
| [ | ||
| Binding( | ||
| key="[", | ||
| value="b", | ||
| original=Original(string="[=b", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| " a = b ", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string=" a = b ", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "export a=b", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="export a=b", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| " export 'a'=b", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string=" export 'a'=b", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "# a=b", | ||
| [ | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="# a=b", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b#c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b#c", | ||
| original=Original(string="a=b#c", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b #c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b #c", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\t#c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\t#c", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b c", | ||
| original=Original(string="a=b c", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\tc", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\tc", | ||
| original=Original(string="a=b\tc", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b c", | ||
| original=Original(string="a=b c", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\u00a0 c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\u00a0 c", | ||
| original=Original(string="a=b\u00a0 c", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b c ", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b c", | ||
| original=Original(string="a=b c ", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a='b c '", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b c ", | ||
| original=Original(string="a='b c '", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="b c "', | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b c ", | ||
| original=Original(string='a="b c "', line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "export export_a=1", | ||
| [ | ||
| Binding( | ||
| key="export_a", | ||
| value="1", | ||
| original=Original(string="export export_a=1", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "export port=8000", | ||
| [ | ||
| Binding( | ||
| key="port", | ||
| value="8000", | ||
| original=Original(string="export port=8000", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="b\nc"', | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\nc", | ||
| original=Original(string='a="b\nc"', line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a='b\nc'", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\nc", | ||
| original=Original(string="a='b\nc'", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="b\nc"', | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\nc", | ||
| original=Original(string='a="b\nc"', line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="b\\nc"', | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\nc", | ||
| original=Original(string='a="b\\nc"', line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a='b\\nc'", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b\\nc", | ||
| original=Original(string="a='b\\nc'", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="b\\"c"', | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value='b"c', | ||
| original=Original(string='a="b\\"c"', line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a='b\\'c'", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b'c", | ||
| original=Original(string="a='b\\'c'", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=à", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="à", | ||
| original=Original(string="a=à", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="à"', | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="à", | ||
| original=Original(string='a="à"', line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "no_value_var", | ||
| [ | ||
| Binding( | ||
| key="no_value_var", | ||
| value=None, | ||
| original=Original(string="no_value_var", line=1), | ||
| error=False, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a: b", | ||
| [ | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="a: b", line=1), | ||
| error=True, | ||
| ) | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\nc=d", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="c", | ||
| value="d", | ||
| original=Original(string="c=d", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\rc=d", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\r", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="c", | ||
| value="d", | ||
| original=Original(string="c=d", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\r\nc=d", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\r\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="c", | ||
| value="d", | ||
| original=Original(string="c=d", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "a=\nb=c", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="", | ||
| original=Original(string="a=\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="b", | ||
| value="c", | ||
| original=Original(string="b=c", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "\n\n", | ||
| [ | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="\n\n", line=1), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\n\n", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="\n", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\n\nc=d", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="c", | ||
| value="d", | ||
| original=Original(string="\nc=d", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| 'a="\nb=c', | ||
| [ | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string='a="\n', line=1), | ||
| error=True, | ||
| ), | ||
| Binding( | ||
| key="b", | ||
| value="c", | ||
| original=Original(string="b=c", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| '# comment\na="b\nc"\nd=e\n', | ||
| [ | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="# comment\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="a", | ||
| value="b\nc", | ||
| original=Original(string='a="b\nc"\n', line=2), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="d", | ||
| value="e", | ||
| original=Original(string="d=e\n", line=4), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "a=b\n# comment 1", | ||
| [ | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="# comment 1", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| "# comment 1\n# comment 2", | ||
| [ | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="# comment 1\n", line=1), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key=None, | ||
| value=None, | ||
| original=Original(string="# comment 2", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ( | ||
| 'uglyKey[%$="S3cr3t_P4ssw#rD" #\na=b', | ||
| [ | ||
| Binding( | ||
| key="uglyKey[%$", | ||
| value="S3cr3t_P4ssw#rD", | ||
| original=Original( | ||
| string='uglyKey[%$="S3cr3t_P4ssw#rD" #\n', line=1 | ||
| ), | ||
| error=False, | ||
| ), | ||
| Binding( | ||
| key="a", | ||
| value="b", | ||
| original=Original(string="a=b", line=2), | ||
| error=False, | ||
| ), | ||
| ], | ||
| ), | ||
| ], | ||
| ) | ||
| def test_parse_stream(test_input, expected): | ||
@@ -170,0 +552,0 @@ result = parse_stream(io.StringIO(test_input)) |
+15
-9
@@ -5,10 +5,16 @@ from dotenv import get_cli_string as c | ||
| def test_to_cli_string(): | ||
| assert c() == 'dotenv' | ||
| assert c(path='/etc/.env') == 'dotenv -f /etc/.env' | ||
| assert c(path='/etc/.env', action='list') == 'dotenv -f /etc/.env list' | ||
| assert c(action='list') == 'dotenv list' | ||
| assert c(action='get', key='DEBUG') == 'dotenv get DEBUG' | ||
| assert c(action='set', key='DEBUG', value='True') == 'dotenv set DEBUG True' | ||
| assert c(action='set', key='SECRET', value='=@asdfasf') == 'dotenv set SECRET =@asdfasf' | ||
| assert c(action='set', key='SECRET', value='a b') == 'dotenv set SECRET "a b"' | ||
| assert c(action='set', key='SECRET', value='a b', quote="always") == 'dotenv -q always set SECRET "a b"' | ||
| assert c() == "dotenv" | ||
| assert c(path="/etc/.env") == "dotenv -f /etc/.env" | ||
| assert c(path="/etc/.env", action="list") == "dotenv -f /etc/.env list" | ||
| assert c(action="list") == "dotenv list" | ||
| assert c(action="get", key="DEBUG") == "dotenv get DEBUG" | ||
| assert c(action="set", key="DEBUG", value="True") == "dotenv set DEBUG True" | ||
| assert ( | ||
| c(action="set", key="SECRET", value="=@asdfasf") | ||
| == "dotenv set SECRET =@asdfasf" | ||
| ) | ||
| assert c(action="set", key="SECRET", value="a b") == 'dotenv set SECRET "a b"' | ||
| assert ( | ||
| c(action="set", key="SECRET", value="a b", quote="always") | ||
| == 'dotenv -q always set SECRET "a b"' | ||
| ) |
@@ -30,3 +30,3 @@ import pytest | ||
| ), | ||
| ] | ||
| ], | ||
| ) | ||
@@ -33,0 +33,0 @@ def test_parse_variables(value, expected): |
| import os | ||
| import sys | ||
| import sh | ||
| import textwrap | ||
@@ -9,3 +8,5 @@ from typing import List | ||
| import sh | ||
| def walk_to_root(path: str): | ||
@@ -29,12 +30,12 @@ last_dir = None | ||
| dirs_init_py_added_to = set() | ||
| with ZipFile(zip_file_path, "w") as zip: | ||
| with ZipFile(zip_file_path, "w") as zipfile: | ||
| for f in files: | ||
| zip.writestr(data=f.content, zinfo_or_arcname=f.path) | ||
| for dir in walk_to_root(os.path.dirname(f.path)): | ||
| if dir not in dirs_init_py_added_to: | ||
| print(os.path.join(dir, "__init__.py")) | ||
| zip.writestr( | ||
| data="", zinfo_or_arcname=os.path.join(dir, "__init__.py") | ||
| zipfile.writestr(data=f.content, zinfo_or_arcname=f.path) | ||
| for dirname in walk_to_root(os.path.dirname(f.path)): | ||
| if dirname not in dirs_init_py_added_to: | ||
| print(os.path.join(dirname, "__init__.py")) | ||
| zipfile.writestr( | ||
| data="", zinfo_or_arcname=os.path.join(dirname, "__init__.py") | ||
| ) | ||
| dirs_init_py_added_to.add(dir) | ||
| dirs_init_py_added_to.add(dirname) | ||
| return zip_file_path | ||
@@ -41,0 +42,0 @@ |
+8
-5
@@ -11,2 +11,3 @@ [tox] | ||
| 3.13: py313, lint, manifest | ||
| 3.14: py314 | ||
| pypy-3.9: pypy3 | ||
@@ -20,7 +21,7 @@ | ||
| click | ||
| py{39,310,311,312,313,pypy3}: ipython | ||
| py{39,310,311,312,313,3.14,pypy3}: ipython | ||
| commands = pytest --cov --cov-report=term-missing --cov-config setup.cfg {posargs} | ||
| depends = | ||
| py{39,310,311,312,313},pypy3: coverage-clean | ||
| coverage-report: py{39,310,311,312,313},pypy3 | ||
| py{39,310,311,312,313,314},pypy3: coverage-clean | ||
| coverage-report: py{39,310,311,312,313,314},pypy3 | ||
@@ -30,6 +31,8 @@ [testenv:lint] | ||
| deps = | ||
| flake8 | ||
| ruff | ||
| mypy | ||
| commands = | ||
| flake8 src tests | ||
| ruff check src | ||
| ruff check tests | ||
| mypy --python-version=3.14 src tests | ||
| mypy --python-version=3.13 src tests | ||
@@ -36,0 +39,0 @@ mypy --python-version=3.12 src tests |
-71
| from setuptools import setup | ||
| def read_files(files): | ||
| data = [] | ||
| for file in files: | ||
| with open(file, encoding="utf-8") as f: | ||
| data.append(f.read()) | ||
| return "\n".join(data) | ||
| long_description = read_files(["README.md", "CHANGELOG.md"]) | ||
| meta = {} | ||
| with open("./src/dotenv/version.py", encoding="utf-8") as f: | ||
| exec(f.read(), meta) | ||
| setup( | ||
| name="python-dotenv", | ||
| description="Read key-value pairs from a .env file and set them as environment variables", | ||
| long_description=long_description, | ||
| long_description_content_type="text/markdown", | ||
| version=meta["__version__"], | ||
| author="Saurabh Kumar", | ||
| author_email="me+github@saurabh-kumar.com", | ||
| url="https://github.com/theskumar/python-dotenv", | ||
| keywords=[ | ||
| "environment variables", | ||
| "deployments", | ||
| "settings", | ||
| "env", | ||
| "dotenv", | ||
| "configurations", | ||
| "python", | ||
| ], | ||
| packages=["dotenv"], | ||
| package_dir={"": "src"}, | ||
| package_data={ | ||
| "dotenv": ["py.typed"], | ||
| }, | ||
| python_requires=">=3.9", | ||
| extras_require={ | ||
| "cli": [ | ||
| "click>=5.0", | ||
| ], | ||
| }, | ||
| entry_points={ | ||
| "console_scripts": [ | ||
| "dotenv=dotenv.__main__:cli", | ||
| ], | ||
| }, | ||
| license="BSD-3-Clause", | ||
| classifiers=[ | ||
| "Development Status :: 5 - Production/Stable", | ||
| "Programming Language :: Python", | ||
| "Programming Language :: Python :: 3", | ||
| "Programming Language :: Python :: 3.9", | ||
| "Programming Language :: Python :: 3.10", | ||
| "Programming Language :: Python :: 3.11", | ||
| "Programming Language :: Python :: 3.12", | ||
| "Programming Language :: Python :: 3.13", | ||
| "Programming Language :: Python :: Implementation :: PyPy", | ||
| "Intended Audience :: Developers", | ||
| "Intended Audience :: System Administrators", | ||
| "License :: OSI Approved :: BSD License", | ||
| "Operating System :: OS Independent", | ||
| "Topic :: System :: Systems Administration", | ||
| "Topic :: Utilities", | ||
| "Environment :: Web Environment", | ||
| ], | ||
| ) |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
190071
8.62%46
4.55%2278
28.19%