Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: joke2k/django-environ
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.11.1
Choose a base ref
...
head repository: joke2k/django-environ
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref

Commits on Aug 30, 2023

  1. Bump version

    sergeyklay committed Aug 30, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    mifi Mikael Finstad
    Copy the full SHA
    54daf88 View commit details

Commits on Sep 1, 2023

  1. Revert "Add variable expansion (fix #421)"

    This reverts commit c61bfb0.
    sergeyklay committed Sep 1, 2023
    Copy the full SHA
    b343e05 View commit details
  2. Update change log

    sergeyklay committed Sep 1, 2023
    Copy the full SHA
    55ff0e3 View commit details
  3. Copy the full SHA
    5561b35 View commit details
  4. Copy the full SHA
    928a654 View commit details
  5. Bump version

    sergeyklay committed Sep 1, 2023
    Copy the full SHA
    18423bd View commit details
  6. Revert "Add variable expansion (fix #421)"

    This reverts commit c61bfb0.
    sergeyklay committed Sep 1, 2023
    Copy the full SHA
    a2848cd View commit details
  7. Update change log

    sergeyklay committed Sep 1, 2023
    Copy the full SHA
    66888f1 View commit details
  8. Copy the full SHA
    b3ab191 View commit details
  9. Change version: v0.12.0 -> v0.11.2

    sergeyklay committed Sep 1, 2023
    Copy the full SHA
    df301b6 View commit details

Commits on Sep 6, 2023

  1. Bump actions/checkout from 3.6.0 to 4.0.0

    Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.0.0.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](actions/checkout@v3.6.0...v4.0.0)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Sep 6, 2023
    Copy the full SHA
    7b5d7f9 View commit details

Commits on Sep 22, 2023

  1. Make inline comments handling optional and disabled by default

    Disabled inline comments handling (that was introduced in #475)
    by default due to potential side effects. While the feature itself is
    useful, the project's philosophy dictates that it should not be enabled
    by default for all users.
    
    This also fix the issue described in the #499.
    sergeyklay committed Sep 22, 2023
    Copy the full SHA
    e3e7fc9 View commit details

Commits on Oct 27, 2023

  1. Copy the full SHA
    b108444 View commit details
  2. Bump actions/checkout from 4.0.0 to 4.1.1

    Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.1.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](actions/checkout@v4.0.0...v4.1.1)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-type: direct:production
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 27, 2023
    Copy the full SHA
    de22b7e View commit details
  3. Bump actions/setup-python from 4.7.0 to 4.7.1

    Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.0 to 4.7.1.
    - [Release notes](https://github.com/actions/setup-python/releases)
    - [Commits](actions/setup-python@v4.7.0...v4.7.1)
    
    ---
    updated-dependencies:
    - dependency-name: actions/setup-python
      dependency-type: direct:production
      update-type: version-update:semver-patch
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 27, 2023
    Copy the full SHA
    46c035e View commit details

Commits on Oct 28, 2023

  1. Test on Django 5.0

    adamchainz authored and sergeyklay committed Oct 28, 2023
    2
    Copy the full SHA
    a1113e4 View commit details

Commits on Oct 25, 2024

  1. Bump github/codeql-action from 2 to 3

    Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
    - [Release notes](https://github.com/github/codeql-action/releases)
    - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
    - [Commits](github/codeql-action@v2...v3)
    
    ---
    updated-dependencies:
    - dependency-name: github/codeql-action
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 25, 2024
    Copy the full SHA
    7e9e32a View commit details
  2. Bump actions/setup-python from 4.7.1 to 5.0.0

    Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.1 to 5.0.0.
    - [Release notes](https://github.com/actions/setup-python/releases)
    - [Commits](actions/setup-python@v4.7.1...v5.0.0)
    
    ---
    updated-dependencies:
    - dependency-name: actions/setup-python
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 25, 2024
    Copy the full SHA
    bead9cc View commit details
  3. Copy the full SHA
    be3d746 View commit details
  4. Bump pypy version to 3.10

    sergeyklay committed Oct 25, 2024
    Copy the full SHA
    3a611db View commit details
  5. Copy the full SHA
    3af3921 View commit details
  6. Drop Django 1.x support

    sergeyklay committed Oct 25, 2024
    Copy the full SHA
    3a58838 View commit details
  7. Bump sphinx and furo

    sergeyklay committed Oct 25, 2024
    Copy the full SHA
    ac6c0d4 View commit details
  8. Copy the full SHA
    839b1f3 View commit details
  9. Copy the full SHA
    01cd8e0 View commit details
  10. Copy the full SHA
    6321605 View commit details
  11. Copy the full SHA
    04642ad View commit details
  12. Copy the full SHA
    7af0b84 View commit details
  13. Add support for Django 5.1

    sergeyklay committed Oct 25, 2024
    Copy the full SHA
    09ef21a View commit details
  14. Bump actions/upload-artifact from 3 to 4

    Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
    - [Release notes](https://github.com/actions/upload-artifact/releases)
    - [Commits](actions/upload-artifact@v3...v4)
    
    ---
    updated-dependencies:
    - dependency-name: actions/upload-artifact
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 25, 2024
    Copy the full SHA
    0ee9141 View commit details
  15. Include prefix in the ImproperlyConfigured error

    spenserblack authored and sergeyklay committed Oct 25, 2024
    Copy the full SHA
    8968660 View commit details
  16. Bump version

    sergeyklay committed Oct 25, 2024
    Copy the full SHA
    023491a View commit details

Commits on Oct 29, 2024

  1. Bump actions/setup-python from 5.0.0 to 5.3.0

    Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.0.0 to 5.3.0.
    - [Release notes](https://github.com/actions/setup-python/releases)
    - [Commits](actions/setup-python@v5.0.0...v5.3.0)
    
    ---
    updated-dependencies:
    - dependency-name: actions/setup-python
      dependency-type: direct:production
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 29, 2024
    Copy the full SHA
    6c12976 View commit details
  2. Bump Vankka/pr-target-branch-action from 2 to 3

    Bumps [Vankka/pr-target-branch-action](https://github.com/vankka/pr-target-branch-action) from 2 to 3.
    - [Release notes](https://github.com/vankka/pr-target-branch-action/releases)
    - [Commits](Vankka/pr-target-branch-action@v2...v3)
    
    ---
    updated-dependencies:
    - dependency-name: Vankka/pr-target-branch-action
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 29, 2024
    Copy the full SHA
    9fa66f2 View commit details
  3. Bump actions/checkout from 4.1.1 to 4.2.2

    Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.2.2.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](actions/checkout@v4.1.1...v4.2.2)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-type: direct:production
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored and sergeyklay committed Oct 29, 2024
    Copy the full SHA
    f53b3f4 View commit details

Commits on Nov 3, 2024

  1. Add support for Django CockroachDB driver

    As per #509 adds support for Django CockroachDB
    stevewilliamsuk authored and sergeyklay committed Nov 3, 2024
    Copy the full SHA
    6f0a91b View commit details

Commits on Nov 4, 2024

  1. Update copyright notice

    sergeyklay committed Nov 4, 2024
    Copy the full SHA
    c8379ed View commit details
  2. Update change log

    sergeyklay committed Nov 4, 2024
    Copy the full SHA
    0f2e088 View commit details
  3. feat: Channels URL support

    lasuillard authored and sergeyklay committed Nov 4, 2024
    Copy the full SHA
    8729165 View commit details
  4. feat: Redis Pub/Sub

    lasuillard authored and sergeyklay committed Nov 4, 2024
    Copy the full SHA
    8b70d9f View commit details
  5. chore: Prefer Redis default port 6379

    lasuillard authored and sergeyklay committed Nov 4, 2024
    Copy the full SHA
    60164fd View commit details
  6. Update change log

    sergeyklay committed Nov 4, 2024
    Copy the full SHA
    d92e11b View commit details
  7. Add tests for kwarg overriding engine/backend from urls

    fdemmer authored and sergeyklay committed Nov 4, 2024
    Copy the full SHA
    6abdb86 View commit details
  8. Correct code style

    sergeyklay committed Nov 4, 2024
    Copy the full SHA
    8e844ac View commit details

Commits on Nov 8, 2024

  1. Update change log

    sergeyklay committed Nov 8, 2024
    Copy the full SHA
    00c8203 View commit details
  2. Merge branch 'main' into develop

    sergeyklay committed Nov 8, 2024
    Copy the full SHA
    993b36c View commit details

Commits on Jan 13, 2025

  1. Merge pull request #545 from joke2k/develop

    Release v0.12.0
    joke2k authored Jan 13, 2025
    Copy the full SHA
    176e812 View commit details
20 changes: 10 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -24,17 +24,17 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2

- name: Set up Python 3.10
uses: actions/setup-python@v4.7.0
- name: Set up Python 3.12
uses: actions/setup-python@v5.3.0
with:
python-version: '3.10'
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
pip install tox tox-gh-actions setuptools
- name: Check MANIFEST.in for completeness
run: tox -e manifest
@@ -44,7 +44,7 @@ jobs:

- name: Archive build artifacts
if: success()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
# To ensure that jobs don't overwrite existing artifacts,
# use a different name per job.
@@ -64,12 +64,12 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2

- name: Set up Python 3.10
uses: actions/setup-python@v4.7.0
- name: Set up Python 3.12
uses: actions/setup-python@v5.3.0
with:
python-version: '3.10'
python-version: '3.12'

- name: Install in dev mode
run: python -m pip install -e .
2 changes: 1 addition & 1 deletion .github/workflows/change-pr-target.yml
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ jobs:
check-branch:
runs-on: ubuntu-latest
steps:
- uses: Vankka/pr-target-branch-action@v2
- uses: Vankka/pr-target-branch-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
21 changes: 6 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -47,38 +47,29 @@ jobs:

matrix:
python:
- '3.8'
- '3.9'
- '3.10'
- '3.11'
- 'pypy-3.7'
- '3.12'
- '3.13'
- 'pypy-3.10'
os: [ ubuntu-latest, macos-latest, windows-latest ]

# These versions are no longer supported by Python team, and may
# eventually be dropped from GitHub Actions. The support of these
# versions by django-environ will continue for as long as possible,
# and may be discontinued at any time.
include:
- python: '3.6'
os: ubuntu-20.04
- python: '3.7'
os: ubuntu-20.04

steps:
- name: Checkout code
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2
with:
fetch-depth: 5

- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4.7.0
uses: actions/setup-python@v5.3.0
with:
python-version: ${{ matrix.python }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install tox tox-gh-actions
python -m pip install tox tox-gh-actions setuptools
- name: Setuptools self-test
run: |
8 changes: 4 additions & 4 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -45,16 +45,16 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}

- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
10 changes: 5 additions & 5 deletions .github/workflows/cs.yml
Original file line number Diff line number Diff line change
@@ -25,17 +25,17 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2

- name: Set up Python 3.10
uses: actions/setup-python@v4.7.0
- name: Set up Python 3.12
uses: actions/setup-python@v5.3.0
with:
python-version: '3.10'
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
pip install tox tox-gh-actions setuptools
- name: Lint with tox
run: tox -e lint
12 changes: 6 additions & 6 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -26,17 +26,17 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2

- name: Set up Python 3.10
uses: actions/setup-python@v4.7.0
- name: Set up Python 3.12
uses: actions/setup-python@v5.3.0
with:
python-version: '3.10'
python-version: '3.12'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions
pip install tox tox-gh-actions setuptools
- name: Check external links in the package documentation
run: tox -e linkcheck
@@ -46,7 +46,7 @@ jobs:

- name: Archive docs artifacts
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: docs
path: docs
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
4 changes: 2 additions & 2 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -17,7 +17,7 @@ build:
tools:
# Keep version in sync with tox.ini (testenv:docs) and
# docs.yml (GitHub Action Workflow).
python: '3.10'
python: '3.12'

python:
install:
45 changes: 44 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -5,6 +5,47 @@ All notable changes to this project will be documented in this file.
The format is inspired by `Keep a Changelog <https://keepachangelog.com/en/1.0.0/>`_
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.

`v0.12.0`_ - 8-November-2024
-----------------------------
Fixed
+++++
- Include prefix in the ``ImproperlyConfigured`` error message
`#513 <https://github.com/joke2k/django-environ/issues/513>`_.

Added
+++++
- Add support for Python 3.12 and 3.13
`#538 <https://github.com/joke2k/django-environ/issues/538>`_.
- Add support for Django 5.1
`#535 <https://github.com/joke2k/django-environ/issues/535>`_.
- Add support for Django CockroachDB driver
`#509 <https://github.com/joke2k/django-environ/issues/509>`_.
- Add support for Django Channels
`#266 <https://github.com/joke2k/django-environ/issues/266>`_.

Changed
+++++++
- Disabled inline comments handling by default due to potential side effects.
While the feature itself is useful, the project's philosophy dictates that
it should not be enabled by default for all users
`#499 <https://github.com/joke2k/django-environ/issues/499>`_.

Removed
+++++++
- Removed support of Python 3.6, 3.7 and 3.8
`#538 <https://github.com/joke2k/django-environ/issues/538>`_.
- Removed support of Django 1.x.
`#538 <https://github.com/joke2k/django-environ/issues/538>`_.


`v0.11.2`_ - 1-September-2023
-----------------------------
Fixed
+++++
- Revert "Add variable expansion." feature
due to `#490 <https://github.com/joke2k/django-environ/issues/490>`_.


`v0.11.1`_ - 30-August-2023
---------------------------
Fixed
@@ -23,7 +64,7 @@ Added
`#463 <https://github.com/joke2k/django-environ/pull/463>`_.
- Added variable expansion
`#468 <https://github.com/joke2k/django-environ/pull/468>`_.
- Added capability to handle comments after #, after quoted values,
- Added capability to handle comments after ``#``, after quoted values,
like ``KEY= 'part1 # part2' # comment``
`#475 <https://github.com/joke2k/django-environ/pull/475>`_.
- Added support for ``interpolate`` parameter
@@ -380,6 +421,8 @@ Added
- Initial release.


.. _v0.12.0: https://github.com/joke2k/django-environ/compare/v0.11.2...v0.12.0
.. _v0.11.2: https://github.com/joke2k/django-environ/compare/v0.11.1...v0.11.2
.. _v0.11.1: https://github.com/joke2k/django-environ/compare/v0.11.0...v0.11.1
.. _v0.11.0: https://github.com/joke2k/django-environ/compare/v0.10.0...v0.11.0
.. _v0.10.0: https://github.com/joke2k/django-environ/compare/v0.9.0...v0.10.0
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
7 changes: 4 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
@@ -97,7 +97,8 @@ approach, some connection strings are expressed as url, so this package can pars
it and return a ``urllib.parse.ParseResult``. These strings from ``os.environ``
are loaded from a ``.env`` file and filled in ``os.environ`` with ``setdefault``
method, to avoid to overwrite the real environ.
A similar approach is used in `Two Scoops of Django <https://www.feldroy.com/books/two-scoops-of-django-3-x>`_
A similar approach is used in
`Two Scoops of Django <https://web.archive.org/web/20240121133956/https://www.feldroy.com/books/two-scoops-of-django-3-x>`_
book and explained in `12factor-django <https://wellfire.co/learn/easier-12-factor-django>`_
article.

@@ -126,8 +127,8 @@ its documentation lives at `Read the Docs <https://django-environ.readthedocs.io
the code on `GitHub <https://github.com/joke2k/django-environ>`_,
and the latest release on `PyPI <https://pypi.org/project/django-environ/>`_.

It’s rigorously tested on Python 3.6+, and officially supports
Django 1.11, 2.2, 3.0, 3.1, 3.2, 4.0, 4.1 and 4.2.
It’s rigorously tested on Python 3.9+, and officially supports
Django 2.2, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2, 5.0, and 5.1.

If you'd like to contribute to ``django-environ`` you're most welcome!

2 changes: 1 addition & 1 deletion SECURITY.rst
Original file line number Diff line number Diff line change
@@ -6,5 +6,5 @@ Reporting a Vulnerability
-------------------------

If you discover a security vulnerability within ``django-environ``, please
send an e-mail to Serghei Iakovlev via egrep@protonmail.ch. All security
send an e-mail to Serghei Iakovlev via oss@serghei.pl. All security
vulnerabilities will be promptly addressed.
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2023, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion docs/docutils.conf
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
4 changes: 2 additions & 2 deletions docs/install.rst
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@ Installation
Requirements
============

* `Django <https://www.djangoproject.com/>`_ >= 1.11
* `Python <https://www.python.org/>`_ >= 3.5
* `Django <https://www.djangoproject.com/>`_ >= 2.2
* `Python <https://www.python.org/>`_ >= 3.9

Installing django-environ
=========================
22 changes: 0 additions & 22 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
@@ -23,28 +23,6 @@ And use it with ``settings.py`` as follows:
:start-after: -code-begin-
:end-before: -overview-

Variables can contain references to another variables: ``$VAR`` or ``${VAR}``.
Referenced variables are searched in the environment and within all definitions
in the ``.env`` file. References are checked for recursion (self-reference).
Exception is thrown if any reference results in infinite loop on any level
of recursion. Variable values are substituted similar to shell parameter
expansion. Example:

.. code-block:: shell
# shell
export POSTGRES_USERNAME='user' POSTGRES_PASSWORD='SECRET'
.. code-block:: shell
# .env
POSTGRES_HOSTNAME='example.com'
POSTGRES_DB='database'
DATABASE_URL="postgres://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@${POSTGRES_HOSTNAME}:5432/${POSTGRES_DB}"
The value of ``DATABASE_URL`` variable will become
``postgres://user:SECRET@example.com:5432/database``.

The ``.env`` file should be specific to the environment and not checked into
version control, it is best practice documenting the ``.env`` file with an example.
For example, you can also add ``.env.dist`` with a template of your variables to
65 changes: 65 additions & 0 deletions docs/tips.rst
Original file line number Diff line number Diff line change
@@ -2,6 +2,71 @@
Tips
====

Handling Inline Comments in .env Files
======================================

``django-environ`` provides an optional feature to parse inline comments in ``.env``
files. This is controlled by the ``parse_comments`` parameter in the ``read_env``
method.

Modes
-----

- **Enabled (``parse_comments=True``)**: Inline comments starting with ``#`` will be ignored.
- **Disabled (``parse_comments=False``)**: The entire line, including comments, will be read as the value.
- **Default**: The behavior is the same as when ``parse_comments=False``.

Side Effects
------------

While this feature can be useful for adding context to your ``.env`` files,
it can introduce unexpected behavior. For example, if your value includes
a ``#`` symbol, it will be truncated when ``parse_comments=True``.

Why Disabled by Default?
------------------------

In line with the project's philosophy of being explicit and avoiding unexpected behavior,
this feature is disabled by default. If you understand the implications and find the feature
useful, you can enable it explicitly.

Example
-------

Here is an example demonstrating the different modes of handling inline comments.

**.env file contents**:

.. code-block:: shell
# .env file contents
BOOL_TRUE_WITH_COMMENT=True # This is a comment
STR_WITH_HASH=foo#bar # This is also a comment
**Python code**:

.. code-block:: python
import environ
# Using parse_comments=True
env = environ.Env()
env.read_env(parse_comments=True)
print(env('BOOL_TRUE_WITH_COMMENT')) # Output: True
print(env('STR_WITH_HASH')) # Output: foo
# Using parse_comments=False
env = environ.Env()
env.read_env(parse_comments=False)
print(env('BOOL_TRUE_WITH_COMMENT')) # Output: True # This is a comment
print(env('STR_WITH_HASH')) # Output: foo#bar # This is also a comment
# Using default behavior
env = environ.Env()
env.read_env()
print(env('BOOL_TRUE_WITH_COMMENT')) # Output: True # This is a comment
print(env('STR_WITH_HASH')) # Output: foo#bar # This is also a comment
Docker-style file based variables
=================================
8 changes: 4 additions & 4 deletions environ/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2023, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -18,10 +18,10 @@
from .environ import *


__copyright__ = 'Copyright (C) 2013-2022 Daniele Faraglia'
__copyright__ = 'Copyright (C) 2013-2023 Daniele Faraglia'
"""The copyright notice of the package."""

__version__ = '0.11.1'
__version__ = '0.12.0'
"""The version of the package."""

__license__ = 'MIT'
@@ -36,7 +36,7 @@
__maintainer__ = 'Serghei Iakovlev'
"""The maintainer of the package."""

__maintainer_email__ = 'egrep@protonmail.ch'
__maintainer_email__ = 'oss@serghei.pl'
"""The email of the maintainer of the package."""

__url__ = 'https://django-environ.readthedocs.org'
2 changes: 1 addition & 1 deletion environ/compat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
158 changes: 92 additions & 66 deletions environ/environ.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -17,9 +17,7 @@
import os
import re
import sys
import threading
import warnings
from os.path import expandvars
from urllib.parse import (
parse_qs,
ParseResult,
@@ -42,9 +40,6 @@
Openable = (str, os.PathLike)
logger = logging.getLogger(__name__)

# Variables which values should not be expanded
NOT_EXPANDED = 'DJANGO_SECRET_KEY', 'CACHE_URL'


def _cast(value):
# Safely evaluate an expression node or a string containing a Python
@@ -120,6 +115,7 @@ class Env:
'psql': DJANGO_POSTGRES,
'pgsql': DJANGO_POSTGRES,
'postgis': 'django.contrib.gis.db.backends.postgis',
'cockroachdb': 'django_cockroachdb',
'mysql': 'django.db.backends.mysql',
'mysql2': 'django.db.backends.mysql',
'mysql-connector': 'mysql.connector.django',
@@ -194,11 +190,14 @@ class Env:
for s in ('', 's')]
CLOUDSQL = 'cloudsql'

VAR = re.compile(r'(?<!\\)\$\{?(?P<name>[A-Z_][0-9A-Z_]*)}?',
re.IGNORECASE)
DEFAULT_CHANNELS_ENV = "CHANNELS_URL"
CHANNELS_SCHEMES = {
"inmemory": "channels.layers.InMemoryChannelLayer",
"redis": "channels_redis.core.RedisChannelLayer",
"redis+pubsub": "channels_redis.pubsub.RedisPubSubChannelLayer"
}

def __init__(self, **scheme):
self._local = threading.local()
self.smart_cast = True
self.escape_proxy = False
self.prefix = ""
@@ -346,19 +345,28 @@ def search_url(self, var=DEFAULT_SEARCH_ENV, default=NOTSET, engine=None):
engine=engine
)

def channels_url(self, var=DEFAULT_CHANNELS_ENV, default=NOTSET,
backend=None):
"""Returns a config dictionary, defaulting to CHANNELS_URL.
:rtype: dict
"""
return self.channels_url_config(
self.url(var, default=default),
backend=backend
)

channels = channels_url

def path(self, var, default=NOTSET, **kwargs):
"""
:rtype: Path
"""
return Path(self.get_value(var, default=default), **kwargs)

def get_value(self, var, cast=None, # pylint: disable=R0913
default=NOTSET, parse_default=False, add_prefix=True):
def get_value(self, var, cast=None, default=NOTSET, parse_default=False):
"""Return value for given environment variable.
- Expand variables referenced as ``$VAR`` or ``${VAR}``.
- Detect infinite recursion in expansion (self-reference).
:param str var:
Name of variable.
:param collections.abc.Callable or None cast:
@@ -367,33 +375,15 @@ def get_value(self, var, cast=None, # pylint: disable=R0913
If var not present in environ, return this instead.
:param bool parse_default:
Force to parse default.
:param bool add_prefix:
Whether to add prefix to variable name.
:returns: Value from environment or default (if set).
:rtype: typing.IO[typing.Any]
"""
var_name = f'{self.prefix}{var}' if add_prefix else var
if not hasattr(self._local, 'vars'):
self._local.vars = set()
if var_name in self._local.vars:
error_msg = f"Environment variable '{var_name}' recursively "\
"references itself (eventually)"
raise ImproperlyConfigured(error_msg)

self._local.vars.add(var_name)
try:
return self._get_value(
var_name, cast=cast, default=default,
parse_default=parse_default)
finally:
self._local.vars.remove(var_name)

def _get_value(self, var_name, cast=None, default=NOTSET,
parse_default=False):

logger.debug(
"get '%s' casted as '%s' with default '%s'",
var_name, cast, default)
var, cast, default)

var_name = f'{self.prefix}{var}'
if var_name in self.scheme:
var_info = self.scheme[var_name]

@@ -424,32 +414,21 @@ def _get_value(self, var_name, cast=None, default=NOTSET,

value = default

# Expand variables
if isinstance(value, (bytes, str)) and var_name not in NOT_EXPANDED:
def repl(match_):
return self.get_value(
match_.group('name'), cast=cast, default=default,
parse_default=parse_default, add_prefix=False)

is_bytes = isinstance(value, bytes)
if is_bytes:
value = value.decode('utf-8')
value = self.VAR.sub(repl, value)
value = expandvars(value)
if is_bytes:
value = value.encode('utf-8')

# Resolve any proxied values
prefix = b'$' if isinstance(value, bytes) else '$'
escape = rb'\$' if isinstance(value, bytes) else r'\$'
if hasattr(value, 'startswith') and value.startswith(prefix):
value = value.lstrip(prefix)
value = self.get_value(value, cast=cast, default=default)

if self.escape_proxy and hasattr(value, 'replace'):
value = value.replace(escape, prefix)

# Smart casting
if self.smart_cast and cast is None and default is not None \
and not isinstance(default, NoValue):
cast = type(default)
if self.smart_cast:
if cast is None and default is not None and \
not isinstance(default, NoValue):
cast = type(default)

value = None if default is None and value == '' else value

@@ -778,6 +757,33 @@ def email_url_config(cls, url, backend=None):

return config

@classmethod
def channels_url_config(cls, url, backend=None):
"""Parse an arbitrary channels URL.
:param urllib.parse.ParseResult or str url:
Email URL to parse.
:param str or None backend:
If None, the backend is evaluates from the ``url``.
:return: Parsed channels URL.
:rtype: dict
"""
config = {}
url = urlparse(url) if not isinstance(url, cls.URL_CLASS) else url

if backend:
config["BACKEND"] = backend
elif url.scheme not in cls.CHANNELS_SCHEMES:
raise ImproperlyConfigured(f"Invalid channels schema {url.scheme}")
else:
config["BACKEND"] = cls.CHANNELS_SCHEMES[url.scheme]
if url.scheme in ("redis", "redis+pubsub"):
config["CONFIG"] = {
"hosts": [url._replace(scheme="redis").geturl()]
}

return config

@classmethod
def _parse_common_search_params(cls, url):
cfg = {}
@@ -851,7 +857,7 @@ def search_url_config(cls, url, engine=None):
:param urllib.parse.ParseResult or str url:
Search URL to parse.
:param str or None engine:
If None, the engine is evaluates from the ``url``.
If None, the engine is evaluating from the ``url``.
:return: Parsed search URL.
:rtype: dict
"""
@@ -904,8 +910,8 @@ def search_url_config(cls, url, engine=None):
return config

@classmethod
def read_env(cls, env_file=None, overwrite=False, encoding='utf8',
**overrides):
def read_env(cls, env_file=None, overwrite=False, parse_comments=False,
encoding='utf8', **overrides):
r"""Read a .env file into os.environ.
If not given a path to a dotenv path, does filthy magic stack
@@ -925,6 +931,8 @@ def read_env(cls, env_file=None, overwrite=False, encoding='utf8',
the Django settings module from the Django project root.
:param overwrite: ``overwrite=True`` will force an overwrite of
existing environment variables.
:param parse_comments: Determines whether to recognize and ignore
inline comments in the .env file. Default is False.
:param encoding: The encoding to use when reading the environment file.
:param \**overrides: Any additional keyword arguments provided directly
to read_env will be added to the environment. If the key matches an
@@ -969,22 +977,40 @@ def _keep_escaped_format_characters(match):
for line in content.splitlines():
m1 = re.match(r'\A(?:export )?([A-Za-z_0-9]+)=(.*)\Z', line)
if m1:

# Example:
#
# line: KEY_499=abc#def
# key: KEY_499
# val: abc#def
key, val = m1.group(1), m1.group(2)
# Look for value in quotes, ignore post-# comments
# (outside quotes)
m2 = re.match(r"\A\s*'(?<!\\)(.*)'\s*(#.*\s*)?\Z", val)
if m2:
val = m2.group(1)

if not parse_comments:
# Default behavior
#
# Look for value in single quotes
m2 = re.match(r"\A'(.*)'\Z", val)
if m2:
val = m2.group(1)
else:
# For no quotes, find value, ignore comments
# after the first #
m2a = re.match(r"\A(.*?)(#.*\s*)?\Z", val)
if m2a:
val = m2a.group(1)
# Ignore post-# comments (outside quotes).
# Something like ['val' # comment] becomes ['val'].
m2 = re.match(r"\A\s*'(?<!\\)(.*)'\s*(#.*\s*)?\Z", val)
if m2:
val = m2.group(1)
else:
# For no quotes, find value, ignore comments
# after the first #
m2a = re.match(r"\A(.*?)(#.*\s*)?\Z", val)
if m2a:
val = m2a.group(1)

# Look for value in double quotes
m3 = re.match(r'\A"(.*)"\Z', val)
if m3:
val = re.sub(r'\\(.)', _keep_escaped_format_characters,
m3.group(1))

overrides[key] = str(val)
elif not line or line.startswith('#'):
# ignore warnings for empty line-breaks or comments
2 changes: 1 addition & 1 deletion environ/fileaware_mapping.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
19 changes: 9 additions & 10 deletions setup.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
#
# This file is part of the django-environ.
#
# Copyright (c) 2021-2023, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -135,16 +135,15 @@ def get_version_string():
'Development Status :: 5 - Production/Stable',

'Framework :: Django',
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.0',
'Framework :: Django :: 2.1',
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.0',
'Framework :: Django :: 3.1',
'Framework :: Django :: 3.2',
'Framework :: Django :: 4.0',
'Framework :: Django :: 4.1',
'Framework :: Django :: 4.2',
'Framework :: Django :: 5.0',
'Framework :: Django :: 5.1',

'Operating System :: OS Independent',

@@ -153,12 +152,11 @@ def get_version_string():

'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',

@@ -183,11 +181,12 @@ def get_version_string():
'testing': [
'coverage[toml]>=5.0a4', # Code coverage measurement for Python
'pytest>=4.6.11', # Our tests framework
'setuptools>=71.0.0', # Needed as a dependency for some tests
],
# Dependencies that are required to build documentation
'docs': [
'furo>=2021.8.17b43,==2021.8.*', # Sphinx documentation theme
'sphinx>=3.5.0', # Python documentation generator
'furo>=2024.8.6', # Sphinx documentation theme
'sphinx>=5.0', # Python documentation generator
'sphinx-notfound-page', # Create a custom 404 page
],
}
@@ -230,7 +229,7 @@ def get_version_string():
platforms=['any'],
include_package_data=True,
zip_safe=False,
python_requires='>=3.6,<4',
python_requires='>=3.9,<4',
install_requires=INSTALL_REQUIRES,
dependency_links=DEPENDENCY_LINKS,
extras_require=EXTRAS_REQUIRE,
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion tests/asserts.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion tests/fixtures.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion tests/test_cache.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
25 changes: 25 additions & 0 deletions tests/test_channels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
# the LICENSE.txt file that was distributed with this source code.

from environ import Env


def test_channels_parsing():
url = "inmemory://"
result = Env.channels_url_config(url)
assert result["BACKEND"] == "channels.layers.InMemoryChannelLayer"

url = "redis://user:password@localhost:6379/0"
result = Env.channels_url_config(url)
assert result["BACKEND"] == "channels_redis.core.RedisChannelLayer"
assert result["CONFIG"]["hosts"][0] == "redis://user:password@localhost:6379/0"

url = "redis+pubsub://user:password@localhost:6379/0"
result = Env.channels_url_config(url)
assert result["BACKEND"] == "channels_redis.pubsub.RedisPubSubChannelLayer"
assert result["CONFIG"]["hosts"][0] == "redis://user:password@localhost:6379/0"
21 changes: 20 additions & 1 deletion tests/test_db.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -59,6 +59,14 @@
'',
''
),
# cockroachdb://username:secret@test.example.com:26258/dbname
('cockroachdb://username:secret@test.example.com:26258/dbname',
'django_cockroachdb',
'dbname',
'test.example.com',
'username',
'secret',
26258),
# mysqlgis://user:password@host:port/dbname
('mysqlgis://enigma:secret@example.com:5431/dbname',
'django.contrib.gis.db.backends.mysql',
@@ -156,6 +164,7 @@
'postgis',
'postgres_cluster',
'postgres_no_ports',
'cockroachdb',
'mysqlgis',
'cleardb',
'mysql_no_password',
@@ -188,6 +197,16 @@ def test_db_parsing(url, engine, name, host, user, passwd, port):
assert config['OPTIONS'] == {'reconnect': 'true'}


def test_custom_db_engine():
"""Override ENGINE determined from schema."""
env_url = 'postgres://enigma:secret@example.com:5431/dbname'

engine = 'mypackage.backends.whatever'
url = Env.db_url_config(env_url, engine=engine)

assert url['ENGINE'] == engine


def test_postgres_complex_db_name_parsing():
"""Make sure we can use complex postgres host."""
env_url = (
12 changes: 11 additions & 1 deletion tests/test_email.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -22,3 +22,13 @@ def test_smtp_parsing():
assert url['EMAIL_PORT'] == 587
assert url['EMAIL_USE_TLS'] is True
assert url['EMAIL_FILE_PATH'] == ''


def test_custom_email_backend():
"""Override EMAIL_BACKEND determined from schema."""
url = 'smtps://user@domain.com:password@smtp.example.com:587'

backend = 'mypackage.backends.whatever'
url = Env.email_url_config(url, backend=backend)

assert url['EMAIL_BACKEND'] == backend
67 changes: 62 additions & 5 deletions tests/test_env.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
# the LICENSE.txt file that was distributed with this source code.

import os
import tempfile
from urllib.parse import quote

import pytest
@@ -21,6 +22,59 @@
from .fixtures import FakeEnv


@pytest.mark.parametrize(
'variable,value,raw_value,parse_comments',
[
# parse_comments=True
('BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT', 'True', "'True' # comment\n", True),
('BOOL_TRUE_BOOL_WITH_COMMENT', 'True ', "True # comment\n", True),
('STR_QUOTED_IGNORE_COMMENT', 'foo', " 'foo' # comment\n", True),
('STR_QUOTED_INCLUDE_HASH', 'foo # with hash', "'foo # with hash' # not comment\n", True),
('SECRET_KEY_1', '"abc', '"abc#def"\n', True),
('SECRET_KEY_2', 'abc', 'abc#def\n', True),
('SECRET_KEY_3', 'abc#def', "'abc#def'\n", True),
# parse_comments=False
('BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT', "'True' # comment", "'True' # comment\n", False),
('BOOL_TRUE_BOOL_WITH_COMMENT', 'True # comment', "True # comment\n", False),
('STR_QUOTED_IGNORE_COMMENT', " 'foo' # comment", " 'foo' # comment\n", False),
('STR_QUOTED_INCLUDE_HASH', "'foo # with hash' # not comment", "'foo # with hash' # not comment\n", False),
('SECRET_KEY_1', 'abc#def', '"abc#def"\n', False),
('SECRET_KEY_2', 'abc#def', 'abc#def\n', False),
('SECRET_KEY_3', 'abc#def', "'abc#def'\n", False),
# parse_comments is not defined (default behavior)
('BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT', "'True' # comment", "'True' # comment\n", None),
('BOOL_TRUE_BOOL_WITH_COMMENT', 'True # comment', "True # comment\n", None),
('STR_QUOTED_IGNORE_COMMENT', " 'foo' # comment", " 'foo' # comment\n", None),
('STR_QUOTED_INCLUDE_HASH', "'foo # with hash' # not comment", "'foo # with hash' # not comment\n", None),
('SECRET_KEY_1', 'abc#def', '"abc#def"\n', None),
('SECRET_KEY_2', 'abc#def', 'abc#def\n', None),
('SECRET_KEY_3', 'abc#def', "'abc#def'\n", None),
],
)
def test_parse_comments(variable, value, raw_value, parse_comments):
old_environ = os.environ

with tempfile.TemporaryDirectory() as temp_dir:
env_path = os.path.join(temp_dir, '.env')

with open(env_path, 'w') as f:
f.write(f'{variable}={raw_value}\n')
f.flush()

env = Env()
Env.ENVIRON = {}
if parse_comments is None:
env.read_env(env_path)
else:
env.read_env(env_path, parse_comments=parse_comments)

assert env(variable) == value

os.environ = old_environ


class TestEnv:
def setup_method(self, method):
"""
@@ -112,10 +166,8 @@ def test_float(self, value, variable):
[
(True, 'BOOL_TRUE_STRING_LIKE_INT'),
(True, 'BOOL_TRUE_STRING_LIKE_BOOL'),
(True, 'BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT'),
(True, 'BOOL_TRUE_INT'),
(True, 'BOOL_TRUE_BOOL'),
(True, 'BOOL_TRUE_BOOL_WITH_COMMENT'),
(True, 'BOOL_TRUE_STRING_1'),
(True, 'BOOL_TRUE_STRING_2'),
(True, 'BOOL_TRUE_STRING_3'),
@@ -341,8 +393,6 @@ def test_path(self):

def test_smart_cast(self):
assert self.env.get_value('STR_VAR', default='string') == 'bar'
assert self.env.get_value('STR_QUOTED_IGNORE_COMMENT', default='string') == 'foo'
assert self.env.get_value('STR_QUOTED_INCLUDE_HASH', default='string') == 'foo # with hash'
assert self.env.get_value('BOOL_TRUE_STRING_LIKE_INT', default=True)
assert not self.env.get_value(
'BOOL_FALSE_STRING_LIKE_INT',
@@ -357,6 +407,13 @@ def test_prefix(self):
self.env.prefix = 'PREFIX_'
assert self.env('TEST') == 'foo'

def test_prefix_and_not_present_without_default(self):
self.env.prefix = 'PREFIX_'
with pytest.raises(ImproperlyConfigured) as excinfo:
self.env('not_present')
assert str(excinfo.value) == 'Set the PREFIX_not_present environment variable'
assert excinfo.value.__cause__ is not None


class TestFileEnv(TestEnv):
def setup_method(self, method):
4 changes: 0 additions & 4 deletions tests/test_env.txt
Original file line number Diff line number Diff line change
@@ -25,8 +25,6 @@ BOOL_TRUE_STRING_3='yes'
BOOL_TRUE_STRING_4='y'
BOOL_TRUE_STRING_5='true'
BOOL_TRUE_BOOL=True
BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT='True' # comment
BOOL_TRUE_BOOL_WITH_COMMENT=True # comment
BOOL_FALSE_STRING_LIKE_INT='0'
BOOL_FALSE_INT=0
BOOL_FALSE_STRING_LIKE_BOOL='False'
@@ -47,8 +45,6 @@ INT_VAR=42
STR_LIST_WITH_SPACES= foo, spaces
STR_LIST_WITH_SPACES_QUOTED=' foo',' quoted'
STR_VAR=bar
STR_QUOTED_IGNORE_COMMENT= 'foo' # comment
STR_QUOTED_INCLUDE_HASH='foo # with hash' # not comment
MULTILINE_STR_VAR=foo\nbar
MULTILINE_QUOTED_STR_VAR="---BEGIN---\r\n---END---"
MULTILINE_ESCAPED_STR_VAR=---BEGIN---\\n---END---
27 changes: 0 additions & 27 deletions tests/test_expansion.py

This file was deleted.

9 changes: 0 additions & 9 deletions tests/test_expansion.txt

This file was deleted.

2 changes: 1 addition & 1 deletion tests/test_fileaware.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion tests/test_path.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
2 changes: 1 addition & 1 deletion tests/test_schema.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
12 changes: 11 additions & 1 deletion tests/test_search.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -86,6 +86,16 @@ def test_elasticsearch_parsing(url, engine, scheme):
assert url["URL"].startswith(scheme + ":")


def test_custom_search_engine():
"""Override ENGINE determined from schema."""
env_url = 'elasticsearch://127.0.0.1:9200/index'

engine = 'mypackage.backends.whatever'
url = Env.db_url_config(env_url, engine=engine)

assert url['ENGINE'] == engine


@pytest.mark.parametrize('storage', ['file', 'ram'])
def test_whoosh_parsing(whoosh_url, storage):
post_limit = 128 * 1024 * 1024
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2022, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
21 changes: 9 additions & 12 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is part of the django-environ.
#
# Copyright (c) 2021-2023, Serghei Iakovlev <egrep@protonmail.ch>
# Copyright (c) 2021-2024, Serghei Iakovlev <oss@serghei.pl>
# Copyright (c) 2013-2021, Daniele Faraglia <daniele.faraglia@gmail.com>
#
# For the full copyright and license information, please view
@@ -18,26 +18,23 @@ envlist =
docs
lint
manifest
py{36,37,38,39,310,311}-django{111,22}
py{36,37,38,39,310,311}-django{30,31,32}
py{38,39,310,311}-django{40,41,42}
pypy-django{111,22,30,31,32}
py{39,310,311,312,313}-django{22,30,31,32,40,41,42}
py{310,311,312,313}-django{50,51}
pypy-django{22,30,31,32}

[gh-actions]
python =
3.6: py36
3.7: py37
3.8: py38
3.9: py39
3.10: py310
3.11: py311
pypy-3.7: pypy
3.12: py312
3.13: py313
pypy-3.10: pypy

[testenv]
description = Unit tests
extras = testing
deps =
django111: Django>=1.11,<2
django22: Django>=2.2,<3.0
django30: Django>=3.0,<3.1
django31: Django>=3.1,<3.2
@@ -91,7 +88,7 @@ commands =
description = Check external links in the package documentation
# Keep basepython in sync with .readthedocs.yml and docs.yml
# (GitHub Action Workflow).
basepython = python3.10
basepython = python3.12
extras = docs
commands =
{envpython} -m sphinx \
@@ -109,7 +106,7 @@ isolated_build = true
description = Build package documentation (HTML)
# Keep basepython in sync with .readthedocs.yml and docs.yml
# (GitHub Action Workflow).
basepython = python3.10
basepython = python3.12
extras = docs
commands =
{envpython} -m sphinx \