Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-django-environ for 
openSUSE:Factory checked in at 2026-03-23 17:11:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-environ (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-environ.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-environ"

Mon Mar 23 17:11:38 2026 rev:9 rq:1341706 version:0.13.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-environ/python-django-environ.changes  
    2026-02-17 16:47:59.393044140 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-django-environ.new.8177/python-django-environ.changes
    2026-03-23 17:11:57.706164012 +0100
@@ -1,0 +2,15 @@
+Sat Mar 21 14:44:05 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 0.13.0:
+  * Added optional warnings when defaults are used #582.
+  * Added choices argument support for value validation in
+    Env.str(...) #555.
+  * Added Valkey support via valkey:// and valkeys:// cache URL
+    schemes #554.
+  * Added support for rediss:// scheme in channels URL parsing
+    #573.
+  * Added django-prometheus database backend aliases to DB URL
+    parsing schemes #559.
+  * Declared support for Python 3.14 #580.
+
+-------------------------------------------------------------------

Old:
----
  django_environ-0.12.1.tar.gz

New:
----
  django_environ-0.13.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-environ.spec ++++++
--- /var/tmp/diff_new_pack.0nZDv1/_old  2026-03-23 17:11:58.338190336 +0100
+++ /var/tmp/diff_new_pack.0nZDv1/_new  2026-03-23 17:11:58.338190336 +0100
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-django-environ
-Version:        0.12.1
+Version:        0.13.0
 Release:        0
 Summary:        Django application configuration via environment variables
 License:        MIT

++++++ django_environ-0.12.1.tar.gz -> django_environ-0.13.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_environ-0.12.1/.github/copilot-instructions.md 
new/django_environ-0.13.0/.github/copilot-instructions.md
--- old/django_environ-0.12.1/.github/copilot-instructions.md   1970-01-01 
01:00:00.000000000 +0100
+++ new/django_environ-0.13.0/.github/copilot-instructions.md   2026-02-17 
22:58:34.000000000 +0100
@@ -0,0 +1,44 @@
+# Copilot instructions for `django-environ`
+
+## Build, test, and lint commands
+
+- Install local dev dependencies:
+  - `python3 -m venv .venv && . .venv/bin/activate`
+  - `python -m pip install -U pip tox tox-gh-actions setuptools`
+- Run the default local test workflow (matches CI entrypoint): `tox`
+- Run one tox environment explicitly (recommended for iteration): `tox -e 
py312-django51`
+- Run a single test: `tox -e py312-django51 -- 
tests/test_env.py::TestEnv::test_int`
+- Lint: `tox -e lint`
+- Build/package checks: `tox -e build`
+- Docs and doctests: `tox -e docs`
+- Link checks in docs: `tox -e linkcheck`
+- Manifest validation: `tox -e manifest`
+- Coverage combine/report after test runs: `tox -e coverage-report`
+- Locally ZSH is used, keep in mind when you executing commands (like commits 
messages using back-ticks)
+- For GitHub CLI commands that can invoke a pager (`gh pr view`, `gh issue 
view`, etc.), use `GH_PAGER=cat` to avoid interactive blocking.
+
+## High-level architecture
+
+- Core implementation is concentrated in `environ/environ.py`:
+  - `Env` provides typed env access (`env()`, `env.bool()`, `env.db()`, 
`env.cache()`, etc.).
+  - URL-to-Django-settings parsing is centralized in class methods and scheme 
maps (`DB_SCHEMES`, `CACHE_SCHEMES`, `EMAIL_SCHEMES`, `SEARCH_SCHEMES`, 
`CHANNELS_SCHEMES`).
+  - `Env.read_env()` loads `.env` values into `os.environ` using `setdefault` 
semantics unless `overwrite=True`.
+  - `Path` is a small path helper used by users in Django settings modules.
+- `FileAwareEnv` (in `environ/environ.py`) swaps `ENVIRON` to 
`FileAwareMapping` (`environ/fileaware_mapping.py`) so `FOO_FILE` takes 
precedence over `FOO`.
+- Compatibility/backward-driver selection lives in `environ/compat.py` 
(Django/redis/postgres/pymemcache driver decisions).
+- Public package API is exported via `environ/__init__.py` (`from .environ 
import *`), and package metadata/version are also defined there and reused by 
`setup.py`.
+- Tests are behavior-first and organized by parser domain (`tests/test_db.py`, 
`tests/test_cache.py`, `tests/test_email.py`, `tests/test_search.py`, etc.).
+
+## Key repository conventions
+
+- Keep changes small and targeted; this project expects focused PRs against 
`develop` (see `CONTRIBUTING.rst`).
+- For public-facing text (e.g., commit messages, PR/issue comments), always 
write in English.
+- Use `develop` as the operational integration branch; do not create extra 
release/aggregation branches unless explicitly requested.
+- When adding/changing URL parsing behavior, update both the relevant scheme 
map(s) in `Env` and matching domain tests under `tests/`.
+- Preserve `.env` loading behavior in `read_env()`:
+  - default is non-overwriting (`setdefault`)
+  - comments parsing is opt-in (`parse_comments=True`)
+  - invalid lines should warn rather than crash.
+- Tests often isolate environment state by replacing `os.environ` / 
`Env.ENVIRON` with fixtures (for example `FakeEnv.generate_data()`); follow 
that pattern for deterministic tests.
+- CI matrix is driven by tox env naming (`py<python>-django<version>`), with 
mapping in `[gh-actions]` in `tox.ini`; keep new env names consistent there.
+- For docs tasks, keep Python version assumptions aligned between tox docs 
envs and GitHub workflows (`docs.yml`, comments in `tox.ini`).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_environ-0.12.1/.github/skills/django-environ-release/SKILL.md 
new/django_environ-0.13.0/.github/skills/django-environ-release/SKILL.md
--- old/django_environ-0.12.1/.github/skills/django-environ-release/SKILL.md    
1970-01-01 01:00:00.000000000 +0100
+++ new/django_environ-0.13.0/.github/skills/django-environ-release/SKILL.md    
2026-02-18 01:49:59.000000000 +0100
@@ -0,0 +1,144 @@
+---
+name: django-environ-release
+description: Run and validate the django-environ release workflow (prepare, 
verify, and publish).
+---
+
+# django-environ release skill
+
+Use this skill when preparing a new django-environ release (for example 
`0.12.2`).
+
+## Prerequisites
+
+- Work from `develop` unless maintainers explicitly choose another branch.
+- Ensure local branch is up to date:
+
+```bash
+git checkout develop
+git fetch origin
+git pull --ff-only origin develop
+```
+
+- Confirm working tree is clean for tracked files.
+
+## 1) Update version and release notes
+
+- Bump version in `environ/__init__.py` (`__version__`).
+- Update `CHANGELOG.rst` with release entry and key changes mentioning 
contributors if possibile.
+- If needed, align `RELEASE_NOTES.md` / roadmap references.
+
+## 2) Validate locally
+
+Run the project checks used by CI/release flow:
+
+```bash
+tox -e lint
+tox -e build
+pytest -q
+```
+
+Optional (recommended):
+
+```bash
+tox -e docs
+tox -e manifest
+```
+
+## 3) Commit and push
+
+- Create focused commits (version bump and notes can be split if useful).
+- Include Co-authored-by trailer when required by project policy.
+- Push branch and open PR targeting `develop`.
+
+## 4) Release readiness checklist
+
+- [ ] Version bumped and consistent
+- [ ] Changelog updated
+- [ ] Build artifacts pass (`tox -e build`)
+- [ ] Tests pass (`pytest -q`)
+- [ ] Lint passes (`tox -e lint`)
+- [ ] Docs build passes (`tox -e docs`, if doc changes)
+- [ ] PR merged into `develop`
+
+## 5) Publish (maintainer step)
+
+When maintainers are ready to publish:
+
+- Create/tag release commit for the new version.
+- Trigger release pipeline / upload to PyPI according to current maintainer 
process.
+- Announce release with concise highlights.
+
+## 6) Copy/paste command blocks (required output)
+
+When using this skill, always provide a ready-to-run command list for:
+- merge into `main`
+- build artifacts
+- upload to TestPyPI
+- install from TestPyPI and run smoke test
+- upload to production PyPI
+
+Use command blocks like these (adjust version/tag as needed):
+
+```bash
+# Merge develop into main and push
+git checkout main
+git fetch origin
+git pull --ff-only origin main
+git merge --ff-only origin/develop
+git push origin main
+```
+
+```bash
+# Build distribution artifacts
+python -m pip install -U pip build twine
+rm -rf dist/ build/ *.egg-info
+python -m build
+twine check dist/*
+```
+
+```bash
+# Upload to TestPyPI
+python -m twine upload --repository testpypi dist/*
+```
+
+```bash
+# Install from TestPyPI and run smoke test
+python -m venv /tmp/django-environ-smoke
+. /tmp/django-environ-smoke/bin/activate
+python -m pip install -U pip
+python -m pip install --index-url https://test.pypi.org/simple/ 
--extra-index-url https://pypi.org/simple django-environ==<VERSION>
+python - <<'PY'
+import environ
+print(environ.__version__)
+env = environ.Env()
+print(env.str("DJANGO_ENV", default="ok"))
+PY
+deactivate
+```
+
+```bash
+# Upload to production PyPI
+python -m twine upload --repository django-environ dist/*
+```
+
+## 7) Social announcement draft (Twitter/X)
+
+At the end of the release flow, also generate a short Twitter/X draft
+highlighting the most interesting changes (max 280 chars), for example:
+
+```text
+django-environ v<VERSION> is out 🎉
+Highlights:
+✅ Optional warnings when defaults are used
+✅ New `choices` support in `Env.str(...)`
+✅ Valkey + rediss improvements
+✅ Docs improvements (including code copy button)
+
+Upgrade: https://pypi.org/project/django-environ/
+#django #python
+```
+
+## Notes specific to this repository
+
+- CI matrix is tox-driven (`tox.ini`).
+- Packaging is currently setup.py-based; v1.0 roadmap includes migration to 
`pyproject.toml` (see #570 reference tasks).
+- Keep release changes minimal and avoid unrelated refactors.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/.github/workflows/build.yml 
new/django_environ-0.13.0/.github/workflows/build.yml
--- old/django_environ-0.12.1/.github/workflows/build.yml       2026-02-14 
01:37:35.000000000 +0100
+++ new/django_environ-0.13.0/.github/workflows/build.yml       2026-02-17 
22:04:46.000000000 +0100
@@ -24,10 +24,10 @@
 
     steps:
       - name: Checkout code
-        uses: actions/[email protected]
+        uses: actions/[email protected]
 
       - name: Set up Python 3.12
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           python-version: '3.12'
 
@@ -64,10 +64,10 @@
 
     steps:
       - name: Checkout code
-        uses: actions/[email protected]
+        uses: actions/[email protected]
 
       - name: Set up Python 3.12
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           python-version: '3.12'
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/.github/workflows/ci.yml 
new/django_environ-0.13.0/.github/workflows/ci.yml
--- old/django_environ-0.12.1/.github/workflows/ci.yml  2025-01-13 
16:27:16.000000000 +0100
+++ new/django_environ-0.13.0/.github/workflows/ci.yml  2026-02-17 
22:04:46.000000000 +0100
@@ -52,17 +52,18 @@
           - '3.11'
           - '3.12'
           - '3.13'
+          - '3.14'
           - 'pypy-3.10'
         os: [ ubuntu-latest, macos-latest, windows-latest ]
 
     steps:
       - name: Checkout code
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           fetch-depth: 5
 
       - name: Set up Python ${{ matrix.python }}
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           python-version: ${{ matrix.python }}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/.github/workflows/codeql.yml 
new/django_environ-0.13.0/.github/workflows/codeql.yml
--- old/django_environ-0.12.1/.github/workflows/codeql.yml      2025-01-13 
16:27:16.000000000 +0100
+++ new/django_environ-0.13.0/.github/workflows/codeql.yml      2026-02-17 
22:04:46.000000000 +0100
@@ -45,16 +45,16 @@
 
     steps:
     - name: Checkout repository
-      uses: actions/[email protected]
+      uses: actions/[email protected]
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@v3
+      uses: github/codeql-action/[email protected]
       with:
         languages: ${{ matrix.language }}
         
     - name: Autobuild
-      uses: github/codeql-action/autobuild@v3
+      uses: github/codeql-action/[email protected]
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v3
+      uses: github/codeql-action/[email protected]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/.github/workflows/cs.yml 
new/django_environ-0.13.0/.github/workflows/cs.yml
--- old/django_environ-0.12.1/.github/workflows/cs.yml  2025-01-13 
16:27:16.000000000 +0100
+++ new/django_environ-0.13.0/.github/workflows/cs.yml  2026-02-17 
22:04:46.000000000 +0100
@@ -25,10 +25,10 @@
 
     steps:
       - name: Checkout code
-        uses: actions/[email protected]
+        uses: actions/[email protected]
 
       - name: Set up Python 3.12
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           python-version: '3.12'
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/.github/workflows/docs.yml 
new/django_environ-0.13.0/.github/workflows/docs.yml
--- old/django_environ-0.12.1/.github/workflows/docs.yml        2025-01-13 
16:27:16.000000000 +0100
+++ new/django_environ-0.13.0/.github/workflows/docs.yml        2026-02-17 
22:04:46.000000000 +0100
@@ -26,10 +26,10 @@
 
     steps:
       - name: Checkout code
-        uses: actions/[email protected]
+        uses: actions/[email protected]
 
       - name: Set up Python 3.12
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           python-version: '3.12'
 
@@ -46,7 +46,7 @@
 
       - name: Archive docs artifacts
         if: always()
-        uses: actions/upload-artifact@v4
+        uses: actions/[email protected]
         with:
           name: docs
           path: docs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/BACKERS.rst 
new/django_environ-0.13.0/BACKERS.rst
--- old/django_environ-0.12.1/BACKERS.rst       2026-02-14 01:37:35.000000000 
+0100
+++ new/django_environ-0.13.0/BACKERS.rst       2026-02-18 01:22:58.000000000 
+0100
@@ -11,7 +11,7 @@
 Support this project by becoming a sponsor. Your logo will show up here with a
 link to your website. `Became sponsor 
<https://opencollective.com/django-environ/contribute/sponsors-3474/checkout>`_.
 
-|ocsponsor0| |ocsponsor1|
+|ocsponsor|
 
 Backers
 -------
@@ -20,10 +20,7 @@
 
 |ocbackerimage|
 
-.. |ocsponsor0| image:: 
https://opencollective.com/django-environ/sponsor/0/avatar.svg
-    :target: https://opencollective.com/triplebyte
-    :alt: Sponsor
-.. |ocsponsor1| image:: 
https://opencollective.com/static/images/become_sponsor.svg
+.. |ocsponsor| image:: 
https://opencollective.com/static/images/become_sponsor.svg
     :target: 
https://opencollective.com/django-environ/contribute/sponsors-3474/checkout
     :alt: Become a Sponsor
 .. |ocbackerimage| image:: 
https://opencollective.com/django-environ/backers.svg?width=890
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/CHANGELOG.rst 
new/django_environ-0.13.0/CHANGELOG.rst
--- old/django_environ-0.12.1/CHANGELOG.rst     2026-02-14 01:37:35.000000000 
+0100
+++ new/django_environ-0.13.0/CHANGELOG.rst     2026-02-18 01:52:30.000000000 
+0100
@@ -5,6 +5,36 @@
 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.13.0`_ - 18-February-2026
+-----------------------------
+Added
++++++
+- Added optional warnings when defaults are used
+  `#582 <https://github.com/joke2k/django-environ/pull/582>`_.
+- Added `choices` argument support for value validation in ``Env.str(...)``
+  `#555 <https://github.com/joke2k/django-environ/pull/555>`_.
+- Added Valkey support via ``valkey://`` and ``valkeys://`` cache URL schemes
+  `#554 <https://github.com/joke2k/django-environ/pull/554>`_.
+- Added support for ``rediss://`` scheme in channels URL parsing
+  `#573 <https://github.com/joke2k/django-environ/pull/573>`_.
+- Added django-prometheus database backend aliases to DB URL parsing schemes
+  `#559 <https://github.com/joke2k/django-environ/pull/559>`_.
+
+Changed
++++++++
+- Declared support for Python 3.14
+  `#580 <https://github.com/joke2k/django-environ/pull/580>`_.
+- Declared support for Django 5.2 and Django 6.0
+  `#578 <https://github.com/joke2k/django-environ/pull/578>`_.
+
+Fixed
++++++
+- Improved type hint coverage and related lint issues
+  `#546 <https://github.com/joke2k/django-environ/pull/546>`_.
+- Fixed typos in the FAQ page
+  `#445 <https://github.com/joke2k/django-environ/pull/445>`_.
+
+
 `v0.12.1`_ - 13-February-2026
 -----------------------------
 Fixed
@@ -434,6 +464,7 @@
 - Initial release.
 
 
+.. _v0.13.0: https://github.com/joke2k/django-environ/compare/v0.12.1...v0.13.0
 .. _v0.12.1: https://github.com/joke2k/django-environ/compare/v0.12.0...v0.12.1
 .. _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
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/PKG-INFO 
new/django_environ-0.13.0/PKG-INFO
--- old/django_environ-0.12.1/PKG-INFO  2026-02-14 01:49:57.984252500 +0100
+++ new/django_environ-0.13.0/PKG-INFO  2026-02-18 02:03:20.751607700 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: django-environ
-Version: 0.12.1
+Version: 0.13.0
 Summary: A package that allows you to utilize 12factor inspired environment 
variables to configure your Django application.
 Home-page: https://django-environ.readthedocs.org
 Author: Daniele Faraglia
@@ -27,6 +27,8 @@
 Classifier: Framework :: Django :: 4.2
 Classifier: Framework :: Django :: 5.0
 Classifier: Framework :: Django :: 5.1
+Classifier: Framework :: Django :: 5.2
+Classifier: Framework :: Django :: 6.0
 Classifier: Operating System :: OS Independent
 Classifier: Intended Audience :: Developers
 Classifier: Natural Language :: English
@@ -37,11 +39,11 @@
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Utilities
-Classifier: License :: OSI Approved :: MIT License
 Requires-Python: >=3.9,<4
 Description-Content-Type: text/x-rst
 License-File: LICENSE.txt
@@ -53,6 +55,7 @@
 Provides-Extra: docs
 Requires-Dist: furo>=2024.8.6; extra == "docs"
 Requires-Dist: sphinx>=5.0; extra == "docs"
+Requires-Dist: sphinx-copybutton; extra == "docs"
 Requires-Dist: sphinx-notfound-page; extra == "docs"
 Provides-Extra: develop
 Requires-Dist: coverage[toml]>=5.0a4; extra == "develop"
@@ -60,6 +63,7 @@
 Requires-Dist: setuptools>=71.0.0; extra == "develop"
 Requires-Dist: furo>=2024.8.6; extra == "develop"
 Requires-Dist: sphinx>=5.0; extra == "develop"
+Requires-Dist: sphinx-copybutton; extra == "develop"
 Requires-Dist: sphinx-notfound-page; extra == "develop"
 Dynamic: author
 Dynamic: author-email
@@ -154,7 +158,7 @@
 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://web.archive.org/web/20240121133956/https://www.feldroy.com/books/two-scoops-of-django-3-x>`_
+`Two Scoops of Django <https://www.feldroy.com/two-scoops-of-django>`_
 book and explained in `12factor-django 
<https://dev.to/ale_jacques/django-drf-12-factor-app-with-examples-36jg>`_
 article.
 
@@ -184,7 +188,7 @@
 and the latest release on `PyPI <https://pypi.org/project/django-environ/>`_.
 
 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.
+Django 2.2, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2, and 6.0.
 
 If you'd like to contribute to ``django-environ`` you're most welcome!
 
@@ -281,17 +285,34 @@
 Release Information
 ===================
 
-v0.12.1 - 13-February-2026
+v0.13.0 - 18-February-2026
 -----------------------------
+Added
++++++
+- Added optional warnings when defaults are used
+  `#582 <https://github.com/joke2k/django-environ/pull/582>`_.
+- Added `choices` argument support for value validation in ``Env.str(...)``
+  `#555 <https://github.com/joke2k/django-environ/pull/555>`_.
+- Added Valkey support via ``valkey://`` and ``valkeys://`` cache URL schemes
+  `#554 <https://github.com/joke2k/django-environ/pull/554>`_.
+- Added support for ``rediss://`` scheme in channels URL parsing
+  `#573 <https://github.com/joke2k/django-environ/pull/573>`_.
+- Added django-prometheus database backend aliases to DB URL parsing schemes
+  `#559 <https://github.com/joke2k/django-environ/pull/559>`_.
+
+Changed
++++++++
+- Declared support for Python 3.14
+  `#580 <https://github.com/joke2k/django-environ/pull/580>`_.
+- Declared support for Django 5.2 and Django 6.0
+  `#578 <https://github.com/joke2k/django-environ/pull/578>`_.
+
 Fixed
 +++++
-- Fixed PostgreSQL cluster URL parsing with bracketed IPv6 hosts in recent
-  Python versions, preventing failures in runtime URL parsing and related
-  regression tests
-  `#574 <https://github.com/joke2k/django-environ/issues/574>`_.
-- Fixed debug logging in ``Env.get_value()`` to avoid evaluating lazy default
-  objects when DEBUG logging is enabled
-  `#571 <https://github.com/joke2k/django-environ/issues/571>`_.
+- Improved type hint coverage and related lint issues
+  `#546 <https://github.com/joke2k/django-environ/pull/546>`_.
+- Fixed typos in the FAQ page
+  `#445 <https://github.com/joke2k/django-environ/pull/445>`_.
 
 `Full changelog 
<https://django-environ.readthedocs.org/en/latest/changelog.html>`_.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/README.rst 
new/django_environ-0.13.0/README.rst
--- old/django_environ-0.12.1/README.rst        2026-02-14 01:37:35.000000000 
+0100
+++ new/django_environ-0.13.0/README.rst        2026-02-18 01:59:08.000000000 
+0100
@@ -98,7 +98,7 @@
 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://web.archive.org/web/20240121133956/https://www.feldroy.com/books/two-scoops-of-django-3-x>`_
+`Two Scoops of Django <https://www.feldroy.com/two-scoops-of-django>`_
 book and explained in `12factor-django 
<https://dev.to/ale_jacques/django-drf-12-factor-app-with-examples-36jg>`_
 article.
 
@@ -128,7 +128,7 @@
 and the latest release on `PyPI <https://pypi.org/project/django-environ/>`_.
 
 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.
+Django 2.2, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2, and 6.0.
 
 If you'd like to contribute to ``django-environ`` you're most welcome!
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_environ-0.12.1/django_environ.egg-info/PKG-INFO 
new/django_environ-0.13.0/django_environ.egg-info/PKG-INFO
--- old/django_environ-0.12.1/django_environ.egg-info/PKG-INFO  2026-02-14 
01:49:57.000000000 +0100
+++ new/django_environ-0.13.0/django_environ.egg-info/PKG-INFO  2026-02-18 
02:03:20.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: django-environ
-Version: 0.12.1
+Version: 0.13.0
 Summary: A package that allows you to utilize 12factor inspired environment 
variables to configure your Django application.
 Home-page: https://django-environ.readthedocs.org
 Author: Daniele Faraglia
@@ -27,6 +27,8 @@
 Classifier: Framework :: Django :: 4.2
 Classifier: Framework :: Django :: 5.0
 Classifier: Framework :: Django :: 5.1
+Classifier: Framework :: Django :: 5.2
+Classifier: Framework :: Django :: 6.0
 Classifier: Operating System :: OS Independent
 Classifier: Intended Audience :: Developers
 Classifier: Natural Language :: English
@@ -37,11 +39,11 @@
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Classifier: Topic :: Utilities
-Classifier: License :: OSI Approved :: MIT License
 Requires-Python: >=3.9,<4
 Description-Content-Type: text/x-rst
 License-File: LICENSE.txt
@@ -53,6 +55,7 @@
 Provides-Extra: docs
 Requires-Dist: furo>=2024.8.6; extra == "docs"
 Requires-Dist: sphinx>=5.0; extra == "docs"
+Requires-Dist: sphinx-copybutton; extra == "docs"
 Requires-Dist: sphinx-notfound-page; extra == "docs"
 Provides-Extra: develop
 Requires-Dist: coverage[toml]>=5.0a4; extra == "develop"
@@ -60,6 +63,7 @@
 Requires-Dist: setuptools>=71.0.0; extra == "develop"
 Requires-Dist: furo>=2024.8.6; extra == "develop"
 Requires-Dist: sphinx>=5.0; extra == "develop"
+Requires-Dist: sphinx-copybutton; extra == "develop"
 Requires-Dist: sphinx-notfound-page; extra == "develop"
 Dynamic: author
 Dynamic: author-email
@@ -154,7 +158,7 @@
 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://web.archive.org/web/20240121133956/https://www.feldroy.com/books/two-scoops-of-django-3-x>`_
+`Two Scoops of Django <https://www.feldroy.com/two-scoops-of-django>`_
 book and explained in `12factor-django 
<https://dev.to/ale_jacques/django-drf-12-factor-app-with-examples-36jg>`_
 article.
 
@@ -184,7 +188,7 @@
 and the latest release on `PyPI <https://pypi.org/project/django-environ/>`_.
 
 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.
+Django 2.2, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2, 5.0, 5.1, 5.2, and 6.0.
 
 If you'd like to contribute to ``django-environ`` you're most welcome!
 
@@ -281,17 +285,34 @@
 Release Information
 ===================
 
-v0.12.1 - 13-February-2026
+v0.13.0 - 18-February-2026
 -----------------------------
+Added
++++++
+- Added optional warnings when defaults are used
+  `#582 <https://github.com/joke2k/django-environ/pull/582>`_.
+- Added `choices` argument support for value validation in ``Env.str(...)``
+  `#555 <https://github.com/joke2k/django-environ/pull/555>`_.
+- Added Valkey support via ``valkey://`` and ``valkeys://`` cache URL schemes
+  `#554 <https://github.com/joke2k/django-environ/pull/554>`_.
+- Added support for ``rediss://`` scheme in channels URL parsing
+  `#573 <https://github.com/joke2k/django-environ/pull/573>`_.
+- Added django-prometheus database backend aliases to DB URL parsing schemes
+  `#559 <https://github.com/joke2k/django-environ/pull/559>`_.
+
+Changed
++++++++
+- Declared support for Python 3.14
+  `#580 <https://github.com/joke2k/django-environ/pull/580>`_.
+- Declared support for Django 5.2 and Django 6.0
+  `#578 <https://github.com/joke2k/django-environ/pull/578>`_.
+
 Fixed
 +++++
-- Fixed PostgreSQL cluster URL parsing with bracketed IPv6 hosts in recent
-  Python versions, preventing failures in runtime URL parsing and related
-  regression tests
-  `#574 <https://github.com/joke2k/django-environ/issues/574>`_.
-- Fixed debug logging in ``Env.get_value()`` to avoid evaluating lazy default
-  objects when DEBUG logging is enabled
-  `#571 <https://github.com/joke2k/django-environ/issues/571>`_.
+- Improved type hint coverage and related lint issues
+  `#546 <https://github.com/joke2k/django-environ/pull/546>`_.
+- Fixed typos in the FAQ page
+  `#445 <https://github.com/joke2k/django-environ/pull/445>`_.
 
 `Full changelog 
<https://django-environ.readthedocs.org/en/latest/changelog.html>`_.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_environ-0.12.1/django_environ.egg-info/SOURCES.txt 
new/django_environ-0.13.0/django_environ.egg-info/SOURCES.txt
--- old/django_environ-0.12.1/django_environ.egg-info/SOURCES.txt       
2026-02-14 01:49:57.000000000 +0100
+++ new/django_environ-0.13.0/django_environ.egg-info/SOURCES.txt       
2026-02-18 02:03:20.000000000 +0100
@@ -7,11 +7,12 @@
 MANIFEST.in
 README.rst
 SECURITY.rst
-setup.cfg
 setup.py
 tox.ini
 .github/FUNDING.yml
+.github/copilot-instructions.md
 .github/dependabot.yml
+.github/skills/django-environ-release/SKILL.md
 .github/workflows/build.yml
 .github/workflows/change-pr-target.yml
 .github/workflows/ci.yml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django_environ-0.12.1/django_environ.egg-info/requires.txt 
new/django_environ-0.13.0/django_environ.egg-info/requires.txt
--- old/django_environ-0.12.1/django_environ.egg-info/requires.txt      
2026-02-14 01:49:57.000000000 +0100
+++ new/django_environ-0.13.0/django_environ.egg-info/requires.txt      
2026-02-18 02:03:20.000000000 +0100
@@ -5,11 +5,13 @@
 setuptools>=71.0.0
 furo>=2024.8.6
 sphinx>=5.0
+sphinx-copybutton
 sphinx-notfound-page
 
 [docs]
 furo>=2024.8.6
 sphinx>=5.0
+sphinx-copybutton
 sphinx-notfound-page
 
 [testing]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/docs/api.rst 
new/django_environ-0.13.0/docs/api.rst
--- old/django_environ-0.12.1/docs/api.rst      2022-06-15 12:00:11.000000000 
+0200
+++ new/django_environ-0.13.0/docs/api.rst      2026-02-18 01:22:58.000000000 
+0100
@@ -25,6 +25,14 @@
 The ``environ`` module
 ======================
 
+.. autoclass:: environ.NoValue
+    :members:
+    :no-undoc-members:
+
+.. autoclass:: environ.DefaultValueWarning
+    :members:
+    :no-undoc-members:
+
 .. autoclass:: environ.Env
     :members:
     :no-undoc-members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/docs/conf.py 
new/django_environ-0.13.0/docs/conf.py
--- old/django_environ-0.12.1/docs/conf.py      2026-02-14 01:37:35.000000000 
+0100
+++ new/django_environ-0.13.0/docs/conf.py      2026-02-18 01:22:58.000000000 
+0100
@@ -61,6 +61,7 @@
     "sphinx.ext.intersphinx",
     "sphinx.ext.todo",
     "sphinx.ext.viewcode",
+    "sphinx_copybutton",
     "notfound.extension",
 ]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/docs/faq.rst 
new/django_environ-0.13.0/docs/faq.rst
--- old/django_environ-0.12.1/docs/faq.rst      2023-03-03 00:03:41.000000000 
+0100
+++ new/django_environ-0.13.0/docs/faq.rst      2026-02-18 00:15:07.000000000 
+0100
@@ -5,14 +5,14 @@
 
 #. **Can django-environ determine the location of .env file automatically?**
 
-   django-environ will try to get and read ``.env`` file from the project
-   root if you haven't specified the path for it when call 
:meth:`.environ.Env.read_env`.
+   ``django-environ`` will try to get and read ``.env`` file from the project
+   root if you haven't specified the path for it when you call 
:meth:`.environ.Env.read_env`.
    However, this is not the recommended way. When it is possible always specify
-   the path tho ``.env`` file. Alternatively, you can use a trick with a
+   the path to the ``.env`` file. Alternatively, you can use a trick with an
    environment variable pointing to the actual location of ``.env`` file.
    For details see ":ref:`multiple-env-files-label`".
 
-#. **What (where) is the root part of the project, is it part of the project 
where are settings?**
+#. **What (where) is the root part of the project, is it the directory that 
contains settings.py file?**
 
    Where your ``manage.py`` file is (that is your project root directory).
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/docs/tips.rst 
new/django_environ-0.13.0/docs/tips.rst
--- old/django_environ-0.12.1/docs/tips.rst     2026-02-14 01:37:35.000000000 
+0100
+++ new/django_environ-0.13.0/docs/tips.rst     2026-02-18 01:22:58.000000000 
+0100
@@ -129,6 +129,24 @@
    The next major release will disable it by default.
 
 
+Warn when defaults are used
+===========================
+
+If you want visibility when a missing environment variable falls back to a
+default value, enable warnings on the ``Env`` instance:
+
+.. code-block:: python
+
+   import environ
+
+   env = environ.Env()
+   env.warn_on_default = True
+   value = env("MISSING_VAR", default="fallback")
+
+When enabled, ``django-environ`` emits ``DefaultValueWarning`` for missing
+variables that return an explicit default.
+
+
 Multiple redis cache locations
 ==============================
 
@@ -287,6 +305,29 @@
    print(env.str('ESCAPED_CERT', multiline=False))
    # ---BEGIN---\\n---END---
 
+Restrict string values with choices
+===================================
+
+You can restrict ``env.str()`` to an allowed list of values using
+``choices``. If the value is not in the provided list,
+``ImproperlyConfigured`` is raised.
+
+.. code-block:: python
+
+   import environ
+   from django.core.exceptions import ImproperlyConfigured
+
+   env = environ.Env()
+
+   # APP_ENV=prod
+   env.str("APP_ENV", choices=("dev", "prod", "staging"))  # "prod"
+
+   # APP_ENV=unknown
+   try:
+       env.str("APP_ENV", choices=("dev", "prod", "staging"))
+   except ImproperlyConfigured:
+       ...
+
 Proxy value
 ===========
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/docs/types.rst 
new/django_environ-0.13.0/docs/types.rst
--- old/django_environ-0.12.1/docs/types.rst    2023-08-30 14:48:23.000000000 
+0200
+++ new/django_environ-0.13.0/docs/types.rst    2026-02-17 23:10:22.000000000 
+0100
@@ -147,6 +147,7 @@
   * ``pylibmc://``
 
 * Redis: ``rediscache://``, ``redis://``, or ``rediss://``
+* Valkey: ``valkey://``, or ``valkeys://``
 
 
 .. _environ-env-search-url:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/environ/__init__.py 
new/django_environ-0.13.0/environ/__init__.py
--- old/django_environ-0.12.1/environ/__init__.py       2026-02-14 
01:48:41.000000000 +0100
+++ new/django_environ-0.13.0/environ/__init__.py       2026-02-18 
01:52:13.000000000 +0100
@@ -22,7 +22,7 @@
 __copyright__ = 'Copyright (C) 2013-2026 Daniele Faraglia'
 """The copyright notice of the package."""
 
-__version__ = '0.12.1'
+__version__ = '0.13.0'
 """The version of the package."""
 
 __license__ = 'MIT'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/environ/environ.py 
new/django_environ-0.13.0/environ/environ.py
--- old/django_environ-0.12.1/environ/environ.py        2026-02-14 
01:37:35.000000000 +0100
+++ new/django_environ-0.13.0/environ/environ.py        2026-02-18 
01:22:58.000000000 +0100
@@ -19,6 +19,7 @@
 import re
 import sys
 import warnings
+from typing import Dict, List, Tuple, Union
 from urllib.parse import (
     parse_qs,
     ParseResult,
@@ -72,6 +73,10 @@
         return f'<{self.__class__.__name__}>'
 
 
+class DefaultValueWarning(UserWarning):
+    """Warning used when returning an explicit default value."""
+
+
 class Env:
     """Provide scheme-based lookups of environment variables so that each
     caller doesn't have to pass in ``cast`` and ``default`` parameters.
@@ -107,7 +112,15 @@
     BOOLEAN_TRUE_STRINGS = ('true', 'on', 'ok', 'y', 'yes', '1')
     URL_CLASS = ParseResult
 
-    POSTGRES_FAMILY = ['postgres', 'postgresql', 'psql', 'pgsql', 'postgis']
+    POSTGRES_FAMILY = [
+        'postgres',
+        'postgresql',
+        'psql',
+        'pgsql',
+        'postgis',
+        'prometheus_postgresql',
+        'prometheus_postgis',
+    ]
 
     DEFAULT_DATABASE_ENV = 'DATABASE_URL'
     DB_SCHEMES = {
@@ -116,17 +129,22 @@
         'psql': DJANGO_POSTGRES,
         'pgsql': DJANGO_POSTGRES,
         'postgis': 'django.contrib.gis.db.backends.postgis',
+        'prometheus_postgresql': 'django_prometheus.db.backends.postgresql',
+        'prometheus_postgis': 'django_prometheus.db.backends.postgis',
         'cockroachdb': 'django_cockroachdb',
         'mysql': 'django.db.backends.mysql',
         'mysql2': 'django.db.backends.mysql',
         'mysql-connector': 'mysql.connector.django',
         'mysqlgis': 'django.contrib.gis.db.backends.mysql',
+        'prometheus_mysql': 'django_prometheus.db.backends.mysql',
         'mssql': 'mssql',
         'oracle': 'django.db.backends.oracle',
         'pyodbc': 'sql_server.pyodbc',
         'redshift': 'django_redshift_backend',
         'spatialite': 'django.contrib.gis.db.backends.spatialite',
+        'prometheus_spatialite': 'django_prometheus.db.backends.spatialite',
         'sqlite': 'django.db.backends.sqlite3',
+        'prometheus_sqlite': 'django_prometheus.db.backends.sqlite3',
         'ldap': 'ldapdb.backends.ldap',
     }
     _DB_BASE_OPTIONS = [
@@ -149,6 +167,8 @@
         'rediscache': REDIS_DRIVER,
         'redis': REDIS_DRIVER,
         'rediss': REDIS_DRIVER,
+        'valkey': REDIS_DRIVER,
+        'valkeys': REDIS_DRIVER,
     }
     _CACHE_BASE_OPTIONS = [
         'TIMEOUT',
@@ -195,12 +215,15 @@
     CHANNELS_SCHEMES = {
         "inmemory": "channels.layers.InMemoryChannelLayer",
         "redis": "channels_redis.core.RedisChannelLayer",
-        "redis+pubsub": "channels_redis.pubsub.RedisPubSubChannelLayer"
+        "rediss": "channels_redis.core.RedisChannelLayer",
+        "redis+pubsub": "channels_redis.pubsub.RedisPubSubChannelLayer",
+        "rediss+pubsub": "channels_redis.pubsub.RedisPubSubChannelLayer",
     }
 
     def __init__(self, **scheme):
         self.smart_cast = True
         self.escape_proxy = False
+        self.warn_on_default = False
         self.prefix = ""
         self.scheme = scheme
 
@@ -215,16 +238,31 @@
     def __contains__(self, var):
         return var in self.ENVIRON
 
-    def str(self, var, default=NOTSET, multiline=False):
+    def str(
+            self,
+            var,
+            default: Union[str, NoValue] = NOTSET,
+            multiline=False,
+            choices=NOTSET) -> str:
         """
         :rtype: str
         """
         value = self.get_value(var, cast=str, default=default)
         if multiline:
             return re.sub(r'(\\r)?\\n', r'\n', value)
+        if choices is not self.NOTSET:
+            # if choices is provided, check that the value is in choices
+            if value not in choices:
+                raise ImproperlyConfigured(
+                    f"Invalid value: {value} not in {choices}"
+                )
         return value
 
-    def bytes(self, var, default=NOTSET, encoding='utf8'):
+    def bytes(
+            self,
+            var,
+            default: Union[bytes, NoValue] = NOTSET,
+            encoding='utf8') -> bytes:
         """
         :rtype: bytes
         """
@@ -233,19 +271,19 @@
             return value.encode(encoding)
         return value
 
-    def bool(self, var, default=NOTSET):
+    def bool(self, var, default: Union[bool, NoValue] = NOTSET) -> bool:
         """
         :rtype: bool
         """
         return self.get_value(var, cast=bool, default=default)
 
-    def int(self, var, default=NOTSET):
+    def int(self, var, default: Union[int, NoValue] = NOTSET) -> int:
         """
         :rtype: int
         """
         return self.get_value(var, cast=int, default=default)
 
-    def float(self, var, default=NOTSET):
+    def float(self, var, default: Union[float, NoValue] = NOTSET) -> float:
         """
         :rtype: float
         """
@@ -257,7 +295,7 @@
         """
         return self.get_value(var, cast=json.loads, default=default)
 
-    def list(self, var, cast=None, default=NOTSET):
+    def list(self, var, cast=None, default=NOTSET) -> List:
         """
         :rtype: list
         """
@@ -267,7 +305,7 @@
             default=default
         )
 
-    def tuple(self, var, cast=None, default=NOTSET):
+    def tuple(self, var, cast=None, default=NOTSET) -> Tuple:
         """
         :rtype: tuple
         """
@@ -277,13 +315,13 @@
             default=default
         )
 
-    def dict(self, var, cast=dict, default=NOTSET):
+    def dict(self, var, cast=dict, default=NOTSET) -> Dict:
         """
         :rtype: dict
         """
         return self.get_value(var, cast=cast, default=default)
 
-    def url(self, var, default=NOTSET):
+    def url(self, var, default=NOTSET) -> ParseResult:
         """
         :rtype: urllib.parse.ParseResult
         """
@@ -294,7 +332,11 @@
             parse_default=True
         )
 
-    def db_url(self, var=DEFAULT_DATABASE_ENV, default=NOTSET, engine=None):
+    def db_url(
+            self,
+            var=DEFAULT_DATABASE_ENV,
+            default=NOTSET,
+            engine=None) -> Dict:
         """Returns a config dictionary, defaulting to DATABASE_URL.
 
         The db method is an alias for db_url.
@@ -308,7 +350,11 @@
 
     db = db_url
 
-    def cache_url(self, var=DEFAULT_CACHE_ENV, default=NOTSET, backend=None):
+    def cache_url(
+            self,
+            var=DEFAULT_CACHE_ENV,
+            default=NOTSET,
+            backend=None) -> Dict:
         """Returns a config dictionary, defaulting to CACHE_URL.
 
         The cache method is an alias for cache_url.
@@ -322,7 +368,11 @@
 
     cache = cache_url
 
-    def email_url(self, var=DEFAULT_EMAIL_ENV, default=NOTSET, backend=None):
+    def email_url(
+            self,
+            var=DEFAULT_EMAIL_ENV,
+            default=NOTSET,
+            backend=None) -> Dict:
         """Returns a config dictionary, defaulting to EMAIL_URL.
 
         The email method is an alias for email_url.
@@ -336,7 +386,11 @@
 
     email = email_url
 
-    def search_url(self, var=DEFAULT_SEARCH_ENV, default=NOTSET, engine=None):
+    def search_url(
+            self,
+            var=DEFAULT_SEARCH_ENV,
+            default: Union[Dict, NoValue] = NOTSET,
+            engine=None) -> Dict:
         """Returns a config dictionary, defaulting to SEARCH_URL.
 
         :rtype: dict
@@ -346,8 +400,11 @@
             engine=engine
         )
 
-    def channels_url(self, var=DEFAULT_CHANNELS_ENV, default=NOTSET,
-                     backend=None):
+    def channels_url(
+            self,
+            var=DEFAULT_CHANNELS_ENV,
+            default: Union[Dict, NoValue] = NOTSET,
+            backend=None) -> Dict:
         """Returns a config dictionary, defaulting to CHANNELS_URL.
 
         :rtype: dict
@@ -359,7 +416,11 @@
 
     channels = channels_url
 
-    def path(self, var, default=NOTSET, **kwargs):
+    def path(
+            self,
+            var,
+            default: Union['Path', NoValue] = NOTSET,
+            **kwargs) -> 'Path':
         """
         :rtype: Path
         """
@@ -414,6 +475,13 @@
                 raise ImproperlyConfigured(error_msg) from exc
 
             value = default
+            if self.warn_on_default:
+                warnings.warn(
+                    f'{var_name} environment variable not set; '
+                    'using default value',
+                    DefaultValueWarning,
+                    stacklevel=2,
+                )
 
         # Resolve any proxied values
         prefix = b'$' if isinstance(value, bytes) else '$'
@@ -699,19 +767,28 @@
             else:
                 config['LOCATION'] = locations
 
+        if backend:
+            config['BACKEND'] = backend
+
         if url.query:
             config_options = {}
+            # Django Redis cache backend expects options in lower case
+            # while "django_redis" expects them in upper case
+            backend = config['BACKEND']
+            if backend == 'django.core.cache.backends.redis.RedisCache':
+                key_modifier = 'lower'
+            else:
+                key_modifier = 'upper'
+
             for k, v in parse_qs(url.query).items():
-                opt = {k.upper(): _cast(v[0])}
+                key = getattr(k, key_modifier)()
+                opt = {key: _cast(v[0])}
                 if k.upper() in cls._CACHE_BASE_OPTIONS:
                     config.update(opt)
                 else:
                     config_options.update(opt)
             config['OPTIONS'] = config_options
 
-        if backend:
-            config['BACKEND'] = backend
-
         return config
 
     @classmethod
@@ -787,9 +864,10 @@
             raise ImproperlyConfigured(f"Invalid channels schema {url.scheme}")
         else:
             config["BACKEND"] = cls.CHANNELS_SCHEMES[url.scheme]
-            if url.scheme in ("redis", "redis+pubsub"):
+            if url.scheme.startswith("redis"):
+                redis_scheme, *_ = url.scheme.split("+")
                 config["CONFIG"] = {
-                    "hosts": [url._replace(scheme="redis").geturl()]
+                    "hosts": [url._replace(scheme=redis_scheme).geturl()]
                 }
 
         return config
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/setup.cfg 
new/django_environ-0.13.0/setup.cfg
--- old/django_environ-0.12.1/setup.cfg 2026-02-14 01:49:57.985591200 +0100
+++ new/django_environ-0.13.0/setup.cfg 2026-02-18 02:03:20.753000700 +0100
@@ -1,6 +1,3 @@
-[bdist_wheel]
-universal = 1
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/setup.py 
new/django_environ-0.13.0/setup.py
--- old/django_environ-0.12.1/setup.py  2026-02-14 01:37:35.000000000 +0100
+++ new/django_environ-0.13.0/setup.py  2026-02-18 01:22:58.000000000 +0100
@@ -145,6 +145,8 @@
     'Framework :: Django :: 4.2',
     'Framework :: Django :: 5.0',
     'Framework :: Django :: 5.1',
+    'Framework :: Django :: 5.2',
+    'Framework :: Django :: 6.0',
 
     'Operating System :: OS Independent',
 
@@ -158,13 +160,13 @@
     'Programming Language :: Python :: 3.11',
     'Programming Language :: Python :: 3.12',
     'Programming Language :: Python :: 3.13',
+    'Programming Language :: Python :: 3.14',
     'Programming Language :: Python :: Implementation :: CPython',
     'Programming Language :: Python :: Implementation :: PyPy',
 
     'Topic :: Software Development :: Libraries :: Python Modules',
     'Topic :: Utilities',
 
-    'License :: OSI Approved :: MIT License',
 ]
 
 # Dependencies that are downloaded by pip on installation and why.
@@ -188,6 +190,7 @@
     'docs': [
         'furo>=2024.8.6',  # Sphinx documentation theme
         'sphinx>=5.0',  # Python documentation generator
+        'sphinx-copybutton',  # Add copy button to code blocks
         'sphinx-notfound-page',  # Create a custom 404 page
     ],
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/tests/test_cache.py 
new/django_environ-0.13.0/tests/test_cache.py
--- old/django_environ-0.12.1/tests/test_cache.py       2026-02-14 
01:37:35.000000000 +0100
+++ new/django_environ-0.13.0/tests/test_cache.py       2026-02-17 
23:00:29.000000000 +0100
@@ -20,7 +20,7 @@
 )
 
 
-def test_base_options_parsing():
+def test_base_options_parsing_memcache():
     url = ('memcache://127.0.0.1:11211/?timeout=0&'
            'key_prefix=cache_&key_function=foo.get_key&version=1')
     url = Env.cache_url_config(url)
@@ -30,10 +30,29 @@
     assert url['TIMEOUT'] == 0
     assert url['VERSION'] == 1
 
-    url = 'redis://127.0.0.1:6379/?timeout=None'
-    url = Env.cache_url_config(url)
 
-    assert url['TIMEOUT'] is None
[email protected]('redis_driver,timeout_key',
+    [
+        ('django.core.cache.backends.redis.RedisCache', 'timeout'),
+        ('django_redis.cache.RedisCache', 'TIMEOUT'),
+    ],
+    ids=[
+        'django',
+        'django_redis',
+    ],
+)
+def test_base_options_parsing_redis(redis_driver, timeout_key):
+    mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
+    mocked_cache_schemes.update({
+        'rediscache': redis_driver,
+        'redis': redis_driver,
+        'rediss': redis_driver,
+    })
+    with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
+        url = 'redis://127.0.0.1:6379/?timeout=None'
+        url = Env.cache_url_config(url)
+
+        assert url[timeout_key] is None
 
 
 @pytest.mark.parametrize(
@@ -135,27 +154,63 @@
             else:
                 assert driver == redis_cache
 
-def test_redis_parsing():
-    url = ('rediscache://127.0.0.1:6379/1?client_class='
-           'django_redis.client.DefaultClient&password=secret')
-    url = Env.cache_url_config(url)
 
-    assert url['BACKEND'] == REDIS_DRIVER
-    assert url['LOCATION'] == 'redis://127.0.0.1:6379/1'
-    assert url['OPTIONS'] == {
-        'CLIENT_CLASS': 'django_redis.client.DefaultClient',
-        'PASSWORD': 'secret',
-    }
[email protected]('redis_driver,client_class_key,password_key',
+    [
+        ('django.core.cache.backends.redis.RedisCache', 'client_class', 
'password'),
+        ('django_redis.cache.RedisCache', 'CLIENT_CLASS', 'PASSWORD'),
+    ],
+    ids=[
+        'django',
+        'django_redis',
+    ],
+)
+def test_redis_parsing(redis_driver, client_class_key, password_key):
+    mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
+    mocked_cache_schemes.update({
+        'rediscache': redis_driver,
+        'redis': redis_driver,
+        'rediss': redis_driver,
+    })
+    with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
+        url = ('rediscache://127.0.0.1:6379/1?client_class='
+               'django_redis.client.DefaultClient&password=secret')
+        url = Env.cache_url_config(url)
+
+        assert url['BACKEND'] == redis_driver
+        assert url['LOCATION'] == 'redis://127.0.0.1:6379/1'
+        assert url['OPTIONS'] == {
+            client_class_key: 'django_redis.client.DefaultClient',
+            password_key: 'secret',
+        }
 
 
-def test_redis_socket_url():
-    url = 'redis://:redispass@/path/to/socket.sock?db=0'
-    url = Env.cache_url_config(url)
-    assert REDIS_DRIVER == url['BACKEND']
-    assert url['LOCATION'] == 'unix://:redispass@/path/to/socket.sock'
-    assert url['OPTIONS'] == {
-        'DB': 0
-    }
[email protected]('redis_driver,db_key',
+    [
+        ('django.core.cache.backends.redis.RedisCache', 'db'),
+        ('django_redis.cache.RedisCache', 'DB'),
+    ],
+    ids=[
+        'django',
+        'django_redis',
+    ],
+)
+def test_redis_socket_url(redis_driver, db_key):
+    mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
+    mocked_cache_schemes.update({
+        'rediscache': redis_driver,
+        'redis': redis_driver,
+        'rediss': redis_driver,
+    })
+    with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
+        url = 'redis://:redispass@/path/to/socket.sock?db=0'
+        url = Env.cache_url_config(url)
+
+        assert url['BACKEND'] == redis_driver
+        assert url['LOCATION'] == 'unix://:redispass@/path/to/socket.sock'
+        assert url['OPTIONS'] == {
+            db_key: 0
+        }
 
 
 def test_options_parsing():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/tests/test_channels.py 
new/django_environ-0.13.0/tests/test_channels.py
--- old/django_environ-0.12.1/tests/test_channels.py    2026-02-14 
01:37:35.000000000 +0100
+++ new/django_environ-0.13.0/tests/test_channels.py    2026-02-17 
22:04:46.000000000 +0100
@@ -22,11 +22,20 @@
     assert result["BACKEND"] == "channels_redis.core.RedisChannelLayer"
     assert result["CONFIG"]["hosts"][0] == 
"redis://user:password@localhost:6379/0"
 
+    url = "rediss://user:password@localhost:6379/0"
+    result = Env.channels_url_config(url)
+    assert result["BACKEND"] == "channels_redis.core.RedisChannelLayer"
+    assert result["CONFIG"]["hosts"][0] == 
"rediss://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"
 
+    url = "rediss+pubsub://user:password@localhost:6379/0"
+    result = Env.channels_url_config(url)
+    assert result["BACKEND"] == "channels_redis.pubsub.RedisPubSubChannelLayer"
+    assert result["CONFIG"]["hosts"][0] == 
"rediss://user:password@localhost:6379/0"
 
 def test_channels_backend_override():
     result = Env.channels_url_config("unsupported://", 
backend="custom.backend")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/tests/test_env.py 
new/django_environ-0.13.0/tests/test_env.py
--- old/django_environ-0.12.1/tests/test_env.py 2026-02-14 01:37:35.000000000 
+0100
+++ new/django_environ-0.13.0/tests/test_env.py 2026-02-18 01:22:58.000000000 
+0100
@@ -9,13 +9,15 @@
 
 import os
 import tempfile
+from unittest import mock
 import logging
 import io
+import warnings
 from urllib.parse import quote
 
 import pytest
 
-from environ import Env, Path
+from environ import DefaultValueWarning, Env, Path
 from environ.compat import (
     DJANGO_POSTGRES,
     ImproperlyConfigured,
@@ -102,6 +104,19 @@
     def test_not_present_with_default(self):
         assert self.env('not_present', default=3) == 3
 
+    def test_not_present_with_default_warning_disabled(self):
+        with warnings.catch_warnings(record=True) as warns:
+            warnings.simplefilter('always')
+            assert self.env('not_present', default=3) == 3
+        assert warns == []
+
+    def test_not_present_with_default_warning_enabled(self):
+        self.env.warn_on_default = True
+        with pytest.warns(
+                DefaultValueWarning,
+                match='not_present environment variable not set'):
+            assert self.env('not_present', default=3) == 3
+
     def test_not_present_without_default(self):
         with pytest.raises(ImproperlyConfigured) as excinfo:
             self.env('not_present')
@@ -114,22 +129,31 @@
         assert 'I_AM_NOT_A_VAR' not in self.env
 
     @pytest.mark.parametrize(
-        'var,val,multiline',
+        'var,val,multiline,choices',
         [
-            ('STR_VAR', 'bar', False),
-            ('MULTILINE_STR_VAR', 'foo\\nbar', False),
-            ('MULTILINE_STR_VAR', 'foo\nbar', True),
-            ('MULTILINE_QUOTED_STR_VAR', '---BEGIN---\\r\\n---END---', False),
-            ('MULTILINE_QUOTED_STR_VAR', '---BEGIN---\n---END---', True),
-            ('MULTILINE_ESCAPED_STR_VAR', '---BEGIN---\\\\n---END---', False),
-            ('MULTILINE_ESCAPED_STR_VAR', '---BEGIN---\\\n---END---', True),
+            ('STR_VAR', 'bar', False, Env.NOTSET),
+            ('STR_VAR', 'bar', False, ['foo', 'bar']),
+            ('STR_VAR', 'bar', False, ['pow', 'foo']),
+            ('MULTILINE_STR_VAR', 'foo\\nbar', False, Env.NOTSET),
+            ('MULTILINE_STR_VAR', 'foo\\nbar', False, ['foo\\nbar', '***']),
+            ('MULTILINE_STR_VAR', 'foo\\nbar', False, ['***', '***']),
+            ('MULTILINE_STR_VAR', 'foo\nbar', True, Env.NOTSET),
+            ('MULTILINE_QUOTED_STR_VAR', '---BEGIN---\\r\\n---END---', False, 
Env.NOTSET),
+            ('MULTILINE_QUOTED_STR_VAR', '---BEGIN---\n---END---', True, 
Env.NOTSET),
+            ('MULTILINE_ESCAPED_STR_VAR', '---BEGIN---\\\\n---END---', False, 
Env.NOTSET),
+            ('MULTILINE_ESCAPED_STR_VAR', '---BEGIN---\\\n---END---', True, 
Env.NOTSET),
         ],
     )
-    def test_str(self, var, val, multiline):
-        assert isinstance(self.env(var), str)
-        if not multiline:
-            assert self.env(var) == val
-        assert self.env.str(var, multiline=multiline) == val
+    def test_str(self, var, val, multiline, choices):
+        if choices is Env.NOTSET or val in choices:
+            assert isinstance(self.env(var), str)
+            if not multiline:
+                assert self.env(var) == val
+            assert self.env.str(var, multiline=multiline) == val
+        else:
+            with pytest.raises(ImproperlyConfigured) as excinfo:
+                self.env.str(var, multiline=multiline, choices=choices)
+            assert str(excinfo.value) == f"Invalid value: {val} not in 
{choices}"
 
     @pytest.mark.parametrize(
         'var,val,default',
@@ -355,26 +379,40 @@
             (Env.DEFAULT_CACHE_ENV,
              'django.core.cache.backends.memcached.MemcachedCache',
              '127.0.0.1:11211', None),
-            ('CACHE_REDIS', REDIS_DRIVER,
+            ('CACHE_REDIS',
+             'django.core.cache.backends.redis.RedisCache',
+             'redis://127.0.0.1:6379/1',
+             {'client_class': 'django_redis.client.DefaultClient',
+              'password': 'secret'}),
+            ('CACHE_REDIS',
+             'django_redis.cache.RedisCache',
              'redis://127.0.0.1:6379/1',
              {'CLIENT_CLASS': 'django_redis.client.DefaultClient',
               'PASSWORD': 'secret'}),
         ],
         ids=[
             'memcached',
-            'redis',
+            'django',  # Django Redis cache backend
+            'redis_django',  # django_redis backend
         ],
     )
     def test_cache_url_value(self, var, backend, location, options):
-        config = self.env.cache_url(var)
+        mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
+        mocked_cache_schemes.update({
+            'rediscache': backend,
+            'redis': backend,
+            'rediss': backend,
+        })
+        with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
+            config = self.env.cache_url(var)
 
-        assert config['BACKEND'] == backend
-        assert config['LOCATION'] == location
+            assert config['BACKEND'] == backend
+            assert config['LOCATION'] == location
 
-        if options is None:
-            assert 'OPTIONS' not in config
-        else:
-            assert config['OPTIONS'] == options
+            if options is None:
+                assert 'OPTIONS' not in config
+            else:
+                assert config['OPTIONS'] == options
 
     def test_email_url_value(self):
         email_config = self.env.email_url()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django_environ-0.12.1/tox.ini 
new/django_environ-0.13.0/tox.ini
--- old/django_environ-0.12.1/tox.ini   2026-02-14 01:37:35.000000000 +0100
+++ new/django_environ-0.13.0/tox.ini   2026-02-17 22:04:46.000000000 +0100
@@ -21,6 +21,8 @@
     manifest
     py{39,310,311,312,313}-django{22,30,31,32,40,41,42}
     py{310,311,312,313}-django{50,51}
+    py{310,311,312,313,314}-django{52}
+    py{312,313,314}-django{60}
     pypy-django{22,30,31,32}
 
 [gh-actions]
@@ -30,6 +32,7 @@
     3.11: py311
     3.12: py312
     3.13: py313
+    3.14: py314
     pypy-3.10: pypy
 
 [testenv]
@@ -43,6 +46,10 @@
     django40: Django>=4.0,<4.1
     django41: Django>=4.1,<4.2
     django42: Django>=4.2,<5.0
+    django50: Django>=5.0,<5.1
+    django51: Django>=5.1,<5.2
+    django52: Django>=5.2,<6.0
+    django60: Django>=6.0,<6.1
 commands_pre =
     python -m pip install --upgrade pip
     python -m pip install .
@@ -145,13 +152,13 @@
 description = Build and test package distribution
 skip_install = true
 deps =
+    build
     twine
     check-wheel-contents
 commands_pre =
     python -m pip install -U pip setuptools wheel
-    python setup.py bdist_wheel -d {envtmpdir}/build
-    python setup.py sdist -d {envtmpdir}/build
 commands =
+    python -m build --outdir {envtmpdir}/build
     twine check {envtmpdir}/build/*
     check-wheel-contents {envtmpdir}/build
 

Reply via email to