From bf45358a48444682b85ed40362a1f26e04d1ae89 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:15:37 +0300 Subject: [PATCH 01/49] Bump version --- CHANGELOG.rst | 10 ++++++++-- environ/__init__.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index dd34e763..093e23b3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,8 +5,14 @@ All notable changes to this project will be documented in this file. The format is inspired by `Keep a Changelog `_ and this project adheres to `Semantic Versioning `_. -`v0.8.1`_ - 20-October-2021 +`v0.9.0`_ - 00-Unreleased-2021 +------------------------------ + +- WIP + +`v0.8.1`_ - 20-October-2021 +--------------------------- Fixed +++++ - Fixed "Invalid line" spam logs on blank lines in env file @@ -16,7 +22,7 @@ Fixed `v0.8.0`_ - 17-October-2021 ------------------------------- +--------------------------- Added +++++ - Log invalid lines when parse .env file diff --git a/environ/__init__.py b/environ/__init__.py index 97733f4d..e1d99cc4 100644 --- a/environ/__init__.py +++ b/environ/__init__.py @@ -31,7 +31,7 @@ __copyright__ = 'Copyright (C) 2021 Daniele Faraglia' -__version__ = '0.8.1' +__version__ = '0.9.0' __license__ = 'MIT' __author__ = 'Daniele Faraglia' __author_email__ = 'daniele.faraglia@gmail.com' From 38c716614bd39dceaa637fa3d2593db78c65cc9d Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:17:43 +0300 Subject: [PATCH 02/49] Update documentation --- environ/fileaware_mapping.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/environ/fileaware_mapping.py b/environ/fileaware_mapping.py index e26ee8d1..706b92cd 100644 --- a/environ/fileaware_mapping.py +++ b/environ/fileaware_mapping.py @@ -1,3 +1,13 @@ +# This file is part of the django-environ. +# +# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2013-2021, Daniele Faraglia +# +# For the full copyright and license information, please view +# the LICENSE.txt file that was distributed with this source code. + +"""Docker-style file variable support module.""" + import os from collections.abc import MutableMapping From 6832d5538400fb51b20bc03b342dbf56a32fdcdd Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:29:10 +0300 Subject: [PATCH 03/49] Bump Python version used on CI --- .github/workflows/build.yml | 8 ++++---- .github/workflows/ci.yml | 2 +- .github/workflows/cs.yml | 4 ++-- .github/workflows/docs.yml | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe711895..e594329f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,10 +29,10 @@ jobs: - name: Checkout code uses: actions/checkout@v2.3.5 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v2.2.2 with: - python-version: '3.9' + python-version: '3.10' - name: Install dependencies run: | @@ -69,10 +69,10 @@ jobs: - name: Checkout code uses: actions/checkout@v2.3.5 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v2.2.2 with: - python-version: '3.9' + python-version: '3.10' - name: Install in dev mode run: python -m pip install -e . diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c836edc9..7f30ed06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: - '3.7' - '3.8' - '3.9' - - '3.10.0-beta - 3.10' + - '3.10' - 'pypy-3.7' os: [ ubuntu-latest, macos-latest, windows-latest ] diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index e4283d10..1d486450 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -23,10 +23,10 @@ jobs: - name: Checkout code uses: actions/checkout@v2.3.5 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v2.2.2 with: - python-version: '3.9' + python-version: '3.10' - name: Install dependencies run: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5c68593c..9db9696d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,10 +23,10 @@ jobs: - name: Checkout code uses: actions/checkout@v2.3.5 - - name: Set up Python 3.8 + - name: Set up Python 3.10 uses: actions/setup-python@v2.2.2 with: - python-version: '3.8' + python-version: '3.10' - name: Install dependencies run: | From 2f7ada3d2f8942c681fbb4a3c3f7811b1022f2ac Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:29:30 +0300 Subject: [PATCH 04/49] Update RTD configuration --- .readthedocs.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 78a20224..91a97dae 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -6,13 +6,18 @@ # For the full copyright and license information, please view # the LICENSE.txt file that was distributed with this source code. +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + --- version: 2 -python: - # Keep version in sync with tox.ini (testenv:docs) and - # docs.yml (GitHub Action Workflow). - version: '3.8' +build: + os: ubuntu-20.04 + tools: + python: '3.10' + +python: install: - method: pip path: . From 67cc5411518ae99f020fdd9c1b93c1a80602eb64 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:48:46 +0300 Subject: [PATCH 05/49] Bump Python version in tox --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 4877c140..760c790b 100644 --- a/tox.ini +++ b/tox.ini @@ -74,7 +74,7 @@ commands = flake8 environ setup.py description = Check external links in the package documentation # Keep basepython in sync with .readthedocs.yml and docs.yml # (GitHub Action Workflow). -basepython = python3.8 +basepython = python3.10 extras = docs commands = {envpython} -m sphinx \ @@ -92,7 +92,7 @@ isolated_build = true description = Build package documentation (HTML) # Keep basepython in sync with .readthedocs.yml and docs.yml # (GitHub Action Workflow). -basepython = python3.8 +basepython = python3.10 extras = docs commands = {envpython} -m sphinx \ From 37a62866173ee62eec508e171d873a5ba83a7933 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:49:59 +0300 Subject: [PATCH 06/49] Fix change log --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 093e23b3..c1790c05 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -262,6 +262,7 @@ Added - Initial release. +.. _v0.9.0: https://github.com/joke2k/django-environ/compare/v0.8.1...develop .. _v0.8.1: https://github.com/joke2k/django-environ/compare/v0.8.0...v0.8.1 .. _v0.8.0: https://github.com/joke2k/django-environ/compare/v0.7.0...v0.8.0 .. _v0.7.0: https://github.com/joke2k/django-environ/compare/v0.6.0...v0.7.0 From a8682d76f455c843fd69f5bdbe12866c2326590b Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Oct 2021 23:50:23 +0300 Subject: [PATCH 07/49] Prettify tests output --- .readthedocs.yml | 2 ++ tests/test_search.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 91a97dae..27f93688 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -15,6 +15,8 @@ version: 2 build: os: ubuntu-20.04 tools: + # Keep version in sync with tox.ini (testenv:docs) and + # docs.yml (GitHub Action Workflow). python: '3.10' python: diff --git a/tests/test_search.py b/tests/test_search.py index a81471e5..0992bf98 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -43,6 +43,12 @@ def test_solr_multicore_parsing(solr_url): 'elasticsearch5_backend.Elasticsearch5SearchEngine'), ('elasticsearch7://127.0.0.1:9200/index', 'elasticsearch7_backend.Elasticsearch7SearchEngine'), + ], + ids=[ + 'elasticsearch', + 'elasticsearch2', + 'elasticsearch5', + 'elasticsearch7', ] ) def test_elasticsearch_parsing(url, engine): From dffba964c8dff92bd45c8579cb88fece944e3022 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Thu, 21 Oct 2021 01:56:31 +0300 Subject: [PATCH 08/49] Add documentation for complex format for dicts Closes #300 --- docs/tips.rst | 42 ++++++++++++++++++++++++++++++++++++++++++ tests/fixtures.py | 9 ++++++++- tests/test_env.py | 7 +++++++ tests/test_env.txt | 3 +++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/docs/tips.rst b/docs/tips.rst index dbb762fb..983b07f8 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -125,6 +125,48 @@ You can use something like this to handle similar cases. ADMINS = tuple(parseaddr(email) for email in env.list('DJANGO_ADMINS')) +Complex format for dicts +======================== + +Sometimes we need to get a bit more complex dict type than usual. For example, +consider Djangosaml2's ``SAML_ATTRIBUTE_MAPPING``: + +.. code-block:: python + + SAML_ATTRIBUTE_MAPPING = { + 'uid': ('username', ), + 'mail': ('email', ), + 'cn': ('first_name', ), + 'sn': ('last_name', ), + } + +A dict of this format can be obtained as shown below: + +**.env file**: + +.. code-block:: shell + + # .env file contents + SAML_ATTRIBUTE_MAPPING="uid=username;mail=email;cn=first_name;sn=last_name;" + +**settings.py file**: + +.. code-block:: python + + # settings.py file contents + import environ + + + env = environ.Env() + + # {'uid': ('username',), 'mail': ('email',), 'cn': ('first_name',), 'sn': ('last_name',)} + SAML_ATTRIBUTE_MAPPING = env.dict( + 'SAML_ATTRIBUTE_MAPPING', + cast={"value": tuple}, + default={} + ) + + Multiline value =============== diff --git a/tests/fixtures.py b/tests/fixtures.py index 25213ac7..c443ec9d 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -27,6 +27,12 @@ class FakeEnv: DICT = dict(foo='bar', test='on') PATH = '/home/dev' EXPORTED = 'exported var' + SAML_ATTRIBUTE_MAPPING = dict( + uid=('username', ), + mail=('email', ), + cn=('first_name', ), + sn=('last_name', ) + ) @classmethod def generate_data(cls): @@ -75,4 +81,5 @@ def generate_data(cls): URL_VAR=cls.URL, JSON_VAR=json.dumps(cls.JSON), PATH_VAR=cls.PATH, - EXPORTED_VAR=cls.EXPORTED) + EXPORTED_VAR=cls.EXPORTED, + SAML_ATTRIBUTE_MAPPING='uid=username;mail=email;cn=first_name;sn=last_name;') diff --git a/tests/test_env.py b/tests/test_env.py index 0be9a60a..59110e35 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -156,6 +156,13 @@ def test_empty_list(self): def test_dict_value(self): assert_type_and_value(dict, FakeEnv.DICT, self.env.dict('DICT_VAR')) + def test_complex_dict_value(self): + assert_type_and_value( + dict, + FakeEnv.SAML_ATTRIBUTE_MAPPING, + self.env.dict('SAML_ATTRIBUTE_MAPPING', cast={'value': tuple}) + ) + @pytest.mark.parametrize( 'value,cast,expected', [ diff --git a/tests/test_env.txt b/tests/test_env.txt index 78422b18..f326359f 100644 --- a/tests/test_env.txt +++ b/tests/test_env.txt @@ -54,5 +54,8 @@ DATABASE_ORACLE_URL=oracle://user:password@host:1521/sid DATABASE_REDSHIFT_URL=redshift://user:password@examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439/dev DATABASE_CUSTOM_BACKEND_URL=custom.backend://user:password@example.com:5430/database +# Djangosaml2's SAML_ATTRIBUTE_MAPPING +SAML_ATTRIBUTE_MAPPING="uid=username;mail=email;cn=first_name;sn=last_name;" + # Exports export EXPORTED_VAR="exported var" From 434c3f02d89b312a7799f6ac10d6e0dae27093a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Nov 2021 17:18:12 +0000 Subject: [PATCH 09/49] Bump actions/checkout from 2.3.5 to 2.4.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 2.3.5 to 2.4.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2.3.5...v2.4.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 4 ++-- .github/workflows/ci.yml | 2 +- .github/workflows/cs.yml | 2 +- .github/workflows/docs.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe711895..d66e6fb4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.5 + uses: actions/checkout@v2.4.0 - name: Set up Python 3.9 uses: actions/setup-python@v2.2.2 @@ -67,7 +67,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.5 + uses: actions/checkout@v2.4.0 - name: Set up Python 3.9 uses: actions/setup-python@v2.2.2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c836edc9..05b490ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.5 + uses: actions/checkout@v2.4.0 with: fetch-depth: 5 diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index e4283d10..fb15efb8 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.5 + uses: actions/checkout@v2.4.0 - name: Set up Python 3.9 uses: actions/setup-python@v2.2.2 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5c68593c..b75adc1d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.3.5 + uses: actions/checkout@v2.4.0 - name: Set up Python 3.8 uses: actions/setup-python@v2.2.2 From 77efc463fa75842a5ed404945d86e32629458ba3 Mon Sep 17 00:00:00 2001 From: John Carter Date: Fri, 19 Nov 2021 14:39:38 +1300 Subject: [PATCH 10/49] Typo fix This looks like it was a typo? --- docs/tips.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tips.rst b/docs/tips.rst index dbb762fb..ea74b99c 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -199,7 +199,7 @@ Escape Proxy ============ If you're having trouble with values starting with dollar sign ($) without the intention of proxying the value to -another, You should enbale the ``escape_proxy`` and prepend a backslash to it. +another, You should enable the ``escape_proxy`` and prepend a backslash to it. .. code-block:: python From 7defe30b43054b8a5ed47341f730484f2fa84c9f Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 22 Nov 2021 00:24:14 +0200 Subject: [PATCH 11/49] Bump actions/setup-python from 2.2.2 to 2.3.0 --- .github/workflows/build.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/cs.yml | 2 +- .github/workflows/docs.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1931c146..4eae6d8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v2.3.0 with: python-version: '3.10' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30bac7b6..c58dc5d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: fetch-depth: 5 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v2.3.0 with: python-version: ${{ matrix.python }} diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index 6e26bcb8..7de4818d 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v2.3.0 with: python-version: '3.10' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b088c4ec..4ade77ba 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v2.3.0 with: python-version: '3.10' From 509cf7737363a56e4464962dbae4ab72b801c7b3 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 22 Nov 2021 00:26:32 +0200 Subject: [PATCH 12/49] Rephrase documentation header --- docs/tips.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tips.rst b/docs/tips.rst index 182a205f..848f7f7a 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -125,8 +125,8 @@ You can use something like this to handle similar cases. ADMINS = tuple(parseaddr(email) for email in env.list('DJANGO_ADMINS')) -Complex format for dicts -======================== +Complex dict format +=================== Sometimes we need to get a bit more complex dict type than usual. For example, consider Djangosaml2's ``SAML_ATTRIBUTE_MAPPING``: @@ -162,7 +162,7 @@ A dict of this format can be obtained as shown below: # {'uid': ('username',), 'mail': ('email',), 'cn': ('first_name',), 'sn': ('last_name',)} SAML_ATTRIBUTE_MAPPING = env.dict( 'SAML_ATTRIBUTE_MAPPING', - cast={"value": tuple}, + cast={'value': tuple}, default={} ) From 0ca26c55c551c741fc7e327ab984a7b6bcb58809 Mon Sep 17 00:00:00 2001 From: Damir Boltachev Date: Thu, 9 Dec 2021 15:05:43 +0400 Subject: [PATCH 13/49] fix[db_url_config]: added postgres cluster DSN support (ie postgresql://username:password@host1:port1,host2:port2/database) --- environ/environ.py | 21 +++++++++++++++++++-- tests/test_db.py | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index acdfb153..efae8c86 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -12,6 +12,7 @@ """ import ast +import itertools import logging import os import re @@ -501,13 +502,29 @@ def db_url_config(cls, url, engine=None): if url.port: path += ':{port}'.format(port=url.port) + if url.scheme in cls.POSTGRES_FAMILY and ',' in url.netloc.rsplit('@', 1)[-1]: + # Parsing postgres cluster dsn + hostinfo = list( + itertools.zip_longest( + *( + host.rsplit(':', 1) + for host in url.netloc.rsplit('@', 1)[-1].split(',') + ) + ) + ) + hostname = ','.join(hostinfo[0]) + port = ','.join(filter(None, hostinfo[1])) if len(hostinfo) == 2 else '' + else: + hostname = url.hostname + port = url.port + # Update with environment configuration. config.update({ 'NAME': path or '', 'USER': _cast_urlstr(url.username) or '', 'PASSWORD': _cast_urlstr(url.password) or '', - 'HOST': url.hostname or '', - 'PORT': _cast_int(url.port) or '', + 'HOST': hostname or '', + 'PORT': _cast_int(port) or '', }) if ( diff --git a/tests/test_db.py b/tests/test_db.py index c4958504..4f06bfc6 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -40,6 +40,22 @@ 'uf07k1i6d8ia0v', 'wegauwhgeuioweg', 5431), + ('postgres://username:p@ss:12,wor:34d@host1:111,22.55.44.88:222,[2001:db8::1234]:333/db', + DJANGO_POSTGRES, + 'db', + 'host1,22.55.44.88,[2001:db8::1234]', + 'username', + 'p@ss:12,wor:34d', + '111,222,333' + ), + ('postgres://node1,node2,node3/db', + DJANGO_POSTGRES, + 'db', + 'node1,node2,node3', + '', + '', + '' + ), ('mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.' 'compute-1.amazonaws.com:5431/d8r82722r2kuvn', 'django.contrib.gis.db.backends.mysql', @@ -97,6 +113,8 @@ 'postgres', 'postgres_unix_domain', 'postgis', + 'postgres_cluster', + 'postgres_no_ports', 'mysqlgis', 'cleardb', 'mysql_no_password', From 41f9e736f4d5ae6f244692234f488d8e4c138215 Mon Sep 17 00:00:00 2001 From: Kyle Kaniecki Date: Mon, 13 Dec 2021 10:29:06 -0500 Subject: [PATCH 14/49] fix unquote vs unquote_plus issue --- environ/environ.py | 3 ++- tests/test_utils.py | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index acdfb153..eee5fe31 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -22,6 +22,7 @@ parse_qs, ParseResult, unquote_plus, + unquote, urlparse, urlunparse, ) @@ -61,7 +62,7 @@ def _cast_int(v): def _cast_urlstr(v): - return unquote_plus(v) if isinstance(v, str) else v + return unquote(v) if isinstance(v, str) else v class NoValue: diff --git a/tests/test_utils.py b/tests/test_utils.py index 523a72d3..71f59fb3 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -7,7 +7,7 @@ # the LICENSE.txt file that was distributed with this source code. import pytest -from environ.environ import _cast +from environ.environ import _cast, _cast_urlstr @pytest.mark.parametrize( @@ -20,3 +20,20 @@ def test_cast(literal): See https://github.com/joke2k/django-environ/issues/200 for details.""" assert _cast(literal) == literal + +@pytest.mark.parametrize( + "quoted_url_str,expected_unquoted_str", + [ + ("Le-%7BFsIaYnaQw%7Da2B%2F%5BV8bS+", "Le-{FsIaYnaQw}a2B/[V8bS+"), + ("my_test-string+", "my_test-string+"), + ("my%20test%20string+", "my test string+") + ] +) +def test_cast_urlstr(quoted_url_str, expected_unquoted_str): + """Make sure that a url str that contains plus sign literals does not get unquoted incorrectly + Plus signs should not be converted to spaces, since spaces are encoded with %20 in URIs + + see https://github.com/joke2k/django-environ/issues/200 for details. + related to https://github.com/joke2k/django-environ/pull/69""" + + assert _cast_urlstr(quoted_url_str) == expected_unquoted_str From 88c0dacc7e3e4c98c426a3e8634b6a077da34608 Mon Sep 17 00:00:00 2001 From: Kyle Kaniecki Date: Mon, 13 Dec 2021 10:30:10 -0500 Subject: [PATCH 15/49] fix issue link in test --- tests/test_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 71f59fb3..f32d7cc3 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -33,7 +33,7 @@ def test_cast_urlstr(quoted_url_str, expected_unquoted_str): """Make sure that a url str that contains plus sign literals does not get unquoted incorrectly Plus signs should not be converted to spaces, since spaces are encoded with %20 in URIs - see https://github.com/joke2k/django-environ/issues/200 for details. + see https://github.com/joke2k/django-environ/issues/357 for details. related to https://github.com/joke2k/django-environ/pull/69""" assert _cast_urlstr(quoted_url_str) == expected_unquoted_str From f253eb31f5827ac06db5a20722363789e01db053 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff Date: Thu, 30 Dec 2021 16:42:35 +0100 Subject: [PATCH 16/49] Attach cause to ImproperlyConfigured exception - The test only asserts that the `__cause__` attribute is set to anything but `None`. This asserts that Python has attached a cause, but does not constrain the exception type to allow for compatible changes. --- environ/environ.py | 4 ++-- tests/test_env.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index acdfb153..5eb6b781 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -365,10 +365,10 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False): try: value = self.ENVIRON[var] - except KeyError: + except KeyError as exc: if default is self.NOTSET: error_msg = "Set the {} environment variable".format(var) - raise ImproperlyConfigured(error_msg) + raise ImproperlyConfigured(error_msg) from exc value = default diff --git a/tests/test_env.py b/tests/test_env.py index 59110e35..d1547088 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -45,6 +45,7 @@ def test_not_present_without_default(self): with pytest.raises(ImproperlyConfigured) as excinfo: self.env('not_present') assert str(excinfo.value) == 'Set the not_present environment variable' + assert excinfo.value.__cause__ is not None def test_contains(self): assert 'STR_VAR' in self.env From 3ea5d6908d80672a6b35b6452e5b81cc52652faf Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 2 Jan 2022 17:14:04 +0200 Subject: [PATCH 17/49] Fix order of imported names --- environ/environ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environ/environ.py b/environ/environ.py index eee5fe31..0123c02a 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -21,8 +21,8 @@ from urllib.parse import ( parse_qs, ParseResult, - unquote_plus, unquote, + unquote_plus, urlparse, urlunparse, ) From 0a508ceac7b5ee365b11f562e45fb5c25c831b1d Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 2 Jan 2022 17:52:27 +0200 Subject: [PATCH 18/49] Reduce line length --- environ/environ.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index efae8c86..bb1d6104 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -502,13 +502,14 @@ def db_url_config(cls, url, engine=None): if url.port: path += ':{port}'.format(port=url.port) - if url.scheme in cls.POSTGRES_FAMILY and ',' in url.netloc.rsplit('@', 1)[-1]: + user_host = url.netloc.rsplit('@', 1) + if url.scheme in cls.POSTGRES_FAMILY and ',' in user_host[-1]: # Parsing postgres cluster dsn hostinfo = list( itertools.zip_longest( *( host.rsplit(':', 1) - for host in url.netloc.rsplit('@', 1)[-1].split(',') + for host in user_host[-1].split(',') ) ) ) From 6e74baa245d3ef71a3fe907432f86163d94cd449 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 2 Jan 2022 17:55:21 +0200 Subject: [PATCH 19/49] Reduce line length --- environ/environ.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index bb1d6104..324eec6a 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -505,7 +505,7 @@ def db_url_config(cls, url, engine=None): user_host = url.netloc.rsplit('@', 1) if url.scheme in cls.POSTGRES_FAMILY and ',' in user_host[-1]: # Parsing postgres cluster dsn - hostinfo = list( + hinfo = list( itertools.zip_longest( *( host.rsplit(':', 1) @@ -513,8 +513,8 @@ def db_url_config(cls, url, engine=None): ) ) ) - hostname = ','.join(hostinfo[0]) - port = ','.join(filter(None, hostinfo[1])) if len(hostinfo) == 2 else '' + hostname = ','.join(hinfo[0]) + port = ','.join(filter(None, hinfo[1])) if len(hinfo) == 2 else '' else: hostname = url.hostname port = url.port From 248590f8b8ac0aeb6ce2315b258920ca77aa1925 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 2 Jan 2022 18:21:00 +0200 Subject: [PATCH 20/49] Update change log --- CHANGELOG.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c1790c05..0be2bc57 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,8 +7,22 @@ and this project adheres to `Semantic Versioning `. + -- WIP +Fixed ++++++ +- Fix ``_cast_urlstr`` unquoting + `#357 `. + + +Changed ++++++++ +- Attach cause to ``ImproperlyConfigured`` exception + `#360 `. `v0.8.1`_ - 20-October-2021 From e961c48a943aeaf11bb0936e5bd6fffa592e389c Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 2 Jan 2022 18:21:56 +0200 Subject: [PATCH 21/49] Update change log --- CHANGELOG.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0be2bc57..3c05fcff 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,19 +10,19 @@ and this project adheres to `Semantic Versioning `. + `#355 `_. Fixed +++++ - Fix ``_cast_urlstr`` unquoting - `#357 `. + `#357 `_. Changed +++++++ - Attach cause to ``ImproperlyConfigured`` exception - `#360 `. + `#360 `_. `v0.8.1`_ - 20-October-2021 From a99b54ccd562b7d86e3ed77898b20583fb8dc213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Poni=C5=84ski?= Date: Fri, 28 Jan 2022 16:41:17 +0100 Subject: [PATCH 22/49] Support prefix in environ.Env --- environ/environ.py | 8 +++++--- tests/fixtures.py | 12 +++++++----- tests/test_env.py | 4 ++++ tests/test_env.txt | 3 +++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index a6e19d03..b69c7b94 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -171,6 +171,7 @@ class Env: def __init__(self, **scheme): self.smart_cast = True self.escape_proxy = False + self.prefix = "" self.scheme = scheme def __call__(self, var, cast=None, default=NOTSET, parse_default=False): @@ -344,8 +345,9 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False): var, cast, default )) - if var in self.scheme: - var_info = self.scheme[var] + var_name = "{}{}".format(self.prefix, var) + if var_name in self.scheme: + var_info = self.scheme[var_name] try: has_default = len(var_info) == 2 @@ -366,7 +368,7 @@ def get_value(self, var, cast=None, default=NOTSET, parse_default=False): cast = var_info try: - value = self.ENVIRON[var] + value = self.ENVIRON[var_name] except KeyError as exc: if default is self.NOTSET: error_msg = "Set the {} environment variable".format(var) diff --git a/tests/fixtures.py b/tests/fixtures.py index c443ec9d..f87877e9 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -28,10 +28,10 @@ class FakeEnv: PATH = '/home/dev' EXPORTED = 'exported var' SAML_ATTRIBUTE_MAPPING = dict( - uid=('username', ), - mail=('email', ), - cn=('first_name', ), - sn=('last_name', ) + uid=('username',), + mail=('email',), + cn=('first_name',), + sn=('last_name',) ) @classmethod @@ -82,4 +82,6 @@ def generate_data(cls): JSON_VAR=json.dumps(cls.JSON), PATH_VAR=cls.PATH, EXPORTED_VAR=cls.EXPORTED, - SAML_ATTRIBUTE_MAPPING='uid=username;mail=email;cn=first_name;sn=last_name;') + SAML_ATTRIBUTE_MAPPING='uid=username;mail=email;cn=first_name;sn=last_name;', + PREFIX_TEST='foo', + ) diff --git a/tests/test_env.py b/tests/test_env.py index d1547088..8c4d0525 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -312,6 +312,10 @@ def test_smart_cast(self): def test_exported(self): assert self.env('EXPORTED_VAR') == FakeEnv.EXPORTED + def test_prefix(self): + self.env.prefix = 'PREFIX_' + assert self.env('TEST') == 'foo' + class TestFileEnv(TestEnv): def setup_method(self, method): diff --git a/tests/test_env.txt b/tests/test_env.txt index f326359f..48cd6127 100644 --- a/tests/test_env.txt +++ b/tests/test_env.txt @@ -59,3 +59,6 @@ SAML_ATTRIBUTE_MAPPING="uid=username;mail=email;cn=first_name;sn=last_name;" # Exports export EXPORTED_VAR="exported var" + +# Prefixed +PREFIX_TEST='foo' From 91f72e6619dc292a68e063d349f205923aee7dd0 Mon Sep 17 00:00:00 2001 From: Michael Heumann Date: Thu, 17 Mar 2022 23:12:25 +0100 Subject: [PATCH 23/49] Update tips to work with Python3 --- docs/tips.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/tips.rst b/docs/tips.rst index dbb762fb..af29c0d7 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -42,7 +42,8 @@ the example ``docker-compose.yml`` for would contain: Using unsafe characters in URLs =============================== -In order to use unsafe characters you have to encode with ``urllib.parse.encode`` before you set into ``.env`` file. +In order to use unsafe characters you have to encode with ``urllib.parse.quote`` before you set into ``.env`` file. +Encode only the value (i.e. the password) not the whole url. .. code-block:: shell From 098c8376f890ae5eec071bc3f0eae34beed83995 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 6 Apr 2022 15:20:28 +0000 Subject: [PATCH 24/49] Add support for Django 4.0 Fix #371 --- README.rst | 2 +- setup.py | 1 + tox.ini | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 99a52ef1..6f0976a3 100644 --- a/README.rst +++ b/README.rst @@ -127,7 +127,7 @@ the code on `GitHub `_, and the latest release on `PyPI `_. It’s rigorously tested on Python 3.5+, and officially supports -Django 1.11, 2.2, 3.0, 3.1 and 3.2. +Django 1.11, 2.2, 3.0, 3.1, 3.2 and 4.0. If you'd like to contribute to ``django-environ`` you're most welcome! diff --git a/setup.py b/setup.py index 24e8a85f..4de0becc 100644 --- a/setup.py +++ b/setup.py @@ -130,6 +130,7 @@ def get_version_string(): 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', + 'Framework :: Django :: 4.0', 'Operating System :: OS Independent', diff --git a/tox.ini b/tox.ini index 4877c140..76dcbe28 100644 --- a/tox.ini +++ b/tox.ini @@ -20,7 +20,8 @@ envlist = manifest py{35,36,37,38,39,310}-django{111,22} py{36,37,38,39,310}-django{30,31,32} - pypy-django{111,22,30,31,32} + py{38,39,310}-django{40} + pypy-django{111,22,30,31,32,40} [gh-actions] python = @@ -41,6 +42,7 @@ deps = django30: Django>=3.0,<3.1 django31: Django>=3.1,<3.2 django32: Django>=3.2,<3.3 + django40: Django>=4.0,<4.1 commands_pre = python -m pip install --upgrade pip python -m pip install . From e40eecd69ce3a37ec2a4d914b96414c541ba8507 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Wed, 6 Apr 2022 15:36:35 +0000 Subject: [PATCH 25/49] Remove Django 4.0 from pypy --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 76dcbe28..f5530f31 100644 --- a/tox.ini +++ b/tox.ini @@ -21,7 +21,7 @@ envlist = py{35,36,37,38,39,310}-django{111,22} py{36,37,38,39,310}-django{30,31,32} py{38,39,310}-django{40} - pypy-django{111,22,30,31,32,40} + pypy-django{111,22,30,31,32} [gh-actions] python = From f8fb3687b31212ba87d71914c32449533ffc76fa Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 13 Jun 2022 18:27:42 +0200 Subject: [PATCH 26/49] Bump actions/setup-python to 4.0.0 --- .github/workflows/build.yml | 4 ++-- .github/workflows/ci.yml | 2 +- .github/workflows/cs.yml | 2 +- .github/workflows/docs.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4eae6d8b..4561cbf0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.3.0 + uses: actions/setup-python@v4.0.0 with: python-version: '3.10' @@ -70,7 +70,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.2.2 + uses: actions/setup-python@v4.0.0 with: python-version: '3.10' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c58dc5d8..0b0156dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: fetch-depth: 5 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2.3.0 + uses: actions/setup-python@v4.0.0 with: python-version: ${{ matrix.python }} diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index 7de4818d..1e8fa278 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.3.0 + uses: actions/setup-python@v4.0.0 with: python-version: '3.10' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4ade77ba..8023534b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -24,7 +24,7 @@ jobs: uses: actions/checkout@v2.4.0 - name: Set up Python 3.10 - uses: actions/setup-python@v2.3.0 + uses: actions/setup-python@v4.0.0 with: python-version: '3.10' From c1a81ab91b1535814f8f6a69af93cd45c088e129 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 13 Jun 2022 18:28:48 +0200 Subject: [PATCH 27/49] Bump actions/checkout to 3.0.2 --- .github/workflows/build.yml | 4 ++-- .github/workflows/ci.yml | 2 +- .github/workflows/cs.yml | 2 +- .github/workflows/docs.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4561cbf0..29588a7e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3.0.2 - name: Set up Python 3.10 uses: actions/setup-python@v4.0.0 @@ -67,7 +67,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3.0.2 - name: Set up Python 3.10 uses: actions/setup-python@v4.0.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b0156dc..7c5d59f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3.0.2 with: fetch-depth: 5 diff --git a/.github/workflows/cs.yml b/.github/workflows/cs.yml index 1e8fa278..bf4e81fd 100644 --- a/.github/workflows/cs.yml +++ b/.github/workflows/cs.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3.0.2 - name: Set up Python 3.10 uses: actions/setup-python@v4.0.0 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8023534b..cedc3267 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2.4.0 + uses: actions/checkout@v3.0.2 - name: Set up Python 3.10 uses: actions/setup-python@v4.0.0 From 474f56cb9ddd1f3156453705553bc274cf301a91 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 13 Jun 2022 18:29:47 +0200 Subject: [PATCH 28/49] Bump actions/upload-artifact to 3 --- .github/workflows/build.yml | 2 +- .github/workflows/docs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 29588a7e..5b12957e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: - name: Archive build artifacts if: success() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: # To ensure that jobs don't overwrite existing artifacts, # use a different name per job. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index cedc3267..f89231e1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -41,7 +41,7 @@ jobs: - name: Archive docs artifacts if: always() - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: docs path: docs From 02a2f37432581f783b18a6d4b393f7f348c1f38b Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 13 Jun 2022 18:40:26 +0200 Subject: [PATCH 29/49] Do not use no longer needed pip feature "in-tree-build" --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b12957e..c2769ef2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip --use-feature=in-tree-build install tox tox-gh-actions + pip install tox tox-gh-actions - name: Check MANIFEST.in for completeness run: tox -e manifest From df11c285b632ce810232d36d57008fe94187b3e7 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Mon, 13 Jun 2022 20:03:46 +0200 Subject: [PATCH 30/49] Update change log --- CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3c05fcff..a1731fe8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,12 +11,16 @@ Added +++++ - Added support for Postgresql cluster URI `#355 `_. +- Added support for Django 4.0 + `#376 `_. Fixed +++++ - Fix ``_cast_urlstr`` unquoting `#357 `_. +- Fix documentation regarding unsafe characters in URLs + `#220 `_. Changed From e1fa7cc88665e4bd6cb1e578bba919ac263a10cf Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 00:21:45 +0200 Subject: [PATCH 31/49] Update docuemntation --- CHANGELOG.rst | 2 ++ docs/tips.rst | 31 ++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a1731fe8..26b273dc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -13,6 +13,8 @@ Added `#355 `_. - Added support for Django 4.0 `#376 `_. +- Added support for prefixed variables + `#362 `_. Fixed diff --git a/docs/tips.rst b/docs/tips.rst index 09190f14..1c7dd6b3 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -317,7 +317,7 @@ It is possible to use of ``pathlib.Path`` objects when reading environment file Overwriting existing environment values from env files ------------------------------------------------------ -If you want variables set within your env files to take higher precidence than +If you want variables set within your env files to take higher precedence than an existing set environment variable, use the ``overwrite=True`` argument of ``read_env``. For example: @@ -325,3 +325,32 @@ an existing set environment variable, use the ``overwrite=True`` argument of env = environ.Env() env.read_env(BASE_DIR('.env'), overwrite=True) + + +Handling prefixes +================= + +Sometimes it is desirable to be able to prefix all environment variables. For +example, if you are using Django, you may want to prefix all environment +variables with ``DJANGO_``. This can be done by setting the ``prefix`` +to desired prefix. For example: + +**.env file**: + +.. code-block:: shell + + # .env file contents + DJANGO_TEST="foo" + +**settings.py file**: + +.. code-block:: python + + # settings.py file contents + import environ + + + env = environ.Env() + env.prefix = 'DJANGO_' + + env.str('TEST') # foo From 2b201809167f7cc3892ca3e6212be7536686128c Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 01:07:36 +0200 Subject: [PATCH 32/49] Amend tests --- tests/test_env.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_env.py b/tests/test_env.py index 8c4d0525..47ee8c6c 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -140,11 +140,16 @@ def test_int_list(self): assert_type_and_value(list, [42, 33], self.env('INT_LIST', cast=[int])) assert_type_and_value(list, [42, 33], self.env.list('INT_LIST', int)) - def test_int_tuple(self): + def test_int_list_cast_tuple(self): assert_type_and_value(tuple, (42, 33), self.env('INT_LIST', cast=(int,))) assert_type_and_value(tuple, (42, 33), self.env.tuple('INT_LIST', int)) assert_type_and_value(tuple, ('42', '33'), self.env.tuple('INT_LIST')) + def test_int_tuple(self): + assert_type_and_value(tuple, (42, 33), self.env('INT_TUPLE', cast=(int,))) + assert_type_and_value(tuple, (42, 33), self.env.tuple('INT_TUPLE', int)) + assert_type_and_value(tuple, ('42', '33'), self.env.tuple('INT_TUPLE')) + def test_str_list_with_spaces(self): assert_type_and_value(list, [' foo', ' bar'], self.env('STR_LIST_WITH_SPACES', cast=[str])) From bdc48f5abe2fd4370b5d0988bb8dd2feb7afc0d1 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 01:23:14 +0200 Subject: [PATCH 33/49] Update change log --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 26b273dc..a60149da 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,7 +12,7 @@ Added - Added support for Postgresql cluster URI `#355 `_. - Added support for Django 4.0 - `#376 `_. + `#371 `_. - Added support for prefixed variables `#362 `_. From 1ef4926659ac668a1d307ff5f2d64e01d922f5f7 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 03:22:57 +0200 Subject: [PATCH 34/49] Add test for issue #387 --- tests/fixtures.py | 3 ++- tests/test_env.py | 11 ++++++++++- tests/test_env.txt | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index f87877e9..c7f8a0be 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -63,6 +63,7 @@ def generate_data(cls): ESCAPED_VAR=r'\$baz', INT_LIST='42,33', INT_TUPLE='(42,33)', + MIX_TUPLE='(42,Test)', STR_LIST_WITH_SPACES=' foo, bar', EMPTY_LIST='', DICT_VAR='foo=bar,test=on', diff --git a/tests/test_env.py b/tests/test_env.py index 47ee8c6c..cfde92e0 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -150,6 +150,15 @@ def test_int_tuple(self): assert_type_and_value(tuple, (42, 33), self.env.tuple('INT_TUPLE', int)) assert_type_and_value(tuple, ('42', '33'), self.env.tuple('INT_TUPLE')) + def test_mix_tuple_issue_387(self): + """Cast a tuple of mixed types. + + Casts a string like "(42,Test)" to a tuple like (42, 'Test'). + See: https://github.com/joke2k/django-environ/issues/387 for details.""" + caster = lambda v: int(v) if v.isdigit() else v.strip() + cast = lambda t: tuple(map(caster, [c for c in t.strip('()').split(',')])) + assert_type_and_value(tuple, (42, 'Test'), self.env( 'MIX_TUPLE', default=(0, ''), cast=cast)) + def test_str_list_with_spaces(self): assert_type_and_value(list, [' foo', ' bar'], self.env('STR_LIST_WITH_SPACES', cast=[str])) diff --git a/tests/test_env.txt b/tests/test_env.txt index 48cd6127..69dc84e0 100644 --- a/tests/test_env.txt +++ b/tests/test_env.txt @@ -49,6 +49,7 @@ MULTILINE_ESCAPED_STR_VAR=---BEGIN---\\n---END--- INT_LIST=42,33 CYRILLIC_VAR=фуубар INT_TUPLE=(42,33) +MIX_TUPLE=(42,Test) DATABASE_ORACLE_TNS_URL=oracle://user:password@sid DATABASE_ORACLE_URL=oracle://user:password@host:1521/sid DATABASE_REDSHIFT_URL=redshift://user:password@examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439/dev From 90e75e0c63260fb01c5e272d002efacc6df3fca9 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 10:49:54 +0200 Subject: [PATCH 35/49] Deprecate Env.unicode --- CHANGELOG.rst | 18 ++++++++++++------ environ/environ.py | 15 ++++++++++++--- tests/test_env.py | 13 +++++++++++++ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a60149da..11155de6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,12 +17,10 @@ Added `#362 `_. -Fixed -+++++ -- Fix ``_cast_urlstr`` unquoting - `#357 `_. -- Fix documentation regarding unsafe characters in URLs - `#220 `_. +Deprecated +++++++++++ +- ``Env.unicode()`` is deprecated and will be removed in future, + use ``Env.str()`` instead. Changed @@ -31,6 +29,14 @@ Changed `#360 `_. +Fixed ++++++ +- Fix ``_cast_urlstr`` unquoting + `#357 `_. +- Fix documentation regarding unsafe characters in URLs + `#220 `_. + + `v0.8.1`_ - 20-October-2021 --------------------------- Fixed diff --git a/environ/environ.py b/environ/environ.py index b69c7b94..eec976e0 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -200,6 +200,15 @@ def unicode(self, var, default=NOTSET): """Helper for python2 :rtype: unicode """ + warnings.warn( + '`%s.unicode` is deprecated, use `%s.str` instead' % ( + self.__class__.__name__, + self.__class__.__name__, + ), + DeprecationWarning, + stacklevel=2 + ) + return self.get_value(var, cast=str, default=default) def bytes(self, var, default=NOTSET, encoding='utf8'): @@ -780,7 +789,7 @@ def search_url_config(cls, url, engine=None): @classmethod def read_env(cls, env_file=None, overwrite=False, **overrides): - """Read a .env file into os.environ. + r"""Read a .env file into os.environ. If not given a path to a dotenv path, does filthy magic stack backtracking to find the dotenv in the same directory as the file that @@ -794,12 +803,12 @@ def read_env(cls, env_file=None, overwrite=False, **overrides): - https://wellfire.co/learn/easier-12-factor-django - https://gist.github.com/bennylope/2999704 - :param env_file: The path to the `.env` file your application should + :param env_file: The path to the ``.env`` file your application should use. If a path is not provided, `read_env` will attempt to import the Django settings module from the Django project root. :param overwrite: ``overwrite=True`` will force an overwrite of existing environment variables. - :param **overrides: Any additional keyword arguments provided directly + :param \**overrides: Any additional keyword arguments provided directly to read_env will be added to the environment. If the key matches an existing environment variable, the value will be overridden. """ diff --git a/tests/test_env.py b/tests/test_env.py index cfde92e0..1d814d0b 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -8,6 +8,7 @@ import os from urllib.parse import quote +from warnings import catch_warnings import pytest @@ -70,6 +71,18 @@ def test_str(self, var, val, multiline): assert self.env(var) == val assert self.env.str(var, multiline=multiline) == val + def test_unicode(self, recwarn): + assert self.env.unicode('CYRILLIC_VAR', default='фуубар') == 'фуубар' + assert len(recwarn) == 1 + w = recwarn.pop(DeprecationWarning) + assert issubclass(w.category, DeprecationWarning) + assert str(w.message) == '`%s.unicode` is deprecated, use `%s.str` instead' %( + self.env.__class__.__name__, + self.env.__class__.__name__, + ) + assert w.filename + assert w.lineno + @pytest.mark.parametrize( 'var,val,default', [ From 4ad80a072c8239ebbbf15757f03999fb696b469f Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 10:51:22 +0200 Subject: [PATCH 36/49] Update documentation --- AUTHORS.rst | 2 +- README.rst | 4 ++-- docs/Makefile | 2 +- docs/api.rst | 45 ++++++++++++++++++++++++++++++++++++ docs/conf.py | 38 +++++++++++++++++++++++++----- docs/deprecations.rst | 12 ++++++++++ docs/docutils.conf | 2 +- docs/getting-started.rst | 6 ++--- docs/index.rst | 4 ++++ docs/tips.rst | 11 +++++---- environ/__init__.py | 6 ++--- environ/compat.py | 4 ++-- environ/environ.py | 2 +- environ/fileaware_mapping.py | 6 ++--- 14 files changed, 116 insertions(+), 28 deletions(-) create mode 100644 docs/api.rst create mode 100644 docs/deprecations.rst diff --git a/AUTHORS.rst b/AUTHORS.rst index 6bf39824..0a910fa7 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -13,7 +13,7 @@ The existence of ``django-environ`` would have been impossible without these projects: - `rconradharris/envparse `_ -- `jacobian/dj-database-url `_ +- `jazzband/dj-database-url `_ - `migonzalvar/dj-email-url `_ - `ghickman/django-cache-url `_ - `dstufft/dj-search-url `_ diff --git a/README.rst b/README.rst index 6f0976a3..256f0a16 100644 --- a/README.rst +++ b/README.rst @@ -104,8 +104,8 @@ article. Using ``django-environ`` you can stop to make a lot of unversioned ``settings_*.py`` to configure your app. -See `cookiecutter-django `_ for -a concrete example on using with a django project. +See `cookiecutter-django `_ +for a concrete example on using with a django project. **Feature Support** diff --git a/docs/Makefile b/docs/Makefile index 2b50c273..887c0bba 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 00000000..db2db6db --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,45 @@ +============= +API Reference +============= + +.. currentmodule:: environ + + +The ``__init__`` module +======================= + +.. automodule:: environ + :members: + :no-undoc-members: + + +The ``compat`` module +====================== + +.. automodule:: environ.compat + :members: + :no-undoc-members: + + +The ``environ`` module +====================== + +.. autoclass:: environ.Env + :members: + :no-undoc-members: + +.. autoclass:: environ.FileAwareEnv + :members: + :no-undoc-members: + +.. autoclass:: environ.Path + :members: + :no-undoc-members: + + +The ``fileaware_mapping`` module +================================ + +.. autoclass:: environ.fileaware_mapping.FileAwareMapping + :members: + :no-undoc-members: diff --git a/docs/conf.py b/docs/conf.py index 17536146..53f6bd67 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -12,9 +12,14 @@ import codecs import os +import sys import re +PROJECT_DIR = os.path.abspath('..') +sys.path.insert(0, PROJECT_DIR) + + def read_file(filepath): """Read content from a UTF-8 encoded text file.""" with codecs.open(filepath, 'rb', 'utf-8') as file_handle: @@ -23,9 +28,7 @@ def read_file(filepath): def find_version(meta_file): """Extract ``__version__`` from meta_file.""" - here = os.path.abspath(os.path.dirname(__file__)) - contents = read_file(os.path.join(here, meta_file)) - + contents = read_file(os.path.join(PROJECT_DIR, meta_file)) meta_match = re.search( r"^__version__\s+=\s+['\"]([^'\"]*)['\"]", contents, @@ -56,6 +59,7 @@ def find_version(meta_file): "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.todo", + "sphinx.ext.viewcode", "notfound.extension", ] @@ -75,7 +79,7 @@ def find_version(meta_file): # The version info # The short X.Y version. -release = find_version('../environ/__init__.py') +release = find_version('environ/__init__.py') version = release.rsplit(u".", 1)[0] # The full version, including alpha/beta/rc tags. @@ -85,11 +89,33 @@ def find_version(meta_file): # The reST default role (used for this markup: `text`) to use for all # documents. -default_role = "any" +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True +# +# -- Options for linkcheck ---------------------------------------------------- +# + +linkcheck_ignore = [ + # We run into GitHub's rate limits. + r"https://github.com/.*/(issues|pull)/\d+", +] + +# +# -- Options for nitpick ---------------------------------------------------- +# + +nitpicky = True + +# In nitpick mode (-n), still ignore any of the following "broken" references +# to non-types. +nitpick_ignore = [ + ('py:class', 'file'), + ('py:class', 'urlparse.ParseResult'), +] + # # -- Options for extlinks ---------------------------------------------------- # diff --git a/docs/deprecations.rst b/docs/deprecations.rst new file mode 100644 index 00000000..73189118 --- /dev/null +++ b/docs/deprecations.rst @@ -0,0 +1,12 @@ +============ +Deprecations +============ + +Features deprecated in 0.9.0 +============================ + +Methods +------- + +* The :meth:`.environ.Env.unicode` method is deprecated as it was used + for Python 2.x only. Use :meth:`.environ.Env.str` instead. diff --git a/docs/docutils.conf b/docs/docutils.conf index 841ea73e..4ed0bf5a 100644 --- a/docs/docutils.conf +++ b/docs/docutils.conf @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view diff --git a/docs/getting-started.rst b/docs/getting-started.rst index 007e92f3..98c25282 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -10,7 +10,7 @@ Requirements ------------ * `Django `_ >= 1.11 -* `Python `_ >= 3.4 +* `Python `_ >= 3.5 Installing django-environ _________________________ @@ -52,7 +52,7 @@ it to your system. More information about ``pip`` and PyPI can be found here: * `Install pip `_ -* `Python Packaging User Guide `_ +* `Python Packaging User Guide `_ Usage ===== @@ -112,7 +112,7 @@ FAQ #. **Can django-environ determine the location of .env file automatically?** ``django-environ`` will try to get and read ``.env`` file from the project - root if you haven't specified the path for it when call ``read_env``. + root if you haven't specified the path for it when call :meth:`.environ.Env.read_env`. However, this is not the recommended way. When it is possible always specify the path tho ``.env`` file. Alternatively, you can use a trick with a environment variable pointing to the actual location of .env file. diff --git a/docs/index.rst b/docs/index.rst index 08aaf750..fca07a98 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,3 +1,5 @@ +.. module:: environ + ======================================= Welcome to django-environ documentation ======================================= @@ -38,6 +40,7 @@ Full Table of Contents tips contributing backers + api .. include:: ../README.rst :start-after: -project-information- @@ -47,4 +50,5 @@ Full Table of Contents :maxdepth: 1 license + deprecations changelog diff --git a/docs/tips.rst b/docs/tips.rst index 1c7dd6b3..3b45ddb1 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -10,7 +10,7 @@ Docker (swarm) and Kubernetes are two widely used platforms that store their secrets in tmpfs inside containers as individual files, providing a secure way to be able to share configuration data between containers. -Use ``environ.FileAwareEnv`` rather than ``environ.Env`` to first look for +Use :class:`.environ.FileAwareEnv` rather than :class:`.environ.Env` to first look for environment variables with ``_FILE`` appended. If found, their contents will be read from the file system and used instead. @@ -42,8 +42,8 @@ the example ``docker-compose.yml`` for would contain: Using unsafe characters in URLs =============================== -In order to use unsafe characters you have to encode with ``urllib.parse.quote`` before you set into ``.env`` file. -Encode only the value (i.e. the password) not the whole url. +In order to use unsafe characters you have to encode with :py:func:`urllib.parse.quote` +before you set into ``.env`` file. Encode only the value (i.e. the password) not the whole url. .. code-block:: shell @@ -292,7 +292,8 @@ while ``./manage.py runserver`` uses ``.env``. Using Path objects when reading env ----------------------------------- -It is possible to use of ``pathlib.Path`` objects when reading environment file from the filesystem: +It is possible to use of :py:class:`pathlib.Path` objects when reading environment +file from the filesystem: .. code-block:: python @@ -319,7 +320,7 @@ Overwriting existing environment values from env files If you want variables set within your env files to take higher precedence than an existing set environment variable, use the ``overwrite=True`` argument of -``read_env``. For example: +:meth:`.environ.Env.read_env`. For example: .. code-block:: python diff --git a/environ/__init__.py b/environ/__init__.py index e1d99cc4..338fb244 100644 --- a/environ/__init__.py +++ b/environ/__init__.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -23,9 +23,9 @@ __url__ __description__ -Refer to the `documentation `_ +Refer to the `documentation `_ for details on the use of this package. -""" +""" # noqa: E501 from .environ import * diff --git a/environ/compat.py b/environ/compat.py index 8c259f85..a4ae3666 100644 --- a/environ/compat.py +++ b/environ/compat.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -39,8 +39,8 @@ class ImproperlyConfigured(Exception): REDIS_DRIVER = 'django_redis.cache.RedisCache' -# back compatibility for pymemcache def choose_pymemcache_driver(): + """Backward compatibility for pymemcache.""" old_django = DJANGO_VERSION is not None and DJANGO_VERSION < (3, 2) if old_django or not find_loader('pymemcache'): # The original backend choice for the 'pymemcache' scheme is diff --git a/environ/environ.py b/environ/environ.py index eec976e0..3d0102d5 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view diff --git a/environ/fileaware_mapping.py b/environ/fileaware_mapping.py index 706b92cd..6da95f58 100644 --- a/environ/fileaware_mapping.py +++ b/environ/fileaware_mapping.py @@ -1,6 +1,6 @@ # This file is part of the django-environ. # -# Copyright (c) 2021, Serghei Iakovlev +# Copyright (c) 2021-2022, Serghei Iakovlev # Copyright (c) 2013-2021, Daniele Faraglia # # For the full copyright and license information, please view @@ -14,14 +14,14 @@ class FileAwareMapping(MutableMapping): """ - A mapping that wraps os.environ, first checking for the existance of a key + A mapping that wraps os.environ, first checking for the existence of a key appended with ``_FILE`` whenever reading a value. If a matching file key is found then the value is instead read from the file system at this location. By default, values read from the file system are cached so future lookups do not hit the disk again. - A ``_FILE`` key has higher precidence than a value is set directly in the + A ``_FILE`` key has higher precedence than a value is set directly in the environment, and an exception is raised if the file can not be found. """ From 712bf39efe63fd7d5e8f99d3a1edab1baa42e84f Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 11:07:24 +0200 Subject: [PATCH 37/49] Correct test_unicode --- tests/test_env.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_env.py b/tests/test_env.py index 1d814d0b..b5c1e397 100644 --- a/tests/test_env.py +++ b/tests/test_env.py @@ -72,7 +72,10 @@ def test_str(self, var, val, multiline): assert self.env.str(var, multiline=multiline) == val def test_unicode(self, recwarn): - assert self.env.unicode('CYRILLIC_VAR', default='фуубар') == 'фуубар' + actual = self.env.unicode('CYRILLIC_VAR', default='фуубар') + expected = self.env.str('CYRILLIC_VAR', default='фуубар') + + assert actual == expected assert len(recwarn) == 1 w = recwarn.pop(DeprecationWarning) assert issubclass(w.category, DeprecationWarning) From 452b8bb82a7f13031ef33df13aca5e9e2c9a9cb3 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 12:56:05 +0200 Subject: [PATCH 38/49] Update documentation --- CHANGELOG.rst | 4 ++-- docs/conf.py | 4 ---- docs/getting-started.rst | 14 ++++++++++---- environ/__init__.py | 18 +++++++++--------- environ/environ.py | 13 ++++++------- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 11155de6..5c586d2a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,8 +19,8 @@ Added Deprecated ++++++++++ -- ``Env.unicode()`` is deprecated and will be removed in future, - use ``Env.str()`` instead. +- ``Env.unicode()`` is deprecated and will be removed in the next + major release. Use ``Env.str()`` instead. Changed diff --git a/docs/conf.py b/docs/conf.py index 53f6bd67..1c5390ad 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -107,13 +107,9 @@ def find_version(meta_file): # -- Options for nitpick ---------------------------------------------------- # -nitpicky = True - # In nitpick mode (-n), still ignore any of the following "broken" references # to non-types. nitpick_ignore = [ - ('py:class', 'file'), - ('py:class', 'urlparse.ParseResult'), ] # diff --git a/docs/getting-started.rst b/docs/getting-started.rst index 98c25282..e7056fa1 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -15,8 +15,9 @@ Requirements Installing django-environ _________________________ -``django-environ`` is a Python-only package `hosted on PyPI `_. -The recommended installation method is `pip `_-installing into a virtualenv: +``django-environ`` is a Python-only package `hosted_on_pypi`_. +The recommended installation method is `pip`_-installing into a +:mod:`virtualenv `: .. code-block:: console @@ -26,13 +27,18 @@ The recommended installation method is `pip `_-i After installing ``django-environ``, no need to add it to ``INSTALLED_APPS``. + +.. _hosted_on_pypi: https://pypi.org/project/django-environ/ +.. _pip: https://pip.pypa.io/en/stable/ + + Unstable version ________________ The master of all the material is the Git repository at https://github.com/joke2k/django-environ. So, you can also install the latest unreleased development version directly from the ``develop`` branch on GitHub. It is a work-in-progress of a future stable release so the -experience might be not as smooth.: +experience might be not as smooth: .. code-block:: console @@ -115,7 +121,7 @@ FAQ root if you haven't specified the path for it when call :meth:`.environ.Env.read_env`. However, this is not the recommended way. When it is possible always specify the path tho ``.env`` file. Alternatively, you can use a trick with a - environment variable pointing to the actual location of .env file. + environment variable pointing to the actual location of ``.env`` file. For details see ":ref:`multiple-env-files-label`". #. **What (where) is the root part of the project, is it part of the project where are settings?** diff --git a/environ/__init__.py b/environ/__init__.py index 338fb244..305494c8 100644 --- a/environ/__init__.py +++ b/environ/__init__.py @@ -13,15 +13,15 @@ Misc variables: - __copyright__ - __version__ - __license__ - __author__ - __author_email__ - __maintainer__ - __maintainer_email__ - __url__ - __description__ +* ``__copyright__``: The copyright notice of the package. +* ``__version__``: The version of the package. +* ``__license__``: The license of the package. +* ``__author__``: The author of the package. +* ``__author_email__``: The email of the author of the package. +* ``__maintainer__``: The maintainer of the package. +* ``__maintainer_email__``: The email of the maintainer of the package. +* ``__url__``: The URL of the package. +* ``__description__``: The description of the package. Refer to the `documentation `_ for details on the use of this package. diff --git a/environ/environ.py b/environ/environ.py index 3d0102d5..a222a47b 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -272,7 +272,7 @@ def dict(self, var, cast=dict, default=NOTSET): def url(self, var, default=NOTSET): """ - :rtype: urlparse.ParseResult + :rtype: urllib.parse.ParseResult """ return self.get_value( var, @@ -911,13 +911,12 @@ def path(self, *paths, **kwargs): return self.__class__(self.__root__, *paths, **kwargs) def file(self, name, *args, **kwargs): - """Open a file. + r"""Open a file. - :param name: Filename appended to self.root - :param args: passed to open() - :param kwargs: passed to open() - - :rtype: file + :param str name: Filename appended to :py:attr:`~root` + :param \*args: ``*args`` passed to :py:func:`open` + :param \**kwargs: ``**kwargs`` passed to :py:func:`open` + :rtype: typing.IO[typing.Any] """ return open(self(name), *args, **kwargs) From e28d6335e24bcca5c983783166420af8e320a501 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 14:53:35 +0200 Subject: [PATCH 39/49] Small docs refactor --- docs/api.rst | 1 + docs/conf.py | 31 +++++---- docs/faq.rst | 31 +++++++++ docs/getting-started.rst | 143 --------------------------------------- docs/index.rst | 73 ++++++++++++++------ docs/install.rst | 58 ++++++++++++++++ docs/quickstart.rst | 59 ++++++++++++++++ environ/__init__.py | 29 ++++---- environ/environ.py | 25 +++++-- 9 files changed, 259 insertions(+), 191 deletions(-) create mode 100644 docs/faq.rst delete mode 100644 docs/getting-started.rst create mode 100644 docs/install.rst create mode 100644 docs/quickstart.rst diff --git a/docs/api.rst b/docs/api.rst index db2db6db..e1556abc 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -10,6 +10,7 @@ The ``__init__`` module .. automodule:: environ :members: + :special-members: :no-undoc-members: diff --git a/docs/conf.py b/docs/conf.py index 1c5390ad..cd25bf22 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ # the LICENSE.txt file that was distributed with this source code. # -# -- Utils ----------------------------------------------------- +# -- Utils --------------------------------------------------------- # import codecs @@ -15,6 +15,8 @@ import sys import re +from datetime import date + PROJECT_DIR = os.path.abspath('..') sys.path.insert(0, PROJECT_DIR) @@ -38,7 +40,7 @@ def find_version(meta_file): if meta_match: return meta_match.group(1) raise RuntimeError( - 'Unable to find __version__ string in package meta file') + "Unable to find __version__ string in package meta file") # @@ -46,9 +48,9 @@ def find_version(meta_file): # # General information about the project. -project = 'django-environ' -copyright = '2013-2021, Daniele Faraglia and other contributors' -author = u"Daniele Faraglia" +project = "django-environ" +copyright = f'2013-{date.today().year}, Daniele Faraglia and other contributors' +author = u"Daniele Faraglia \\and Serghei Iakovlev" # # -- General configuration --------------------------------------------------- @@ -79,7 +81,7 @@ def find_version(meta_file): # The version info # The short X.Y version. -release = find_version('environ/__init__.py') +release = find_version(os.path.join("environ", "__init__.py")) version = release.rsplit(u".", 1)[0] # The full version, including alpha/beta/rc tags. @@ -89,13 +91,13 @@ def find_version(meta_file): # The reST default role (used for this markup: `text`) to use for all # documents. -# default_role = None +default_role = "any" # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # -# -- Options for linkcheck ---------------------------------------------------- +# -- Options for linkcheck --------------------------------------------------- # linkcheck_ignore = [ @@ -104,7 +106,7 @@ def find_version(meta_file): ] # -# -- Options for nitpick ---------------------------------------------------- +# -- Options for nitpick ----------------------------------------------------- # # In nitpick mode (-n), still ignore any of the following "broken" references @@ -115,6 +117,7 @@ def find_version(meta_file): # # -- Options for extlinks ---------------------------------------------------- # + extlinks = { "pypi": ("https://pypi.org/project/%s/", ""), } @@ -122,6 +125,7 @@ def find_version(meta_file): # # -- Options for intersphinx ------------------------------------------------- # + intersphinx_mapping = { "python": ("https://docs.python.org/3", None), "sphinx": ("https://www.sphinx-doc.org/en/master", None), @@ -130,14 +134,15 @@ def find_version(meta_file): # # -- Options for TODOs ------------------------------------------------------- # + todo_include_todos = True -# -- Options for HTML output ---------------------------------------------- +# -- Options for HTML output ------------------------------------------------- # html_favicon = None html_theme = "furo" -html_title = "django-environ" +html_title = project html_theme_options = {} @@ -173,7 +178,7 @@ def find_version(meta_file): htmlhelp_basename = "django-environ-doc" # -# -- Options for manual page output --------------------------------------- +# -- Options for manual page output ------------------------------------------ # # One entry per manual page. List of tuples @@ -183,7 +188,7 @@ def find_version(meta_file): ] # -# -- Options for Texinfo output ------------------------------------------- +# -- Options for Texinfo output ---------------------------------------------- # # Grouping the document tree into Texinfo files. List of tuples diff --git a/docs/faq.rst b/docs/faq.rst new file mode 100644 index 00000000..9e23ef6f --- /dev/null +++ b/docs/faq.rst @@ -0,0 +1,31 @@ +=== +FAQ +=== + + +#. **Can django-environ determine the location of .env file automatically?** + + ``django-environ`` will try to get and read ``.env`` file from the project + root if you haven't specified the path for it when call :meth:`.environ.Env.read_env`. + However, this is not the recommended way. When it is possible always specify + the path tho ``.env`` file. Alternatively, you can use a trick with a + environment variable pointing to the actual location of ``.env`` file. + For details see ":ref:`multiple-env-files-label`". + +#. **What (where) is the root part of the project, is it part of the project where are settings?** + + Where your ``manage.py`` file is (that is your project root directory). + +#. **What kind of file should .env be?** + + ``.env`` is a plain text file. + +#. **Should name of the file be simply .env (or something.env)?** + + Just ``.env``. However, this is not a strict rule, but just a common + practice. Formally, you can use any filename. + +#. **Is .env file going to be imported in settings file?** + + No need to import, ``django-environ`` automatically picks variables + from there. diff --git a/docs/getting-started.rst b/docs/getting-started.rst deleted file mode 100644 index e7056fa1..00000000 --- a/docs/getting-started.rst +++ /dev/null @@ -1,143 +0,0 @@ -=============== -Getting Started -=============== - -Installation -============ - - -Requirements ------------- - -* `Django `_ >= 1.11 -* `Python `_ >= 3.5 - -Installing django-environ -_________________________ - -``django-environ`` is a Python-only package `hosted_on_pypi`_. -The recommended installation method is `pip`_-installing into a -:mod:`virtualenv `: - -.. code-block:: console - - $ python -m pip install django-environ - -.. note:: - - After installing ``django-environ``, no need to add it to ``INSTALLED_APPS``. - - -.. _hosted_on_pypi: https://pypi.org/project/django-environ/ -.. _pip: https://pip.pypa.io/en/stable/ - - -Unstable version -________________ - -The master of all the material is the Git repository at https://github.com/joke2k/django-environ. -So, you can also install the latest unreleased development version directly from the -``develop`` branch on GitHub. It is a work-in-progress of a future stable release so the -experience might be not as smooth: - -.. code-block:: console - - $ pip install -e git://github.com/joke2k/django-environ.git#egg=django-environ - # OR - $ pip install --upgrade https://github.com/joke2k/django-environ.git/archive/develop.tar.gz - -This command will download the latest version of ``django-environ`` and install -it to your system. - -.. note:: - - The ``develop`` branch will always contain the latest unstable version, so the experience - might be not as smooth. If you wish to check older versions or formal, tagged release, - please switch to the relevant `tag `_. - -More information about ``pip`` and PyPI can be found here: - -* `Install pip `_ -* `Python Packaging User Guide `_ - -Usage -===== - -Create a ``.env`` file in project root directory. The file format can be understood -from the example below: - -.. code-block:: shell - - DEBUG=on - SECRET_KEY=your-secret-key - DATABASE_URL=psql://user:un-githubbedpassword@127.0.0.1:8458/database - SQLITE_URL=sqlite:///my-local-sqlite.db - CACHE_URL=memcache://127.0.0.1:11211,127.0.0.1:11212,127.0.0.1:11213 - REDIS_URL=rediscache://127.0.0.1:6379/1?client_class=django_redis.client.DefaultClient&password=ungithubbed-secret - -And use it with ``settings.py`` as follows: - -.. include:: ../README.rst - :start-after: -code-begin- - :end-before: -overview- - -The ``.env`` file should be specific to the environment and not checked into -version control, it is best practice documenting the ``.env`` file with an example. -For example, you can also add ``.env.dist`` with a template of your variables to -the project repo. This file should describe the mandatory variables for the -Django application, and it can be committed to version control. This provides a -useful reference and speeds up the on-boarding process for new team members, since -the time to dig through the codebase to find out what has to be set up is reduced. - -A good ``.env.dist`` could look like this: - -.. code-block:: shell - - # SECURITY WARNING: don't run with the debug turned on in production! - DEBUG=True - - # Should robots.txt allow everything to be crawled? - ALLOW_ROBOTS=False - - # SECURITY WARNING: keep the secret key used in production secret! - SECRET_KEY=secret - - # A list of all the people who get code error notifications. - ADMINS="John Doe , Mary " - - # A list of all the people who should get broken link notifications. - MANAGERS="Blake , Alice Judge " - - # By default, Django will send system email from root@localhost. - # However, some mail providers reject all email from this address. - SERVER_EMAIL=webmaster@example.com - -FAQ -=== - -#. **Can django-environ determine the location of .env file automatically?** - - ``django-environ`` will try to get and read ``.env`` file from the project - root if you haven't specified the path for it when call :meth:`.environ.Env.read_env`. - However, this is not the recommended way. When it is possible always specify - the path tho ``.env`` file. Alternatively, you can use a trick with a - environment variable pointing to the actual location of ``.env`` file. - For details see ":ref:`multiple-env-files-label`". - -#. **What (where) is the root part of the project, is it part of the project where are settings?** - - Where your ``manage.py`` file is (that is your project root directory). - -#. **What kind of file should .env be?** - - ``.env`` is a plain text file. - -#. **Should name of the file be simply .env (or something.env)?** - - Just ``.env``. However, this is not a strict rule, but just a common - practice. Formally, you can use any filename. - -#. **Is .env file going to be imported in settings file?** - - No need to import, ``django-environ`` automatically picks variables - from there. diff --git a/docs/index.rst b/docs/index.rst index fca07a98..8d205055 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,38 +17,73 @@ Overview :start-after: -overview- :end-before: -project-information- -Quick Start -=========== - -.. include:: ../README.rst - :start-after: -code-begin- - :end-before: -overview- - -.. include:: ../README.rst - :start-after: -support- - ---- Full Table of Contents ====================== +The User Guide +-------------- + +This part of the documentation, which is mostly prose, begins with some +background information about django-environ, then focuses on step-by-step +instructions for getting the most out of django-environ. + +.. toctree:: + :maxdepth: 2 + + install + quickstart + + +The Community Guide +------------------- + +This part of the documentation, which is mostly prose, details the +django-environ ecosystem and community. + .. toctree:: :maxdepth: 2 - getting-started + faq types tips - contributing - backers - api - -.. include:: ../README.rst - :start-after: -project-information- - :end-before: -support- .. toctree:: :maxdepth: 1 - license deprecations changelog + + +The API Documentation / Guide +----------------------------- + +If you are looking for information on a specific function, class, or method, +this part of the documentation is for you. + +.. toctree:: + :maxdepth: 2 + + api + + +The Contributor Guide +--------------------- + +If you want to contribute to the project, this part of the documentation is for +you. + +.. toctree:: + :maxdepth: 3 + + contributing + backers + license + +.. include:: ../README.rst + :start-after: -support- + +.. include:: ../README.rst + :start-after: -project-information- + :end-before: -support- diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 00000000..a70ca1a3 --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,58 @@ +============ +Installation +============ + + +Requirements +============ + +* `Django `_ >= 1.11 +* `Python `_ >= 3.5 + +Installing django-environ +========================= + +``django-environ`` is a Python-only package `hosted_on_pypi`_. +The recommended installation method is `pip`_-installing into a +:mod:`virtualenv `: + +.. code-block:: console + + $ python -m pip install django-environ + +.. note:: + + After installing ``django-environ``, no need to add it to ``INSTALLED_APPS``. + + +.. _hosted_on_pypi: https://pypi.org/project/django-environ/ +.. _pip: https://pip.pypa.io/en/stable/ + + +Unstable version +================ + +The master of all the material is the Git repository at https://github.com/joke2k/django-environ. +So, you can also install the latest unreleased development version directly from the +``develop`` branch on GitHub. It is a work-in-progress of a future stable release so the +experience might be not as smooth: + +.. code-block:: console + + $ pip install -e git://github.com/joke2k/django-environ.git#egg=django-environ + # OR + $ pip install --upgrade https://github.com/joke2k/django-environ.git/archive/develop.tar.gz + +This command will download the latest version of ``django-environ`` and install +it to your system. + +.. note:: + + The ``develop`` branch will always contain the latest unstable version, so the experience + might be not as smooth. If you wish to check older versions or formal, tagged release, + please switch to the relevant `tag `_. + +More information about ``pip`` and PyPI can be found here: + +* `Install pip `_ +* `Python Packaging User Guide `_ diff --git a/docs/quickstart.rst b/docs/quickstart.rst new file mode 100644 index 00000000..f5c5b209 --- /dev/null +++ b/docs/quickstart.rst @@ -0,0 +1,59 @@ +=========== +Quick Start +=========== + +.. include:: ../README.rst + :start-after: -code-begin- + :end-before: -overview- + +Usage +===== + +Create a ``.env`` file in project root directory. The file format can be understood +from the example below: + +.. code-block:: shell + + DEBUG=on + SECRET_KEY=your-secret-key + DATABASE_URL=psql://user:un-githubbedpassword@127.0.0.1:8458/database + SQLITE_URL=sqlite:///my-local-sqlite.db + CACHE_URL=memcache://127.0.0.1:11211,127.0.0.1:11212,127.0.0.1:11213 + REDIS_URL=rediscache://127.0.0.1:6379/1?client_class=django_redis.client.DefaultClient&password=ungithubbed-secret + +And use it with ``settings.py`` as follows: + +.. include:: ../README.rst + :start-after: -code-begin- + :end-before: -overview- + +The ``.env`` file should be specific to the environment and not checked into +version control, it is best practice documenting the ``.env`` file with an example. +For example, you can also add ``.env.dist`` with a template of your variables to +the project repo. This file should describe the mandatory variables for the +Django application, and it can be committed to version control. This provides a +useful reference and speeds up the on-boarding process for new team members, since +the time to dig through the codebase to find out what has to be set up is reduced. + +A good ``.env.dist`` could look like this: + +.. code-block:: shell + + # SECURITY WARNING: don't run with the debug turned on in production! + DEBUG=True + + # Should robots.txt allow everything to be crawled? + ALLOW_ROBOTS=False + + # SECURITY WARNING: keep the secret key used in production secret! + SECRET_KEY=secret + + # A list of all the people who get code error notifications. + ADMINS="John Doe , Mary " + + # A list of all the people who should get broken link notifications. + MANAGERS="Blake , Alice Judge " + + # By default, Django will send system email from root@localhost. + # However, some mail providers reject all email from this address. + SERVER_EMAIL=webmaster@example.com diff --git a/environ/__init__.py b/environ/__init__.py index 305494c8..72a30ba9 100644 --- a/environ/__init__.py +++ b/environ/__init__.py @@ -11,18 +11,6 @@ This module tracks the version of the package as well as the base package info used by various functions within django-environ. -Misc variables: - -* ``__copyright__``: The copyright notice of the package. -* ``__version__``: The version of the package. -* ``__license__``: The license of the package. -* ``__author__``: The author of the package. -* ``__author_email__``: The email of the author of the package. -* ``__maintainer__``: The maintainer of the package. -* ``__maintainer_email__``: The email of the maintainer of the package. -* ``__url__``: The URL of the package. -* ``__description__``: The description of the package. - Refer to the `documentation `_ for details on the use of this package. """ # noqa: E501 @@ -31,11 +19,28 @@ __copyright__ = 'Copyright (C) 2021 Daniele Faraglia' +"""The copyright notice of the package.""" + __version__ = '0.9.0' +"""The version of the package.""" + __license__ = 'MIT' +"""The license of the package.""" + __author__ = 'Daniele Faraglia' +"""The author of the package.""" + __author_email__ = 'daniele.faraglia@gmail.com' +"""The email of the author of the package.""" + __maintainer__ = 'Serghei Iakovlev' +"""The maintainer of the package.""" + __maintainer_email__ = 'egrep@protonmail.ch' +"""The email of the maintainer of the package.""" + __url__ = 'https://django-environ.readthedocs.org' +"""The URL of the package.""" + __description__ = 'A package that allows you to utilize 12factor inspired environment variables to configure your Django application.' # noqa: E501 +"""The description of the package.""" diff --git a/environ/environ.py b/environ/environ.py index a222a47b..1dbe8064 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -74,13 +74,30 @@ def __repr__(self): class Env: """Provide scheme-based lookups of environment variables so that each - caller doesn't have to pass in `cast` and `default` parameters. + caller doesn't have to pass in ``cast`` and ``default`` parameters. Usage::: - env = Env(MAIL_ENABLED=bool, SMTP_LOGIN=(str, 'DEFAULT')) - if env('MAIL_ENABLED'): - ... + import environ + import os + + env = environ.Env( + # set casting, default value + MAIL_ENABLED=(bool, False), + SMTP_LOGIN=(str, 'DEFAULT') + ) + + # Set the project base directory + BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + # Take environment variables from .env file + environ.Env.read_env(os.path.join(BASE_DIR, '.env')) + + # False if not in os.environ due to casting above + MAIL_ENABLED = env('MAIL_ENABLED') + + # 'DEFAULT' if not in os.environ due to casting above + SMTP_LOGIN = env('SMTP_LOGIN') """ ENVIRON = os.environ From 3cebc499385e4d77f6fdf9bdbbacff478b6bb106 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 14:55:42 +0200 Subject: [PATCH 40/49] Correct code style --- environ/environ.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index 1dbe8064..061ac256 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -88,8 +88,10 @@ class Env: ) # Set the project base directory - BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - + BASE_DIR = os.path.dirname( + os.path.dirname(os.path.abspath(__file__)) + ) + # Take environment variables from .env file environ.Env.read_env(os.path.join(BASE_DIR, '.env')) From 5cb85fb5dc23e2fc0f77f7f0be6aaac831fe65dc Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 16:26:12 +0200 Subject: [PATCH 41/49] Amend documentation --- CHANGELOG.rst | 1 + docs/tips.rst | 2 + docs/types.rst | 175 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 120 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5c586d2a..1fa05e1e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,7 @@ Added `#371 `_. - Added support for prefixed variables `#362 `_. +- Amended documentation. Deprecated diff --git a/docs/tips.rst b/docs/tips.rst index 3b45ddb1..2af01a4a 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -126,6 +126,8 @@ You can use something like this to handle similar cases. ADMINS = tuple(parseaddr(email) for email in env.list('DJANGO_ADMINS')) +.. _complex_dict_format: + Complex dict format =================== diff --git a/docs/types.rst b/docs/types.rst index 292ed64f..d205aab2 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -2,61 +2,120 @@ Supported types =============== -* ``str`` -* ``bool`` -* ``int`` -* ``float`` -* ``json`` -* ``list``: ``(FOO=a,b,c)`` -* ``tuple``: ``(FOO=(a,b,c))`` -* ``dict``: ``(BAR=key=val,foo=bar)``, ``environ.Env(BAR=(dict, {}))`` -* ``dict``: ``(BAR=key=val;foo=1.1;baz=True)``, ``environ.Env(BAR=(dict(value=unicode, cast=dict(foo=float,baz=bool)), {}))`` -* ``url`` -* ``path``: ``(environ.Path)`` -* ``db_url`` - - * PostgreSQL: ``postgres://``, ``pgsql://``, ``psql://`` or ``postgresql://`` - * PostGIS: ``postgis://`` - * MySQL: ``mysql://`` or ``mysql2://`` - * MySQL for GeoDjango: ``mysqlgis://`` - * MySQL Connector Python from Oracle: ``mysql-connector://`` - * SQLite: ``sqlite://`` - * SQLite with SpatiaLite for GeoDjango: ``spatialite://`` - * Oracle: ``oracle://`` - * Microsoft SQL Server: ``mssql://`` - * PyODBC: ``pyodbc://`` - * Amazon Redshift: ``redshift://`` - * LDAP: ``ldap://`` - -* ``cache_url`` - - * Database: ``dbcache://`` - * Dummy: ``dummycache://`` - * File: ``filecache://`` - * Memory: ``locmemcache://`` - * Memcached: - * ``memcache://`` (uses ``python-memcached`` backend, deprecated in Django 3.2) - * ``pymemcache://`` (uses ``pymemcache`` backend if Django >=3.2 and package is installed, otherwise will use ``pylibmc`` backend to keep config backwards compatibility) - * ``pylibmc://`` - * Redis: ``rediscache://``, ``redis://``, or ``rediss://`` - -* ``search_url`` - - * Elasticsearch: ``elasticsearch://`` - * Elasticsearch2: ``elasticsearch2://`` - * Elasticsearch5: ``elasticsearch5://`` - * Elasticsearch7: ``elasticsearch7://`` - * Solr: ``solr://`` - * Whoosh: ``whoosh://`` - * Xapian: ``xapian://`` - * Simple cache: ``simple://`` - -* ``email_url`` - - * SMTP: ``smtp://`` - * SMTP+SSL: ``smtp+ssl://`` - * SMTP+TLS: ``smtp+tls://`` - * Console mail: ``consolemail://`` - * File mail: ``filemail://`` - * LocMem mail: ``memorymail://`` - * Dummy mail: ``dummymail://`` +The following are all type-casting methods of :py:class:`.environ.Env`. + +* :py:meth:`~.environ.Env.str` +* :py:meth:`~.environ.Env.bool` +* :py:meth:`~.environ.Env.int` +* :py:meth:`~.environ.Env.float` +* :py:meth:`~.environ.Env.json` +* :py:meth:`~.environ.Env.url` +* :py:meth:`~.environ.Env.list`: (accepts values like ``(FOO=a,b,c)``) +* :py:meth:`~.environ.Env.tuple`: (accepts values like ``(FOO=(a,b,c))``) +* :py:meth:`~.environ.Env.path`: (accepts values like ``(environ.Path)``) +* :py:meth:`~.environ.Env.dict`: (see below, ":ref:`environ-env-dict`" section) +* :py:meth:`~.environ.Env.db_url` (see below, ":ref:`environ-env-db-url`" section) +* :py:meth:`~.environ.Env.cache_url` (see below, ":ref:`environ-env-cache-url`" section) +* :py:meth:`~.environ.Env.search_url` (see below, ":ref:`environ-env-search-url`" section) +* :py:meth:`~.environ.Env.email_url` (see below, ":ref:`environ-env-email-url`" section) + + +.. _environ-env-dict: + +``environ.Env.dict`` +====================== + +:py:class:`.environ.Env` may parse complex variables like ``BAR=key=val,foo=bar`` +with the following type-casting ``BAR=(dict, {})``. For example: + +.. code-block:: python + + import environ + + + env = environ.Env() + + # {'key': 'val', 'foo': 'bar'} + env.parse_value('key=val,foo=bar', dict) + + # {'key': 'val', 'foo': 1.1, 'baz': True} + env.parse_value( + 'key=val;foo=1.1;baz=True', + dict(value=str, cast=dict(foo=float,baz=bool)) + ) + +For more detailed example see ":ref:`complex_dict_format`". + + +.. _environ-env-db-url: + +``environ.Env.db_url`` +====================== + +:py:meth:`~.environ.Env.db_url` supports the following schemes: + +* PostgreSQL: ``postgres://``, ``pgsql://``, ``psql://`` or ``postgresql://`` +* PostGIS: ``postgis://`` +* MySQL: ``mysql://`` or ``mysql2://`` +* MySQL for GeoDjango: ``mysqlgis://`` +* MySQL Connector Python from Oracle: ``mysql-connector://`` +* SQLite: ``sqlite://`` +* SQLite with SpatiaLite for GeoDjango: ``spatialite://`` +* Oracle: ``oracle://`` +* Microsoft SQL Server: ``mssql://`` +* PyODBC: ``pyodbc://`` +* Amazon Redshift: ``redshift://`` +* LDAP: ``ldap://`` + + +.. _environ-env-cache-url: + +``environ.Env.cache_url`` +========================= + +:py:meth:`~.environ.Env.cache_url` supports the following schemes: + +* Database: ``dbcache://`` +* Dummy: ``dummycache://`` +* File: ``filecache://`` +* Memory: ``locmemcache://`` +* Memcached: + + * ``memcache://`` (uses ``python-memcached`` backend, deprecated in Django 3.2) + * ``pymemcache://`` (uses ``pymemcache`` backend if Django >=3.2 and package is installed, otherwise will use ``pylibmc`` backend to keep config backwards compatibility) + * ``pylibmc://`` + +* Redis: ``rediscache://``, ``redis://``, or ``rediss://`` + + +.. _environ-env-search-url: + +``environ.Env.search_url`` +========================== + +:py:meth:`~.environ.Env.search_url` supports the following schemes: + +* Elasticsearch: ``elasticsearch://`` +* Elasticsearch2: ``elasticsearch2://`` +* Elasticsearch5: ``elasticsearch5://`` +* Elasticsearch7: ``elasticsearch7://`` +* Solr: ``solr://`` +* Whoosh: ``whoosh://`` +* Xapian: ``xapian://`` +* Simple cache: ``simple://`` + + +.. _environ-env-email-url: + +``environ.Env.email_url`` +========================== + +:py:meth:`~.environ.Env.email_url` supports the following schemes: + +* SMTP: ``smtp://`` +* SMTP+SSL: ``smtp+ssl://`` +* SMTP+TLS: ``smtp+tls://`` +* Console mail: ``consolemail://`` +* File mail: ``filemail://`` +* LocMem mail: ``memorymail://`` +* Dummy mail: ``dummymail://`` From 31c574377deddf6c71bee6bd1cd9702492eb7501 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 20:37:30 +0200 Subject: [PATCH 42/49] Add db_url tests for mysql --- environ/environ.py | 43 +++++++++++++------ tests/test_db.py | 102 ++++++++++++++++++++++++++++++--------------- 2 files changed, 98 insertions(+), 47 deletions(-) diff --git a/environ/environ.py b/environ/environ.py index 061ac256..57b8bc21 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -488,15 +488,29 @@ def parse_value(cls, value, cast): @classmethod def db_url_config(cls, url, engine=None): - """Pulled from DJ-Database-URL, parse an arbitrary Database URL. - - Support currently exists for PostgreSQL, PostGIS, MySQL, Oracle and - SQLite. - - SQLite connects to file based databases. The same URL format is used, - omitting the hostname, and using the "file" portion as the filename of - the database. This has the effect of four slashes being present for an - absolute file path. + """Parse an arbitrary database URL. + + Supports the following URL schemas: + + * PostgreSQL: ``postgres://``, ``pgsql://``, ``psql://`` or ``postgresql://`` + * PostGIS: ``postgis://`` + * MySQL: ``mysql://`` or ``mysql2://`` + * MySQL (GIS): ``mysqlgis://`` + * MySQL Connector Python from Oracle: ``mysql-connector://`` + * SQLite: ``sqlite://`` + * SQLite with SpatiaLite for GeoDjango: ``spatialite://`` + * Oracle: ``oracle://`` + * Microsoft SQL Server: ``mssql://`` + * PyODBC: ``pyodbc://`` + * Amazon Redshift: ``redshift://`` + * LDAP: ``ldap://`` + + :param urllib.parse.ParseResult or str url: + Database URL to parse. + :param str or None engine: + If None, the database engine is evaluates from the ``url``. + :return: Parsed database URL. + :rtype: dict """ if not isinstance(url, cls.URL_CLASS): if url == 'sqlite://:memory:': @@ -601,11 +615,14 @@ def db_url_config(cls, url, engine=None): @classmethod def cache_url_config(cls, url, backend=None): - """Pulled from DJ-Cache-URL, parse an arbitrary Cache URL. + """Parse an arbitrary cache URL. - :param url: - :param backend: - :return: + :param urllib.parse.ParseResult or str url: + Cache URL to parse. + :param str or None backend: + If None, the backend is evaluates from the ``url``. + :return: Parsed cache URL. + :rtype: dict """ if not isinstance(url, cls.URL_CLASS): if not url: diff --git a/tests/test_db.py b/tests/test_db.py index 4f06bfc6..20d807ff 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -17,29 +17,31 @@ @pytest.mark.parametrize( 'url,engine,name,host,user,passwd,port', [ - ('postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.' - 'compute-1.amazonaws.com:5431/d8r82722r2kuvn', + # postgres://user:password@host:port/dbname + ('postgres://enigma:secret@example.com:5431/dbname', DJANGO_POSTGRES, - 'd8r82722r2kuvn', - 'ec2-107-21-253-135.compute-1.amazonaws.com', - 'uf07k1i6d8ia0v', - 'wegauwhgeuioweg', + 'dbname', + 'example.com', + 'enigma', + 'secret', 5431), - ('postgres:////var/run/postgresql/db', + # postgres://path/dbname + ('postgres:////var/run/postgresql/dbname', DJANGO_POSTGRES, - 'db', + 'dbname', '/var/run/postgresql', '', '', ''), - ('postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.' - 'compute-1.amazonaws.com:5431/d8r82722r2kuvn', + # postgis://user:password@host:port/dbname + ('postgis://enigma:secret@example.com:5431/dbname', 'django.contrib.gis.db.backends.postgis', - 'd8r82722r2kuvn', - 'ec2-107-21-253-135.compute-1.amazonaws.com', - 'uf07k1i6d8ia0v', - 'wegauwhgeuioweg', + 'dbname', + 'example.com', + 'enigma', + 'secret', 5431), + # postgres://user:password@host:port,host:port,host:port/dbname ('postgres://username:p@ss:12,wor:34d@host1:111,22.55.44.88:222,[2001:db8::1234]:333/db', DJANGO_POSTGRES, 'db', @@ -48,6 +50,7 @@ 'p@ss:12,wor:34d', '111,222,333' ), + # postgres://host,host,host/dbname ('postgres://node1,node2,node3/db', DJANGO_POSTGRES, 'db', @@ -56,29 +59,31 @@ '', '' ), - ('mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.' - 'compute-1.amazonaws.com:5431/d8r82722r2kuvn', + # mysqlgis://user:password@host:port/dbname + ('mysqlgis://enigma:secret@example.com:5431/dbname', 'django.contrib.gis.db.backends.mysql', - 'd8r82722r2kuvn', - 'ec2-107-21-253-135.compute-1.amazonaws.com', - 'uf07k1i6d8ia0v', - 'wegauwhgeuioweg', + 'dbname', + 'example.com', + 'enigma', + 'secret', 5431), - ('mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com' - '/heroku_97681db3eff7580?reconnect=true', + # mysql://user:password@host/dbname?options + ('mysql://enigma:secret@reconnect.com/dbname?reconnect=true', 'django.db.backends.mysql', - 'heroku_97681db3eff7580', - 'us-cdbr-east.cleardb.com', - 'bea6eb025ca0d8', - '69772142', + 'dbname', + 'reconnect.com', + 'enigma', + 'secret', ''), - ('mysql://travis@localhost/test_db', + # mysql://user@host/dbname + ('mysql://enigma@localhost/dbname', 'django.db.backends.mysql', - 'test_db', + 'dbname', 'localhost', - 'travis', + 'enigma', '', ''), + # sqlite:// ('sqlite://', 'django.db.backends.sqlite3', ':memory:', @@ -86,6 +91,7 @@ '', '', ''), + # sqlite:////absolute/path/to/db/file ('sqlite:////full/path/to/your/file.sqlite', 'django.db.backends.sqlite3', '/full/path/to/your/file.sqlite', @@ -93,6 +99,7 @@ '', '', ''), + # sqlite://:memory: ('sqlite://:memory:', 'django.db.backends.sqlite3', ':memory:', @@ -100,14 +107,32 @@ '', '', ''), - ('ldap://cn=admin,dc=nodomain,dc=org:' - 'some_secret_password@ldap.nodomain.org/', + # ldap://user:password@host + ('ldap://cn=admin,dc=nodomain,dc=org:secret@example.com', 'ldapdb.backends.ldap', - 'ldap://ldap.nodomain.org', - 'ldap.nodomain.org', + 'ldap://example.com', + 'example.com', 'cn=admin,dc=nodomain,dc=org', - 'some_secret_password', + 'secret', + ''), + # mysql://user:password@host/dbname + ('mssql://enigma:secret@example.com/dbname' + '?driver=ODBC Driver 13 for SQL Server', + 'sql_server.pyodbc', + 'dbname', + 'example.com', + 'enigma', + 'secret', ''), + # mysql://user:password@host:port/dbname + ('mssql://enigma:secret@amazonaws.com\\insnsnss:12345/dbname' + '?driver=ODBC Driver 13 for SQL Server', + 'sql_server.pyodbc', + 'dbname', + 'amazonaws.com\\insnsnss', + 'enigma', + 'secret', + 12345), ], ids=[ 'postgres', @@ -122,6 +147,8 @@ 'sqlite_file', 'sqlite_memory', 'ldap', + 'mssql', + 'mssql_port', ], ) def test_db_parsing(url, engine, name, host, user, passwd, port): @@ -136,6 +163,13 @@ def test_db_parsing(url, engine, name, host, user, passwd, port): assert config['USER'] == user assert config['HOST'] == host + if engine == 'sql_server.pyodbc': + assert config['OPTIONS'] == {'driver': 'ODBC Driver 13 for SQL Server'} + + if host == 'reconnect.com': + assert config['OPTIONS'] == {'reconnect': 'true'} + + def test_postgres_complex_db_name_parsing(): """Make sure we can use complex postgres host.""" From ea308232d474ad1eb7fc6826869baad6b7136b96 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Tue, 14 Jun 2022 21:12:46 +0200 Subject: [PATCH 43/49] Update docs --- docs/conf.py | 12 ++++++ docs/tips.rst | 2 +- docs/types.rst | 97 +++++++++++++++++++++++++++++++++++++--------- environ/environ.py | 28 ++++++++++--- 4 files changed, 115 insertions(+), 24 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index cd25bf22..e7d7fe57 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -96,6 +96,18 @@ def find_version(meta_file): # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True +# +# -- Options for autodoc --------------------------------------------------- +# + +# This value selects if automatically documented members are sorted alphabetical +# (value 'alphabetical'), by member type (value 'groupwise') or by source order +# (value 'bysource'). The default is alphabetical. +# +# Note that for source order, the module must be a Python module with the +# source code available. +autodoc_member_order = 'bysource' + # # -- Options for linkcheck --------------------------------------------------- # diff --git a/docs/tips.rst b/docs/tips.rst index 2af01a4a..a8684eb9 100644 --- a/docs/tips.rst +++ b/docs/tips.rst @@ -77,7 +77,7 @@ For redis cache, multiple master/slave or shard locations can be configured as f Email settings ============== -In order to set email configuration for django you can use this code: +In order to set email configuration for Django you can use this code: .. code-block:: python diff --git a/docs/types.rst b/docs/types.rst index d205aab2..3fcdcbbd 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -25,8 +25,8 @@ The following are all type-casting methods of :py:class:`.environ.Env`. ``environ.Env.dict`` ====================== -:py:class:`.environ.Env` may parse complex variables like ``BAR=key=val,foo=bar`` -with the following type-casting ``BAR=(dict, {})``. For example: +:py:class:`.environ.Env` may parse complex variables like with the complex type-casting. +For example: .. code-block:: python @@ -52,20 +52,81 @@ For more detailed example see ":ref:`complex_dict_format`". ``environ.Env.db_url`` ====================== -:py:meth:`~.environ.Env.db_url` supports the following schemes: +:py:meth:`~.environ.Env.db_url` supports the following URL schemas: -* PostgreSQL: ``postgres://``, ``pgsql://``, ``psql://`` or ``postgresql://`` -* PostGIS: ``postgis://`` -* MySQL: ``mysql://`` or ``mysql2://`` -* MySQL for GeoDjango: ``mysqlgis://`` -* MySQL Connector Python from Oracle: ``mysql-connector://`` -* SQLite: ``sqlite://`` -* SQLite with SpatiaLite for GeoDjango: ``spatialite://`` -* Oracle: ``oracle://`` -* Microsoft SQL Server: ``mssql://`` -* PyODBC: ``pyodbc://`` -* Amazon Redshift: ``redshift://`` -* LDAP: ``ldap://`` +.. glossary:: + + Amazon Redshift + **Database Backend:** ``django_redshift_backend`` + + **URL schema:** ``redshift://`` + + LDAP + **Database Backend:** ``ldapdb.backends.ldap`` + + **URL schema:** ``ldap://host:port/dn?attrs?scope?filter?exts`` + + MSSQL + **Database Backend:** ``sql_server.pyodbc`` + + **URL schema:** ``mssql://user:password@host:port/dbname`` + + With MySQL you can use the following schemas: ``mysql``, ``mysql2``. + + MySQL (GIS) + **Database Backend:** ``django.contrib.gis.db.backends.mysql`` + + **URL schema:** ``mysqlgis://user:password@host:port/dbname`` + + MySQL + **Database Backend:** ``django.db.backends.mysql`` + + **URL schema:** ``mysql://user:password@host:port/dbname`` + + MySQL Connector Python from Oracle + **Database Backend:** ``mysql.connector.django`` + + **URL schema:** ``mysql-connector://`` + + Oracle + **Database Backend:** ``django.db.backends.oracle`` + + **URL schema:** ``oracle://user:password@host:port/dbname`` + + PostgreSQL + **Database Backend:** ``django.db.backends.postgresql`` + + **URL schema:** ``postgres://user:password@host:port/dbname`` + + With PostgreSQL you can use the following schemas: ``postgres``, ``postgresql``, ``psql``, ``pgsql``, ``postgis``. + You can also use UNIX domain sockets path instead of hostname. For example: ``postgres://path/dbname``. + The ``django.db.backends.postgresql_psycopg2`` will be used if the Django version is less than ``2.0``. + + PostGIS + **Database Backend:** ``django.contrib.gis.db.backends.postgis`` + + **URL schema:** ``postgis://user:password@host:port/dbname`` + + PyODBC + **Database Backend:** ``sql_server.pyodbc`` + + **URL schema:** ``pyodbc://`` + + SQLite + **Database Backend:** ``django.db.backends.sqlite3`` + + **URL schema:** ``sqlite:////absolute/path/to/db/file`` + + SQLite connects to file based databases. URL schemas ``sqlite://`` or + ``sqlite://:memory:`` means the database is in the memory (not a file on disk). + + SpatiaLite + **Database Backend:** ``django.contrib.gis.db.backends.spatialite`` + + **URL schema:** ``spatialite:///PATH`` + + SQLite connects to file based databases. URL schemas ``sqlite://`` or + ``sqlite://:memory:`` means the database is in the memory (not a file on disk). .. _environ-env-cache-url: @@ -73,7 +134,7 @@ For more detailed example see ":ref:`complex_dict_format`". ``environ.Env.cache_url`` ========================= -:py:meth:`~.environ.Env.cache_url` supports the following schemes: +:py:meth:`~.environ.Env.cache_url` supports the following URL schemas: * Database: ``dbcache://`` * Dummy: ``dummycache://`` @@ -93,7 +154,7 @@ For more detailed example see ":ref:`complex_dict_format`". ``environ.Env.search_url`` ========================== -:py:meth:`~.environ.Env.search_url` supports the following schemes: +:py:meth:`~.environ.Env.search_url` supports the following URL schemas: * Elasticsearch: ``elasticsearch://`` * Elasticsearch2: ``elasticsearch2://`` @@ -110,7 +171,7 @@ For more detailed example see ":ref:`complex_dict_format`". ``environ.Env.email_url`` ========================== -:py:meth:`~.environ.Env.email_url` supports the following schemes: +:py:meth:`~.environ.Env.email_url` supports the following URL schemas: * SMTP: ``smtp://`` * SMTP+SSL: ``smtp+ssl://`` diff --git a/environ/environ.py b/environ/environ.py index 57b8bc21..3e0c21a5 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -492,7 +492,7 @@ def db_url_config(cls, url, engine=None): Supports the following URL schemas: - * PostgreSQL: ``postgres://``, ``pgsql://``, ``psql://`` or ``postgresql://`` + * PostgreSQL: ``postgres[ql]?://`` or ``p[g]?sql://`` * PostGIS: ``postgis://`` * MySQL: ``mysql://`` or ``mysql2://`` * MySQL (GIS): ``mysqlgis://`` @@ -691,7 +691,15 @@ def cache_url_config(cls, url, backend=None): @classmethod def email_url_config(cls, url, backend=None): - """Parses an email URL.""" + """Parse an arbitrary email URL. + + :param urllib.parse.ParseResult or str url: + Email URL to parse. + :param str or None backend: + If None, the backend is evaluates from the ``url``. + :return: Parsed email URL. + :rtype: dict + """ config = {} @@ -736,6 +744,16 @@ def email_url_config(cls, url, backend=None): @classmethod def search_url_config(cls, url, engine=None): + """Parse an arbitrary search URL. + + :param urllib.parse.ParseResult or str url: + Search URL to parse. + :param str or None engine: + If None, the engine is evaluates from the ``url``. + :return: Parsed search URL. + :rtype: dict + """ + config = {} url = urlparse(url) if not isinstance(url, cls.URL_CLASS) else url @@ -829,15 +847,15 @@ def read_env(cls, env_file=None, overwrite=False, **overrides): If not given a path to a dotenv path, does filthy magic stack backtracking to find the dotenv in the same directory as the file that - called read_env. + called ``read_env``. Existing environment variables take precedent and are NOT overwritten by the file content. ``overwrite=True`` will force an overwrite of existing environment variables. Refs: - - https://wellfire.co/learn/easier-12-factor-django - - https://gist.github.com/bennylope/2999704 + + * https://wellfire.co/learn/easier-12-factor-django :param env_file: The path to the ``.env`` file your application should use. If a path is not provided, `read_env` will attempt to import From 27a8a87352769f830989808cda8267440df01009 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 15 Jun 2022 01:18:04 +0200 Subject: [PATCH 44/49] Fix `environ.Path.__eq__()` to compare paths correctly Resolves #86, resolves #197 --- CHANGELOG.rst | 3 +++ environ/environ.py | 20 +++++++++++++------- tests/test_path.py | 4 ++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1fa05e1e..06ed6c09 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,6 +36,9 @@ Fixed `#357 `_. - Fix documentation regarding unsafe characters in URLs `#220 `_. +- Fix ``environ.Path.__eq__()`` to compare paths correctly + `#86 `_ + `#197 `_. `v0.8.1`_ - 20-October-2021 diff --git a/environ/environ.py b/environ/environ.py index 3e0c21a5..fc5eebbe 100644 --- a/environ/environ.py +++ b/environ/environ.py @@ -361,12 +361,16 @@ def path(self, var, default=NOTSET, **kwargs): def get_value(self, var, cast=None, default=NOTSET, parse_default=False): """Return value for given environment variable. - :param var: Name of variable. - :param cast: Type to cast return value as. - :param default: If var not present in environ, return this instead. - :param parse_default: force to parse default.. - - :returns: Value from environment or default (if set) + :param str var: + Name of variable. + :param collections.abc.Callable or None cast: + Type to cast return value as. + :param default: + If var not present in environ, return this instead. + :param bool parse_default: + Force to parse default. + :returns: Value from environment or default (if set). + :rtype: typing.IO[typing.Any] """ logger.debug("get '{}' casted as '{}' with default '{}'".format( @@ -997,7 +1001,9 @@ def __call__(self, *paths, **kwargs): return self._absolute_join(self.__root__, *paths, **kwargs) def __eq__(self, other): - return self.__root__ == other.__root__ + if isinstance(other, Path): + return self.__root__ == other.__root__ + return self.__root__ == other def __ne__(self, other): return not self.__eq__(other) diff --git a/tests/test_path.py b/tests/test_path.py index 502cf780..a7badb46 100644 --- a/tests/test_path.py +++ b/tests/test_path.py @@ -62,6 +62,10 @@ def test_comparison(): assert Path('/home/foo/').__fspath__() == str(Path('/home/foo/')) assert ~Path('/home') == Path('/') + assert Path('/home') == '/home' + assert '/home' == Path('/home') + assert Path('/home') != '/usr' + def test_sum(): """Make sure Path correct handle __add__.""" From 16de0116a4df21b6186c3cf805bd1c1a810ea558 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 15 Jun 2022 01:44:39 +0200 Subject: [PATCH 45/49] Fix Windows tests --- tests/test_path.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_path.py b/tests/test_path.py index a7badb46..ed33a7cb 100644 --- a/tests/test_path.py +++ b/tests/test_path.py @@ -47,7 +47,7 @@ def test_repr(volume): assert root.__repr__() == '' -def test_comparison(): +def test_comparison(volume): root = Path('/home') assert root.__eq__(Path('/home')) assert root in Path('/') @@ -62,10 +62,14 @@ def test_comparison(): assert Path('/home/foo/').__fspath__() == str(Path('/home/foo/')) assert ~Path('/home') == Path('/') - assert Path('/home') == '/home' - assert '/home' == Path('/home') - assert Path('/home') != '/usr' + if sys.platform == 'win32': + assert Path('/home') == '{}home'.format(volume) + assert '{}home'.format(volume) == Path('/home') + else: + assert Path('/home') == '/home' + assert '/home' == Path('/home') + assert Path('/home') != '/usr' def test_sum(): """Make sure Path correct handle __add__.""" From 42da877d3c175737e901600a8bc57d4f9392e1ae Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 15 Jun 2022 02:07:52 +0200 Subject: [PATCH 46/49] Correct chahge log --- CHANGELOG.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 06ed6c09..ba9d9a5d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -32,12 +32,12 @@ Changed Fixed +++++ -- Fix ``_cast_urlstr`` unquoting +- Fixed ``_cast_urlstr`` unquoting `#357 `_. -- Fix documentation regarding unsafe characters in URLs +- Fixed documentation regarding unsafe characters in URLs `#220 `_. -- Fix ``environ.Path.__eq__()`` to compare paths correctly - `#86 `_ +- Fixed ``environ.Path.__eq__()`` to compare paths correctly + `#86 `_, `#197 `_. @@ -144,7 +144,7 @@ Added - Support for Django 2.1 & 2.2. - Added tox.ini targets. - Added secure redis backend URLs via ``rediss://``. -- Add ``cast=str`` to ``str()`` method. +- Added ``cast=str`` to ``str()`` method. Fixed +++++ @@ -185,7 +185,7 @@ Added - Support for Elasticsearch2. - Support for Mysql-connector. - Support for ``pyodbc``. -- Add ``__contains__`` feature to Environ class. +- Added ``__contains__`` feature to Environ class. Fixed +++++ @@ -207,7 +207,7 @@ Added Changed +++++++ -- Fix uwsgi settings reload problem +- Fixed uwsgi settings reload problem `#55 `_. - Update support for ``django-redis`` urls `#109 `_. @@ -220,26 +220,26 @@ Added Changed +++++++ -- Fix for unsafe characters into URLs. +- Fixed for unsafe characters into URLs. - Clarifying warning on missing or unreadable file. Thanks to `@nickcatal `_. -- Fix support for Oracle urls. -- Fix support for ``django-redis``. +- Fixed support for Oracle urls. +- Fixed support for ``django-redis``. `v0.4`_ - 23-September-2015 --------------------------- Added +++++ - New email schemes - ``smtp+ssl`` and ``smtp+tls`` (``smtps`` would be deprecated). -- Add tuple support. Thanks to `@anonymouzz `_. -- Add LDAP url support for database. Thanks to +- Added tuple support. Thanks to `@anonymouzz `_. +- Added LDAP url support for database. Thanks to `django-ldapdb/django-ldapdb `_. Changed +++++++ -- Fix non-ascii values (broken in Python 2.x). +- Fixed non-ascii values (broken in Python 2.x). - ``redis_cache`` replaced by ``django_redis``. -- Fix psql/pgsql url. +- Fixed psql/pgsql url. `v0.3.1`_ - 19 Sep 2015 @@ -261,9 +261,9 @@ Fixed ---------------------- Added +++++ -- Add cache url support. -- Add email url support. -- Add search url support. +- Added cache url support. +- Added email url support. +- Added search url support. Changed +++++++ @@ -279,7 +279,7 @@ v0.2 - 16-April-2013 -------------------- Added +++++ -- Add advanced float parsing (comma and dot symbols to separate thousands and decimals). +- Added advanced float parsing (comma and dot symbols to separate thousands and decimals). Fixed +++++ From 7f6868b2c7f999c43c8c0a139fa243dbb8c0d85c Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 15 Jun 2022 09:29:17 +0200 Subject: [PATCH 47/49] Code cleanup --- docs/index.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 8d205055..0142a3b9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,3 @@ -.. module:: environ - ======================================= Welcome to django-environ documentation ======================================= From 3eab5c44d79fa508b45940978c6cc7703a9c5422 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 15 Jun 2022 11:49:13 +0200 Subject: [PATCH 48/49] Do not check links to compare tags --- docs/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index e7d7fe57..9003ad89 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -115,6 +115,8 @@ def find_version(meta_file): linkcheck_ignore = [ # We run into GitHub's rate limits. r"https://github.com/.*/(issues|pull)/\d+", + # Do not check links to compare tags. + r"https://github.com/joke2k/django-environ/compare/.*", ] # From bccbae4158b2f4276d31d0601c8a0c4251af812b Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 15 Jun 2022 11:49:39 +0200 Subject: [PATCH 49/49] Prepare to release --- CHANGELOG.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ba9d9a5d..2c3e17dd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is inspired by `Keep a Changelog `_ and this project adheres to `Semantic Versioning `_. -`v0.9.0`_ - 00-Unreleased-2021 +`v0.9.0`_ - 15-June-2022 ------------------------------ Added +++++ @@ -292,7 +292,7 @@ Added - Initial release. -.. _v0.9.0: https://github.com/joke2k/django-environ/compare/v0.8.1...develop +.. _v0.9.0: https://github.com/joke2k/django-environ/compare/v0.8.1...v0.9.0 .. _v0.8.1: https://github.com/joke2k/django-environ/compare/v0.8.0...v0.8.1 .. _v0.8.0: https://github.com/joke2k/django-environ/compare/v0.7.0...v0.8.0 .. _v0.7.0: https://github.com/joke2k/django-environ/compare/v0.6.0...v0.7.0