mirror of
https://github.com/jazzband/django-configurations.git
synced 2026-04-19 22:50:58 +00:00
Compare commits
No commits in common. "master" and "2.2" have entirely different histories.
51 changed files with 1088 additions and 804 deletions
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
# 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
53
.github/workflows/release.yml
vendored
|
|
@ -1,53 +0,0 @@
|
||||||
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
57
.github/workflows/test.yml
vendored
|
|
@ -1,57 +0,0 @@
|
||||||
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
1
.gitignore
vendored
|
|
@ -9,4 +9,5 @@ build/
|
||||||
htmlcov/
|
htmlcov/
|
||||||
*.pyc
|
*.pyc
|
||||||
dist/
|
dist/
|
||||||
|
tests/docs/_build/
|
||||||
.eggs/
|
.eggs/
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
repos: []
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
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
|
|
||||||
37
.travis.yml
Normal file
37
.travis.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
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
13
AUTHORS
|
|
@ -1,22 +1,9 @@
|
||||||
Arseny Sokolov
|
|
||||||
Asif Saif Uddin
|
|
||||||
Baptiste Mispelon
|
|
||||||
Brian Helba
|
|
||||||
Bruno Clermont
|
Bruno Clermont
|
||||||
Christoph Krybus
|
|
||||||
Finn-Thorben Sell
|
|
||||||
Gilles Fabio
|
Gilles Fabio
|
||||||
Jannis Leidel
|
Jannis Leidel
|
||||||
John Franey
|
John Franey
|
||||||
Marc Abramowitz
|
Marc Abramowitz
|
||||||
Michael Käufl
|
|
||||||
Michael van Tellingen
|
Michael van Tellingen
|
||||||
Mike Fogel
|
Mike Fogel
|
||||||
Miro Hrončok
|
|
||||||
Nicholas Dujay
|
|
||||||
Paolo Melchiorre
|
Paolo Melchiorre
|
||||||
Peter Bittner
|
Peter Bittner
|
||||||
Richard de Wit
|
|
||||||
Thomas Grainger
|
|
||||||
Tim Gates
|
|
||||||
Victor Seva
|
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# 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/
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2012-2023, Jannis Leidel and other contributors.
|
Copyright (c) 2012-2014, Jannis Leidel and other contributors.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
|
|
||||||
12
MANIFEST.in
12
MANIFEST.in
|
|
@ -1,11 +1,9 @@
|
||||||
include .pre-commit-config.yaml
|
|
||||||
include .readthedocs.yaml
|
|
||||||
include AUTHORS
|
|
||||||
include CODE_OF_CONDUCT.md
|
|
||||||
include CONTRIBUTING.md
|
|
||||||
include LICENSE
|
|
||||||
include README.rst
|
include README.rst
|
||||||
|
include CONTRIBUTING.md
|
||||||
|
include AUTHORS
|
||||||
|
include .travis.yml
|
||||||
include tox.ini
|
include tox.ini
|
||||||
|
recursive-include tests *
|
||||||
recursive-include docs *
|
recursive-include docs *
|
||||||
recursive-include test_project *
|
recursive-include test_project *
|
||||||
recursive-include tests *
|
include LICENSE
|
||||||
|
|
|
||||||
59
README.rst
59
README.rst
|
|
@ -1,7 +1,7 @@
|
||||||
django-configurations |latest-version|
|
django-configurations |latest-version|
|
||||||
======================================
|
======================================
|
||||||
|
|
||||||
|jazzband| |build-status| |codecov| |docs| |python-support| |django-support|
|
|build-status| |codecov| |docs| |python-support| |jazzband|
|
||||||
|
|
||||||
django-configurations eases Django project configuration by relying
|
django-configurations eases Django project configuration by relying
|
||||||
on the composability of Python classes. It extends the notion of
|
on the composability of Python classes. It extends the notion of
|
||||||
|
|
@ -11,33 +11,23 @@ object oriented programming patterns.
|
||||||
Check out the `documentation`_ for more complete examples.
|
Check out the `documentation`_ for more complete examples.
|
||||||
|
|
||||||
.. |latest-version| image:: https://img.shields.io/pypi/v/django-configurations.svg
|
.. |latest-version| image:: https://img.shields.io/pypi/v/django-configurations.svg
|
||||||
:target: https://pypi.python.org/pypi/django-configurations
|
|
||||||
:alt: Latest version on PyPI
|
:alt: Latest version on PyPI
|
||||||
|
:target: https://pypi.python.org/pypi/django-configurations
|
||||||
.. |jazzband| image:: https://jazzband.co/static/img/badge.svg
|
.. |build-status| image:: https://img.shields.io/travis/jazzband/django-configurations/master.svg
|
||||||
:target: https://jazzband.co/
|
:alt: Build status
|
||||||
:alt: Jazzband
|
:target: https://travis-ci.org/jazzband/django-configurations
|
||||||
|
|
||||||
.. |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
|
.. |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
|
: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
|
.. |docs| image:: https://img.shields.io/readthedocs/django-configurations/latest.svg
|
||||||
:target: https://readthedocs.org/projects/django-configurations/
|
|
||||||
:alt: Documentation status
|
:alt: Documentation status
|
||||||
|
:target: https://readthedocs.org/projects/django-configurations/
|
||||||
.. |python-support| image:: https://img.shields.io/pypi/pyversions/django-configurations.svg
|
.. |python-support| image:: https://img.shields.io/pypi/pyversions/django-configurations.svg
|
||||||
:target: https://pypi.python.org/pypi/django-configurations
|
:target: https://pypi.python.org/pypi/django-configurations
|
||||||
:alt: Supported Python versions
|
:alt: Python versions
|
||||||
|
.. |jazzband| image:: https://jazzband.co/static/img/badge.svg
|
||||||
.. |django-support| image:: https://img.shields.io/pypi/djversions/django-configurations
|
:alt: Jazzband
|
||||||
:target: https://pypi.org/project/django-configurations
|
:target: https://jazzband.co/
|
||||||
:alt: Supported Django versions
|
|
||||||
|
|
||||||
.. _documentation: https://django-configurations.readthedocs.io/en/latest/
|
.. _documentation: https://django-configurations.readthedocs.io/en/latest/
|
||||||
|
|
||||||
Quickstart
|
Quickstart
|
||||||
|
|
@ -47,13 +37,13 @@ Install django-configurations:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python -m pip install django-configurations
|
pip install django-configurations
|
||||||
|
|
||||||
or, alternatively, if you want to use URL-based values:
|
or, alternatively, if you want to use URL-based values:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python -m pip install django-configurations[cache,database,email,search]
|
pip install django-configurations[cache,database,email,search]
|
||||||
|
|
||||||
Then subclass the included ``configurations.Configuration`` class in your
|
Then subclass the included ``configurations.Configuration`` class in your
|
||||||
project's **settings.py** or any other module you're using to store the
|
project's **settings.py** or any other module you're using to store the
|
||||||
|
|
@ -73,14 +63,14 @@ you just created, e.g. in bash:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ export DJANGO_CONFIGURATION=Dev
|
export DJANGO_CONFIGURATION=Dev
|
||||||
|
|
||||||
and the ``DJANGO_SETTINGS_MODULE`` environment variable to the module
|
and the ``DJANGO_SETTINGS_MODULE`` environment variable to the module
|
||||||
import path as usual, e.g. in bash:
|
import path as usual, e.g. in bash:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ export DJANGO_SETTINGS_MODULE=mysite.settings
|
export DJANGO_SETTINGS_MODULE=mysite.settings
|
||||||
|
|
||||||
*Alternatively* supply the ``--configuration`` option when using Django
|
*Alternatively* supply the ``--configuration`` option when using Django
|
||||||
management commands along the lines of Django's default ``--settings``
|
management commands along the lines of Django's default ``--settings``
|
||||||
|
|
@ -88,10 +78,10 @@ command line option, e.g.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python -m manage runserver --settings=mysite.settings --configuration=Dev
|
python manage.py runserver --settings=mysite.settings --configuration=Dev
|
||||||
|
|
||||||
To enable Django to use your configuration you now have to modify your
|
To enable Django to use your configuration you now have to modify your
|
||||||
**manage.py**, **wsgi.py** or **asgi.py** script to use django-configurations's versions
|
**manage.py** or **wsgi.py** script to use django-configurations's versions
|
||||||
of the appropriate starter functions, e.g. a typical **manage.py** using
|
of the appropriate starter functions, e.g. a typical **manage.py** using
|
||||||
django-configurations would look like this:
|
django-configurations would look like this:
|
||||||
|
|
||||||
|
|
@ -130,18 +120,5 @@ The same applies to your **wsgi.py** file, e.g.:
|
||||||
Here we don't use the default ``django.core.wsgi.get_wsgi_application``
|
Here we don't use the default ``django.core.wsgi.get_wsgi_application``
|
||||||
function but instead ``configurations.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
|
That's it! You can now use your project with ``manage.py`` and your favorite
|
||||||
WSGI/ASGI enabled server.
|
WSGI enabled server.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
"""
|
|
||||||
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()
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import six
|
||||||
|
|
||||||
from django.conf import global_settings
|
from django.conf import global_settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
@ -31,50 +32,19 @@ class ConfigurationBase(type):
|
||||||
if parents:
|
if parents:
|
||||||
for base in bases[::-1]:
|
for base in bases[::-1]:
|
||||||
settings_vars.update(uppercase_attributes(base))
|
settings_vars.update(uppercase_attributes(base))
|
||||||
|
attrs = dict(settings_vars, **attrs)
|
||||||
deprecated_settings = {
|
# Fix ImproperlyConfigured issue introduced in Django
|
||||||
# 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
|
# https://github.com/django/django/commit/226ebb17290b604ef29e82fb5c1fbac3594ac163#diff-ec2bed07bb264cb95a80f08d71a47c06R163-R170
|
||||||
if "PASSWORD_RESET_TIMEOUT" in settings_vars:
|
if "PASSWORD_RESET_TIMEOUT_DAYS" in attrs and "PASSWORD_RESET_TIMEOUT" in attrs:
|
||||||
deprecated_settings.add("PASSWORD_RESET_TIMEOUT_DAYS")
|
attrs.pop("PASSWORD_RESET_TIMEOUT_DAYS")
|
||||||
# DEFAULT_FILE_STORAGE and STATICFILES_STORAGE are deprecated
|
return super(ConfigurationBase, cls).__new__(cls, name, bases, attrs)
|
||||||
# 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):
|
def __repr__(self):
|
||||||
return "<Configuration '{}.{}'>".format(self.__module__,
|
return "<Configuration '{0}.{1}'>".format(self.__module__,
|
||||||
self.__name__)
|
self.__name__)
|
||||||
|
|
||||||
|
|
||||||
class Configuration(metaclass=ConfigurationBase):
|
class Configuration(six.with_metaclass(ConfigurationBase)):
|
||||||
"""
|
"""
|
||||||
The base configuration class to inherit from.
|
The base configuration class to inherit from.
|
||||||
|
|
||||||
|
|
@ -107,10 +77,10 @@ class Configuration(metaclass=ConfigurationBase):
|
||||||
environment variables from a .env file located in the project root
|
environment variables from a .env file located in the project root
|
||||||
or provided directory.
|
or provided directory.
|
||||||
|
|
||||||
https://wellfire.co/learn/easier-12-factor-django/
|
http://www.wellfireinteractive.com/blog/easier-12-factor-django/
|
||||||
https://gist.github.com/bennylope/2999704
|
https://gist.github.com/bennylope/2999704
|
||||||
"""
|
"""
|
||||||
# check if the class has DOTENV set whether with a path or None
|
# check if the class has DOTENV set wether with a path or None
|
||||||
dotenv = getattr(cls, 'DOTENV', None)
|
dotenv = getattr(cls, 'DOTENV', None)
|
||||||
|
|
||||||
# if DOTENV is falsy we want to disable it
|
# if DOTENV is falsy we want to disable it
|
||||||
|
|
@ -119,12 +89,12 @@ class Configuration(metaclass=ConfigurationBase):
|
||||||
|
|
||||||
# now check if we can access the file since we know we really want to
|
# now check if we can access the file since we know we really want to
|
||||||
try:
|
try:
|
||||||
with open(dotenv) as f:
|
with open(dotenv, 'r') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
except OSError as e:
|
except IOError as e:
|
||||||
raise ImproperlyConfigured("Couldn't read .env file "
|
raise ImproperlyConfigured("Couldn't read .env file "
|
||||||
"with the path {}. Error: "
|
"with the path {}. Error: "
|
||||||
"{}".format(dotenv, e)) from e
|
"{}".format(dotenv, e))
|
||||||
else:
|
else:
|
||||||
for line in content.splitlines():
|
for line in content.splitlines():
|
||||||
m1 = re.match(r'\A([A-Za-z_0-9]+)=(.*)\Z', line)
|
m1 = re.match(r'\A([A-Za-z_0-9]+)=(.*)\Z', line)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from importlib.machinery import PathFinder
|
import imp
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -46,12 +46,12 @@ def install(check_options=False):
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
base.BaseCommand.create_parser = create_parser
|
base.BaseCommand.create_parser = create_parser
|
||||||
importer = ConfigurationFinder(check_options=check_options)
|
importer = ConfigurationImporter(check_options=check_options)
|
||||||
sys.meta_path.insert(0, importer)
|
sys.meta_path.insert(0, importer)
|
||||||
installed = True
|
installed = True
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationFinder(PathFinder):
|
class ConfigurationImporter(object):
|
||||||
modvar = SETTINGS_ENVIRONMENT_VARIABLE
|
modvar = SETTINGS_ENVIRONMENT_VARIABLE
|
||||||
namevar = CONFIGURATION_ENVIRONMENT_VARIABLE
|
namevar = CONFIGURATION_ENVIRONMENT_VARIABLE
|
||||||
error_msg = ("Configuration cannot be imported, "
|
error_msg = ("Configuration cannot be imported, "
|
||||||
|
|
@ -70,7 +70,7 @@ class ConfigurationFinder(PathFinder):
|
||||||
self.announce()
|
self.announce()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<ConfigurationFinder for '{}.{}'>".format(self.module,
|
return "<ConfigurationImporter for '{0}.{1}'>".format(self.module,
|
||||||
self.name)
|
self.name)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -82,10 +82,16 @@ class ConfigurationFinder(PathFinder):
|
||||||
return os.environ.get(self.namevar)
|
return os.environ.get(self.namevar)
|
||||||
|
|
||||||
def check_options(self):
|
def check_options(self):
|
||||||
parser = base.CommandParser(
|
try:
|
||||||
usage="%(prog)s subcommand [options] [args]",
|
parser = base.CommandParser(
|
||||||
add_help=False,
|
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.add_argument('--settings')
|
parser.add_argument('--settings')
|
||||||
parser.add_argument('--pythonpath')
|
parser.add_argument('--pythonpath')
|
||||||
parser.add_argument(CONFIGURATION_ARGUMENT,
|
parser.add_argument(CONFIGURATION_ARGUMENT,
|
||||||
|
|
@ -121,60 +127,58 @@ class ConfigurationFinder(PathFinder):
|
||||||
if (self.argv[1] == 'runserver'
|
if (self.argv[1] == 'runserver'
|
||||||
and os.environ.get('RUN_MAIN') == 'true'):
|
and os.environ.get('RUN_MAIN') == 'true'):
|
||||||
|
|
||||||
message = ("django-configurations version {}, using "
|
message = ("django-configurations version {0}, using "
|
||||||
"configuration {}".format(__version__ or "",
|
"configuration {1}".format(__version__ or "",
|
||||||
self.name))
|
self.name))
|
||||||
self.logger.debug(stylize(message))
|
self.logger.debug(stylize(message))
|
||||||
|
|
||||||
def find_spec(self, fullname, path=None, target=None):
|
def find_module(self, fullname, path=None):
|
||||||
if fullname is not None and fullname == self.module:
|
if fullname is not None and fullname == self.module:
|
||||||
spec = super().find_spec(fullname, path, target)
|
module = fullname.rsplit('.', 1)[-1]
|
||||||
if spec is not None:
|
return ConfigurationLoader(self.name,
|
||||||
wrap_loader(spec.loader, self.name)
|
imp.find_module(module, path))
|
||||||
return spec
|
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
|
||||||
else:
|
else:
|
||||||
return None
|
mod = imp.load_module(fullname, *self.location)
|
||||||
|
cls_path = '{0}.{1}'.format(mod.__name__, self.name)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
def wrap_loader(loader, class_name):
|
setattr(mod, 'CONFIGURATION', '{0}.{1}'.format(fullname,
|
||||||
class ConfigurationLoader(loader.__class__):
|
self.name))
|
||||||
def exec_module(self, module):
|
cls.post_setup()
|
||||||
super().exec_module(module)
|
|
||||||
|
|
||||||
mod = module
|
except Exception as err:
|
||||||
|
reraise(err, "Couldn't setup configuration '{0}'".format(cls_path))
|
||||||
|
|
||||||
cls_path = f'{mod.__name__}.{class_name}'
|
return mod
|
||||||
|
|
||||||
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
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import inspect
|
import inspect
|
||||||
|
import six
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
@ -13,7 +12,8 @@ def isuppercase(name):
|
||||||
|
|
||||||
|
|
||||||
def uppercase_attributes(obj):
|
def uppercase_attributes(obj):
|
||||||
return {name: getattr(obj, name) for name in dir(obj) if isuppercase(name)}
|
return dict((name, getattr(obj, name))
|
||||||
|
for name in filter(isuppercase, dir(obj)))
|
||||||
|
|
||||||
|
|
||||||
def import_by_path(dotted_path, error_prefix=''):
|
def import_by_path(dotted_path, error_prefix=''):
|
||||||
|
|
@ -24,26 +24,25 @@ def import_by_path(dotted_path, error_prefix=''):
|
||||||
|
|
||||||
Backported from Django 1.6.
|
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:
|
try:
|
||||||
module_path, class_name = dotted_path.rsplit('.', 1)
|
module_path, class_name = dotted_path.rsplit('.', 1)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ImproperlyConfigured("{}{} doesn't look like "
|
raise ImproperlyConfigured("{0}{1} doesn't look like "
|
||||||
"a module path".format(error_prefix,
|
"a module path".format(error_prefix,
|
||||||
dotted_path))
|
dotted_path))
|
||||||
try:
|
try:
|
||||||
module = import_module(module_path)
|
module = import_module(module_path)
|
||||||
except ImportError as err:
|
except ImportError as err:
|
||||||
msg = '{}Error importing module {}: "{}"'.format(error_prefix,
|
msg = '{0}Error importing module {1}: "{2}"'.format(error_prefix,
|
||||||
module_path,
|
module_path,
|
||||||
err)
|
err)
|
||||||
raise ImproperlyConfigured(msg).with_traceback(sys.exc_info()[2])
|
six.reraise(ImproperlyConfigured, ImproperlyConfigured(msg),
|
||||||
|
sys.exc_info()[2])
|
||||||
try:
|
try:
|
||||||
attr = getattr(module, class_name)
|
attr = getattr(module, class_name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise ImproperlyConfigured('{}Module "{}" does not define a '
|
raise ImproperlyConfigured('{0}Module "{1}" does not define a '
|
||||||
'"{}" attribute/class'.format(error_prefix,
|
'"{2}" attribute/class'.format(error_prefix,
|
||||||
module_path,
|
module_path,
|
||||||
class_name))
|
class_name))
|
||||||
return attr
|
return attr
|
||||||
|
|
@ -61,41 +60,78 @@ def reraise(exc, prefix=None, suffix=None):
|
||||||
suffix = ''
|
suffix = ''
|
||||||
elif not (suffix.startswith('(') and suffix.endswith(')')):
|
elif not (suffix.startswith('(') and suffix.endswith(')')):
|
||||||
suffix = '(' + suffix + ')'
|
suffix = '(' + suffix + ')'
|
||||||
exc.args = (f'{prefix} {args[0]} {suffix}',) + args[1:]
|
exc.args = ('{0} {1} {2}'.format(prefix, args[0], suffix),) + args[1:]
|
||||||
raise exc
|
raise
|
||||||
|
|
||||||
|
|
||||||
# Copied over from Sphinx
|
# Copied over from Sphinx
|
||||||
def getargspec(func):
|
if sys.version_info >= (3, 0):
|
||||||
"""Like inspect.getargspec but supports functools.partial as well."""
|
from functools import partial
|
||||||
if inspect.ismethod(func):
|
|
||||||
func = func.__func__
|
def getargspec(func):
|
||||||
if type(func) is partial:
|
"""Like inspect.getargspec but supports functools.partial as well."""
|
||||||
orig_func = func.func
|
if inspect.ismethod(func):
|
||||||
argspec = getargspec(orig_func)
|
func = func.__func__
|
||||||
args = list(argspec[0])
|
if type(func) is partial:
|
||||||
defaults = list(argspec[3] or ())
|
orig_func = func.func
|
||||||
kwoargs = list(argspec[4])
|
argspec = getargspec(orig_func)
|
||||||
kwodefs = dict(argspec[5] or {})
|
args = list(argspec[0])
|
||||||
if func.args:
|
defaults = list(argspec[3] or ())
|
||||||
args = args[len(func.args):]
|
kwoargs = list(argspec[4])
|
||||||
for arg in func.keywords or ():
|
kwodefs = dict(argspec[5] or {})
|
||||||
try:
|
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]:
|
||||||
i = args.index(arg) - len(args)
|
i = args.index(arg) - len(args)
|
||||||
del args[i]
|
del args[i]
|
||||||
try:
|
try:
|
||||||
del defaults[i]
|
del func_defaults[i]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
except ValueError: # must be a kwonly arg
|
return inspect.ArgSpec(args, varargs, varkw, func_defaults)
|
||||||
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)
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import ast
|
||||||
import copy
|
import copy
|
||||||
import decimal
|
import decimal
|
||||||
import os
|
import os
|
||||||
|
import six
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.core.exceptions import ValidationError, ImproperlyConfigured
|
from django.core.exceptions import ValidationError, ImproperlyConfigured
|
||||||
from django.utils.module_loading import import_string
|
|
||||||
|
|
||||||
from .utils import getargspec
|
from .utils import import_by_path, getargspec
|
||||||
|
|
||||||
|
|
||||||
def setup_value(target, name, value):
|
def setup_value(target, name, value):
|
||||||
|
|
@ -20,7 +20,7 @@ def setup_value(target, name, value):
|
||||||
setattr(target, multiple_name, multiple_value)
|
setattr(target, multiple_name, multiple_value)
|
||||||
|
|
||||||
|
|
||||||
class Value:
|
class Value(object):
|
||||||
"""
|
"""
|
||||||
A single settings value that is able to interpret env variables
|
A single settings value that is able to interpret env variables
|
||||||
and implements a simple validation scheme.
|
and implements a simple validation scheme.
|
||||||
|
|
@ -92,7 +92,7 @@ class Value:
|
||||||
else:
|
else:
|
||||||
environ_name = name.upper()
|
environ_name = name.upper()
|
||||||
if self.environ_prefix:
|
if self.environ_prefix:
|
||||||
environ_name = f'{self.environ_prefix}_{environ_name}'
|
environ_name = '{0}_{1}'.format(self.environ_prefix, environ_name)
|
||||||
return environ_name
|
return environ_name
|
||||||
|
|
||||||
def setup(self, name):
|
def setup(self, name):
|
||||||
|
|
@ -102,8 +102,8 @@ class Value:
|
||||||
if full_environ_name in os.environ:
|
if full_environ_name in os.environ:
|
||||||
value = self.to_python(os.environ[full_environ_name])
|
value = self.to_python(os.environ[full_environ_name])
|
||||||
elif self.environ_required:
|
elif self.environ_required:
|
||||||
raise ValueError('Value {!r} is required to be set as the '
|
raise ValueError('Value {0!r} is required to be set as the '
|
||||||
'environment variable {!r}'
|
'environment variable {1!r}'
|
||||||
.format(name, full_environ_name))
|
.format(name, full_environ_name))
|
||||||
self.value = value
|
self.value = value
|
||||||
return value
|
return value
|
||||||
|
|
@ -112,12 +112,12 @@ class Value:
|
||||||
"""
|
"""
|
||||||
Convert the given value of a environment variable into an
|
Convert the given value of a environment variable into an
|
||||||
appropriate Python representation of the value.
|
appropriate Python representation of the value.
|
||||||
This should be overridden when subclassing.
|
This should be overriden when subclassing.
|
||||||
"""
|
"""
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class MultipleMixin:
|
class MultipleMixin(object):
|
||||||
multiple = True
|
multiple = True
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -126,9 +126,9 @@ class BooleanValue(Value):
|
||||||
false_values = ('no', 'n', 'false', '0', '')
|
false_values = ('no', 'n', 'false', '0', '')
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(BooleanValue, self).__init__(*args, **kwargs)
|
||||||
if self.default not in (True, False):
|
if self.default not in (True, False):
|
||||||
raise ValueError('Default value {!r} is not a '
|
raise ValueError('Default value {0!r} is not a '
|
||||||
'boolean value'.format(self.default))
|
'boolean value'.format(self.default))
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
|
|
@ -139,30 +139,28 @@ class BooleanValue(Value):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
raise ValueError('Cannot interpret '
|
raise ValueError('Cannot interpret '
|
||||||
'boolean value {!r}'.format(value))
|
'boolean value {0!r}'.format(value))
|
||||||
|
|
||||||
|
|
||||||
class CastingMixin:
|
class CastingMixin(object):
|
||||||
exception = (TypeError, ValueError)
|
exception = (TypeError, ValueError)
|
||||||
message = 'Cannot interpret value {0!r}'
|
message = 'Cannot interpret value {0!r}'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(CastingMixin, self).__init__(*args, **kwargs)
|
||||||
if isinstance(self.caster, str):
|
if isinstance(self.caster, six.string_types):
|
||||||
try:
|
self._caster = import_by_path(self.caster)
|
||||||
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):
|
elif callable(self.caster):
|
||||||
self._caster = self.caster
|
self._caster = self.caster
|
||||||
else:
|
else:
|
||||||
error = 'Cannot use caster of {} ({!r})'.format(self,
|
error = 'Cannot use caster of {0} ({1!r})'.format(self,
|
||||||
self.caster)
|
self.caster)
|
||||||
raise ValueError(error)
|
raise ValueError(error)
|
||||||
try:
|
try:
|
||||||
arg_names = getargspec(self._caster)[0]
|
arg_names = getargspec(self._caster)[0]
|
||||||
self._params = {name: kwargs[name] for name in arg_names if name in kwargs}
|
self._params = dict((name, kwargs[name])
|
||||||
|
for name in arg_names
|
||||||
|
if name in kwargs)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self._params = {}
|
self._params = {}
|
||||||
|
|
||||||
|
|
@ -183,7 +181,7 @@ class IntegerValue(CastingMixin, Value):
|
||||||
class PositiveIntegerValue(IntegerValue):
|
class PositiveIntegerValue(IntegerValue):
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
int_value = super().to_python(value)
|
int_value = super(PositiveIntegerValue, self).to_python(value)
|
||||||
if int_value < 0:
|
if int_value < 0:
|
||||||
raise ValueError(self.message.format(value))
|
raise ValueError(self.message.format(value))
|
||||||
return int_value
|
return int_value
|
||||||
|
|
@ -215,7 +213,7 @@ class SequenceValue(Value):
|
||||||
converter = kwargs.pop('converter', None)
|
converter = kwargs.pop('converter', None)
|
||||||
if converter is not None:
|
if converter is not None:
|
||||||
self.converter = converter
|
self.converter = converter
|
||||||
super().__init__(*args, **kwargs)
|
super(SequenceValue, self).__init__(*args, **kwargs)
|
||||||
# make sure the default is the correct sequence type
|
# make sure the default is the correct sequence type
|
||||||
if self.default is None:
|
if self.default is None:
|
||||||
self.default = self.sequence_type()
|
self.default = self.sequence_type()
|
||||||
|
|
@ -259,7 +257,7 @@ class SingleNestedSequenceValue(SequenceValue):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.seq_separator = kwargs.pop('seq_separator', ';')
|
self.seq_separator = kwargs.pop('seq_separator', ';')
|
||||||
super().__init__(*args, **kwargs)
|
super(SingleNestedSequenceValue, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def _convert(self, items):
|
def _convert(self, items):
|
||||||
# This could receive either a bare or nested sequence
|
# This could receive either a bare or nested sequence
|
||||||
|
|
@ -268,7 +266,8 @@ class SingleNestedSequenceValue(SequenceValue):
|
||||||
super(SingleNestedSequenceValue, self)._convert(i) for i in items
|
super(SingleNestedSequenceValue, self)._convert(i) for i in items
|
||||||
]
|
]
|
||||||
return self.sequence_type(converted_sequences)
|
return self.sequence_type(converted_sequences)
|
||||||
return self.sequence_type(super()._convert(items))
|
return self.sequence_type(
|
||||||
|
super(SingleNestedSequenceValue, self)._convert(items))
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
split_value = [
|
split_value = [
|
||||||
|
|
@ -294,9 +293,9 @@ class BackendsValue(ListValue):
|
||||||
|
|
||||||
def converter(self, value):
|
def converter(self, value):
|
||||||
try:
|
try:
|
||||||
import_string(value)
|
import_by_path(value)
|
||||||
except ImportError as err:
|
except ImproperlyConfigured as err:
|
||||||
raise ValueError(err).with_traceback(sys.exc_info()[2])
|
six.reraise(ValueError, ValueError(err), sys.exc_info()[2])
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -304,28 +303,28 @@ class SetValue(ListValue):
|
||||||
message = 'Cannot interpret set item {0!r} in set {1!r}'
|
message = 'Cannot interpret set item {0!r} in set {1!r}'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(SetValue, self).__init__(*args, **kwargs)
|
||||||
if self.default is None:
|
if self.default is None:
|
||||||
self.default = set()
|
self.default = set()
|
||||||
else:
|
else:
|
||||||
self.default = set(self.default)
|
self.default = set(self.default)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
return set(super().to_python(value))
|
return set(super(SetValue, self).to_python(value))
|
||||||
|
|
||||||
|
|
||||||
class DictValue(Value):
|
class DictValue(Value):
|
||||||
message = 'Cannot interpret dict value {0!r}'
|
message = 'Cannot interpret dict value {0!r}'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(DictValue, self).__init__(*args, **kwargs)
|
||||||
if self.default is None:
|
if self.default is None:
|
||||||
self.default = {}
|
self.default = {}
|
||||||
else:
|
else:
|
||||||
self.default = dict(self.default)
|
self.default = dict(self.default)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
value = super().to_python(value)
|
value = super(DictValue, self).to_python(value)
|
||||||
if not value:
|
if not value:
|
||||||
return {}
|
return {}
|
||||||
try:
|
try:
|
||||||
|
|
@ -337,21 +336,17 @@ class DictValue(Value):
|
||||||
return evaled_value
|
return evaled_value
|
||||||
|
|
||||||
|
|
||||||
class ValidationMixin:
|
class ValidationMixin(object):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super(ValidationMixin, self).__init__(*args, **kwargs)
|
||||||
if isinstance(self.validator, str):
|
if isinstance(self.validator, six.string_types):
|
||||||
try:
|
self._validator = import_by_path(self.validator)
|
||||||
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):
|
elif callable(self.validator):
|
||||||
self._validator = self.validator
|
self._validator = self.validator
|
||||||
else:
|
else:
|
||||||
raise ValueError('Cannot use validator of '
|
raise ValueError('Cannot use validator of '
|
||||||
'{} ({!r})'.format(self, self.validator))
|
'{0} ({1!r})'.format(self, self.validator))
|
||||||
if self.default:
|
if self.default:
|
||||||
self.to_python(self.default)
|
self.to_python(self.default)
|
||||||
|
|
||||||
|
|
@ -385,19 +380,19 @@ class RegexValue(ValidationMixin, Value):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
regex = kwargs.pop('regex', None)
|
regex = kwargs.pop('regex', None)
|
||||||
self.validator = validators.RegexValidator(regex=regex)
|
self.validator = validators.RegexValidator(regex=regex)
|
||||||
super().__init__(*args, **kwargs)
|
super(RegexValue, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class PathValue(Value):
|
class PathValue(Value):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.check_exists = kwargs.pop('check_exists', True)
|
self.check_exists = kwargs.pop('check_exists', True)
|
||||||
super().__init__(*args, **kwargs)
|
super(PathValue, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def setup(self, name):
|
def setup(self, name):
|
||||||
value = super().setup(name)
|
value = super(PathValue, self).setup(name)
|
||||||
value = os.path.expanduser(value)
|
value = os.path.expanduser(value)
|
||||||
if self.check_exists and not os.path.exists(value):
|
if self.check_exists and not os.path.exists(value):
|
||||||
raise ValueError(f'Path {value!r} does not exist.')
|
raise ValueError('Path {0!r} does not exist.'.format(value))
|
||||||
return os.path.abspath(value)
|
return os.path.abspath(value)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -406,15 +401,15 @@ class SecretValue(Value):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs['environ'] = True
|
kwargs['environ'] = True
|
||||||
kwargs['environ_required'] = True
|
kwargs['environ_required'] = True
|
||||||
super().__init__(*args, **kwargs)
|
super(SecretValue, self).__init__(*args, **kwargs)
|
||||||
if self.default is not None:
|
if self.default is not None:
|
||||||
raise ValueError('Secret values are only allowed to '
|
raise ValueError('Secret values are only allowed to '
|
||||||
'be set as environment variables')
|
'be set as environment variables')
|
||||||
|
|
||||||
def setup(self, name):
|
def setup(self, name):
|
||||||
value = super().setup(name)
|
value = super(SecretValue, self).setup(name)
|
||||||
if not value:
|
if not value:
|
||||||
raise ValueError(f'Secret value {name!r} is not set')
|
raise ValueError('Secret value {0!r} is not set'.format(name))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -427,7 +422,7 @@ class EmailURLValue(CastingMixin, MultipleMixin, Value):
|
||||||
kwargs.setdefault('environ', True)
|
kwargs.setdefault('environ', True)
|
||||||
kwargs.setdefault('environ_prefix', None)
|
kwargs.setdefault('environ_prefix', None)
|
||||||
kwargs.setdefault('environ_name', 'EMAIL_URL')
|
kwargs.setdefault('environ_name', 'EMAIL_URL')
|
||||||
super().__init__(*args, **kwargs)
|
super(EmailURLValue, self).__init__(*args, **kwargs)
|
||||||
if self.default is None:
|
if self.default is None:
|
||||||
self.default = {}
|
self.default = {}
|
||||||
else:
|
else:
|
||||||
|
|
@ -442,14 +437,14 @@ class DictBackendMixin(Value):
|
||||||
kwargs.setdefault('environ', True)
|
kwargs.setdefault('environ', True)
|
||||||
kwargs.setdefault('environ_prefix', None)
|
kwargs.setdefault('environ_prefix', None)
|
||||||
kwargs.setdefault('environ_name', self.environ_name)
|
kwargs.setdefault('environ_name', self.environ_name)
|
||||||
super().__init__(*args, **kwargs)
|
super(DictBackendMixin, self).__init__(*args, **kwargs)
|
||||||
if self.default is None:
|
if self.default is None:
|
||||||
self.default = {}
|
self.default = {}
|
||||||
else:
|
else:
|
||||||
self.default = self.to_python(self.default)
|
self.default = self.to_python(self.default)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
value = super().to_python(value)
|
value = super(DictBackendMixin, self).to_python(value)
|
||||||
return {self.alias: value}
|
return {self.alias: value}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from importlib.metadata import PackageNotFoundError, version
|
from pkg_resources import get_distribution, DistributionNotFound
|
||||||
|
|
||||||
try:
|
try:
|
||||||
__version__ = version("django-configurations")
|
__version__ = get_distribution(__name__).version
|
||||||
except PackageNotFoundError:
|
except DistributionNotFound:
|
||||||
# package is not installed
|
# package is not installed
|
||||||
__version__ = None
|
__version__ = None
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@ from . import importer
|
||||||
|
|
||||||
importer.install()
|
importer.install()
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application # noqa: E402
|
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()
|
||||||
|
|
||||||
# this is just for the crazy ones
|
# this is just for the crazy ones
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
|
||||||
153
docs/Makefile
Normal file
153
docs/Makefile
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
# 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."
|
||||||
|
|
@ -3,92 +3,6 @@
|
||||||
Changelog
|
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)
|
v2.2 (2019-12-03)
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
|
||||||
317
docs/conf.py
317
docs/conf.py
|
|
@ -1,44 +1,299 @@
|
||||||
import configurations
|
# -*- 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.
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
import sys
|
||||||
project = 'django-configurations'
|
import os
|
||||||
copyright = '2012-2023, Jannis Leidel and other contributors'
|
|
||||||
author = 'Jannis Leidel and other contributors'
|
|
||||||
|
|
||||||
release = configurations.__version__
|
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.
|
||||||
version = ".".join(release.split(".")[:2])
|
version = ".".join(release.split(".")[:2])
|
||||||
|
|
||||||
# -- General configuration ---------------------------------------------------
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
add_function_parentheses = False
|
# for a list of supported languages.
|
||||||
add_module_names = False
|
#language = None
|
||||||
|
|
||||||
extensions = [
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
'sphinx.ext.autodoc',
|
# non-false value, then it is used:
|
||||||
'sphinx.ext.intersphinx',
|
#today = ''
|
||||||
'sphinx.ext.viewcode',
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
]
|
#today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
|
||||||
intersphinx_mapping = {
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
'python': ('https://docs.python.org/3', None),
|
#default_role = None
|
||||||
'sphinx': ('https://www.sphinx-doc.org/en/master', None),
|
|
||||||
'django': ('https://docs.djangoproject.com/en/dev',
|
|
||||||
'https://docs.djangoproject.com/en/dev/_objects/'),
|
|
||||||
}
|
|
||||||
|
|
||||||
# -- Options for HTML output -------------------------------------------------
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
html_theme = 'furo'
|
#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 Epub output ---------------------------------------------------
|
|
||||||
epub_title = project
|
|
||||||
epub_author = author
|
|
||||||
epub_publisher = author
|
|
||||||
epub_copyright = copyright
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- 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': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
# (source start file, target name, title, author, documentclass)
|
('index', 'django-configurations.tex', u'django-configurations Documentation',
|
||||||
('index', 'django-configurations.tex',
|
u'Jannis Leidel', 'manual'),
|
||||||
'django-configurations Documentation', author, '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 Epub output ---------------------------------------------------
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ Example:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ tree --noreport mysite_env/
|
$ tree mysite_env/
|
||||||
mysite_env/
|
mysite_env/
|
||||||
├── DJANGO_SETTINGS_MODULE
|
├── DJANGO_SETTINGS_MODULE
|
||||||
├── DJANGO_DEBUG
|
├── DJANGO_DEBUG
|
||||||
|
|
@ -82,8 +82,10 @@ Example:
|
||||||
├── DJANGO_CACHE_URL
|
├── DJANGO_CACHE_URL
|
||||||
└── PYTHONSTARTUP
|
└── PYTHONSTARTUP
|
||||||
|
|
||||||
|
0 directories, 3 files
|
||||||
$ cat mysite_env/DJANGO_CACHE_URL
|
$ cat mysite_env/DJANGO_CACHE_URL
|
||||||
redis://user@host:port/1
|
redis://user@host:port/1
|
||||||
|
$
|
||||||
|
|
||||||
Then, to enable the ``mysite_env`` environment variables, simply use the
|
Then, to enable the ``mysite_env`` environment variables, simply use the
|
||||||
``envdir`` command line tool as a prefix for your program, e.g.:
|
``envdir`` command line tool as a prefix for your program, e.g.:
|
||||||
|
|
@ -149,13 +151,13 @@ First install Django 1.8.x and django-configurations:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python -m pip install -r https://raw.github.com/jazzband/django-configurations/templates/1.8.x/requirements.txt
|
$ pip install -r https://raw.github.com/jazzband/django-configurations/templates/1.8.x/requirements.txt
|
||||||
|
|
||||||
Or Django 1.8:
|
Or Django 1.8:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python -m django startproject mysite -v2 --template https://github.com/jazzband/django-configurations/archive/templates/1.8.x.zip
|
$ django-admin.py 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``
|
Now you have a default Django 1.8.x project in the ``mysite``
|
||||||
directory that uses django-configurations.
|
directory that uses django-configurations.
|
||||||
|
|
@ -180,7 +182,7 @@ probably just add the following to the **beginning** of your settings module:
|
||||||
import configurations
|
import configurations
|
||||||
configurations.setup()
|
configurations.setup()
|
||||||
|
|
||||||
That has the same effect as using the ``manage.py``, ``wsgi.py`` or ``asgi.py`` utilities.
|
That has the same effect as using the ``manage.py`` or ``wsgi.py`` utilities.
|
||||||
This will also call ``django.setup()``.
|
This will also call ``django.setup()``.
|
||||||
|
|
||||||
>= 3.1
|
>= 3.1
|
||||||
|
|
@ -330,8 +332,8 @@ Channels
|
||||||
--------
|
--------
|
||||||
|
|
||||||
If you want to deploy a project that uses the Django channels with
|
If you want to deploy a project that uses the Django channels with
|
||||||
`Daphne <http://github.com/django/daphne/>`_ as the
|
`Daphne <http://github.com/django/daphne/>` as the
|
||||||
`interface server <http://channels.readthedocs.io/en/latest/deploying.html#run-interface-servers>`_
|
`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:
|
you have to use a asgi.py script similar to the following:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,6 @@ Bugs and feature requests
|
||||||
As always your mileage may vary, so please don't hesitate to send feature
|
As always your mileage may vary, so please don't hesitate to send feature
|
||||||
requests and bug reports:
|
requests and bug reports:
|
||||||
|
|
||||||
- https://github.com/jazzband/django-configurations/issues
|
https://github.com/jazzband/django-configurations/issues
|
||||||
|
|
||||||
Thanks!
|
Thanks!
|
||||||
190
docs/make.bat
Normal file
190
docs/make.bat
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
@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
|
||||||
|
|
@ -3,7 +3,7 @@ Usage patterns
|
||||||
|
|
||||||
There are various configuration patterns that can be implemented with
|
There are various configuration patterns that can be implemented with
|
||||||
django-configurations. The most common pattern is to have a base class
|
django-configurations. The most common pattern is to have a base class
|
||||||
and various subclasses based on the environment they are supposed to be
|
and various subclasses based on the enviroment they are supposed to be
|
||||||
used in, e.g. in production, staging and development.
|
used in, e.g. in production, staging and development.
|
||||||
|
|
||||||
Server specific settings
|
Server specific settings
|
||||||
|
|
@ -21,6 +21,7 @@ file:
|
||||||
|
|
||||||
class Dev(Base):
|
class Dev(Base):
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
class Prod(Base):
|
class Prod(Base):
|
||||||
TIME_ZONE = 'America/New_York'
|
TIME_ZONE = 'America/New_York'
|
||||||
|
|
@ -31,9 +32,9 @@ it should be ``Prod``. In Bash that would be:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ export DJANGO_SETTINGS_MODULE=mysite.settings
|
export DJANGO_SETTINGS_MODULE=mysite.settings
|
||||||
$ export DJANGO_CONFIGURATION=Prod
|
export DJANGO_CONFIGURATION=Prod
|
||||||
$ python -m manage runserver
|
python manage.py runserver
|
||||||
|
|
||||||
Alternatively you can use the ``--configuration`` option when using Django
|
Alternatively you can use the ``--configuration`` option when using Django
|
||||||
management commands along the lines of Django's default ``--settings``
|
management commands along the lines of Django's default ``--settings``
|
||||||
|
|
@ -41,7 +42,7 @@ command line option, e.g.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
$ python -m manage runserver --settings=mysite.settings --configuration=Prod
|
python manage.py runserver --settings=mysite.settings --configuration=Prod
|
||||||
|
|
||||||
Property settings
|
Property settings
|
||||||
-----------------
|
-----------------
|
||||||
|
|
@ -90,7 +91,7 @@ a few mixin you re-use multiple times:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class FullPageCaching:
|
class FullPageCaching(object):
|
||||||
USE_ETAGS = True
|
USE_ETAGS = True
|
||||||
|
|
||||||
Then import that mixin class in your site settings module and use it with
|
Then import that mixin class in your site settings module and use it with
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
Sphinx>4
|
|
||||||
furo
|
|
||||||
docutils
|
|
||||||
|
|
@ -46,7 +46,7 @@ value:
|
||||||
|
|
||||||
class Dev(Configuration):
|
class Dev(Configuration):
|
||||||
DEBUG = values.BooleanValue(True)
|
DEBUG = values.BooleanValue(True)
|
||||||
DEBUG_PROPAGATE_EXCEPTIONS = values.BooleanValue(DEBUG)
|
TEMPLATE_DEBUG = values.BooleanValue(DEBUG)
|
||||||
|
|
||||||
See the list of :ref:`built-in value classes<built-ins>` for more information.
|
See the list of :ref:`built-in value classes<built-ins>` for more information.
|
||||||
|
|
||||||
|
|
@ -86,11 +86,9 @@ prefixed with ``DJANGO_``. E.g.:
|
||||||
django-configurations will try to read the ``DJANGO_ROOT_URLCONF`` environment
|
django-configurations will try to read the ``DJANGO_ROOT_URLCONF`` environment
|
||||||
variable when deciding which value the ``ROOT_URLCONF`` setting should have.
|
variable when deciding which value the ``ROOT_URLCONF`` setting should have.
|
||||||
When you run the web server simply specify that environment variable
|
When you run the web server simply specify that environment variable
|
||||||
(e.g. in your init script):
|
(e.g. in your init script)::
|
||||||
|
|
||||||
.. code-block:: console
|
DJANGO_ROOT_URLCONF=mysite.debugging_urls gunicorn mysite.wsgi:application
|
||||||
|
|
||||||
$ DJANGO_ROOT_URLCONF=mysite.debugging_urls gunicorn mysite.wsgi:application
|
|
||||||
|
|
||||||
If the environment variable can't be found it'll use the default
|
If the environment variable can't be found it'll use the default
|
||||||
``'mysite.urls'``.
|
``'mysite.urls'``.
|
||||||
|
|
@ -127,9 +125,7 @@ Allow final value to be used outside the configuration context
|
||||||
|
|
||||||
You may use the ``environ_name`` parameter to allow a :class:`~Value` to be
|
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
|
directly converted to its final value for use outside of the configuration
|
||||||
context:
|
context::
|
||||||
|
|
||||||
.. code-block:: pycon
|
|
||||||
|
|
||||||
>>> type(values.Value([]))
|
>>> type(values.Value([]))
|
||||||
<class 'configurations.values.Value'>
|
<class 'configurations.values.Value'>
|
||||||
|
|
@ -164,12 +160,12 @@ the prefix.
|
||||||
|
|
||||||
:param default: the default value of the setting
|
:param default: the default value of the setting
|
||||||
:param environ: toggle for environment use
|
:param environ: toggle for environment use
|
||||||
:param environ_name: capitalized name of environment variable to look for
|
:param environ_name: name of environment variable to look for
|
||||||
:param environ_prefix: capitalized prefix to use when looking for environment variable
|
:param environ_prefix: 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
|
:param environ_required: wheter or not the value is required to be set as an environment variable
|
||||||
:type environ: bool
|
:type environ: bool
|
||||||
:type environ_name: str or None
|
:type environ_name: capitalized string or None
|
||||||
:type environ_prefix: str
|
:type environ_prefix: capitalized string
|
||||||
:type environ_required: bool
|
:type environ_required: bool
|
||||||
|
|
||||||
The ``default`` parameter is effectively the value the setting has
|
The ``default`` parameter is effectively the value the setting has
|
||||||
|
|
@ -260,10 +256,6 @@ Type values
|
||||||
|
|
||||||
MYSITE_CONVERSION_RATE = values.DecimalValue(decimal.Decimal('4.56214'))
|
MYSITE_CONVERSION_RATE = values.DecimalValue(decimal.Decimal('4.56214'))
|
||||||
|
|
||||||
.. class:: SequenceValue
|
|
||||||
|
|
||||||
Common base class for sequence values.
|
|
||||||
|
|
||||||
.. class:: ListValue(default, [separator=',', converter=None])
|
.. class:: ListValue(default, [separator=',', converter=None])
|
||||||
|
|
||||||
A :class:`~SequenceValue` subclass that handles list values.
|
A :class:`~SequenceValue` subclass that handles list values.
|
||||||
|
|
@ -287,21 +279,17 @@ Type values
|
||||||
MONTY_PYTHONS = ListValue(['John Cleese', 'Eric Idle'],
|
MONTY_PYTHONS = ListValue(['John Cleese', 'Eric Idle'],
|
||||||
converter=check_monty_python)
|
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::
|
||||||
|
|
||||||
.. code-block:: console
|
DJANGO_MONTY_PYTHONS="Terry Jones,Graham Chapman" gunicorn mysite.wsgi:application
|
||||||
|
|
||||||
$ DJANGO_MONTY_PYTHONS="Terry Jones,Graham Chapman" gunicorn mysite.wsgi:application
|
|
||||||
|
|
||||||
Use a custom separator::
|
Use a custom separator::
|
||||||
|
|
||||||
EMERGENCY_EMAILS = ListValue(['admin@mysite.net'], separator=';')
|
EMERGENCY_EMAILS = ListValue(['admin@mysite.net'], separator=';')
|
||||||
|
|
||||||
And override it:
|
And override it::
|
||||||
|
|
||||||
.. code-block:: console
|
DJANGO_EMERGENCY_EMAILS="admin@mysite.net;manager@mysite.org;support@mysite.com" gunicorn mysite.wsgi:application
|
||||||
|
|
||||||
$ DJANGO_EMERGENCY_EMAILS="admin@mysite.net;manager@mysite.org;support@mysite.com" gunicorn mysite.wsgi:application
|
|
||||||
|
|
||||||
.. class:: TupleValue
|
.. class:: TupleValue
|
||||||
|
|
||||||
|
|
@ -313,10 +301,6 @@ Type values
|
||||||
|
|
||||||
See the :class:`~ListValue` examples above.
|
See the :class:`~ListValue` examples above.
|
||||||
|
|
||||||
.. class:: SingleNestedSequenceValue
|
|
||||||
|
|
||||||
Common base class for nested sequence values.
|
|
||||||
|
|
||||||
.. class:: SingleNestedTupleValue(default, [seq_separator=';', separator=',', converter=None])
|
.. class:: SingleNestedTupleValue(default, [seq_separator=';', separator=',', converter=None])
|
||||||
|
|
||||||
A :class:`~SingleNestedSequenceValue` subclass that handles single nested tuple values,
|
A :class:`~SingleNestedSequenceValue` subclass that handles single nested tuple values,
|
||||||
|
|
@ -370,10 +354,6 @@ Type values
|
||||||
'it': ['Mike', 'Joe'],
|
'it': ['Mike', 'Joe'],
|
||||||
})
|
})
|
||||||
|
|
||||||
Override using environment variables like this::
|
|
||||||
|
|
||||||
DJANGO_DEPARTMENTS={'it':['Mike','Joe'],'hr':['Emma','Olivia']}
|
|
||||||
|
|
||||||
Validator values
|
Validator values
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|
@ -567,7 +547,7 @@ Other values
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
MIDDLEWARE = values.BackendsValue([
|
MIDDLEWARE_CLASSES = values.BackendsValue([
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
|
@ -604,7 +584,7 @@ Value mixins
|
||||||
requires a ``caster`` class attribute of one of the following types:
|
requires a ``caster`` class attribute of one of the following types:
|
||||||
|
|
||||||
- dotted import path, e.g. ``'mysite.utils.custom_caster'``
|
- dotted import path, e.g. ``'mysite.utils.custom_caster'``
|
||||||
- a callable, e.g. :class:`int`
|
- a callable, e.g. :func:`int`
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
|
@ -625,7 +605,7 @@ Value mixins
|
||||||
validation attempt.
|
validation attempt.
|
||||||
|
|
||||||
- dotted import path, e.g. ``'mysite.validators.custom_validator'``
|
- dotted import path, e.g. ``'mysite.validators.custom_validator'``
|
||||||
- a callable, e.g. :class:`bool`
|
- a callable, e.g. :func:`bool`
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
[wheel]
|
||||||
|
universal = 1
|
||||||
|
|
||||||
[coverage:run]
|
[coverage:run]
|
||||||
source = .
|
source = .
|
||||||
branch = 1
|
branch = 1
|
||||||
|
|
|
||||||
29
setup.py
29
setup.py
|
|
@ -1,3 +1,4 @@
|
||||||
|
from __future__ import print_function
|
||||||
import os
|
import os
|
||||||
import codecs
|
import codecs
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
@ -14,9 +15,6 @@ setup(
|
||||||
use_scm_version={"version_scheme": "post-release", "local_scheme": "dirty-tag"},
|
use_scm_version={"version_scheme": "post-release", "local_scheme": "dirty-tag"},
|
||||||
setup_requires=["setuptools_scm"],
|
setup_requires=["setuptools_scm"],
|
||||||
url='https://django-configurations.readthedocs.io/',
|
url='https://django-configurations.readthedocs.io/',
|
||||||
project_urls={
|
|
||||||
'Source': 'https://github.com/jazzband/django-configurations',
|
|
||||||
},
|
|
||||||
license='BSD',
|
license='BSD',
|
||||||
description="A helper for organizing Django settings.",
|
description="A helper for organizing Django settings.",
|
||||||
long_description=read('README.rst'),
|
long_description=read('README.rst'),
|
||||||
|
|
@ -29,41 +27,34 @@ setup(
|
||||||
'django-cadmin = configurations.management:execute_from_command_line',
|
'django-cadmin = configurations.management:execute_from_command_line',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
install_requires=[
|
install_requires=['six'],
|
||||||
'django>=3.2',
|
|
||||||
],
|
|
||||||
python_requires='>=3.9, <4.0',
|
|
||||||
extras_require={
|
extras_require={
|
||||||
'cache': ['django-cache-url'],
|
'cache': ['django-cache-url'],
|
||||||
'database': ['dj-database-url'],
|
'database': ['dj-database-url'],
|
||||||
'email': ['dj-email-url'],
|
'email': ['dj-email-url'],
|
||||||
'search': ['dj-search-url'],
|
'search': ['dj-search-url'],
|
||||||
'testing': [
|
'testing': [
|
||||||
|
'django-discover-runner',
|
||||||
|
'mock',
|
||||||
'django-cache-url>=1.0.0',
|
'django-cache-url>=1.0.0',
|
||||||
'dj-database-url',
|
'dj-database-url',
|
||||||
'dj-email-url',
|
'dj-email-url',
|
||||||
'dj-search-url',
|
'dj-search-url',
|
||||||
|
'Sphinx>=1.4',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
'Framework :: Django',
|
'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',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: BSD License',
|
'License :: OSI Approved :: BSD License',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 2.7',
|
||||||
'Programming Language :: Python :: 3 :: Only',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.9',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.10',
|
'Programming Language :: Python :: 3.7',
|
||||||
'Programming Language :: Python :: 3.11',
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: 3.12',
|
|
||||||
'Programming Language :: Python :: 3.13',
|
|
||||||
'Programming Language :: Python :: Implementation :: PyPy',
|
'Programming Language :: Python :: Implementation :: PyPy',
|
||||||
'Topic :: Utilities',
|
'Topic :: Utilities',
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ class Base(Configuration):
|
||||||
# Django settings for test_project project.
|
# Django settings for test_project project.
|
||||||
|
|
||||||
DEBUG = values.BooleanValue(True, environ=True)
|
DEBUG = values.BooleanValue(True, environ=True)
|
||||||
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
ADMINS = (
|
ADMINS = (
|
||||||
# ('Your Name', 'your_email@example.com'),
|
# ('Your Name', 'your_email@example.com'),
|
||||||
|
|
@ -94,7 +95,7 @@ class Base(Configuration):
|
||||||
'django.template.loaders.app_directories.Loader',
|
'django.template.loaders.app_directories.Loader',
|
||||||
)
|
)
|
||||||
|
|
||||||
MIDDLEWARE = (
|
MIDDLEWARE_CLASSES = (
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
|
|
||||||
30
tests/docs/conf.py
Normal file
30
tests/docs/conf.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- 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'
|
||||||
2
tests/docs/index.rst
Normal file
2
tests/docs/index.rst
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Test Documentation
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
0
tests/management/__init__.py
Normal file
0
tests/management/__init__.py
Normal file
0
tests/management/commands/__init__.py
Normal file
0
tests/management/commands/__init__.py
Normal file
16
tests/management/commands/old_optparse_command.py
Normal file
16
tests/management/commands/old_optparse_command.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
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
|
||||||
|
|
@ -6,6 +6,3 @@ class DotEnvConfiguration(Configuration):
|
||||||
DOTENV = 'test_project/.env'
|
DOTENV = 'test_project/.env'
|
||||||
|
|
||||||
DOTENV_VALUE = values.Value()
|
DOTENV_VALUE = values.Value()
|
||||||
|
|
||||||
def DOTENV_VALUE_METHOD(self):
|
|
||||||
return values.Value(environ_name="DOTENV_VALUE")
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
from configurations import Configuration
|
|
||||||
|
|
||||||
|
|
||||||
class ErrorConfiguration(Configuration):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def pre_setup(cls):
|
|
||||||
raise ValueError("Error in pre_setup")
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
import django
|
||||||
|
|
||||||
from configurations import Configuration, pristinemethod
|
from configurations import Configuration, pristinemethod
|
||||||
|
from configurations.values import BooleanValue
|
||||||
|
|
||||||
|
|
||||||
class Test(Configuration):
|
class Test(Configuration):
|
||||||
|
|
@ -9,6 +11,8 @@ class Test(Configuration):
|
||||||
os.path.join(os.path.dirname(
|
os.path.join(os.path.dirname(
|
||||||
os.path.abspath(__file__)), os.pardir))
|
os.path.abspath(__file__)), os.pardir))
|
||||||
|
|
||||||
|
ENV_LOADED = BooleanValue(False)
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
|
|
@ -32,9 +36,12 @@ class Test(Configuration):
|
||||||
|
|
||||||
ROOT_URLCONF = 'tests.urls'
|
ROOT_URLCONF = 'tests.urls'
|
||||||
|
|
||||||
|
if django.VERSION[:2] < (1, 6):
|
||||||
|
TEST_RUNNER = 'discover_runner.DiscoverRunner'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ALLOWED_HOSTS(self):
|
def ALLOWED_HOSTS(self):
|
||||||
allowed_hosts = super().ALLOWED_HOSTS[:]
|
allowed_hosts = super(Test, self).ALLOWED_HOSTS[:]
|
||||||
allowed_hosts.append('base')
|
allowed_hosts.append('base')
|
||||||
return allowed_hosts
|
return allowed_hosts
|
||||||
|
|
||||||
|
|
@ -64,7 +71,3 @@ class Test(Configuration):
|
||||||
@classmethod
|
@classmethod
|
||||||
def post_setup(cls):
|
def post_setup(cls):
|
||||||
cls.POST_SETUP_TEST_SETTING = 7
|
cls.POST_SETUP_TEST_SETTING = 7
|
||||||
|
|
||||||
|
|
||||||
class TestWithDefaultSetExplicitely(Test):
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
from configurations import Configuration
|
from configurations import Configuration
|
||||||
|
|
||||||
|
|
||||||
class Mixin1:
|
class Mixin1(object):
|
||||||
@property
|
@property
|
||||||
def ALLOWED_HOSTS(self):
|
def ALLOWED_HOSTS(self):
|
||||||
allowed_hosts = super().ALLOWED_HOSTS[:]
|
allowed_hosts = super(Mixin1, self).ALLOWED_HOSTS[:]
|
||||||
allowed_hosts.append('test1')
|
allowed_hosts.append('test1')
|
||||||
return allowed_hosts
|
return allowed_hosts
|
||||||
|
|
||||||
|
|
||||||
class Mixin2:
|
class Mixin2(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ALLOWED_HOSTS(self):
|
def ALLOWED_HOSTS(self):
|
||||||
allowed_hosts = super().ALLOWED_HOSTS[:]
|
allowed_hosts = super(Mixin2, self).ALLOWED_HOSTS[:]
|
||||||
allowed_hosts.append('test2')
|
allowed_hosts.append('test2')
|
||||||
return allowed_hosts
|
return allowed_hosts
|
||||||
|
|
||||||
|
|
@ -21,6 +21,6 @@ class Mixin2:
|
||||||
class Inheritance(Mixin2, Mixin1, Configuration):
|
class Inheritance(Mixin2, Mixin1, Configuration):
|
||||||
|
|
||||||
def ALLOWED_HOSTS(self):
|
def ALLOWED_HOSTS(self):
|
||||||
allowed_hosts = super().ALLOWED_HOSTS[:]
|
allowed_hosts = super(Inheritance, self).ALLOWED_HOSTS[:]
|
||||||
allowed_hosts.append('test3')
|
allowed_hosts.append('test3')
|
||||||
return allowed_hosts
|
return allowed_hosts
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ from .single_inheritance import Inheritance as BaseInheritance
|
||||||
class Inheritance(BaseInheritance):
|
class Inheritance(BaseInheritance):
|
||||||
|
|
||||||
def ALLOWED_HOSTS(self):
|
def ALLOWED_HOSTS(self):
|
||||||
allowed_hosts = super().ALLOWED_HOSTS[:]
|
allowed_hosts = super(Inheritance, self).ALLOWED_HOSTS[:]
|
||||||
allowed_hosts.append('test-test')
|
allowed_hosts.append('test-test')
|
||||||
return allowed_hosts
|
return allowed_hosts
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ class Inheritance(Base):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ALLOWED_HOSTS(self):
|
def ALLOWED_HOSTS(self):
|
||||||
allowed_hosts = super().ALLOWED_HOSTS[:]
|
allowed_hosts = super(Inheritance, self).ALLOWED_HOSTS[:]
|
||||||
allowed_hosts.append('test')
|
allowed_hosts.append('test')
|
||||||
return allowed_hosts
|
return allowed_hosts
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
"""Used by tests to ensure logging is kept when calling setup() twice."""
|
"""Used by tests to ensure logging is kept when calling setup() twice."""
|
||||||
|
|
||||||
from unittest import mock
|
try:
|
||||||
|
from unittest import mock
|
||||||
|
except ImportError:
|
||||||
|
from mock import mock
|
||||||
|
|
||||||
import configurations
|
import configurations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import os
|
import os
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from unittest.mock import patch
|
from mock import patch
|
||||||
|
|
||||||
|
|
||||||
class DotEnvLoadingTests(TestCase):
|
class DotEnvLoadingTests(TestCase):
|
||||||
|
|
@ -11,5 +11,4 @@ class DotEnvLoadingTests(TestCase):
|
||||||
def test_env_loaded(self):
|
def test_env_loaded(self):
|
||||||
from tests.settings import dot_env
|
from tests.settings import dot_env
|
||||||
self.assertEqual(dot_env.DOTENV_VALUE, 'is set')
|
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)
|
self.assertEqual(dot_env.DOTENV_LOADED, dot_env.DOTENV)
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
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 ",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
@ -2,7 +2,7 @@ import os
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from unittest.mock import patch
|
from mock import patch
|
||||||
|
|
||||||
|
|
||||||
class InheritanceTests(TestCase):
|
class InheritanceTests(TestCase):
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import sys
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from unittest.mock import patch
|
from mock import patch
|
||||||
|
|
||||||
from configurations.importer import ConfigurationFinder
|
from configurations.importer import ConfigurationImporter
|
||||||
|
|
||||||
ROOT_DIR = os.path.dirname(os.path.dirname(__file__))
|
ROOT_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
TEST_PROJECT_DIR = os.path.join(ROOT_DIR, 'test_project')
|
TEST_PROJECT_DIR = os.path.join(ROOT_DIR, 'test_project')
|
||||||
|
|
@ -42,14 +42,12 @@ class MainTests(TestCase):
|
||||||
|
|
||||||
@patch.dict(os.environ, clear=True, DJANGO_CONFIGURATION='Test')
|
@patch.dict(os.environ, clear=True, DJANGO_CONFIGURATION='Test')
|
||||||
def test_empty_module_var(self):
|
def test_empty_module_var(self):
|
||||||
with self.assertRaises(ImproperlyConfigured):
|
self.assertRaises(ImproperlyConfigured, ConfigurationImporter)
|
||||||
ConfigurationFinder()
|
|
||||||
|
|
||||||
@patch.dict(os.environ, clear=True,
|
@patch.dict(os.environ, clear=True,
|
||||||
DJANGO_SETTINGS_MODULE='tests.settings.main')
|
DJANGO_SETTINGS_MODULE='tests.settings.main')
|
||||||
def test_empty_class_var(self):
|
def test_empty_class_var(self):
|
||||||
with self.assertRaises(ImproperlyConfigured):
|
self.assertRaises(ImproperlyConfigured, ConfigurationImporter)
|
||||||
ConfigurationFinder()
|
|
||||||
|
|
||||||
def test_global_settings(self):
|
def test_global_settings(self):
|
||||||
from configurations.base import Configuration
|
from configurations.base import Configuration
|
||||||
|
|
@ -57,12 +55,6 @@ class MainTests(TestCase):
|
||||||
self.assertEqual(repr(Configuration),
|
self.assertEqual(repr(Configuration),
|
||||||
"<Configuration 'configurations.base.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):
|
def test_repr(self):
|
||||||
from tests.settings.main import Test
|
from tests.settings.main import Test
|
||||||
self.assertEqual(repr(Test),
|
self.assertEqual(repr(Test),
|
||||||
|
|
@ -72,21 +64,21 @@ class MainTests(TestCase):
|
||||||
DJANGO_SETTINGS_MODULE='tests.settings.main',
|
DJANGO_SETTINGS_MODULE='tests.settings.main',
|
||||||
DJANGO_CONFIGURATION='Test')
|
DJANGO_CONFIGURATION='Test')
|
||||||
def test_initialization(self):
|
def test_initialization(self):
|
||||||
finder = ConfigurationFinder()
|
importer = ConfigurationImporter()
|
||||||
self.assertEqual(finder.module, 'tests.settings.main')
|
self.assertEqual(importer.module, 'tests.settings.main')
|
||||||
self.assertEqual(finder.name, 'Test')
|
self.assertEqual(importer.name, 'Test')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
repr(finder),
|
repr(importer),
|
||||||
"<ConfigurationFinder for 'tests.settings.main.Test'>")
|
"<ConfigurationImporter for 'tests.settings.main.Test'>")
|
||||||
|
|
||||||
@patch.dict(os.environ, clear=True,
|
@patch.dict(os.environ, clear=True,
|
||||||
DJANGO_SETTINGS_MODULE='tests.settings.inheritance',
|
DJANGO_SETTINGS_MODULE='tests.settings.inheritance',
|
||||||
DJANGO_CONFIGURATION='Inheritance')
|
DJANGO_CONFIGURATION='Inheritance')
|
||||||
def test_initialization_inheritance(self):
|
def test_initialization_inheritance(self):
|
||||||
finder = ConfigurationFinder()
|
importer = ConfigurationImporter()
|
||||||
self.assertEqual(finder.module,
|
self.assertEqual(importer.module,
|
||||||
'tests.settings.inheritance')
|
'tests.settings.inheritance')
|
||||||
self.assertEqual(finder.name, 'Inheritance')
|
self.assertEqual(importer.name, 'Inheritance')
|
||||||
|
|
||||||
@patch.dict(os.environ, clear=True,
|
@patch.dict(os.environ, clear=True,
|
||||||
DJANGO_SETTINGS_MODULE='tests.settings.main',
|
DJANGO_SETTINGS_MODULE='tests.settings.main',
|
||||||
|
|
@ -95,12 +87,12 @@ class MainTests(TestCase):
|
||||||
'--settings=tests.settings.main',
|
'--settings=tests.settings.main',
|
||||||
'--configuration=Test'])
|
'--configuration=Test'])
|
||||||
def test_configuration_option(self):
|
def test_configuration_option(self):
|
||||||
finder = ConfigurationFinder(check_options=False)
|
importer = ConfigurationImporter(check_options=False)
|
||||||
self.assertEqual(finder.module, 'tests.settings.main')
|
self.assertEqual(importer.module, 'tests.settings.main')
|
||||||
self.assertEqual(finder.name, 'NonExisting')
|
self.assertEqual(importer.name, 'NonExisting')
|
||||||
finder = ConfigurationFinder(check_options=True)
|
importer = ConfigurationImporter(check_options=True)
|
||||||
self.assertEqual(finder.module, 'tests.settings.main')
|
self.assertEqual(importer.module, 'tests.settings.main')
|
||||||
self.assertEqual(finder.name, 'Test')
|
self.assertEqual(importer.name, 'Test')
|
||||||
|
|
||||||
def test_configuration_argument_in_cli(self):
|
def test_configuration_argument_in_cli(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -114,22 +106,6 @@ class MainTests(TestCase):
|
||||||
stdout=subprocess.PIPE)
|
stdout=subprocess.PIPE)
|
||||||
self.assertIn('--configuration', proc.communicate()[0].decode('utf-8'))
|
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):
|
def test_django_setup_only_called_once(self):
|
||||||
proc = subprocess.Popen(
|
proc = subprocess.Popen(
|
||||||
[sys.executable, os.path.join(os.path.dirname(__file__),
|
[sys.executable, os.path.join(os.path.dirname(__file__),
|
||||||
|
|
|
||||||
21
tests/test_sphinx.py
Normal file
21
tests/test_sphinx.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- 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)
|
||||||
|
|
@ -2,11 +2,10 @@ import decimal
|
||||||
import os
|
import os
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from django import VERSION as DJANGO_VERSION
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
from unittest.mock import patch
|
from mock import patch
|
||||||
|
|
||||||
from configurations.values import (Value, BooleanValue, IntegerValue,
|
from configurations.values import (Value, BooleanValue, IntegerValue,
|
||||||
FloatValue, DecimalValue, ListValue,
|
FloatValue, DecimalValue, ListValue,
|
||||||
|
|
@ -34,7 +33,7 @@ class ValueTests(TestCase):
|
||||||
|
|
||||||
def test_value_with_default(self):
|
def test_value_with_default(self):
|
||||||
value = Value('default', environ=False)
|
value = Value('default', environ=False)
|
||||||
self.assertEqual(type(value), str)
|
self.assertEqual(type(value), type('default'))
|
||||||
self.assertEqual(value, 'default')
|
self.assertEqual(value, 'default')
|
||||||
self.assertEqual(str(value), 'default')
|
self.assertEqual(str(value), 'default')
|
||||||
|
|
||||||
|
|
@ -44,17 +43,17 @@ class ValueTests(TestCase):
|
||||||
with env(DJANGO_TEST='override'):
|
with env(DJANGO_TEST='override'):
|
||||||
self.assertEqual(value.setup('TEST'), 'default')
|
self.assertEqual(value.setup('TEST'), 'default')
|
||||||
value = Value(environ_name='TEST')
|
value = Value(environ_name='TEST')
|
||||||
self.assertEqual(type(value), str)
|
self.assertEqual(type(value), type('override'))
|
||||||
self.assertEqual(value, 'override')
|
self.assertEqual(value, 'override')
|
||||||
self.assertEqual(str(value), 'override')
|
self.assertEqual(str(value), 'override')
|
||||||
self.assertEqual(f'{value}', 'override')
|
self.assertEqual('{0}'.format(value), 'override')
|
||||||
self.assertEqual('%s' % value, 'override')
|
self.assertEqual('%s' % value, 'override')
|
||||||
|
|
||||||
value = Value(environ_name='TEST', late_binding=True)
|
value = Value(environ_name='TEST', late_binding=True)
|
||||||
self.assertEqual(type(value), Value)
|
self.assertEqual(type(value), Value)
|
||||||
self.assertEqual(value.value, 'override')
|
self.assertEqual(value.value, 'override')
|
||||||
self.assertEqual(str(value), 'override')
|
self.assertEqual(str(value), 'override')
|
||||||
self.assertEqual(f'{value}', 'override')
|
self.assertEqual('{0}'.format(value), 'override')
|
||||||
self.assertEqual('%s' % value, 'override')
|
self.assertEqual('%s' % value, 'override')
|
||||||
|
|
||||||
self.assertEqual(repr(value), repr('override'))
|
self.assertEqual(repr(value), repr('override'))
|
||||||
|
|
@ -271,9 +270,9 @@ class ValueTests(TestCase):
|
||||||
def test_set_values_default(self):
|
def test_set_values_default(self):
|
||||||
value = SetValue()
|
value = SetValue()
|
||||||
with env(DJANGO_TEST='2,2'):
|
with env(DJANGO_TEST='2,2'):
|
||||||
self.assertEqual(value.setup('TEST'), {'2', '2'})
|
self.assertEqual(value.setup('TEST'), set(['2', '2']))
|
||||||
with env(DJANGO_TEST='2, 2 ,'):
|
with env(DJANGO_TEST='2, 2 ,'):
|
||||||
self.assertEqual(value.setup('TEST'), {'2', '2'})
|
self.assertEqual(value.setup('TEST'), set(['2', '2']))
|
||||||
with env(DJANGO_TEST=''):
|
with env(DJANGO_TEST=''):
|
||||||
self.assertEqual(value.setup('TEST'), set())
|
self.assertEqual(value.setup('TEST'), set())
|
||||||
|
|
||||||
|
|
@ -373,23 +372,16 @@ class ValueTests(TestCase):
|
||||||
value = DatabaseURLValue()
|
value = DatabaseURLValue()
|
||||||
self.assertEqual(value.default, {})
|
self.assertEqual(value.default, {})
|
||||||
with env(DATABASE_URL='sqlite://'):
|
with env(DATABASE_URL='sqlite://'):
|
||||||
settings_value = value.setup('DATABASE_URL')
|
self.assertEqual(value.setup('DATABASE_URL'), {
|
||||||
# Compare the embedded dicts in the "default" entry so that the difference can be seen if
|
'default': {
|
||||||
# it fails ... DatabaseURLValue(|) uses an external app that can add additional entries
|
|
||||||
self.assertDictEqual(
|
|
||||||
{
|
|
||||||
'CONN_HEALTH_CHECKS': False,
|
|
||||||
'CONN_MAX_AGE': 0,
|
'CONN_MAX_AGE': 0,
|
||||||
'DISABLE_SERVER_SIDE_CURSORS': False,
|
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'HOST': '',
|
'HOST': '',
|
||||||
'NAME': ':memory:',
|
'NAME': ':memory:',
|
||||||
'PASSWORD': '',
|
'PASSWORD': '',
|
||||||
'PORT': '',
|
'PORT': '',
|
||||||
'USER': '',
|
'USER': '',
|
||||||
},
|
}})
|
||||||
settings_value['default']
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_database_url_additional_args(self):
|
def test_database_url_additional_args(self):
|
||||||
|
|
||||||
|
|
@ -419,7 +411,6 @@ class ValueTests(TestCase):
|
||||||
'EMAIL_HOST_PASSWORD': 'password',
|
'EMAIL_HOST_PASSWORD': 'password',
|
||||||
'EMAIL_HOST_USER': 'user@domain.com',
|
'EMAIL_HOST_USER': 'user@domain.com',
|
||||||
'EMAIL_PORT': 587,
|
'EMAIL_PORT': 587,
|
||||||
'EMAIL_TIMEOUT': None,
|
|
||||||
'EMAIL_USE_SSL': False,
|
'EMAIL_USE_SSL': False,
|
||||||
'EMAIL_USE_TLS': True})
|
'EMAIL_USE_TLS': True})
|
||||||
with env(EMAIL_URL='console://'):
|
with env(EMAIL_URL='console://'):
|
||||||
|
|
@ -430,7 +421,6 @@ class ValueTests(TestCase):
|
||||||
'EMAIL_HOST_PASSWORD': None,
|
'EMAIL_HOST_PASSWORD': None,
|
||||||
'EMAIL_HOST_USER': None,
|
'EMAIL_HOST_USER': None,
|
||||||
'EMAIL_PORT': None,
|
'EMAIL_PORT': None,
|
||||||
'EMAIL_TIMEOUT': None,
|
|
||||||
'EMAIL_USE_SSL': False,
|
'EMAIL_USE_SSL': False,
|
||||||
'EMAIL_USE_TLS': False})
|
'EMAIL_USE_TLS': False})
|
||||||
with env(EMAIL_URL='smtps://user@domain.com:password@smtp.example.com:wrong'): # noqa: E501
|
with env(EMAIL_URL='smtps://user@domain.com:password@smtp.example.com:wrong'): # noqa: E501
|
||||||
|
|
@ -439,7 +429,7 @@ class ValueTests(TestCase):
|
||||||
def test_cache_url_value(self):
|
def test_cache_url_value(self):
|
||||||
cache_setting = {
|
cache_setting = {
|
||||||
'default': {
|
'default': {
|
||||||
'BACKEND': 'django_redis.cache.RedisCache' if DJANGO_VERSION < (4,) else 'django.core.cache.backends.redis.RedisCache', # noqa: E501
|
'BACKEND': 'django_redis.cache.RedisCache',
|
||||||
'LOCATION': 'redis://host:6379/1',
|
'LOCATION': 'redis://host:6379/1',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -495,12 +485,12 @@ class ValueTests(TestCase):
|
||||||
self.assertEqual(value.value, set())
|
self.assertEqual(value.value, set())
|
||||||
|
|
||||||
value = SetValue([1, 2])
|
value = SetValue([1, 2])
|
||||||
self.assertEqual(value.default, {1, 2})
|
self.assertEqual(value.default, set([1, 2]))
|
||||||
self.assertEqual(value.value, {1, 2})
|
self.assertEqual(value.value, set([1, 2]))
|
||||||
|
|
||||||
def test_setup_value(self):
|
def test_setup_value(self):
|
||||||
|
|
||||||
class Target:
|
class Target(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
value = EmailURLValue()
|
value = EmailURLValue()
|
||||||
|
|
@ -513,7 +503,6 @@ class ValueTests(TestCase):
|
||||||
'EMAIL_HOST_PASSWORD': 'password',
|
'EMAIL_HOST_PASSWORD': 'password',
|
||||||
'EMAIL_HOST_USER': 'user@domain.com',
|
'EMAIL_HOST_USER': 'user@domain.com',
|
||||||
'EMAIL_PORT': 587,
|
'EMAIL_PORT': 587,
|
||||||
'EMAIL_TIMEOUT': None,
|
|
||||||
'EMAIL_USE_SSL': False,
|
'EMAIL_USE_SSL': False,
|
||||||
'EMAIL_USE_TLS': True
|
'EMAIL_USE_TLS': True
|
||||||
})
|
})
|
||||||
|
|
|
||||||
62
tox.ini
62
tox.ini
|
|
@ -2,23 +2,23 @@
|
||||||
skipsdist = true
|
skipsdist = true
|
||||||
usedevelop = true
|
usedevelop = true
|
||||||
minversion = 1.8
|
minversion = 1.8
|
||||||
|
whitelist_externals = sphinx-build
|
||||||
envlist =
|
envlist =
|
||||||
py311-checkqa
|
py36-checkqa,
|
||||||
docs
|
py{27,35,36,py}-dj111
|
||||||
py{39}-dj{32,41,42}
|
py{35,36,37,py3}-dj20
|
||||||
py{310,py310}-dj{32,41,42,50,main}
|
py{35,36,37,py3}-dj21
|
||||||
py{311}-dj{41,42,50,51,main}
|
py{35,36,37,38,py3}-dj22
|
||||||
py{312}-dj{50,51,main}
|
py{36,37,38,py3}-dj{30,master}
|
||||||
py{313}-dj{50,51,main}
|
|
||||||
|
|
||||||
[gh-actions]
|
[travis]
|
||||||
python =
|
python =
|
||||||
3.9: py39
|
2.7: py27
|
||||||
3.10: py310
|
3.5: py35
|
||||||
3.11: py311,flake8,readme
|
3.6: py36,flake8,readme
|
||||||
3.12: py312
|
3.7: py37
|
||||||
3.13: py313
|
3.8: py38
|
||||||
pypy-3.10: pypy310
|
pypy3: pypy3
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
usedevelop = true
|
usedevelop = true
|
||||||
|
|
@ -27,27 +27,24 @@ setenv =
|
||||||
DJANGO_CONFIGURATION = Test
|
DJANGO_CONFIGURATION = Test
|
||||||
COVERAGE_PROCESS_START = {toxinidir}/setup.cfg
|
COVERAGE_PROCESS_START = {toxinidir}/setup.cfg
|
||||||
deps =
|
deps =
|
||||||
dj32: django~=3.2.9
|
dj111: django>=1.11,<2.0
|
||||||
dj41: django~=4.1.3
|
dj20: django>=2.0a1,<2.1
|
||||||
dj42: django~=4.2.0
|
dj21: django>=2.1a1,<2.2
|
||||||
dj50: django~=5.0.0
|
dj22: django>=2.2a1,<3.0
|
||||||
dj51: django~=5.1.0
|
dj30: django>=3.0a1,<3.1
|
||||||
djmain: https://github.com/django/django/archive/main.tar.gz
|
djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django
|
||||||
py312: setuptools
|
py27,pypy: mock
|
||||||
py312: wheel
|
|
||||||
py313: setuptools
|
|
||||||
py313: wheel
|
|
||||||
coverage
|
coverage
|
||||||
coverage_enable_subprocess
|
coverage_enable_subprocess
|
||||||
extras = testing
|
extras = testing
|
||||||
commands =
|
commands =
|
||||||
python --version
|
python --version
|
||||||
{envbindir}/coverage run {envbindir}/django-cadmin test -v2 {posargs:tests}
|
{envbindir}/coverage run {envbindir}/django-cadmin test -v2 {posargs:tests}
|
||||||
coverage combine . tests docs
|
coverage combine . tests/docs
|
||||||
coverage report -m --skip-covered
|
coverage report -m --skip-covered
|
||||||
coverage xml
|
coverage xml
|
||||||
|
|
||||||
[testenv:py311-checkqa]
|
[testenv:py36-checkqa]
|
||||||
commands =
|
commands =
|
||||||
flake8 {toxinidir}
|
flake8 {toxinidir}
|
||||||
check-manifest -v
|
check-manifest -v
|
||||||
|
|
@ -57,16 +54,3 @@ deps =
|
||||||
flake8
|
flake8
|
||||||
twine
|
twine
|
||||||
check-manifest
|
check-manifest
|
||||||
|
|
||||||
[testenv:docs]
|
|
||||||
setenv =
|
|
||||||
deps =
|
|
||||||
-r docs/requirements.txt
|
|
||||||
commands =
|
|
||||||
sphinx-build \
|
|
||||||
-b html \
|
|
||||||
-a \
|
|
||||||
-W \
|
|
||||||
-n \
|
|
||||||
docs \
|
|
||||||
docs/_build/html
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue