Compare commits

...

143 commits
2.2 ... master

Author SHA1 Message Date
Christopher Broderick
3d0d4216ca
Merge pull request #399 from jazzband/dependabot/github_actions/github-actions-4ed8cc3c8a
Bump the github-actions group across 1 directory with 2 updates
2024-11-18 23:53:09 +00:00
dependabot[bot]
007161adb0
Bump the github-actions group across 1 directory with 2 updates
Bumps the github-actions group with 2 updates in the / directory: [actions/cache](https://github.com/actions/cache) and [codecov/codecov-action](https://github.com/codecov/codecov-action).


Updates `actions/cache` from 3 to 4
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

Updates `codecov/codecov-action` from 4 to 5
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-18 21:33:41 +00:00
Christopher Broderick
57b6827ae3
Merge pull request #386 from adamchainz/pep_451
Move to PEP-451 style loader
2024-11-18 17:34:02 +00:00
Adam Johnson
b8f66f76ee Move to PEP-451 style loader 2024-11-18 16:52:55 +00:00
Christopher Broderick
fcd03ada0f
Merge pull request #398 from adamchainz/remove_env_loaded
Remove ENV_LOADED from test class
2024-11-18 16:08:45 +00:00
Christopher Broderick
0bf416155e
Merge pull request #384 from adamchainz/deprecated_form_setting
Prevent FORMS_URLFIELD_ASSUME_HTTPS warning on Django 5.0
2024-11-18 13:30:24 +00:00
Adam Johnson
cec5f7492a Remove ENV_LOADED from test class 2024-11-18 12:04:04 +00:00
Adam Johnson
b8e94fd796 Prevent FORMS_URLFIELD_ASSUME_HTTPS warning on Django 5.0 2024-11-18 11:37:17 +00:00
Christopher Broderick
f37ed87d6e
Merge pull request #393 from cclauss/patch-2
Keep GitHub Actions up to date with GitHub's Dependabot
2024-11-09 08:44:30 +00:00
Christopher Broderick
7f0f29d161
Merge pull request #389 from cclauss/patch-1
GitHub Actions: Upgrade the release workflow to Python 3.13
2024-11-08 19:31:36 +00:00
Christian Clauss
9688dae5e1
setup.py: Programming Language :: Python :: 3.13 2024-11-08 20:15:28 +01:00
Christian Clauss
4efa08f81a GitHub Actions: Upgrade to Python 3.12
https://github.com/actions/setup-python/releases
2024-11-08 18:25:41 +01:00
Christian Clauss
c67eab3507
Keep GitHub Actions up to date with GitHub's Dependabot
Fixes software supply chain safety warnings like at the bottom right of

https://github.com/jazzband/django-configurations/actions/runs/11743152917
* [Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot)
* [Configuration options for the dependabot.yml file - package-ecosystem](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem)
2024-11-08 15:13:38 +01:00
Christopher Broderick
711fa66654
Merge pull request #392 from jazzband/fix_code_coverage_and_newly_failing_test
Fix code coverage and newly failing test
2024-11-08 13:36:11 +00:00
Christopher Broderick
448c5ab1b6 Address flake8 issues 2024-11-07 13:33:00 +00:00
Christopher Broderick
65d326a95a Add code coverage token param 2024-11-07 09:59:51 +00:00
Christopher Broderick
63cac8d54e Fix failing test due to external library change 2024-10-28 20:55:21 +00:00
Christopher Broderick
ba24d3c324 Fixes to accomodate v4 codecov/codecov-action changes 2024-10-28 20:54:40 +00:00
Christopher Broderick
e1091160b1
Merge pull request #383 from kloczek/master
really drop support for python<=3.7
2024-09-27 10:52:36 +01:00
Arkadiusz Adamski
880484d3b7 Update README.rst
To be consistent with previous examples.
2024-09-13 17:38:31 +02:00
Kamil Paduszyński
ef4f49d236 Fix #374 -- Fix URL in Configuration.load_dotenv docstring 2024-09-13 17:36:59 +02:00
Tomasz Kłoczko
6dc2340dfe really drop support for python<=3.7
Filer all code over `pyupgrade --py38`.

Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
2024-03-18 16:08:42 +00:00
Paolo Melchiorre
ffe979b63c Fix #372 -- Add support for Python 3.12 2023-11-30 10:53:39 +01:00
Paolo Melchiorre
eba6e2c6d9
Fix #375 -- Add Django 5.0 classifier (#376) 2023-11-28 09:26:45 +01:00
Paolo Melchiorre
38641a9dea Release v2.5 2023-10-20 21:15:30 +02:00
Camilo Nova
9b7bd34812
Merge pull request #369 from pauloxnet/ticket_368
Fix #368 -- Update Python and Django versions
2023-10-16 09:19:10 -05:00
Paolo Melchiorre
df2a7f18fd
Fix #368 -- Update Python and Django versions 2023-10-04 16:52:16 +02:00
Michal Szczesny
27f67a58a4 Replace imp with importlib
This project uses the imp module which has been deprecated since Python 3.4 and set for removal in 3.12:
• Raised PendingDeprecationWarning since 3.4 (2014)
• Raised DeprecationWarning since 3.5 (2015)
• Updated DeprecationWarning to say removal in 3.12 since 3.10 (2021)
• Removal planned for 3.12 (2023)

This change removes the dependency on imp in favour of importlib.

Co-authored-by: @jbkkd
Inspired by: @mgorny

https://github.com/jazzband/django-configurations/issues/358
2023-09-27 14:08:16 +03:00
Michal Szczesny
dce5f37a93
Merge pull request #366 from washeck/master
Add pypy 3.10 to the testing matrix
2023-09-27 11:31:26 +01:00
Vaclav Rehak
2278975744 Add pypy 3.10 to the testing matrix 2023-09-22 17:55:59 +02:00
Paolo Melchiorre
cad6dcb7f0
Fix #355 - Update to Django 4.2 stable (#356) 2023-04-04 12:56:38 +02:00
Paolo Melchiorre
adaf92085f Fix #353 Improve code blocks in documentation 2023-02-15 07:50:29 +01:00
Paolo Melchiorre
a1f072ebf3
Fix #351 Use 'furo' as Sphinx theme (#352) 2023-02-14 16:01:22 +01:00
Ran Benita
6f47271526
Fix "STATICFILES_STORAGE/STORAGES are mutually exclusive" on Django>=4.2 (#349) 2023-02-13 09:18:38 +01:00
Camilo Nova
befe7f1e0d
Merge pull request #346 from pauloxnet/feature/actions-update
Update github actions and fix tests
2023-01-20 12:52:54 -06:00
Paolo Melchiorre
352d95b2ab
Update github actions and fix pipeline 2023-01-19 18:06:05 +01:00
Paolo Melchiorre
1f8bac5ba4 Fixed #344 - Run tests on python 3.11 2022-11-04 18:52:34 +01:00
Camilo Nova
17ca033d33
Merge pull request #342 from timgates42/bugfix_typos
docs: Fix a few typos
2022-09-22 17:04:19 -05:00
Camilo Nova
7520ae4123
Merge pull request #341 from michael-k/fix-release-workflow
Pin publish action in release workflow
2022-09-22 17:03:41 -05:00
Tim Gates
ac5408d7eb
docs: Fix a few typos
There are small typos in:
- configurations/base.py
- configurations/values.py
- docs/patterns.rst

Fixes:
- Should read `whether` rather than `wether`.
- Should read `overridden` rather than `overriden`.
- Should read `environment` rather than `enviroment`.

Signed-off-by: Tim Gates <tim.gates@iress.com>
2022-09-01 20:41:53 +10:00
Michael Käufl
e2861e6327
Pin publish action in release workflow
From https://github.com/pypa/gh-action-pypi-publish/blob/unstable/v1/README.md:

    The `master` branch version has been sunset. Please, change the
    GitHub Action version you use from `master` to `release/v1` or use
    an exact tag, or a full Git commit SHA.
2022-08-31 17:41:17 +02:00
Paolo Melchiorre
40d244d865 Update docs/changes.rst
Co-authored-by: Michael K. <michael-k@users.noreply.github.com>
2022-08-25 10:58:39 +02:00
Paolo Melchiorre
8923ef8c49 Prepare next release 2.4 2022-08-25 10:58:39 +02:00
Andrii Oriekhov
7e473d0f9b
add GitHub URL for PyPi (#331) 2022-08-05 16:43:17 +02:00
Paolo Melchiorre
794b858548
Fixed #336 -- Update Python and Django versions (#337) 2022-08-05 16:42:58 +02:00
Nicolas Delaby
141d8ef2c4
Merge pull request #328 from ticosax/prepare-new-release
Prepare next release 2.3.2
2022-01-25 10:27:57 +01:00
Nicolas Delaby
fcba8b6d92 Prepare next release 2.3.2 2022-01-25 09:26:07 +01:00
Nicolas Delaby
861935fd45
Merge pull request #327 from ticosax/fix-deprecated-settings
Remove deprecated default fields only from the global
2022-01-25 09:12:33 +01:00
Nicolas Delaby
5f438451ea add changelog entry 2022-01-24 18:57:41 +01:00
Nicolas Delaby
45a4557efe Remove deprecated default fields only from the global
And keep user defined values
2022-01-24 18:46:17 +01:00
Jannis Leidel
bb2523ddb9
Fix badge URL. 2021-12-23 20:36:48 +01:00
Paolo Melchiorre
32a41d62b9
Align README badges with other jazzband projects (#326) 2021-12-23 20:35:53 +01:00
Paolo Melchiorre
ded548b19b Update Django 4.0 stable versions 2021-12-23 17:23:58 +06:00
Jannis Leidel
c3b5e8627b
Update changelog. 2021-11-08 16:56:32 +01:00
Brian Helba
93e628e870
Refactor the documentation build process (#311)
* Refactor the documentation build process

Significant improvements:
* Allow easy isolated local builds via a dedicated Tox environment
* Configure Sphinx to use consistent packages and theme across local, CI,
  and ReadTheDocs builds; local builds now look the same as the published
  RTD pages
* Explicitly add a ReadTheDocs configuration as code, per their documented
  best practices
* Remove lots of dead code and simplify the Sphinx configuration file
* Build docs in a dedicated CI step and enable stricter warning checking

* Fix docutils related build error.

* Relax Sphinx version.

* Invalidate RTD build cache.

* No need to combine coverage reporting I think.

* Add combining again.

Co-authored-by: Jannis Leidel <jannis@leidel.info>
2021-11-08 16:49:41 +01:00
Paolo Melchiorre
7ff29852ab
Add pypy versions support (#321)
* Add pypy versions support

* Update Python classifiers
2021-11-08 15:50:21 +01:00
Jannis Leidel
2655ecdd4f
Add python version limiter. 2021-11-08 15:48:22 +01:00
Jannis Leidel
750f143724
Fix documentation build errors. 2021-11-08 15:48:07 +01:00
Paolo Melchiorre
dcce8bccfd
Add support port Django 3.2 and Python 3.10 in tox
Django 3.2.9 adds compatibility with Python 3.10.
https://docs.djangoproject.com/en/3.2/releases/3.2.9/
2021-11-01 12:20:42 +01:00
Brian Helba
1f17d675a1
Merge pull request #319 from jazzband/authors 2021-10-27 12:50:35 -04:00
Brian Helba
381809d88c Add new AUTHORS since 2.2 release
Got this from "git shortlog -s 2.2..master".
2021-10-27 12:41:15 -04:00
Brian Helba
5698fe35b2 Finalize changelog for 2.3 release 2021-10-27 22:40:16 +06:00
Paolo Melchiorre
9f38e87a58
Update Python and Django versions in tox.ini (#307)
* Update Python and Django versions in tox.ini

- remove Django 3.1
- add Django 4.0b1
- add Python 3.10

* Add python 3.10  in github action matrix

* Update tox.ini from django-debug-toolbar

* Fix whitespaces

* Add missing setup classifiers

* Fix typo in tox.ini

* Remove django 4 classifier
2021-10-27 16:16:26 +06:00
Brian Helba
5562322599 Update changelog 2021-10-27 14:24:34 +06:00
Brian Helba
cea79d1a7a Revert "Update release.yml" 2021-10-27 10:23:34 +02:00
Brian Helba
03dc29848e Fix "reference target not found" warnings in the docs
These were found by running Sphinx in nitpicky mode.
2021-10-27 10:17:56 +02:00
Brian Helba
596ab9a1bf Restore an example of a dependent setting to the docs
This was removed by #303. This now uses a real Django setting,
"DEBUG_PROPAGATE_EXCEPTIONS", instead of a deprecated one.
2021-10-27 10:16:59 +02:00
Brian Helba
80f2cee84b Update the copyright date range 2021-10-27 08:06:34 +02:00
Thomas Grainger
089a039efa Added "python -m configurations" entry point.
inspired by e0a46367df/django/__main__.py (L1-L9)

see also https://code.djangoproject.com/ticket/24857
2021-10-27 07:29:02 +06:00
Brian Helba
1dce659946
Merge pull request #305 from jazzband/importlib-version 2021-10-25 14:41:48 -04:00
Brian Helba
f5d6ef7877 Add importlib.metadata backport for older Python versions 2021-10-25 14:33:55 -04:00
Brian Helba
4bd4cf5dd4 Use importlib.metadata instead of pkg_resources to get version 2021-10-25 14:33:54 -04:00
Brian Helba
91b359f74a
Merge pull request #304 from jazzband/rm-mock 2021-10-25 13:21:58 -04:00
Brian Helba
d373c9ab75 Remove "mock" as a test requirement
This is included in the standard library of Python 3.3+.
2021-10-25 13:05:36 -04:00
Brian Helba
62d34c2a16
Merge pull request #253 from linuxmaniac/vseva/fix_250 2021-10-25 13:04:19 -04:00
Brian Helba
02e8f55ac8 Merge remote-tracking branch 'origin/master' into vseva/fix_250 2021-10-25 12:37:36 -04:00
Brian Helba
6c2ea44352
Merge pull request #303 from jazzband/template-debug 2021-10-25 12:33:25 -04:00
Brian Helba
2d9e145a0f
Merge pull request #258 from arsensokolov/master 2021-10-25 12:33:08 -04:00
Brian Helba
b75c8d7a7a Remove references to TEMPLATE_DEBUG
This setting is deprecated since Django 1.8.
2021-10-25 12:18:19 -04:00
Brian Helba
c9c44c59b7
Merge pull request #290 from brianhelba/2.2-deprecated 2021-10-25 11:36:23 -04:00
Brian Helba
38059e1417
Merge pull request #302 from jazzband/rm-command 2021-10-25 11:36:05 -04:00
Brian Helba
df967f4d76 Remove an obsolete command used for testing
The test using this was removed in a045609934.
2021-10-25 11:21:06 -04:00
Brian Helba
6da8420635 Prevent warnings for settings deprecated in Django 2.2
Fixes #233.
2021-10-25 11:15:14 -04:00
Brian Helba
a155ac54ad
Merge pull request #287 from brianhelba/auto-field 2021-10-25 11:13:46 -04:00
Brian Helba
40bdab3f4a Preserve Django warnings when DEFAULT_AUTO_FIELD is not set
Fixes #286.
2021-10-25 10:40:24 -04:00
Brian Helba
b1f62da419
Merge pull request #281 from brianhelba/warning 2021-10-25 10:38:07 -04:00
Brian Helba
f4e2241f44
Merge pull request #300 from jazzband/wsgi-import 2021-10-25 10:35:03 -04:00
Brian Helba
c9c4a02169
Merge pull request #301 from jazzband/space-typo 2021-10-25 10:33:41 -04:00
Brian Helba
ec12828877 Simplify import of django.core.wsgi.get_wsgi_application
This has been available since Django 1.4. The import guard is no longer necessary.
2021-10-25 09:52:21 -04:00
Brian Helba
91ef9fd8ad Fix a double space typo in a string 2021-10-25 09:50:51 -04:00
Brian Helba
df51535afc
Merge pull request #291 from jazzband/feature/asgi 2021-10-25 09:49:54 -04:00
Brian Helba
7c9ac5e53f
Suppress import ordering style error 2021-10-25 09:34:42 -04:00
Brian Helba
36265ab3f1
Merge pull request #288 from brianhelba/require-django 2021-10-25 09:32:59 -04:00
Brian Helba
a5542b2bfc
Merge pull request #299 from jazzband/sort-manifest 2021-10-25 09:26:10 -04:00
Brian Helba
3d1554b4f8
Merge pull request #298 from jazzband/ci-push 2021-10-25 09:21:37 -04:00
Brian Helba
3ac97e539f Require Django as an install_requires
Since "import django" occurs in the code, it is an explicit requirement.
2021-10-25 09:21:17 -04:00
Brian Helba
a56889dbc1
Merge pull request #282 from hroncok/patch-1 2021-10-25 09:17:19 -04:00
Brian Helba
09dfa471e0 Sort MANIFEST.in 2021-10-25 09:12:15 -04:00
Brian Helba
71924a195f
Merge pull request #296 from jazzband/jazzband/sync/default 2021-10-25 09:11:29 -04:00
Brian Helba
55a92d86ad
Merge pull request #252 from timgates42/bugfix_typo_whether 2021-10-25 09:08:12 -04:00
Brian Helba
fad40b8003
Merge pull request #256 from bmispelon/malformed-rst-links 2021-10-25 09:05:23 -04:00
Brian Helba
8ec465de77
Merge pull request #289 from brianhelba/middleware 2021-10-25 09:03:21 -04:00
Brian Helba
7e4b425ea3 Don't run duplicate CI tasks on every PR
The "pull_request" action already runs on pushes to any branch with an open PR,
so only run the "push" action on the "master" branch.
2021-10-25 09:02:06 -04:00
Brian Helba
12e033ed1e Add CODE_OF_CONDUCT.md to the sdist 2021-10-25 08:57:16 -04:00
Brian Helba
38534a7caf Merge remote-tracking branch 'origin/master' into jazzband/sync/default 2021-10-25 08:56:56 -04:00
Brian Helba
96ef5cc182
Merge pull request #297 from jazzband/fix-ci 2021-10-25 08:56:24 -04:00
Brian Helba
fa026af595 Add a pre-commit configuration 2021-10-25 08:38:39 -04:00
jazzband-bot
68cbb437cf Jazzband: Created local 'CODE_OF_CONDUCT.md' from remote 'CODE_OF_CONDUCT.md' 2021-10-25 11:14:26 +00:00
Finn-Thorben Sell
6a4a620891
add additional documentation references to asgi.py 2021-09-09 15:11:49 +02:00
Finn-Thorben Sell
2cae9838b5
add documentation to README about get_asgi_application 2021-09-09 15:08:42 +02:00
Finn-Thorben Sell
c1cb3874f2
remove fallback onto ASGIHandler
ASGIHandler is considered a django internal api with get_asgi_application being the public counterpart.
get_asgi_application is also present since the introduction of django's asgi support so the ImportError should never occur anyways.
2021-09-09 15:01:11 +02:00
Richard de Wit
1ada7d14f7
Add ASGI support
Then in your project's `asgi.py` file use this:

```python
from configurations.asgi import get_asgi_application

application = get_asgi_application()
```
2021-09-09 14:35:09 +02:00
Brian Helba
271f6fb5bb Replace references to MIDDLEWARE_CLASSES with MIDDLEWARE
MIDDLEWARE_CLASSES was removed in Django 2.0.
2021-08-31 22:01:34 -04:00
Brian Helba
d715a719a4 Use a more general structure for removing deprecated settings 2021-08-31 21:38:51 -04:00
Miro Hrončok
58a4f689ff
Require setuptools, configurations/version.py imports pkg_resources 2021-04-12 21:53:16 +02:00
Brian Helba
f85ec1fb89
Prevent an ImproperlyConfigured warning from DEFAULT_HASHING_ALGORITHM 2021-04-09 00:20:36 -04:00
Jannis Leidel
d89fe5a2cb
Rename Django's dev branch to main. (#279)
* Rename Django's dev branch to main.

More information: https://groups.google.com/g/django-developers/c/tctDuKUGosc/
Refs: https://github.com/django/django/pull/14048

* Fix test matrix.

* Remove CODEOWNERS again.

* Fix name of tox env.

* Fix test matrix.
2021-03-09 19:20:31 +01:00
Jannis Leidel
856b55bea8
Merge pull request #275 from jazzband/co
Codeowners update.
2021-01-19 16:41:05 +01:00
Jannis Leidel
3da6af02fb
Codeowners update. 2021-01-19 16:40:19 +01:00
Jannis Leidel
c8153bc081
Merge pull request #274 from jazzband/branch-protection-1
Update release.yml
2021-01-19 16:34:23 +01:00
Jannis Leidel
6fa45aba1b
Update release.yml 2021-01-19 14:55:46 +01:00
Jannis Leidel
58575bfb84
Minor update. 2021-01-19 14:54:47 +01:00
Jannis Leidel
184df10a66
Add code owner for workflows. 2021-01-19 14:48:20 +01:00
Michael Käufl
9b9ff4c0a2 Deprecate utils.import_by_path in favor of django.utils.module_loading.import_string 2021-01-19 13:11:16 +06:00
Michael Käufl
c3d5b4b715 Add support for Python 3.9 2021-01-19 13:11:16 +06:00
Michael Käufl
add9b3ce80 Add support for Django 3.1 and 3.2 2021-01-19 13:11:16 +06:00
Michael Käufl
6cb932e47f Run pyupgrade on the code
But don't touch string formatting.

https://pypi.org/project/pyupgrade/
2021-01-19 13:11:16 +06:00
Michael Käufl
fcdbfc0bcb Drop support for Python 3.5
It has reached its EOL in September 2020, see
https://www.python.org/dev/peps/pep-0478/#release-schedule
2021-01-19 13:11:16 +06:00
Michael Käufl
dd5d6974cb Drop support for Django < 2.2 LTS, incl. Python 2.7 2021-01-19 13:11:16 +06:00
Jannis Leidel
78f9824f0c
Merge pull request #270 from jazzband/gha
Migrate to GitHub Actions
2020-11-27 17:31:02 +01:00
Jannis Leidel
f5bebd4ecc
Add fail_ci_if_error to test workflow. 2020-11-27 09:59:12 +01:00
Jannis Leidel
5053da4357
Add release workflow. 2020-11-26 17:08:22 +01:00
Jannis Leidel
eea5b9ad34
Remove travis cruft. 2020-11-26 17:08:14 +01:00
Jannis Leidel
64eefaef1a
Fix an issue with an old version of dj-email-url. 2020-11-26 16:51:15 +01:00
Jannis Leidel
b540ceadb3
Add GitHub Actions test workflow. 2020-11-26 16:18:58 +01:00
Michael K
3305de960a
Merge pull request #261 from dat2/patch-1
fix version string at runtime
2020-10-07 15:20:56 +00:00
Nicholas Dujay
4fb5928a9c
use package name in get_distribution function call 2020-09-21 09:43:42 -04:00
Arseny Sokolov
b9648bddb3
Add DictValue example 2020-06-17 23:38:48 +08:00
Baptiste Mispelon
566af30ce6 Fixed malformed links in documentation 2020-05-29 12:15:08 +02:00
Victor Seva
ac6d31bb83 testing: remove django-discover-runner
project supports Django 1.11+

> https://pypi.org/project/django-discover-runner/

This runner has been added to Django 1.6 as the default test runner.
If you use Django 1.6 or above you don’t need this app.

fix #250
2020-03-24 10:55:04 +01:00
Tim Gates
3b3f5db60d
docs: Fix simple typo, wheter -> whether
There is a small typo in docs/values.rst.

Should read `whether` rather than `wheter`.
2020-03-12 06:40:15 +11:00
Jannis Leidel
601d52218a
Merge pull request #234 from ckrybus/update-django-trove-classifiers
Update django trove classifiers
2020-01-06 19:45:18 +01:00
Christoph Krybus
6314038a77 Update django trove classifiers 2020-01-06 17:10:39 +01:00
51 changed files with 805 additions and 1089 deletions

13
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,13 @@
# Keep GitHub Actions up to date with GitHub's Dependabot...
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
version: 2
updates:
- package-ecosystem: github-actions
directory: /
groups:
github-actions:
patterns:
- "*" # Group all Actions updates into a single larger pull request
schedule:
interval: weekly

53
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: Release
on:
push:
tags:
- '*'
jobs:
build:
if: github.repository == 'jazzband/django-configurations'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Get pip cache dir
id: pip-cache
run: |
echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
- name: Cache
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: release-${{ hashFiles('**/setup.py') }}
restore-keys: |
release-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade setuptools twine wheel
- name: Build package
run: |
python setup.py --version
python setup.py sdist --format=gztar bdist_wheel
twine check dist/*
- name: Upload packages to Jazzband
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: jazzband
password: ${{ secrets.JAZZBAND_RELEASE_KEY }}
repository-url: https://jazzband.co/projects/django-configurations/upload

57
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,57 @@
name: Test
on:
pull_request:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 6
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.10']
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Get pip cache dir
id: pip-cache
run: |
echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
- name: Cache
uses: actions/cache@v4
with:
path: ${{ steps.pip-cache.outputs.dir }}
key:
${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}
restore-keys: |
${{ matrix.python-version }}-v1-
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade "tox<4" "tox-gh-actions<3"
- name: Tox tests
run: |
tox --verbose
- name: Upload coverage
uses: codecov/codecov-action@v5
with:
name: coverage-data-${{ matrix.python-version }}
path: ".coverage.*"
include-hidden-files: true
merge-multiple: true
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

1
.gitignore vendored
View file

@ -9,5 +9,4 @@ build/
htmlcov/
*.pyc
dist/
tests/docs/_build/
.eggs/

1
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1 @@
repos: []

16
.readthedocs.yaml Normal file
View file

@ -0,0 +1,16 @@
---
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.10"
python:
install:
- requirements: docs/requirements.txt
- method: pip
path: .
sphinx:
configuration: docs/conf.py
formats:
- epub
- pdf

View file

@ -1,37 +0,0 @@
language: python
dist: xenial
cache: pip
python:
- '2.7'
- '3.5'
- '3.6'
- '3.7'
- '3.8'
- 'pypy3'
install: travis_retry pip install tox-travis codecov
script: tox -v
after_success: codecov --required -X gcov fix pycov -f coverage.xml --flags ${TOXENV//-/ }
branches:
except: templates/1.5.x templates/1.6.x
stages:
- test
- name: deploy
if: repo = jazzband/django-configurations AND tag IS present
jobs:
include:
- stage: test
- stage: deploy
install: skip
script: skip
python: 3.7
env: skip
deploy:
provider: pypi
user: jazzband
server: https://jazzband.co/projects/django-configurations/upload
distributions: sdist bdist_wheel
password:
secure: LuserSjUTGSsls9zrvck/FbfL+gFpNU/ywOQ/67ufEbbpGCeDBEgxDzgb0acfHNk8wlAkaPvaAejQBFtcUulhdNT/g0NsmaEAjd6HhCGM+FRJAnYFaj33Js6C+N2tX5wznL7uCBxqgtaaH0hf6ucqC8OXqwoCVGgdxAEnUlC/fY=
on:
tags: true
repo: jazzband/django-configurations

13
AUTHORS
View file

@ -1,9 +1,22 @@
Arseny Sokolov
Asif Saif Uddin
Baptiste Mispelon
Brian Helba
Bruno Clermont
Christoph Krybus
Finn-Thorben Sell
Gilles Fabio
Jannis Leidel
John Franey
Marc Abramowitz
Michael Käufl
Michael van Tellingen
Mike Fogel
Miro Hrončok
Nicholas Dujay
Paolo Melchiorre
Peter Bittner
Richard de Wit
Thomas Grainger
Tim Gates
Victor Seva

46
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,46 @@
# Code of Conduct
As contributors and maintainers of the Jazzband projects, and in the interest of
fostering an open and welcoming community, we pledge to respect all people who
contribute through reporting issues, posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.
We are committed to making participation in the Jazzband a harassment-free experience
for everyone, regardless of the level of experience, gender, gender identity and
expression, sexual orientation, disability, personal appearance, body size, race,
ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery
- Personal attacks
- Trolling or insulting/derogatory comments
- Public or private harassment
- Publishing other's private information, such as physical or electronic addresses,
without explicit permission
- Other unethical or unprofessional conduct
The Jazzband roadies have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are not
aligned to this Code of Conduct, or to ban temporarily or permanently any contributor
for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, the roadies commit themselves to fairly and
consistently applying these principles to every aspect of managing the jazzband
projects. Roadies who do not follow or enforce the Code of Conduct may be permanently
removed from the Jazzband roadies.
This code of conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and
investigated and will result in a response that is deemed necessary and appropriate to
the circumstances. Roadies are obligated to maintain confidentiality with regard to the
reporter of an incident.
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version
1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/3/0/

View file

@ -1,4 +1,4 @@
Copyright (c) 2012-2014, Jannis Leidel and other contributors.
Copyright (c) 2012-2023, Jannis Leidel and other contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

View file

@ -1,9 +1,11 @@
include README.rst
include CONTRIBUTING.md
include .pre-commit-config.yaml
include .readthedocs.yaml
include AUTHORS
include .travis.yml
include CODE_OF_CONDUCT.md
include CONTRIBUTING.md
include LICENSE
include README.rst
include tox.ini
recursive-include tests *
recursive-include docs *
recursive-include test_project *
include LICENSE
recursive-include tests *

View file

@ -1,7 +1,7 @@
django-configurations |latest-version|
======================================
|build-status| |codecov| |docs| |python-support| |jazzband|
|jazzband| |build-status| |codecov| |docs| |python-support| |django-support|
django-configurations eases Django project configuration by relying
on the composability of Python classes. It extends the notion of
@ -11,23 +11,33 @@ object oriented programming patterns.
Check out the `documentation`_ for more complete examples.
.. |latest-version| image:: https://img.shields.io/pypi/v/django-configurations.svg
:alt: Latest version on PyPI
:target: https://pypi.python.org/pypi/django-configurations
.. |build-status| image:: https://img.shields.io/travis/jazzband/django-configurations/master.svg
:alt: Build status
:target: https://travis-ci.org/jazzband/django-configurations
:alt: Latest version on PyPI
.. |jazzband| image:: https://jazzband.co/static/img/badge.svg
:target: https://jazzband.co/
:alt: Jazzband
.. |build-status| image:: https://github.com/jazzband/django-configurations/workflows/Test/badge.svg
:target: https://github.com/jazzband/django-configurations/actions
:alt: Build Status
.. |codecov| image:: https://codecov.io/github/jazzband/django-configurations/coverage.svg?branch=master
:alt: Codecov
:target: https://codecov.io/github/jazzband/django-configurations?branch=master
:alt: Test coverage status
.. |docs| image:: https://img.shields.io/readthedocs/django-configurations/latest.svg
:alt: Documentation status
:target: https://readthedocs.org/projects/django-configurations/
:alt: Documentation status
.. |python-support| image:: https://img.shields.io/pypi/pyversions/django-configurations.svg
:target: https://pypi.python.org/pypi/django-configurations
:alt: Python versions
.. |jazzband| image:: https://jazzband.co/static/img/badge.svg
:alt: Jazzband
:target: https://jazzband.co/
:alt: Supported Python versions
.. |django-support| image:: https://img.shields.io/pypi/djversions/django-configurations
:target: https://pypi.org/project/django-configurations
:alt: Supported Django versions
.. _documentation: https://django-configurations.readthedocs.io/en/latest/
Quickstart
@ -37,13 +47,13 @@ Install django-configurations:
.. code-block:: console
pip install django-configurations
$ python -m pip install django-configurations
or, alternatively, if you want to use URL-based values:
.. code-block:: console
pip install django-configurations[cache,database,email,search]
$ python -m pip install django-configurations[cache,database,email,search]
Then subclass the included ``configurations.Configuration`` class in your
project's **settings.py** or any other module you're using to store the
@ -63,14 +73,14 @@ you just created, e.g. in bash:
.. code-block:: console
export DJANGO_CONFIGURATION=Dev
$ export DJANGO_CONFIGURATION=Dev
and the ``DJANGO_SETTINGS_MODULE`` environment variable to the module
import path as usual, e.g. in bash:
.. code-block:: console
export DJANGO_SETTINGS_MODULE=mysite.settings
$ export DJANGO_SETTINGS_MODULE=mysite.settings
*Alternatively* supply the ``--configuration`` option when using Django
management commands along the lines of Django's default ``--settings``
@ -78,10 +88,10 @@ command line option, e.g.
.. code-block:: console
python manage.py runserver --settings=mysite.settings --configuration=Dev
$ python -m manage runserver --settings=mysite.settings --configuration=Dev
To enable Django to use your configuration you now have to modify your
**manage.py** or **wsgi.py** script to use django-configurations's versions
**manage.py**, **wsgi.py** or **asgi.py** script to use django-configurations's versions
of the appropriate starter functions, e.g. a typical **manage.py** using
django-configurations would look like this:
@ -120,5 +130,18 @@ The same applies to your **wsgi.py** file, e.g.:
Here we don't use the default ``django.core.wsgi.get_wsgi_application``
function but instead ``configurations.wsgi.get_wsgi_application``.
Or if you are not serving your app via WSGI but ASGI instead, you need to modify your **asgi.py** file too.:
.. code-block:: python
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
os.environ.setdefault('DJANGO_CONFIGURATION', 'Dev')
from configurations.asgi import get_asgi_application
application = get_asgi_application()
That's it! You can now use your project with ``manage.py`` and your favorite
WSGI enabled server.
WSGI/ASGI enabled server.

View file

@ -0,0 +1,10 @@
"""
invokes django-cadmin when the configurations module is run as a script.
Example: python -m configurations check
"""
from .management import execute_from_command_line
if __name__ == "__main__":
execute_from_command_line()

8
configurations/asgi.py Normal file
View file

@ -0,0 +1,8 @@
from . import importer
importer.install()
from django.core.asgi import get_asgi_application # noqa: E402
# this is just for the crazy ones
application = get_asgi_application()

View file

@ -1,6 +1,5 @@
import os
import re
import six
from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured
@ -32,19 +31,50 @@ class ConfigurationBase(type):
if parents:
for base in bases[::-1]:
settings_vars.update(uppercase_attributes(base))
attrs = dict(settings_vars, **attrs)
# Fix ImproperlyConfigured issue introduced in Django
deprecated_settings = {
# DEFAULT_HASHING_ALGORITHM is always deprecated, as it's a
# transitional setting
# https://docs.djangoproject.com/en/3.1/releases/3.1/#default-hashing-algorithm-settings
"DEFAULT_HASHING_ALGORITHM",
# DEFAULT_CONTENT_TYPE and FILE_CHARSET are deprecated in
# Django 2.2 and are removed in Django 3.0
"DEFAULT_CONTENT_TYPE",
"FILE_CHARSET",
# When DEFAULT_AUTO_FIELD is not explicitly set, Django's emits a
# system check warning models.W042. This warning should not be
# suppressed, as downstream users are expected to make a decision.
# https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
"DEFAULT_AUTO_FIELD",
# FORMS_URLFIELD_ASSUME_HTTPS is a transitional setting introduced
# in Django 5.0.
# https://docs.djangoproject.com/en/5.0/releases/5.0/#id2
"FORMS_URLFIELD_ASSUME_HTTPS"
}
# PASSWORD_RESET_TIMEOUT_DAYS is deprecated in favor of
# PASSWORD_RESET_TIMEOUT in Django 3.1
# https://github.com/django/django/commit/226ebb17290b604ef29e82fb5c1fbac3594ac163#diff-ec2bed07bb264cb95a80f08d71a47c06R163-R170
if "PASSWORD_RESET_TIMEOUT_DAYS" in attrs and "PASSWORD_RESET_TIMEOUT" in attrs:
attrs.pop("PASSWORD_RESET_TIMEOUT_DAYS")
return super(ConfigurationBase, cls).__new__(cls, name, bases, attrs)
if "PASSWORD_RESET_TIMEOUT" in settings_vars:
deprecated_settings.add("PASSWORD_RESET_TIMEOUT_DAYS")
# DEFAULT_FILE_STORAGE and STATICFILES_STORAGE are deprecated
# in favor of STORAGES.
# https://docs.djangoproject.com/en/dev/releases/4.2/#custom-file-storages
if "STORAGES" in settings_vars:
deprecated_settings.add("DEFAULT_FILE_STORAGE")
deprecated_settings.add("STATICFILES_STORAGE")
for deprecated_setting in deprecated_settings:
if deprecated_setting in settings_vars:
del settings_vars[deprecated_setting]
attrs = {**settings_vars, **attrs}
return super().__new__(cls, name, bases, attrs)
def __repr__(self):
return "<Configuration '{0}.{1}'>".format(self.__module__,
return "<Configuration '{}.{}'>".format(self.__module__,
self.__name__)
class Configuration(six.with_metaclass(ConfigurationBase)):
class Configuration(metaclass=ConfigurationBase):
"""
The base configuration class to inherit from.
@ -77,10 +107,10 @@ class Configuration(six.with_metaclass(ConfigurationBase)):
environment variables from a .env file located in the project root
or provided directory.
http://www.wellfireinteractive.com/blog/easier-12-factor-django/
https://wellfire.co/learn/easier-12-factor-django/
https://gist.github.com/bennylope/2999704
"""
# check if the class has DOTENV set wether with a path or None
# check if the class has DOTENV set whether with a path or None
dotenv = getattr(cls, 'DOTENV', None)
# if DOTENV is falsy we want to disable it
@ -89,12 +119,12 @@ class Configuration(six.with_metaclass(ConfigurationBase)):
# now check if we can access the file since we know we really want to
try:
with open(dotenv, 'r') as f:
with open(dotenv) as f:
content = f.read()
except IOError as e:
except OSError as e:
raise ImproperlyConfigured("Couldn't read .env file "
"with the path {}. Error: "
"{}".format(dotenv, e))
"{}".format(dotenv, e)) from e
else:
for line in content.splitlines():
m1 = re.match(r'\A([A-Za-z_0-9]+)=(.*)\Z', line)

View file

@ -1,4 +1,4 @@
import imp
from importlib.machinery import PathFinder
import logging
import os
import sys
@ -46,12 +46,12 @@ def install(check_options=False):
return parser
base.BaseCommand.create_parser = create_parser
importer = ConfigurationImporter(check_options=check_options)
importer = ConfigurationFinder(check_options=check_options)
sys.meta_path.insert(0, importer)
installed = True
class ConfigurationImporter(object):
class ConfigurationFinder(PathFinder):
modvar = SETTINGS_ENVIRONMENT_VARIABLE
namevar = CONFIGURATION_ENVIRONMENT_VARIABLE
error_msg = ("Configuration cannot be imported, "
@ -70,7 +70,7 @@ class ConfigurationImporter(object):
self.announce()
def __repr__(self):
return "<ConfigurationImporter for '{0}.{1}'>".format(self.module,
return "<ConfigurationFinder for '{}.{}'>".format(self.module,
self.name)
@property
@ -82,16 +82,10 @@ class ConfigurationImporter(object):
return os.environ.get(self.namevar)
def check_options(self):
try:
parser = base.CommandParser(
usage="%(prog)s subcommand [options] [args]",
add_help=False)
except TypeError:
# Django before 2.1 used a `cmd` argument.
parser = base.CommandParser(
None,
usage="%(prog)s subcommand [options] [args]",
add_help=False)
parser = base.CommandParser(
usage="%(prog)s subcommand [options] [args]",
add_help=False,
)
parser.add_argument('--settings')
parser.add_argument('--pythonpath')
parser.add_argument(CONFIGURATION_ARGUMENT,
@ -127,58 +121,60 @@ class ConfigurationImporter(object):
if (self.argv[1] == 'runserver'
and os.environ.get('RUN_MAIN') == 'true'):
message = ("django-configurations version {0}, using "
"configuration {1}".format(__version__ or "",
message = ("django-configurations version {}, using "
"configuration {}".format(__version__ or "",
self.name))
self.logger.debug(stylize(message))
def find_module(self, fullname, path=None):
def find_spec(self, fullname, path=None, target=None):
if fullname is not None and fullname == self.module:
module = fullname.rsplit('.', 1)[-1]
return ConfigurationLoader(self.name,
imp.find_module(module, path))
return None
class ConfigurationLoader(object):
def __init__(self, name, location):
self.name = name
self.location = location
def load_module(self, fullname):
if fullname in sys.modules:
mod = sys.modules[fullname] # pragma: no cover
spec = super().find_spec(fullname, path, target)
if spec is not None:
wrap_loader(spec.loader, self.name)
return spec
else:
mod = imp.load_module(fullname, *self.location)
cls_path = '{0}.{1}'.format(mod.__name__, self.name)
return None
try:
cls = getattr(mod, self.name)
except AttributeError as err: # pragma: no cover
reraise(err, "Couldn't find configuration '{0}' "
"in module '{1}'".format(self.name,
mod.__package__))
try:
cls.pre_setup()
cls.setup()
obj = cls()
attributes = uppercase_attributes(obj).items()
for name, value in attributes:
if callable(value) and not getattr(value, 'pristine', False):
value = value()
# in case a method returns a Value instance we have
# to do the same as the Configuration.setup method
if isinstance(value, Value):
setup_value(mod, name, value)
continue
setattr(mod, name, value)
setattr(mod, 'CONFIGURATION', '{0}.{1}'.format(fullname,
self.name))
cls.post_setup()
def wrap_loader(loader, class_name):
class ConfigurationLoader(loader.__class__):
def exec_module(self, module):
super().exec_module(module)
except Exception as err:
reraise(err, "Couldn't setup configuration '{0}'".format(cls_path))
mod = module
return mod
cls_path = f'{mod.__name__}.{class_name}'
try:
cls = getattr(mod, class_name)
except AttributeError as err: # pragma: no cover
reraise(
err,
(
f"Couldn't find configuration '{class_name}' in "
f"module '{mod.__package__}'"
),
)
try:
cls.pre_setup()
cls.setup()
obj = cls()
attributes = uppercase_attributes(obj).items()
for name, value in attributes:
if callable(value) and not getattr(value, 'pristine', False):
value = value()
# in case a method returns a Value instance we have
# to do the same as the Configuration.setup method
if isinstance(value, Value):
setup_value(mod, name, value)
continue
setattr(mod, name, value)
setattr(mod, 'CONFIGURATION', '{0}.{1}'.format(module.__name__,
class_name))
cls.post_setup()
except Exception as err:
reraise(err, f"Couldn't setup configuration '{cls_path}'")
loader.__class__ = ConfigurationLoader

View file

@ -1,7 +1,8 @@
import inspect
import six
import sys
import warnings
from functools import partial
from importlib import import_module
from django.core.exceptions import ImproperlyConfigured
@ -12,8 +13,7 @@ def isuppercase(name):
def uppercase_attributes(obj):
return dict((name, getattr(obj, name))
for name in filter(isuppercase, dir(obj)))
return {name: getattr(obj, name) for name in dir(obj) if isuppercase(name)}
def import_by_path(dotted_path, error_prefix=''):
@ -24,25 +24,26 @@ def import_by_path(dotted_path, error_prefix=''):
Backported from Django 1.6.
"""
warnings.warn("Function utils.import_by_path is deprecated in favor of "
"django.utils.module_loading.import_string.", DeprecationWarning)
try:
module_path, class_name = dotted_path.rsplit('.', 1)
except ValueError:
raise ImproperlyConfigured("{0}{1} doesn't look like "
raise ImproperlyConfigured("{}{} doesn't look like "
"a module path".format(error_prefix,
dotted_path))
try:
module = import_module(module_path)
except ImportError as err:
msg = '{0}Error importing module {1}: "{2}"'.format(error_prefix,
msg = '{}Error importing module {}: "{}"'.format(error_prefix,
module_path,
err)
six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
sys.exc_info()[2])
raise ImproperlyConfigured(msg).with_traceback(sys.exc_info()[2])
try:
attr = getattr(module, class_name)
except AttributeError:
raise ImproperlyConfigured('{0}Module "{1}" does not define a '
'"{2}" attribute/class'.format(error_prefix,
raise ImproperlyConfigured('{}Module "{}" does not define a '
'"{}" attribute/class'.format(error_prefix,
module_path,
class_name))
return attr
@ -60,78 +61,41 @@ def reraise(exc, prefix=None, suffix=None):
suffix = ''
elif not (suffix.startswith('(') and suffix.endswith(')')):
suffix = '(' + suffix + ')'
exc.args = ('{0} {1} {2}'.format(prefix, args[0], suffix),) + args[1:]
raise
exc.args = (f'{prefix} {args[0]} {suffix}',) + args[1:]
raise exc
# Copied over from Sphinx
if sys.version_info >= (3, 0):
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
if type(func) is partial:
orig_func = func.func
argspec = getargspec(orig_func)
args = list(argspec[0])
defaults = list(argspec[3] or ())
kwoargs = list(argspec[4])
kwodefs = dict(argspec[5] or {})
if func.args:
args = args[len(func.args):]
for arg in func.keywords or ():
try:
i = args.index(arg) - len(args)
del args[i]
try:
del defaults[i]
except IndexError:
pass
except ValueError: # must be a kwonly arg
i = kwoargs.index(arg)
del kwoargs[i]
del kwodefs[arg]
return inspect.FullArgSpec(args, argspec[1], argspec[2],
tuple(defaults), kwoargs,
kwodefs, argspec[6])
while hasattr(func, '__wrapped__'):
func = func.__wrapped__
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
return inspect.getfullargspec(func)
else: # 2.6, 2.7
from functools import partial
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.im_func
parts = 0, ()
if type(func) is partial:
keywords = func.keywords
if keywords is None:
keywords = {}
parts = len(func.args), keywords.keys()
func = func.func
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
args, varargs, varkw = inspect.getargs(func.func_code)
func_defaults = func.func_defaults
if func_defaults is None:
func_defaults = []
else:
func_defaults = list(func_defaults)
if parts[0]:
args = args[parts[0]:]
if parts[1]:
for arg in parts[1]:
def getargspec(func):
"""Like inspect.getargspec but supports functools.partial as well."""
if inspect.ismethod(func):
func = func.__func__
if type(func) is partial:
orig_func = func.func
argspec = getargspec(orig_func)
args = list(argspec[0])
defaults = list(argspec[3] or ())
kwoargs = list(argspec[4])
kwodefs = dict(argspec[5] or {})
if func.args:
args = args[len(func.args):]
for arg in func.keywords or ():
try:
i = args.index(arg) - len(args)
del args[i]
try:
del func_defaults[i]
del defaults[i]
except IndexError:
pass
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
except ValueError: # must be a kwonly arg
i = kwoargs.index(arg)
del kwoargs[i]
del kwodefs[arg]
return inspect.FullArgSpec(args, argspec[1], argspec[2],
tuple(defaults), kwoargs,
kwodefs, argspec[6])
while hasattr(func, '__wrapped__'):
func = func.__wrapped__
if not inspect.isfunction(func):
raise TypeError('%r is not a Python function' % func)
return inspect.getfullargspec(func)

View file

@ -2,13 +2,13 @@ import ast
import copy
import decimal
import os
import six
import sys
from django.core import validators
from django.core.exceptions import ValidationError, ImproperlyConfigured
from django.utils.module_loading import import_string
from .utils import import_by_path, getargspec
from .utils import getargspec
def setup_value(target, name, value):
@ -20,7 +20,7 @@ def setup_value(target, name, value):
setattr(target, multiple_name, multiple_value)
class Value(object):
class Value:
"""
A single settings value that is able to interpret env variables
and implements a simple validation scheme.
@ -92,7 +92,7 @@ class Value(object):
else:
environ_name = name.upper()
if self.environ_prefix:
environ_name = '{0}_{1}'.format(self.environ_prefix, environ_name)
environ_name = f'{self.environ_prefix}_{environ_name}'
return environ_name
def setup(self, name):
@ -102,8 +102,8 @@ class Value(object):
if full_environ_name in os.environ:
value = self.to_python(os.environ[full_environ_name])
elif self.environ_required:
raise ValueError('Value {0!r} is required to be set as the '
'environment variable {1!r}'
raise ValueError('Value {!r} is required to be set as the '
'environment variable {!r}'
.format(name, full_environ_name))
self.value = value
return value
@ -112,12 +112,12 @@ class Value(object):
"""
Convert the given value of a environment variable into an
appropriate Python representation of the value.
This should be overriden when subclassing.
This should be overridden when subclassing.
"""
return value
class MultipleMixin(object):
class MultipleMixin:
multiple = True
@ -126,9 +126,9 @@ class BooleanValue(Value):
false_values = ('no', 'n', 'false', '0', '')
def __init__(self, *args, **kwargs):
super(BooleanValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.default not in (True, False):
raise ValueError('Default value {0!r} is not a '
raise ValueError('Default value {!r} is not a '
'boolean value'.format(self.default))
def to_python(self, value):
@ -139,28 +139,30 @@ class BooleanValue(Value):
return False
else:
raise ValueError('Cannot interpret '
'boolean value {0!r}'.format(value))
'boolean value {!r}'.format(value))
class CastingMixin(object):
class CastingMixin:
exception = (TypeError, ValueError)
message = 'Cannot interpret value {0!r}'
def __init__(self, *args, **kwargs):
super(CastingMixin, self).__init__(*args, **kwargs)
if isinstance(self.caster, six.string_types):
self._caster = import_by_path(self.caster)
super().__init__(*args, **kwargs)
if isinstance(self.caster, str):
try:
self._caster = import_string(self.caster)
except ImportError as err:
msg = f"Could not import {self.caster!r}"
raise ImproperlyConfigured(msg) from err
elif callable(self.caster):
self._caster = self.caster
else:
error = 'Cannot use caster of {0} ({1!r})'.format(self,
error = 'Cannot use caster of {} ({!r})'.format(self,
self.caster)
raise ValueError(error)
try:
arg_names = getargspec(self._caster)[0]
self._params = dict((name, kwargs[name])
for name in arg_names
if name in kwargs)
self._params = {name: kwargs[name] for name in arg_names if name in kwargs}
except TypeError:
self._params = {}
@ -181,7 +183,7 @@ class IntegerValue(CastingMixin, Value):
class PositiveIntegerValue(IntegerValue):
def to_python(self, value):
int_value = super(PositiveIntegerValue, self).to_python(value)
int_value = super().to_python(value)
if int_value < 0:
raise ValueError(self.message.format(value))
return int_value
@ -213,7 +215,7 @@ class SequenceValue(Value):
converter = kwargs.pop('converter', None)
if converter is not None:
self.converter = converter
super(SequenceValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
# make sure the default is the correct sequence type
if self.default is None:
self.default = self.sequence_type()
@ -257,7 +259,7 @@ class SingleNestedSequenceValue(SequenceValue):
def __init__(self, *args, **kwargs):
self.seq_separator = kwargs.pop('seq_separator', ';')
super(SingleNestedSequenceValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def _convert(self, items):
# This could receive either a bare or nested sequence
@ -266,8 +268,7 @@ class SingleNestedSequenceValue(SequenceValue):
super(SingleNestedSequenceValue, self)._convert(i) for i in items
]
return self.sequence_type(converted_sequences)
return self.sequence_type(
super(SingleNestedSequenceValue, self)._convert(items))
return self.sequence_type(super()._convert(items))
def to_python(self, value):
split_value = [
@ -293,9 +294,9 @@ class BackendsValue(ListValue):
def converter(self, value):
try:
import_by_path(value)
except ImproperlyConfigured as err:
six.reraise(ValueError, ValueError(err), sys.exc_info()[2])
import_string(value)
except ImportError as err:
raise ValueError(err).with_traceback(sys.exc_info()[2])
return value
@ -303,28 +304,28 @@ class SetValue(ListValue):
message = 'Cannot interpret set item {0!r} in set {1!r}'
def __init__(self, *args, **kwargs):
super(SetValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.default is None:
self.default = set()
else:
self.default = set(self.default)
def to_python(self, value):
return set(super(SetValue, self).to_python(value))
return set(super().to_python(value))
class DictValue(Value):
message = 'Cannot interpret dict value {0!r}'
def __init__(self, *args, **kwargs):
super(DictValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.default is None:
self.default = {}
else:
self.default = dict(self.default)
def to_python(self, value):
value = super(DictValue, self).to_python(value)
value = super().to_python(value)
if not value:
return {}
try:
@ -336,17 +337,21 @@ class DictValue(Value):
return evaled_value
class ValidationMixin(object):
class ValidationMixin:
def __init__(self, *args, **kwargs):
super(ValidationMixin, self).__init__(*args, **kwargs)
if isinstance(self.validator, six.string_types):
self._validator = import_by_path(self.validator)
super().__init__(*args, **kwargs)
if isinstance(self.validator, str):
try:
self._validator = import_string(self.validator)
except ImportError as err:
msg = f"Could not import {self.validator!r}"
raise ImproperlyConfigured(msg) from err
elif callable(self.validator):
self._validator = self.validator
else:
raise ValueError('Cannot use validator of '
'{0} ({1!r})'.format(self, self.validator))
'{} ({!r})'.format(self, self.validator))
if self.default:
self.to_python(self.default)
@ -380,19 +385,19 @@ class RegexValue(ValidationMixin, Value):
def __init__(self, *args, **kwargs):
regex = kwargs.pop('regex', None)
self.validator = validators.RegexValidator(regex=regex)
super(RegexValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
class PathValue(Value):
def __init__(self, *args, **kwargs):
self.check_exists = kwargs.pop('check_exists', True)
super(PathValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
def setup(self, name):
value = super(PathValue, self).setup(name)
value = super().setup(name)
value = os.path.expanduser(value)
if self.check_exists and not os.path.exists(value):
raise ValueError('Path {0!r} does not exist.'.format(value))
raise ValueError(f'Path {value!r} does not exist.')
return os.path.abspath(value)
@ -401,15 +406,15 @@ class SecretValue(Value):
def __init__(self, *args, **kwargs):
kwargs['environ'] = True
kwargs['environ_required'] = True
super(SecretValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.default is not None:
raise ValueError('Secret values are only allowed to '
'be set as environment variables')
def setup(self, name):
value = super(SecretValue, self).setup(name)
value = super().setup(name)
if not value:
raise ValueError('Secret value {0!r} is not set'.format(name))
raise ValueError(f'Secret value {name!r} is not set')
return value
@ -422,7 +427,7 @@ class EmailURLValue(CastingMixin, MultipleMixin, Value):
kwargs.setdefault('environ', True)
kwargs.setdefault('environ_prefix', None)
kwargs.setdefault('environ_name', 'EMAIL_URL')
super(EmailURLValue, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.default is None:
self.default = {}
else:
@ -437,14 +442,14 @@ class DictBackendMixin(Value):
kwargs.setdefault('environ', True)
kwargs.setdefault('environ_prefix', None)
kwargs.setdefault('environ_name', self.environ_name)
super(DictBackendMixin, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if self.default is None:
self.default = {}
else:
self.default = self.to_python(self.default)
def to_python(self, value):
value = super(DictBackendMixin, self).to_python(value)
value = super().to_python(value)
return {self.alias: value}

View file

@ -1,7 +1,7 @@
from pkg_resources import get_distribution, DistributionNotFound
from importlib.metadata import PackageNotFoundError, version
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
__version__ = version("django-configurations")
except PackageNotFoundError:
# package is not installed
__version__ = None

View file

@ -2,13 +2,7 @@ from . import importer
importer.install()
try:
from django.core.wsgi import get_wsgi_application
except ImportError: # pragma: no cover
from django.core.handlers.wsgi import WSGIHandler
def get_wsgi_application(): # noqa
return WSGIHandler()
from django.core.wsgi import get_wsgi_application # noqa: E402
# this is just for the crazy ones
application = get_wsgi_application()

View file

@ -1,153 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-configurations.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-configurations.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/django-configurations"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-configurations"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

View file

@ -3,6 +3,92 @@
Changelog
---------
Unreleased
^^^^^^^^^^
- Prevent warning about ``FORMS_URLFIELD_ASSUME_HTTPS`` on Django 5.0.
v2.5.1 (2023-11-30)
^^^^^^^^^^^^^^^^^^^
- Add compatibility with Python 3.12
v2.5 (2023-10-20)
^^^^^^^^^^^^^^^^^
- Update Github actions and fix pipeline warnings
- Add compatibility with Django 5.0
- **BACKWARD INCOMPATIBLE** Drop compatibility for Django 4.0
- **BACKWARD INCOMPATIBLE** Drop compatibility for Python 3.7 and PyPy < 3.10
v2.4.2 (2023-09-27)
^^^^^^^^^^^^^^^^^^^
- Replace imp (due for removal in Python 3.12) with importlib
- Test on PyPy 3.10.
v2.4.1 (2023-04-04)
^^^^^^^^^^^^^^^^^^^
- Use furo as documentation theme
- Add compatibility with Django 4.2 - fix "STATICFILES_STORAGE/STORAGES are mutually exclusive" error.
- Test Django 4.1.3+ on Python 3.11
v2.4 (2022-08-24)
^^^^^^^^^^^^^^^^^
- Add compatibility with Django 4.1
- **BACKWARD INCOMPATIBLE** Drop compatibility for Django < 3.2
- **BACKWARD INCOMPATIBLE** Drop compatibility for Python 3.6
v2.3.2 (2022-01-25)
^^^^^^^^^^^^^^^^^^^
- Add compatibility with Django 4.0
- Fix regression where settings receiving a default were ignored. #323 #327
v2.3.1 (2021-11-08)
^^^^^^^^^^^^^^^^^^^
- Test Django 3.2 on Python 3.10 as well.
- Test on PyPy 3.6, 3.7 and 3.8.
- Enforce Python version requirement during installation (>=3.6).
- Fix and refactor the documentation build process.
v2.3 (2021-10-27)
^^^^^^^^^^^^^^^^^
- **BACKWARD INCOMPATIBLE** Drop support for Python 2.7 and 3.5.
- **BACKWARD INCOMPATIBLE** Drop support for Django < 2.2.
- Add support for Django 3.1 and 3.2.
- Add suppport for Python 3.9 and 3.10.
- Deprecate ``utils.import_by_path`` in favor of
``django.utils.module_loading.import_string``.
- Add ASGI support.
- Added "python -m configurations" entry point.
- Make package ``install_requires`` include ``django>=2.2``.
- Prevent an ImproperlyConfigured warning from ``DEFAULT_HASHING_ALGORITHM``.
- Prevent warnings for settings deprecated in Django 2.2
(``DEFAULT_CONTENT_TYPE`` and ``FILE_CHARSET``).
- Preserve Django warnings when ``DEFAULT_AUTO_FIELD`` is not set.
- Miscellaneous documentation fixes.
- Miscellaneous internal improvements.
v2.2 (2019-12-03)
^^^^^^^^^^^^^^^^^

View file

@ -1,299 +1,44 @@
# -*- coding: utf-8 -*-
#
# django-configurations documentation build configuration file, created by
# sphinx-quickstart on Sat Jul 21 15:03:23 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import configurations
import sys
import os
# -- Project information -----------------------------------------------------
project = 'django-configurations'
copyright = '2012-2023, Jannis Leidel and other contributors'
author = 'Jannis Leidel and other contributors'
from pkg_resources import get_distribution
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'django-configurations'
copyright = u'2012-2014, Jannis Leidel and other contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
release = get_distribution("django-configurations").version
# The short X.Y version.
release = configurations.__version__
version = ".".join(release.split(".")[:2])
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# -- General configuration ---------------------------------------------------
add_function_parentheses = False
add_module_names = False
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'django-configurationsdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
'sphinx': ('https://www.sphinx-doc.org/en/master', None),
'django': ('https://docs.djangoproject.com/en/dev',
'https://docs.djangoproject.com/en/dev/_objects/'),
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'django-configurations.tex', u'django-configurations Documentation',
u'Jannis Leidel', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'django-configurations', u'django-configurations Documentation',
[u'Jannis Leidel'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'django-configurations', u'django-configurations Documentation',
u'Jannis Leidel', 'django-configurations', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Options for HTML output -------------------------------------------------
html_theme = 'furo'
# -- Options for Epub output ---------------------------------------------------
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
# Bibliographic Dublin Core info.
epub_title = u'django-configurations'
epub_author = u'Jannis Leidel'
epub_publisher = u'Jannis Leidel'
epub_copyright = u'2012, Jannis Leidel'
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
#epub_scheme = ''
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#epub_identifier = ''
# A unique identification for the text.
#epub_uid = ''
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files shat should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
#epub_exclude_files = []
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
'python': ('http://docs.python.org/2.7', None),
'sphinx': ('http://sphinx.pocoo.org/', None),
'django': ('http://docs.djangoproject.com/en/dev/',
'http://docs.djangoproject.com/en/dev/_objects/'),
}
add_function_parentheses = add_module_names = False
# -- Options for LaTeX output --------------------------------------------------
latex_documents = [
# (source start file, target name, title, author, documentclass)
('index', 'django-configurations.tex',
'django-configurations Documentation', author, 'manual'),
]

View file

@ -74,7 +74,7 @@ Example:
.. code-block:: console
$ tree mysite_env/
$ tree --noreport mysite_env/
mysite_env/
├── DJANGO_SETTINGS_MODULE
├── DJANGO_DEBUG
@ -82,10 +82,8 @@ Example:
├── DJANGO_CACHE_URL
└── PYTHONSTARTUP
0 directories, 3 files
$ cat mysite_env/DJANGO_CACHE_URL
redis://user@host:port/1
$
Then, to enable the ``mysite_env`` environment variables, simply use the
``envdir`` command line tool as a prefix for your program, e.g.:
@ -151,13 +149,13 @@ First install Django 1.8.x and django-configurations:
.. code-block:: console
$ pip install -r https://raw.github.com/jazzband/django-configurations/templates/1.8.x/requirements.txt
$ python -m pip install -r https://raw.github.com/jazzband/django-configurations/templates/1.8.x/requirements.txt
Or Django 1.8:
.. code-block:: console
$ django-admin.py startproject mysite -v2 --template https://github.com/jazzband/django-configurations/archive/templates/1.8.x.zip
$ python -m django startproject mysite -v2 --template https://github.com/jazzband/django-configurations/archive/templates/1.8.x.zip
Now you have a default Django 1.8.x project in the ``mysite``
directory that uses django-configurations.
@ -182,7 +180,7 @@ probably just add the following to the **beginning** of your settings module:
import configurations
configurations.setup()
That has the same effect as using the ``manage.py`` or ``wsgi.py`` utilities.
That has the same effect as using the ``manage.py``, ``wsgi.py`` or ``asgi.py`` utilities.
This will also call ``django.setup()``.
>= 3.1
@ -332,8 +330,8 @@ Channels
--------
If you want to deploy a project that uses the Django channels with
`Daphne <http://github.com/django/daphne/>` as the
`interface server <http://channels.readthedocs.io/en/latest/deploying.html#run-interface-servers>`
`Daphne <http://github.com/django/daphne/>`_ as the
`interface server <http://channels.readthedocs.io/en/latest/deploying.html#run-interface-servers>`_
you have to use a asgi.py script similar to the following:
.. code-block:: python

View file

@ -93,6 +93,6 @@ Bugs and feature requests
As always your mileage may vary, so please don't hesitate to send feature
requests and bug reports:
https://github.com/jazzband/django-configurations/issues
- https://github.com/jazzband/django-configurations/issues
Thanks!

View file

@ -1,190 +0,0 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
set I18NSPHINXOPTS=%SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. texinfo to make Texinfo files
echo. gettext to make PO message catalogs
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-configurations.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-configurations.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "texinfo" (
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
goto end
)
if "%1" == "gettext" (
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

View file

@ -3,7 +3,7 @@ Usage patterns
There are various configuration patterns that can be implemented with
django-configurations. The most common pattern is to have a base class
and various subclasses based on the enviroment they are supposed to be
and various subclasses based on the environment they are supposed to be
used in, e.g. in production, staging and development.
Server specific settings
@ -21,7 +21,6 @@ file:
class Dev(Base):
DEBUG = True
TEMPLATE_DEBUG = DEBUG
class Prod(Base):
TIME_ZONE = 'America/New_York'
@ -32,9 +31,9 @@ it should be ``Prod``. In Bash that would be:
.. code-block:: console
export DJANGO_SETTINGS_MODULE=mysite.settings
export DJANGO_CONFIGURATION=Prod
python manage.py runserver
$ export DJANGO_SETTINGS_MODULE=mysite.settings
$ export DJANGO_CONFIGURATION=Prod
$ python -m manage runserver
Alternatively you can use the ``--configuration`` option when using Django
management commands along the lines of Django's default ``--settings``
@ -42,7 +41,7 @@ command line option, e.g.
.. code-block:: console
python manage.py runserver --settings=mysite.settings --configuration=Prod
$ python -m manage runserver --settings=mysite.settings --configuration=Prod
Property settings
-----------------
@ -91,7 +90,7 @@ a few mixin you re-use multiple times:
.. code-block:: python
class FullPageCaching(object):
class FullPageCaching:
USE_ETAGS = True
Then import that mixin class in your site settings module and use it with

3
docs/requirements.txt Normal file
View file

@ -0,0 +1,3 @@
Sphinx>4
furo
docutils

View file

@ -46,7 +46,7 @@ value:
class Dev(Configuration):
DEBUG = values.BooleanValue(True)
TEMPLATE_DEBUG = values.BooleanValue(DEBUG)
DEBUG_PROPAGATE_EXCEPTIONS = values.BooleanValue(DEBUG)
See the list of :ref:`built-in value classes<built-ins>` for more information.
@ -86,9 +86,11 @@ prefixed with ``DJANGO_``. E.g.:
django-configurations will try to read the ``DJANGO_ROOT_URLCONF`` environment
variable when deciding which value the ``ROOT_URLCONF`` setting should have.
When you run the web server simply specify that environment variable
(e.g. in your init script)::
(e.g. in your init script):
DJANGO_ROOT_URLCONF=mysite.debugging_urls gunicorn mysite.wsgi:application
.. code-block:: console
$ DJANGO_ROOT_URLCONF=mysite.debugging_urls gunicorn mysite.wsgi:application
If the environment variable can't be found it'll use the default
``'mysite.urls'``.
@ -125,7 +127,9 @@ Allow final value to be used outside the configuration context
You may use the ``environ_name`` parameter to allow a :class:`~Value` to be
directly converted to its final value for use outside of the configuration
context::
context:
.. code-block:: pycon
>>> type(values.Value([]))
<class 'configurations.values.Value'>
@ -160,12 +164,12 @@ the prefix.
:param default: the default value of the setting
:param environ: toggle for environment use
:param environ_name: name of environment variable to look for
:param environ_prefix: prefix to use when looking for environment variable
:param environ_required: wheter or not the value is required to be set as an environment variable
:param environ_name: capitalized name of environment variable to look for
:param environ_prefix: capitalized prefix to use when looking for environment variable
:param environ_required: whether or not the value is required to be set as an environment variable
:type environ: bool
:type environ_name: capitalized string or None
:type environ_prefix: capitalized string
:type environ_name: str or None
:type environ_prefix: str
:type environ_required: bool
The ``default`` parameter is effectively the value the setting has
@ -256,6 +260,10 @@ Type values
MYSITE_CONVERSION_RATE = values.DecimalValue(decimal.Decimal('4.56214'))
.. class:: SequenceValue
Common base class for sequence values.
.. class:: ListValue(default, [separator=',', converter=None])
A :class:`~SequenceValue` subclass that handles list values.
@ -279,17 +287,21 @@ Type values
MONTY_PYTHONS = ListValue(['John Cleese', 'Eric Idle'],
converter=check_monty_python)
You can override this list with an environment variable like this::
You can override this list with an environment variable like this:
DJANGO_MONTY_PYTHONS="Terry Jones,Graham Chapman" gunicorn mysite.wsgi:application
.. code-block:: console
$ DJANGO_MONTY_PYTHONS="Terry Jones,Graham Chapman" gunicorn mysite.wsgi:application
Use a custom separator::
EMERGENCY_EMAILS = ListValue(['admin@mysite.net'], separator=';')
And override it::
And override it:
DJANGO_EMERGENCY_EMAILS="admin@mysite.net;manager@mysite.org;support@mysite.com" gunicorn mysite.wsgi:application
.. code-block:: console
$ DJANGO_EMERGENCY_EMAILS="admin@mysite.net;manager@mysite.org;support@mysite.com" gunicorn mysite.wsgi:application
.. class:: TupleValue
@ -301,6 +313,10 @@ Type values
See the :class:`~ListValue` examples above.
.. class:: SingleNestedSequenceValue
Common base class for nested sequence values.
.. class:: SingleNestedTupleValue(default, [seq_separator=';', separator=',', converter=None])
A :class:`~SingleNestedSequenceValue` subclass that handles single nested tuple values,
@ -354,6 +370,10 @@ Type values
'it': ['Mike', 'Joe'],
})
Override using environment variables like this::
DJANGO_DEPARTMENTS={'it':['Mike','Joe'],'hr':['Emma','Olivia']}
Validator values
^^^^^^^^^^^^^^^^
@ -547,7 +567,7 @@ Other values
::
MIDDLEWARE_CLASSES = values.BackendsValue([
MIDDLEWARE = values.BackendsValue([
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@ -584,7 +604,7 @@ Value mixins
requires a ``caster`` class attribute of one of the following types:
- dotted import path, e.g. ``'mysite.utils.custom_caster'``
- a callable, e.g. :func:`int`
- a callable, e.g. :class:`int`
Example::
@ -605,7 +625,7 @@ Value mixins
validation attempt.
- dotted import path, e.g. ``'mysite.validators.custom_validator'``
- a callable, e.g. :func:`bool`
- a callable, e.g. :class:`bool`
Example::

View file

@ -1,6 +1,3 @@
[wheel]
universal = 1
[coverage:run]
source = .
branch = 1

View file

@ -1,4 +1,3 @@
from __future__ import print_function
import os
import codecs
from setuptools import setup
@ -15,6 +14,9 @@ setup(
use_scm_version={"version_scheme": "post-release", "local_scheme": "dirty-tag"},
setup_requires=["setuptools_scm"],
url='https://django-configurations.readthedocs.io/',
project_urls={
'Source': 'https://github.com/jazzband/django-configurations',
},
license='BSD',
description="A helper for organizing Django settings.",
long_description=read('README.rst'),
@ -27,34 +29,41 @@ setup(
'django-cadmin = configurations.management:execute_from_command_line',
],
},
install_requires=['six'],
install_requires=[
'django>=3.2',
],
python_requires='>=3.9, <4.0',
extras_require={
'cache': ['django-cache-url'],
'database': ['dj-database-url'],
'email': ['dj-email-url'],
'search': ['dj-search-url'],
'testing': [
'django-discover-runner',
'mock',
'django-cache-url>=1.0.0',
'dj-database-url',
'dj-email-url',
'dj-search-url',
'Sphinx>=1.4',
],
},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Framework :: Django',
'Framework :: Django :: 3.2',
'Framework :: Django :: 4.1',
'Framework :: Django :: 4.2',
'Framework :: Django :: 5.0',
'Framework :: Django :: 5.1',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Utilities',
],

View file

@ -5,7 +5,6 @@ class Base(Configuration):
# Django settings for test_project project.
DEBUG = values.BooleanValue(True, environ=True)
TEMPLATE_DEBUG = DEBUG
ADMINS = (
# ('Your Name', 'your_email@example.com'),
@ -95,7 +94,7 @@ class Base(Configuration):
'django.template.loaders.app_directories.Loader',
)
MIDDLEWARE_CLASSES = (
MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',

View file

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
# setup Django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.main")
os.environ.setdefault('DJANGO_CONFIGURATION', 'Test')
extensions = [
'configurations.sphinx',
]
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'django-configurations'
copyright = '2012-2014, Jannis Leidel and other contributors'
version = release = 'test'
exclude_patterns = ['_build']
html_theme = 'default'

View file

@ -1,2 +0,0 @@
Test Documentation
^^^^^^^^^^^^^^^^^^

View file

@ -1,16 +0,0 @@
from optparse import make_option
from django.core.management.base import BaseCommand
class Command(BaseCommand):
# Used by a specific test to see how unupgraded
# management commands play with configurations.
# See the test code for more details.
option_list = BaseCommand.option_list + (
make_option('--arg1', action='store_true'),
)
def handle(self, *args, **options):
pass

View file

@ -6,3 +6,6 @@ class DotEnvConfiguration(Configuration):
DOTENV = 'test_project/.env'
DOTENV_VALUE = values.Value()
def DOTENV_VALUE_METHOD(self):
return values.Value(environ_name="DOTENV_VALUE")

8
tests/settings/error.py Normal file
View file

@ -0,0 +1,8 @@
from configurations import Configuration
class ErrorConfiguration(Configuration):
@classmethod
def pre_setup(cls):
raise ValueError("Error in pre_setup")

View file

@ -1,9 +1,7 @@
import os
import uuid
import django
from configurations import Configuration, pristinemethod
from configurations.values import BooleanValue
class Test(Configuration):
@ -11,8 +9,6 @@ class Test(Configuration):
os.path.join(os.path.dirname(
os.path.abspath(__file__)), os.pardir))
ENV_LOADED = BooleanValue(False)
DEBUG = True
SITE_ID = 1
@ -36,12 +32,9 @@ class Test(Configuration):
ROOT_URLCONF = 'tests.urls'
if django.VERSION[:2] < (1, 6):
TEST_RUNNER = 'discover_runner.DiscoverRunner'
@property
def ALLOWED_HOSTS(self):
allowed_hosts = super(Test, self).ALLOWED_HOSTS[:]
allowed_hosts = super().ALLOWED_HOSTS[:]
allowed_hosts.append('base')
return allowed_hosts
@ -71,3 +64,7 @@ class Test(Configuration):
@classmethod
def post_setup(cls):
cls.POST_SETUP_TEST_SETTING = 7
class TestWithDefaultSetExplicitely(Test):
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View file

@ -1,19 +1,19 @@
from configurations import Configuration
class Mixin1(object):
class Mixin1:
@property
def ALLOWED_HOSTS(self):
allowed_hosts = super(Mixin1, self).ALLOWED_HOSTS[:]
allowed_hosts = super().ALLOWED_HOSTS[:]
allowed_hosts.append('test1')
return allowed_hosts
class Mixin2(object):
class Mixin2:
@property
def ALLOWED_HOSTS(self):
allowed_hosts = super(Mixin2, self).ALLOWED_HOSTS[:]
allowed_hosts = super().ALLOWED_HOSTS[:]
allowed_hosts.append('test2')
return allowed_hosts
@ -21,6 +21,6 @@ class Mixin2(object):
class Inheritance(Mixin2, Mixin1, Configuration):
def ALLOWED_HOSTS(self):
allowed_hosts = super(Inheritance, self).ALLOWED_HOSTS[:]
allowed_hosts = super().ALLOWED_HOSTS[:]
allowed_hosts.append('test3')
return allowed_hosts

View file

@ -4,6 +4,6 @@ from .single_inheritance import Inheritance as BaseInheritance
class Inheritance(BaseInheritance):
def ALLOWED_HOSTS(self):
allowed_hosts = super(Inheritance, self).ALLOWED_HOSTS[:]
allowed_hosts = super().ALLOWED_HOSTS[:]
allowed_hosts.append('test-test')
return allowed_hosts

View file

@ -5,6 +5,6 @@ class Inheritance(Base):
@property
def ALLOWED_HOSTS(self):
allowed_hosts = super(Inheritance, self).ALLOWED_HOSTS[:]
allowed_hosts = super().ALLOWED_HOSTS[:]
allowed_hosts.append('test')
return allowed_hosts

View file

@ -1,9 +1,6 @@
"""Used by tests to ensure logging is kept when calling setup() twice."""
try:
from unittest import mock
except ImportError:
from mock import mock
from unittest import mock
import configurations

View file

@ -1,6 +1,6 @@
import os
from django.test import TestCase
from mock import patch
from unittest.mock import patch
class DotEnvLoadingTests(TestCase):
@ -11,4 +11,5 @@ class DotEnvLoadingTests(TestCase):
def test_env_loaded(self):
from tests.settings import dot_env
self.assertEqual(dot_env.DOTENV_VALUE, 'is set')
self.assertEqual(dot_env.DOTENV_VALUE_METHOD, 'is set')
self.assertEqual(dot_env.DOTENV_LOADED, dot_env.DOTENV)

22
tests/test_error.py Normal file
View file

@ -0,0 +1,22 @@
import os
from django.test import TestCase
from unittest.mock import patch
class ErrorTests(TestCase):
@patch.dict(os.environ, clear=True,
DJANGO_CONFIGURATION='ErrorConfiguration',
DJANGO_SETTINGS_MODULE='tests.settings.error')
def test_env_loaded(self):
with self.assertRaises(ValueError) as cm:
from tests.settings import error # noqa: F401
self.assertIsInstance(cm.exception, ValueError)
self.assertEqual(
cm.exception.args,
(
"Couldn't setup configuration "
"'tests.settings.error.ErrorConfiguration': Error in pre_setup ",
)
)

View file

@ -2,7 +2,7 @@ import os
from django.test import TestCase
from mock import patch
from unittest.mock import patch
class InheritanceTests(TestCase):

View file

@ -5,9 +5,9 @@ import sys
from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured
from mock import patch
from unittest.mock import patch
from configurations.importer import ConfigurationImporter
from configurations.importer import ConfigurationFinder
ROOT_DIR = os.path.dirname(os.path.dirname(__file__))
TEST_PROJECT_DIR = os.path.join(ROOT_DIR, 'test_project')
@ -42,12 +42,14 @@ class MainTests(TestCase):
@patch.dict(os.environ, clear=True, DJANGO_CONFIGURATION='Test')
def test_empty_module_var(self):
self.assertRaises(ImproperlyConfigured, ConfigurationImporter)
with self.assertRaises(ImproperlyConfigured):
ConfigurationFinder()
@patch.dict(os.environ, clear=True,
DJANGO_SETTINGS_MODULE='tests.settings.main')
def test_empty_class_var(self):
self.assertRaises(ImproperlyConfigured, ConfigurationImporter)
with self.assertRaises(ImproperlyConfigured):
ConfigurationFinder()
def test_global_settings(self):
from configurations.base import Configuration
@ -55,6 +57,12 @@ class MainTests(TestCase):
self.assertEqual(repr(Configuration),
"<Configuration 'configurations.base.Configuration'>")
def test_deprecated_settings_but_set_by_user(self):
from tests.settings.main import TestWithDefaultSetExplicitely
TestWithDefaultSetExplicitely.setup()
self.assertEqual(TestWithDefaultSetExplicitely.DEFAULT_AUTO_FIELD,
"django.db.models.BigAutoField")
def test_repr(self):
from tests.settings.main import Test
self.assertEqual(repr(Test),
@ -64,21 +72,21 @@ class MainTests(TestCase):
DJANGO_SETTINGS_MODULE='tests.settings.main',
DJANGO_CONFIGURATION='Test')
def test_initialization(self):
importer = ConfigurationImporter()
self.assertEqual(importer.module, 'tests.settings.main')
self.assertEqual(importer.name, 'Test')
finder = ConfigurationFinder()
self.assertEqual(finder.module, 'tests.settings.main')
self.assertEqual(finder.name, 'Test')
self.assertEqual(
repr(importer),
"<ConfigurationImporter for 'tests.settings.main.Test'>")
repr(finder),
"<ConfigurationFinder for 'tests.settings.main.Test'>")
@patch.dict(os.environ, clear=True,
DJANGO_SETTINGS_MODULE='tests.settings.inheritance',
DJANGO_CONFIGURATION='Inheritance')
def test_initialization_inheritance(self):
importer = ConfigurationImporter()
self.assertEqual(importer.module,
finder = ConfigurationFinder()
self.assertEqual(finder.module,
'tests.settings.inheritance')
self.assertEqual(importer.name, 'Inheritance')
self.assertEqual(finder.name, 'Inheritance')
@patch.dict(os.environ, clear=True,
DJANGO_SETTINGS_MODULE='tests.settings.main',
@ -87,12 +95,12 @@ class MainTests(TestCase):
'--settings=tests.settings.main',
'--configuration=Test'])
def test_configuration_option(self):
importer = ConfigurationImporter(check_options=False)
self.assertEqual(importer.module, 'tests.settings.main')
self.assertEqual(importer.name, 'NonExisting')
importer = ConfigurationImporter(check_options=True)
self.assertEqual(importer.module, 'tests.settings.main')
self.assertEqual(importer.name, 'Test')
finder = ConfigurationFinder(check_options=False)
self.assertEqual(finder.module, 'tests.settings.main')
self.assertEqual(finder.name, 'NonExisting')
finder = ConfigurationFinder(check_options=True)
self.assertEqual(finder.module, 'tests.settings.main')
self.assertEqual(finder.name, 'Test')
def test_configuration_argument_in_cli(self):
"""
@ -106,6 +114,22 @@ class MainTests(TestCase):
stdout=subprocess.PIPE)
self.assertIn('--configuration', proc.communicate()[0].decode('utf-8'))
def test_configuration_argument_in_runypy_cli(self):
"""
Verify that's configuration option has been added to managements
commands when using the -m entry point
"""
proc = subprocess.Popen(
[sys.executable, '-m', 'configurations', 'test', '--help'],
stdout=subprocess.PIPE,
)
self.assertIn('--configuration', proc.communicate()[0].decode('utf-8'))
proc = subprocess.Popen(
[sys.executable, '-m', 'configurations', 'runserver', '--help'],
stdout=subprocess.PIPE,
)
self.assertIn('--configuration', proc.communicate()[0].decode('utf-8'))
def test_django_setup_only_called_once(self):
proc = subprocess.Popen(
[sys.executable, os.path.join(os.path.dirname(__file__),

View file

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
import subprocess
import os
from django.test import TestCase
from django.conf import settings
class SphinxTests(TestCase):
docs_dir = os.path.join(settings.BASE_DIR, 'docs')
def test_multiprocessing(self):
output = subprocess.check_output([
'sphinx-build',
'-b',
'html',
'-j 2',
'.',
'_build/html',
], cwd=self.docs_dir, stderr=subprocess.STDOUT)
self.assertIn(b'build succeeded.', output)

View file

@ -2,10 +2,11 @@ import decimal
import os
from contextlib import contextmanager
from django import VERSION as DJANGO_VERSION
from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured
from mock import patch
from unittest.mock import patch
from configurations.values import (Value, BooleanValue, IntegerValue,
FloatValue, DecimalValue, ListValue,
@ -33,7 +34,7 @@ class ValueTests(TestCase):
def test_value_with_default(self):
value = Value('default', environ=False)
self.assertEqual(type(value), type('default'))
self.assertEqual(type(value), str)
self.assertEqual(value, 'default')
self.assertEqual(str(value), 'default')
@ -43,17 +44,17 @@ class ValueTests(TestCase):
with env(DJANGO_TEST='override'):
self.assertEqual(value.setup('TEST'), 'default')
value = Value(environ_name='TEST')
self.assertEqual(type(value), type('override'))
self.assertEqual(type(value), str)
self.assertEqual(value, 'override')
self.assertEqual(str(value), 'override')
self.assertEqual('{0}'.format(value), 'override')
self.assertEqual(f'{value}', 'override')
self.assertEqual('%s' % value, 'override')
value = Value(environ_name='TEST', late_binding=True)
self.assertEqual(type(value), Value)
self.assertEqual(value.value, 'override')
self.assertEqual(str(value), 'override')
self.assertEqual('{0}'.format(value), 'override')
self.assertEqual(f'{value}', 'override')
self.assertEqual('%s' % value, 'override')
self.assertEqual(repr(value), repr('override'))
@ -270,9 +271,9 @@ class ValueTests(TestCase):
def test_set_values_default(self):
value = SetValue()
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), set(['2', '2']))
self.assertEqual(value.setup('TEST'), {'2', '2'})
with env(DJANGO_TEST='2, 2 ,'):
self.assertEqual(value.setup('TEST'), set(['2', '2']))
self.assertEqual(value.setup('TEST'), {'2', '2'})
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), set())
@ -372,16 +373,23 @@ class ValueTests(TestCase):
value = DatabaseURLValue()
self.assertEqual(value.default, {})
with env(DATABASE_URL='sqlite://'):
self.assertEqual(value.setup('DATABASE_URL'), {
'default': {
settings_value = value.setup('DATABASE_URL')
# Compare the embedded dicts in the "default" entry so that the difference can be seen if
# it fails ... DatabaseURLValue(|) uses an external app that can add additional entries
self.assertDictEqual(
{
'CONN_HEALTH_CHECKS': False,
'CONN_MAX_AGE': 0,
'DISABLE_SERVER_SIDE_CURSORS': False,
'ENGINE': 'django.db.backends.sqlite3',
'HOST': '',
'NAME': ':memory:',
'PASSWORD': '',
'PORT': '',
'USER': '',
}})
},
settings_value['default']
)
def test_database_url_additional_args(self):
@ -411,6 +419,7 @@ class ValueTests(TestCase):
'EMAIL_HOST_PASSWORD': 'password',
'EMAIL_HOST_USER': 'user@domain.com',
'EMAIL_PORT': 587,
'EMAIL_TIMEOUT': None,
'EMAIL_USE_SSL': False,
'EMAIL_USE_TLS': True})
with env(EMAIL_URL='console://'):
@ -421,6 +430,7 @@ class ValueTests(TestCase):
'EMAIL_HOST_PASSWORD': None,
'EMAIL_HOST_USER': None,
'EMAIL_PORT': None,
'EMAIL_TIMEOUT': None,
'EMAIL_USE_SSL': False,
'EMAIL_USE_TLS': False})
with env(EMAIL_URL='smtps://user@domain.com:password@smtp.example.com:wrong'): # noqa: E501
@ -429,7 +439,7 @@ class ValueTests(TestCase):
def test_cache_url_value(self):
cache_setting = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'BACKEND': 'django_redis.cache.RedisCache' if DJANGO_VERSION < (4,) else 'django.core.cache.backends.redis.RedisCache', # noqa: E501
'LOCATION': 'redis://host:6379/1',
}
}
@ -485,12 +495,12 @@ class ValueTests(TestCase):
self.assertEqual(value.value, set())
value = SetValue([1, 2])
self.assertEqual(value.default, set([1, 2]))
self.assertEqual(value.value, set([1, 2]))
self.assertEqual(value.default, {1, 2})
self.assertEqual(value.value, {1, 2})
def test_setup_value(self):
class Target(object):
class Target:
pass
value = EmailURLValue()
@ -503,6 +513,7 @@ class ValueTests(TestCase):
'EMAIL_HOST_PASSWORD': 'password',
'EMAIL_HOST_USER': 'user@domain.com',
'EMAIL_PORT': 587,
'EMAIL_TIMEOUT': None,
'EMAIL_USE_SSL': False,
'EMAIL_USE_TLS': True
})

62
tox.ini
View file

@ -2,23 +2,23 @@
skipsdist = true
usedevelop = true
minversion = 1.8
whitelist_externals = sphinx-build
envlist =
py36-checkqa,
py{27,35,36,py}-dj111
py{35,36,37,py3}-dj20
py{35,36,37,py3}-dj21
py{35,36,37,38,py3}-dj22
py{36,37,38,py3}-dj{30,master}
py311-checkqa
docs
py{39}-dj{32,41,42}
py{310,py310}-dj{32,41,42,50,main}
py{311}-dj{41,42,50,51,main}
py{312}-dj{50,51,main}
py{313}-dj{50,51,main}
[travis]
[gh-actions]
python =
2.7: py27
3.5: py35
3.6: py36,flake8,readme
3.7: py37
3.8: py38
pypy3: pypy3
3.9: py39
3.10: py310
3.11: py311,flake8,readme
3.12: py312
3.13: py313
pypy-3.10: pypy310
[testenv]
usedevelop = true
@ -27,24 +27,27 @@ setenv =
DJANGO_CONFIGURATION = Test
COVERAGE_PROCESS_START = {toxinidir}/setup.cfg
deps =
dj111: django>=1.11,<2.0
dj20: django>=2.0a1,<2.1
dj21: django>=2.1a1,<2.2
dj22: django>=2.2a1,<3.0
dj30: django>=3.0a1,<3.1
djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django
py27,pypy: mock
dj32: django~=3.2.9
dj41: django~=4.1.3
dj42: django~=4.2.0
dj50: django~=5.0.0
dj51: django~=5.1.0
djmain: https://github.com/django/django/archive/main.tar.gz
py312: setuptools
py312: wheel
py313: setuptools
py313: wheel
coverage
coverage_enable_subprocess
extras = testing
commands =
python --version
{envbindir}/coverage run {envbindir}/django-cadmin test -v2 {posargs:tests}
coverage combine . tests/docs
coverage combine . tests docs
coverage report -m --skip-covered
coverage xml
[testenv:py36-checkqa]
[testenv:py311-checkqa]
commands =
flake8 {toxinidir}
check-manifest -v
@ -54,3 +57,16 @@ deps =
flake8
twine
check-manifest
[testenv:docs]
setenv =
deps =
-r docs/requirements.txt
commands =
sphinx-build \
-b html \
-a \
-W \
-n \
docs \
docs/_build/html