mirror of
https://github.com/jazzband/dj-database-url.git
synced 2026-03-16 22:20:24 +00:00
Compare commits
84 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b73ad6d9c9 | ||
|
|
088b969573 | ||
|
|
e77149f799 | ||
|
|
6beffe6de6 | ||
|
|
f9c31305ae | ||
|
|
5337838ac3 | ||
|
|
6fc366459f | ||
|
|
19805c97f3 | ||
|
|
1b102cd428 | ||
|
|
e41afda72b | ||
|
|
dba6077081 | ||
|
|
e6f4ccc49d | ||
|
|
a87f12185e | ||
|
|
1ca8ccf7db | ||
|
|
afad969e94 | ||
|
|
6273af6660 | ||
|
|
0a59175489 | ||
|
|
51b51776a9 | ||
|
|
d8f63c8801 | ||
|
|
36ec238b03 | ||
|
|
84fd0508ff | ||
|
|
2bf2118232 | ||
|
|
527962ec04 | ||
|
|
992999193d | ||
|
|
59b3cd6283 | ||
|
|
37b252e678 | ||
|
|
a01d6b1551 | ||
|
|
989429838a | ||
|
|
438ec393ab | ||
|
|
8a3c372deb | ||
|
|
390d524112 | ||
|
|
58ed5a2276 | ||
|
|
7cc1121b29 | ||
|
|
0819957676 | ||
|
|
34170b5aef | ||
|
|
ab20a3aebd | ||
|
|
7d47e451a0 | ||
|
|
ae2fabf95e | ||
|
|
7c47649053 | ||
|
|
3e8b69aafc | ||
|
|
4977c7d0d6 | ||
|
|
a139abcf22 | ||
|
|
e4ccc63ec6 | ||
|
|
9598fb0d85 | ||
|
|
cdb5d645ec | ||
|
|
15ac40b0a7 | ||
|
|
79d929b4e0 | ||
|
|
c469885ebf | ||
|
|
e90f975526 | ||
|
|
5f5af49340 | ||
|
|
8501773f73 | ||
|
|
358746c41d | ||
|
|
0f8cfdf1c9 | ||
|
|
3505200b18 | ||
|
|
396344b251 | ||
|
|
64660c7c5f | ||
|
|
eff9f27514 | ||
|
|
08d9cf0978 | ||
|
|
99128b507b | ||
|
|
45ac3bbf89 | ||
|
|
8d32603e18 | ||
|
|
ed306f37ca | ||
|
|
3aad9adc71 | ||
|
|
51773f3a89 | ||
|
|
5a91d7a2c7 | ||
|
|
cda5b0c3ce | ||
|
|
7906f3bf80 | ||
|
|
5106a19129 | ||
|
|
b27bd63e82 | ||
|
|
bcf163e3ab | ||
|
|
86f04ef5c6 | ||
|
|
fc0dca1b25 | ||
|
|
ebb86f9b9a | ||
|
|
1412cef6b5 | ||
|
|
186900d937 | ||
|
|
d23a2ca4c5 | ||
|
|
0879f127b9 | ||
|
|
9a74cc5283 | ||
|
|
44bfba5fa2 | ||
|
|
ee2cb860f5 | ||
|
|
9ea2808ed2 | ||
|
|
2d46742db5 | ||
|
|
4a5a75c0f2 | ||
|
|
f600d9089b |
13 changed files with 1717 additions and 425 deletions
5
.flake8
5
.flake8
|
|
@ -1,5 +0,0 @@
|
||||||
[flake8]
|
|
||||||
max-line-length = 88
|
|
||||||
extend-ignore = E203
|
|
||||||
per-file-ignores=
|
|
||||||
tests/test_dj_database_url.py: E501, E265
|
|
||||||
32
.github/workflows/release.yml
vendored
32
.github/workflows/release.yml
vendored
|
|
@ -1,9 +1,8 @@
|
||||||
name: Release
|
name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
tags:
|
types: [published]
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
@ -11,28 +10,29 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set version from release/tag
|
||||||
uses: actions/setup-python@v2
|
id: version
|
||||||
with:
|
|
||||||
python-version: '3.10'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
run: |
|
||||||
python -m pip install -U pip
|
VERSION=${GITHUB_REF#refs/*/}
|
||||||
python -m pip install -U setuptools wheel twine
|
VERSION=${VERSION#v}
|
||||||
|
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: astral-sh/setup-uv@v7
|
||||||
|
with:
|
||||||
|
python-version: 3.12
|
||||||
|
|
||||||
- name: Build package
|
- name: Build package
|
||||||
run: |
|
run: |
|
||||||
python setup.py --version
|
uv version ${{ env.VERSION }}
|
||||||
python setup.py sdist --format=gztar bdist_wheel
|
uv build
|
||||||
twine check dist/*
|
uvx twine check dist/*
|
||||||
|
|
||||||
- name: Upload packages to Jazzband
|
- name: Upload packages to Jazzband
|
||||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
user: jazzband
|
user: jazzband
|
||||||
|
|
|
||||||
87
.github/workflows/test.yml
vendored
87
.github/workflows/test.yml
vendored
|
|
@ -1,55 +1,74 @@
|
||||||
name: test
|
name: test
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
|
formatting:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
|
- name: Run ruff
|
||||||
|
run: uvx ruff check
|
||||||
|
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
|
- name: Run mypy
|
||||||
|
run: uvx mypy dj_database_url
|
||||||
|
|
||||||
|
- name: Run pyright
|
||||||
|
run: uvx pyright dj_database_url
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: [formatting, typecheck]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||||
django-version: ["3.2", "4.0", "4.1"]
|
django-version: ["4.2", "5.2", "6.0"]
|
||||||
exclude:
|
exclude:
|
||||||
# Python 3.7 is not compatible with 4.0
|
# django 4.x is not compatible with python 3.13 or higher
|
||||||
- python-version: "3.7"
|
- python-version: "3.13"
|
||||||
django-version: "4.0"
|
django-version: "4.2"
|
||||||
# Python 3.7 is not compatible with 4.1
|
- python-version: "3.14"
|
||||||
- python-version: "3.7"
|
django-version: "4.2"
|
||||||
django-version: "4.1"
|
# django 6.x is not compatible with python 3.11 or lower
|
||||||
# Python 3.11 is not compatible with 3.2
|
- python-version: "3.10"
|
||||||
|
django-version: "6.0"
|
||||||
- python-version: "3.11"
|
- python-version: "3.11"
|
||||||
django-version: "3.2"
|
django-version: "6.0"
|
||||||
# Python 3.11 is not compatible with 4.0
|
|
||||||
- python-version: "3.11"
|
|
||||||
django-version: "4.0"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
steps:
|
||||||
uses: actions/setup-python@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install uv and set the Python version
|
||||||
|
uses: astral-sh/setup-uv@v7
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache: 'pip'
|
activate-environment: 'true'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
pip install -r requirements.txt
|
uv pip install "Django~=${{ matrix.django-version }}.0"
|
||||||
pip install "Django~=${{ matrix.django-version }}.0" .
|
|
||||||
|
|
||||||
- name: Run mypy
|
|
||||||
run: |
|
|
||||||
python -m mypy dj_database_url
|
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
echo "$(python --version) / Django $(django-admin --version)"
|
echo "$(python --version) / Django $(django-admin --version)"
|
||||||
coverage run --source=dj_database_url --branch -m unittest discover -v
|
uvx coverage run --source=dj_database_url --branch -m unittest discover -v
|
||||||
coverage report
|
uvx coverage report
|
||||||
coverage xml
|
uvx coverage xml
|
||||||
|
|
||||||
- uses: codecov/codecov-action@v3
|
- uses: codecov/codecov-action@v4
|
||||||
|
|
||||||
- name: Check mypy types installation
|
|
||||||
run: |
|
|
||||||
pip install .
|
|
||||||
cd tests
|
|
||||||
python -m mypy .
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
[settings]
|
|
||||||
profile = black
|
|
||||||
|
|
@ -1,23 +1,21 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
|
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: "5.12.0"
|
rev: v0.15.4
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: ruff-check
|
||||||
args: ["--profile", "black"]
|
args:
|
||||||
|
- --fix
|
||||||
|
- id: ruff-format
|
||||||
|
args:
|
||||||
|
- --quiet
|
||||||
|
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/crate-ci/typos
|
||||||
rev: 23.3.0
|
rev: v1
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: typos
|
||||||
args: [--target-version=py38]
|
|
||||||
|
|
||||||
- repo: https://github.com/pycqa/flake8
|
|
||||||
rev: '6.0.0'
|
|
||||||
hooks:
|
|
||||||
- id: flake8
|
|
||||||
|
|
|
||||||
84
CHANGELOG.md
84
CHANGELOG.md
|
|
@ -1,49 +1,69 @@
|
||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
## v2.1.0 (2023-08-15)
|
## v3.1.0 (2026-01-03)
|
||||||
|
* Add support for Django 6.0
|
||||||
|
* Update CI structure.
|
||||||
|
* Migrate to UV for dependency management and builds.
|
||||||
|
* Python >3.10 support.
|
||||||
|
|
||||||
|
## v3.0.1 (2025-07-01)
|
||||||
|
* Drop dependency on `typing_extensions`.
|
||||||
|
|
||||||
|
## v3.0.0 (2025-05-18)
|
||||||
|
> Bumping to version 3; changes to code do break some API compatibility.
|
||||||
|
* Implement a new decorator registry pattern to implement checks on database connection string.
|
||||||
|
* You can now support and implement your own database strings by extending the @register functionality.
|
||||||
|
* Update supported python versions and django versions.
|
||||||
|
|
||||||
|
## v2.3.0 (2024-10-23)
|
||||||
|
* Remove Python 3.8 support.
|
||||||
|
* Remove Django 3 support.
|
||||||
|
* Add python 3.13 support.
|
||||||
|
* Add Django 5.1 to the testing library.
|
||||||
|
|
||||||
|
## v2.2.0 (2024-05-28)
|
||||||
|
* Add disable_server_side_cursors parameter
|
||||||
|
* Enhance Query String Parsing for Server-Side Binding in Django 4.2 with psycopg 3.1.8+
|
||||||
|
* Update django 5.0 python compatibility by @mattseymour in #239
|
||||||
|
* Improved internals
|
||||||
|
* Improved documentation
|
||||||
|
|
||||||
|
## v2.1.0 (2023-08-15)
|
||||||
* Add value to int parsing when deconstructing url string.
|
* Add value to int parsing when deconstructing url string.
|
||||||
|
|
||||||
## v2.0.0 (2023-04-27)
|
## v2.0.0 (2023-04-27)
|
||||||
|
* Update project setup such that we now install it as a package.
|
||||||
* Update project setup such that we now install as a package.
|
|
||||||
|
|
||||||
_Notes_: while this does not alter the underlying application code, we are bumping to
|
_Notes_: while this does not alter the underlying application code, we are bumping to
|
||||||
2.0 incase there are unforeseen knock on use-case issues.
|
2.0 incase there are unforeseen knock-on use-case issues.
|
||||||
|
|
||||||
## v1.3.0 (2023-03-27)
|
## v1.3.0 (2023-03-27)
|
||||||
|
|
||||||
* Cosmetic changes to the generation of schemes.
|
* Cosmetic changes to the generation of schemes.
|
||||||
* Bump isort version - 5.11.5.
|
* Bump isort version - 5.11.5.
|
||||||
* raise warning message if database_url is not set.
|
* raise a warning message if `database_url` is not set.
|
||||||
* CONN_MAX_AGE fix type - Optional[int].
|
* `CONN_MAX_AGE` fix type - `Optional[int]`.
|
||||||
|
|
||||||
## v1.2.0 (2022-12-13)
|
## v1.2.0 (2022-12-13)
|
||||||
|
|
||||||
* Add the ability to add test databases.
|
* Add the ability to add test databases.
|
||||||
* Improve url parsing and encoding.
|
* Improve url parsing and encoding.
|
||||||
* Fix missing parameter conn_health_check in check function.
|
* Fix missing parameter `conn_health_check` in check function.
|
||||||
|
|
||||||
## v1.1.0 (2022-12-12)
|
## v1.1.0 (2022-12-12)
|
||||||
|
|
||||||
* Option for connection health checks parameter.
|
* Option for connection health checks parameter.
|
||||||
* Update supported version python 3.11.
|
* Update supported version python 3.11.
|
||||||
* Code changes, various improvments.
|
* Code changes, various improvements.
|
||||||
* Add project links to setup.py
|
* Add project links to `setup.py`.
|
||||||
|
|
||||||
## v1.0.0 (2022-06-18)
|
## v1.0.0 (2022-06-18)
|
||||||
|
Initial release of code, dj-database-urls is now part of [Jazzband](https://jazzband.co/).
|
||||||
Initial release of code now dj-database-urls is part of jazzband.
|
|
||||||
|
|
||||||
* Add support for cockroachdb.
|
* Add support for cockroachdb.
|
||||||
* Add support for the offical MSSQL connector.
|
* Add support for the official MSSQL connector.
|
||||||
* Update License to be compatible with Jazzband.
|
* Update License to be compatible with Jazzband.
|
||||||
* Remove support for Python < 3.5 including Python 2.7
|
* Remove support for Python < 3.5 including Python 2.7
|
||||||
* Update source code to Black format.
|
* Update source code to Black format.
|
||||||
* Update CI using pre-commit
|
* Update CI using pre-commit
|
||||||
|
|
||||||
## v0.5.0 (2018-03-01)
|
## v0.5.0 (2018-03-01)
|
||||||
|
|
||||||
- Use str port for mssql
|
- Use str port for mssql
|
||||||
- Added license
|
- Added license
|
||||||
- Add mssql to readme
|
- Add mssql to readme
|
||||||
|
|
@ -63,17 +83,15 @@ Initial release of code now dj-database-urls is part of jazzband.
|
||||||
- Added SpatiaLite in README.rst
|
- Added SpatiaLite in README.rst
|
||||||
|
|
||||||
## v0.4.1 (2016-04-06)
|
## v0.4.1 (2016-04-06)
|
||||||
|
|
||||||
- Enable CA providing for MySQL URIs
|
- Enable CA providing for MySQL URIs
|
||||||
- Update Readme
|
- Update Readme
|
||||||
- Update trove classifiers
|
- Update trove classifiers
|
||||||
- Updated setup.py description
|
- Updated setup.py description
|
||||||
|
|
||||||
## v0.4.0 (2016-02-04)
|
## v0.4.0 (2016-02-04)
|
||||||
|
|
||||||
- Update readme
|
- Update readme
|
||||||
- Fix for python3
|
- Fix for python3
|
||||||
- Handle search path config in connect url for postgres
|
- Handle search path config in connection url for postgres
|
||||||
- Add tox config to ease testing against multiple Python versions
|
- Add tox config to ease testing against multiple Python versions
|
||||||
- Simplified the querystring parse logic
|
- Simplified the querystring parse logic
|
||||||
- Cleaned up querystring parsing
|
- Cleaned up querystring parsing
|
||||||
|
|
@ -90,27 +108,25 @@ Initial release of code now dj-database-urls is part of jazzband.
|
||||||
- Added support for python mysql-connector
|
- Added support for python mysql-connector
|
||||||
|
|
||||||
## v0.3.0 (2014-03-10)
|
## v0.3.0 (2014-03-10)
|
||||||
|
- Add `.gitignore` file
|
||||||
- Add .gitignore file
|
- Remove `.pyc` file
|
||||||
- Remove .pyc file
|
- Remove travis-ci unsupported python versions (Travis CI supports Python versions 2.6, 2.7, 3.2 and 3.3)
|
||||||
- Remove travis-ci unsupported python version Per docs http://docs.travis-ci.com/user/languages/python/ "Travis CI support Python versions 2.6, 2.7, 3.2 and 3.3"
|
|
||||||
- Fix cleardb test
|
- Fix cleardb test
|
||||||
- Add setup.cfg for wheel support
|
- Add `setup.cfg` for wheel support
|
||||||
- Add trove classifiers for python versions
|
- Add trove classifiers for python versions
|
||||||
- Replace Python 3.1 with Python 3.3
|
- Replace Python 3.1 with Python 3.3
|
||||||
- Add MySQL (GIS) support
|
- Add MySQL (GIS) support
|
||||||
- Ability to set different engine
|
- Ability to set different engine
|
||||||
|
|
||||||
## v0.2.2 (2013-07-17)
|
## v0.2.2 (2013-07-17)
|
||||||
|
|
||||||
- Added spatialite to uses_netloc too
|
- Added spatialite to uses_netloc too
|
||||||
- Added spatialite backend
|
- Added spatialite backend
|
||||||
- Replacing tab with spaces
|
- Replacing tab with spaces
|
||||||
- Handling special case of sqlite://:memory:
|
- Handling special case of `sqlite://:memory:`
|
||||||
- Empty sqlite path will now use a :memory: database
|
- Empty sqlite path will now use a :memory: database
|
||||||
- Fixing test to actually use the result of the parse
|
- Fixing test to actually use the result of the parse
|
||||||
- Adding in tests to ensure sqlite in-memory databases work
|
- Adding in tests to ensure sqlite in-memory databases work
|
||||||
- Fixed too-short title underline
|
- Fixed a too-short title underline
|
||||||
- Added :target: attribute to Travis status image in README
|
- Added :target: attribute to Travis status image in README
|
||||||
- Added docs for default argument to config
|
- Added docs for default argument to config
|
||||||
- Add "pgsql" as a PostgreSQL URL scheme.
|
- Add "pgsql" as a PostgreSQL URL scheme.
|
||||||
|
|
@ -118,7 +134,6 @@ Initial release of code now dj-database-urls is part of jazzband.
|
||||||
- fixed url
|
- fixed url
|
||||||
|
|
||||||
## v0.2.1 (2012-06-19)
|
## v0.2.1 (2012-06-19)
|
||||||
|
|
||||||
- Add python3 support
|
- Add python3 support
|
||||||
- Adding travis status and tests
|
- Adding travis status and tests
|
||||||
- Adding test environment variables
|
- Adding test environment variables
|
||||||
|
|
@ -130,28 +145,23 @@ Initial release of code now dj-database-urls is part of jazzband.
|
||||||
- RedHat's OpenShift platform uses the 'postgresql' scheme
|
- RedHat's OpenShift platform uses the 'postgresql' scheme
|
||||||
- Registered postgis URL scheme
|
- Registered postgis URL scheme
|
||||||
- Added `postgis://` url scheme
|
- Added `postgis://` url scheme
|
||||||
- Use get() on os.environ instead of an if
|
- Use `get()` on `os.environ` instead of an `if`.
|
||||||
|
|
||||||
## v0.2.0 (2012-05-30)
|
## v0.2.0 (2012-05-30)
|
||||||
|
|
||||||
- Fix parse(s)
|
- Fix parse(s)
|
||||||
|
|
||||||
## v0.1.4 (2012-05-30)
|
## v0.1.4 (2012-05-30)
|
||||||
|
|
||||||
- Add defaults for env
|
- Add defaults for env
|
||||||
- Set the DATABASES dict rather than assigning to it
|
- Set the DATABASES dict rather than assigning to it
|
||||||
|
|
||||||
## v0.1.3 (2012-05-01)
|
## v0.1.3 (2012-05-01)
|
||||||
|
- Add a note to README on supported databases
|
||||||
- Add note to README on supported databases
|
|
||||||
- Add support for SQLite
|
- Add support for SQLite
|
||||||
- Clean dependencies
|
- Clean dependencies
|
||||||
|
|
||||||
## v0.1.2 (2012-04-30)
|
## v0.1.2 (2012-04-30)
|
||||||
|
|
||||||
- Update readme
|
- Update readme
|
||||||
- Refactor config and use new parse function
|
- Refactor config and use a new parse function
|
||||||
|
|
||||||
## v0.1.1 (2012-04-30) First release
|
## v0.1.1 (2012-04-30) First release
|
||||||
|
|
||||||
🐍 ✨
|
🐍 ✨
|
||||||
|
|
|
||||||
64
README.rst
64
README.rst
|
|
@ -22,12 +22,6 @@ also a `conn_max_age` argument to easily enable Django's connection pool.
|
||||||
If you'd rather not use an environment variable, you can pass a URL in directly
|
If you'd rather not use an environment variable, you can pass a URL in directly
|
||||||
instead to ``dj_database_url.parse``.
|
instead to ``dj_database_url.parse``.
|
||||||
|
|
||||||
Supported Databases
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Support currently exists for PostgreSQL, PostGIS, MySQL, MySQL (GIS),
|
|
||||||
Oracle, Oracle (GIS), Redshift, CockroachDB, Timescale, Timescale (GIS) and SQLite.
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
@ -148,6 +142,63 @@ and should instead be passed as:
|
||||||
DATABASES['default'] = dj_database_url.config(default='postgres://...', test_options={'NAME': 'mytestdatabase'})
|
DATABASES['default'] = dj_database_url.config(default='postgres://...', test_options={'NAME': 'mytestdatabase'})
|
||||||
|
|
||||||
|
|
||||||
|
Supported Databases
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Support currently exists for PostgreSQL, PostGIS, MySQL, MySQL (GIS),
|
||||||
|
Oracle, Oracle (GIS), Redshift, CockroachDB, Timescale, Timescale (GIS) and SQLite.
|
||||||
|
|
||||||
|
If you want to use
|
||||||
|
some non-default backends, you need to register them first:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import dj_database_url
|
||||||
|
|
||||||
|
# registration should be performed only once
|
||||||
|
dj_database_url.register("mysql-connector", "mysql.connector.django")
|
||||||
|
|
||||||
|
assert dj_database_url.parse("mysql-connector://user:password@host:port/db-name") == {
|
||||||
|
"ENGINE": "mysql.connector.django",
|
||||||
|
# ...other connection params
|
||||||
|
}
|
||||||
|
|
||||||
|
Some backends need further config adjustments (e.g. oracle and mssql
|
||||||
|
expect ``PORT`` to be a string). For such cases you can provide a
|
||||||
|
post-processing function to ``register()`` (note that ``register()`` is
|
||||||
|
used as a **decorator(!)** in this case):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import dj_database_url
|
||||||
|
|
||||||
|
@dj_database_url.register("mssql", "sql_server.pyodbc")
|
||||||
|
def stringify_port(config):
|
||||||
|
config["PORT"] = str(config["PORT"])
|
||||||
|
|
||||||
|
@dj_database_url.register("redshift", "django_redshift_backend")
|
||||||
|
def apply_current_schema(config):
|
||||||
|
options = config["OPTIONS"]
|
||||||
|
schema = options.pop("currentSchema", None)
|
||||||
|
if schema:
|
||||||
|
options["options"] = f"-c search_path={schema}"
|
||||||
|
|
||||||
|
@dj_database_url.register("snowflake", "django_snowflake")
|
||||||
|
def adjust_snowflake_config(config):
|
||||||
|
config.pop("PORT", None)
|
||||||
|
config["ACCOUNT"] = config.pop("HOST")
|
||||||
|
name, _, schema = config["NAME"].partition("/")
|
||||||
|
if schema:
|
||||||
|
config["SCHEMA"] = schema
|
||||||
|
config["NAME"] = name
|
||||||
|
options = config.get("OPTIONS", {})
|
||||||
|
warehouse = options.pop("warehouse", None)
|
||||||
|
if warehouse:
|
||||||
|
config["WAREHOUSE"] = warehouse
|
||||||
|
role = options.pop("role", None)
|
||||||
|
if role:
|
||||||
|
config["ROLE"] = role
|
||||||
|
|
||||||
URL schema
|
URL schema
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
@ -155,6 +206,7 @@ URL schema
|
||||||
| Engine | Django Backend | URL |
|
| Engine | Django Backend | URL |
|
||||||
+======================+===============================================+==================================================+
|
+======================+===============================================+==================================================+
|
||||||
| PostgreSQL | ``django.db.backends.postgresql`` [1]_ | ``postgres://USER:PASSWORD@HOST:PORT/NAME`` [2]_ |
|
| PostgreSQL | ``django.db.backends.postgresql`` [1]_ | ``postgres://USER:PASSWORD@HOST:PORT/NAME`` [2]_ |
|
||||||
|
| | | ``postgresql://USER:PASSWORD@HOST:PORT/NAME`` |
|
||||||
+----------------------+-----------------------------------------------+--------------------------------------------------+
|
+----------------------+-----------------------------------------------+--------------------------------------------------+
|
||||||
| PostGIS | ``django.contrib.gis.db.backends.postgis`` | ``postgis://USER:PASSWORD@HOST:PORT/NAME`` |
|
| PostGIS | ``django.contrib.gis.db.backends.postgis`` | ``postgis://USER:PASSWORD@HOST:PORT/NAME`` |
|
||||||
+----------------------+-----------------------------------------------+--------------------------------------------------+
|
+----------------------+-----------------------------------------------+--------------------------------------------------+
|
||||||
|
|
|
||||||
|
|
@ -1,76 +1,156 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import urllib.parse as urlparse
|
import urllib.parse as urlparse
|
||||||
from typing import Any, Dict, Optional, Union
|
from collections.abc import Callable
|
||||||
|
from typing import Any, TypedDict
|
||||||
from typing_extensions import TypedDict
|
|
||||||
|
|
||||||
DEFAULT_ENV = "DATABASE_URL"
|
DEFAULT_ENV = "DATABASE_URL"
|
||||||
|
ENGINE_SCHEMES: dict[str, "Engine"] = {}
|
||||||
SCHEMES = {
|
|
||||||
"postgres": "django.db.backends.postgresql",
|
|
||||||
"postgresql": "django.db.backends.postgresql",
|
|
||||||
"pgsql": "django.db.backends.postgresql",
|
|
||||||
"postgis": "django.contrib.gis.db.backends.postgis",
|
|
||||||
"mysql": "django.db.backends.mysql",
|
|
||||||
"mysql2": "django.db.backends.mysql",
|
|
||||||
"mysqlgis": "django.contrib.gis.db.backends.mysql",
|
|
||||||
"mysql-connector": "mysql.connector.django",
|
|
||||||
"mssql": "sql_server.pyodbc",
|
|
||||||
"mssqlms": "mssql",
|
|
||||||
"spatialite": "django.contrib.gis.db.backends.spatialite",
|
|
||||||
"sqlite": "django.db.backends.sqlite3",
|
|
||||||
"oracle": "django.db.backends.oracle",
|
|
||||||
"oraclegis": "django.contrib.gis.db.backends.oracle",
|
|
||||||
"redshift": "django_redshift_backend",
|
|
||||||
"cockroach": "django_cockroachdb",
|
|
||||||
"timescale": "timescale.db.backends.postgresql",
|
|
||||||
"timescalegis": "timescale.db.backends.postgis",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Register database schemes in URLs.
|
|
||||||
for key in SCHEMES.keys():
|
|
||||||
urlparse.uses_netloc.append(key)
|
|
||||||
|
|
||||||
|
|
||||||
# From https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
# From https://docs.djangoproject.com/en/stable/ref/settings/#databases
|
||||||
class DBConfig(TypedDict, total=False):
|
class DBConfig(TypedDict, total=False):
|
||||||
ATOMIC_REQUESTS: bool
|
ATOMIC_REQUESTS: bool
|
||||||
AUTOCOMMIT: bool
|
AUTOCOMMIT: bool
|
||||||
CONN_MAX_AGE: Optional[int]
|
CONN_MAX_AGE: int | None
|
||||||
CONN_HEALTH_CHECKS: bool
|
CONN_HEALTH_CHECKS: bool
|
||||||
DISABLE_SERVER_SIDE_CURSORS: bool
|
DISABLE_SERVER_SIDE_CURSORS: bool
|
||||||
ENGINE: str
|
ENGINE: str
|
||||||
HOST: str
|
HOST: str
|
||||||
NAME: str
|
NAME: str
|
||||||
OPTIONS: Optional[Dict[str, Any]]
|
OPTIONS: dict[str, Any]
|
||||||
PASSWORD: str
|
PASSWORD: str
|
||||||
PORT: Union[str, int]
|
PORT: str | int
|
||||||
TEST: Dict[str, Any]
|
TEST: dict[str, Any]
|
||||||
TIME_ZONE: str
|
TIME_ZONE: str
|
||||||
USER: str
|
USER: str
|
||||||
|
|
||||||
|
|
||||||
|
PostprocessCallable = Callable[[DBConfig], None]
|
||||||
|
OptionType = int | str | bool
|
||||||
|
|
||||||
|
|
||||||
|
class ParseError(ValueError):
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return (
|
||||||
|
"This string is not a valid url, possibly because some of its parts"
|
||||||
|
" is not properly urllib.parse.quote()'ed."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownSchemeError(ValueError):
|
||||||
|
def __init__(self, scheme: str):
|
||||||
|
self.scheme = scheme
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
schemes = ", ".join(sorted(ENGINE_SCHEMES.keys()))
|
||||||
|
return (
|
||||||
|
f"Scheme '{self.scheme}://' is unknown."
|
||||||
|
" Did you forget to register custom backend?"
|
||||||
|
f" Following schemes have registered backends: {schemes}."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def default_postprocess(parsed_config: DBConfig) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Engine:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
backend: str,
|
||||||
|
postprocess: PostprocessCallable = default_postprocess,
|
||||||
|
):
|
||||||
|
self.backend = backend
|
||||||
|
self.postprocess = postprocess
|
||||||
|
|
||||||
|
|
||||||
|
def register(
|
||||||
|
scheme: str, backend: str
|
||||||
|
) -> Callable[[PostprocessCallable], PostprocessCallable]:
|
||||||
|
engine = Engine(backend)
|
||||||
|
if scheme not in ENGINE_SCHEMES:
|
||||||
|
urlparse.uses_netloc.append(scheme)
|
||||||
|
ENGINE_SCHEMES[scheme] = engine
|
||||||
|
|
||||||
|
def inner(func: PostprocessCallable) -> PostprocessCallable:
|
||||||
|
engine.postprocess = func
|
||||||
|
return func
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
register("spatialite", "django.contrib.gis.db.backends.spatialite")
|
||||||
|
register("mysql-connector", "mysql.connector.django")
|
||||||
|
register("mysqlgis", "django.contrib.gis.db.backends.mysql")
|
||||||
|
register("oraclegis", "django.contrib.gis.db.backends.oracle")
|
||||||
|
register("cockroach", "django_cockroachdb")
|
||||||
|
|
||||||
|
|
||||||
|
@register("sqlite", "django.db.backends.sqlite3")
|
||||||
|
def default_to_in_memory_db(parsed_config: DBConfig) -> None:
|
||||||
|
# mimic sqlalchemy behaviour
|
||||||
|
if not parsed_config.get("NAME"):
|
||||||
|
parsed_config["NAME"] = ":memory:"
|
||||||
|
|
||||||
|
|
||||||
|
@register("oracle", "django.db.backends.oracle")
|
||||||
|
@register("mssqlms", "mssql")
|
||||||
|
@register("mssql", "sql_server.pyodbc")
|
||||||
|
def stringify_port(parsed_config: DBConfig) -> None:
|
||||||
|
parsed_config["PORT"] = str(parsed_config.get("PORT", ""))
|
||||||
|
|
||||||
|
|
||||||
|
@register("mysql", "django.db.backends.mysql")
|
||||||
|
@register("mysql2", "django.db.backends.mysql")
|
||||||
|
def apply_ssl_ca(parsed_config: DBConfig) -> None:
|
||||||
|
options = parsed_config.get("OPTIONS", {})
|
||||||
|
ca = options.pop("ssl-ca", None)
|
||||||
|
if ca:
|
||||||
|
options["ssl"] = {"ca": ca}
|
||||||
|
|
||||||
|
|
||||||
|
@register("postgres", "django.db.backends.postgresql")
|
||||||
|
@register("postgresql", "django.db.backends.postgresql")
|
||||||
|
@register("pgsql", "django.db.backends.postgresql")
|
||||||
|
@register("postgis", "django.contrib.gis.db.backends.postgis")
|
||||||
|
@register("redshift", "django_redshift_backend")
|
||||||
|
@register("timescale", "timescale.db.backends.postgresql")
|
||||||
|
@register("timescalegis", "timescale.db.backends.postgis")
|
||||||
|
def apply_current_schema(parsed_config: DBConfig) -> None:
|
||||||
|
options = parsed_config.get("OPTIONS", {})
|
||||||
|
schema = options.pop("currentSchema", None)
|
||||||
|
if schema:
|
||||||
|
options["options"] = f"-c search_path={schema}"
|
||||||
|
|
||||||
|
|
||||||
def config(
|
def config(
|
||||||
env: str = DEFAULT_ENV,
|
env: str = DEFAULT_ENV,
|
||||||
default: Optional[str] = None,
|
default: str | None = None,
|
||||||
engine: Optional[str] = None,
|
engine: str | None = None,
|
||||||
conn_max_age: Optional[int] = 0,
|
conn_max_age: int | None = 0,
|
||||||
conn_health_checks: bool = False,
|
conn_health_checks: bool = False,
|
||||||
|
disable_server_side_cursors: bool = False,
|
||||||
ssl_require: bool = False,
|
ssl_require: bool = False,
|
||||||
test_options: Optional[Dict] = None,
|
test_options: dict[str, Any] | None = None,
|
||||||
) -> DBConfig:
|
) -> DBConfig:
|
||||||
"""Returns configured DATABASE dictionary from DATABASE_URL."""
|
"""Returns configured DATABASE dictionary from DATABASE_URL."""
|
||||||
s = os.environ.get(env, default)
|
s = os.environ.get(env, default)
|
||||||
|
|
||||||
if s is None:
|
if s is None:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"No %s environment variable set, and so no databases setup" % env
|
"No %s environment variable set, and so no databases setup", env
|
||||||
)
|
)
|
||||||
|
|
||||||
if s:
|
if s:
|
||||||
return parse(
|
return parse(
|
||||||
s, engine, conn_max_age, conn_health_checks, ssl_require, test_options
|
s,
|
||||||
|
engine,
|
||||||
|
conn_max_age,
|
||||||
|
conn_health_checks,
|
||||||
|
disable_server_side_cursors,
|
||||||
|
ssl_require,
|
||||||
|
test_options,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
@ -78,110 +158,96 @@ def config(
|
||||||
|
|
||||||
def parse(
|
def parse(
|
||||||
url: str,
|
url: str,
|
||||||
engine: Optional[str] = None,
|
engine: str | None = None,
|
||||||
conn_max_age: Optional[int] = 0,
|
conn_max_age: int | None = 0,
|
||||||
conn_health_checks: bool = False,
|
conn_health_checks: bool = False,
|
||||||
|
disable_server_side_cursors: bool = False,
|
||||||
ssl_require: bool = False,
|
ssl_require: bool = False,
|
||||||
test_options: Optional[dict] = None,
|
test_options: dict[str, Any] | None = None,
|
||||||
) -> DBConfig:
|
) -> DBConfig:
|
||||||
"""Parses a database URL."""
|
"""Parses a database URL and returns configured DATABASE dictionary."""
|
||||||
|
settings = _convert_to_settings(
|
||||||
|
engine,
|
||||||
|
conn_max_age,
|
||||||
|
conn_health_checks,
|
||||||
|
disable_server_side_cursors,
|
||||||
|
ssl_require,
|
||||||
|
test_options,
|
||||||
|
)
|
||||||
|
|
||||||
if url == "sqlite://:memory:":
|
if url == "sqlite://:memory:":
|
||||||
# this is a special case, because if we pass this URL into
|
# this is a special case, because if we pass this URL into
|
||||||
# urlparse, urlparse will choke trying to interpret "memory"
|
# urlparse, urlparse will choke trying to interpret "memory"
|
||||||
# as a port number
|
# as a port number
|
||||||
return {"ENGINE": SCHEMES["sqlite"], "NAME": ":memory:"}
|
return {"ENGINE": ENGINE_SCHEMES["sqlite"].backend, "NAME": ":memory:"}
|
||||||
# note: no other settings are required for sqlite
|
# note: no other settings are required for sqlite
|
||||||
|
|
||||||
# otherwise parse the url as normal
|
try:
|
||||||
parsed_config: DBConfig = {}
|
split_result = urlparse.urlsplit(url)
|
||||||
|
engine_obj = ENGINE_SCHEMES.get(split_result.scheme)
|
||||||
if test_options is None:
|
if engine_obj is None:
|
||||||
test_options = {}
|
raise UnknownSchemeError(split_result.scheme)
|
||||||
|
path = split_result.path[1:]
|
||||||
spliturl = urlparse.urlsplit(url)
|
query = urlparse.parse_qs(split_result.query)
|
||||||
|
options = {k: _parse_option_values(v) for k, v in query.items()}
|
||||||
# Split query strings from path.
|
parsed_config: DBConfig = {
|
||||||
path = spliturl.path[1:]
|
"ENGINE": engine_obj.backend,
|
||||||
query = urlparse.parse_qs(spliturl.query)
|
"USER": urlparse.unquote(split_result.username or ""),
|
||||||
|
"PASSWORD": urlparse.unquote(split_result.password or ""),
|
||||||
# If we are using sqlite and we have no path, then assume we
|
"HOST": urlparse.unquote(split_result.hostname or ""),
|
||||||
# want an in-memory database (this is the behaviour of sqlalchemy)
|
"PORT": split_result.port or "",
|
||||||
if spliturl.scheme == "sqlite" and path == "":
|
"NAME": urlparse.unquote(path),
|
||||||
path = ":memory:"
|
"OPTIONS": options,
|
||||||
|
|
||||||
# Handle postgres percent-encoded paths.
|
|
||||||
hostname = spliturl.hostname or ""
|
|
||||||
if "%" in hostname:
|
|
||||||
# Switch to url.netloc to avoid lower cased paths
|
|
||||||
hostname = spliturl.netloc
|
|
||||||
if "@" in hostname:
|
|
||||||
hostname = hostname.rsplit("@", 1)[1]
|
|
||||||
# Use URL Parse library to decode % encodes
|
|
||||||
hostname = urlparse.unquote(hostname)
|
|
||||||
|
|
||||||
# Lookup specified engine.
|
|
||||||
if engine is None:
|
|
||||||
engine = SCHEMES.get(spliturl.scheme)
|
|
||||||
if engine is None:
|
|
||||||
raise ValueError(
|
|
||||||
"No support for '%s'. We support: %s"
|
|
||||||
% (spliturl.scheme, ", ".join(sorted(SCHEMES.keys())))
|
|
||||||
)
|
|
||||||
|
|
||||||
port = (
|
|
||||||
str(spliturl.port)
|
|
||||||
if spliturl.port
|
|
||||||
and engine in (SCHEMES["oracle"], SCHEMES["mssql"], SCHEMES["mssqlms"])
|
|
||||||
else spliturl.port
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update with environment configuration.
|
|
||||||
parsed_config.update(
|
|
||||||
{
|
|
||||||
"NAME": urlparse.unquote(path or ""),
|
|
||||||
"USER": urlparse.unquote(spliturl.username or ""),
|
|
||||||
"PASSWORD": urlparse.unquote(spliturl.password or ""),
|
|
||||||
"HOST": hostname,
|
|
||||||
"PORT": port or "",
|
|
||||||
"CONN_MAX_AGE": conn_max_age,
|
|
||||||
"CONN_HEALTH_CHECKS": conn_health_checks,
|
|
||||||
"ENGINE": engine,
|
|
||||||
}
|
}
|
||||||
)
|
except UnknownSchemeError:
|
||||||
if test_options:
|
raise
|
||||||
parsed_config.update(
|
except ValueError:
|
||||||
{
|
raise ParseError() from None
|
||||||
'TEST': test_options,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Pass the query string into OPTIONS.
|
# Guarantee that config has options, possibly empty, when postprocess() is called
|
||||||
options: Dict[str, Any] = {}
|
assert isinstance(parsed_config["OPTIONS"], dict)
|
||||||
for key, values in query.items():
|
engine_obj.postprocess(parsed_config)
|
||||||
if spliturl.scheme == "mysql" and key == "ssl-ca":
|
|
||||||
options["ssl"] = {"ca": values[-1]}
|
|
||||||
continue
|
|
||||||
|
|
||||||
try:
|
# Update the final config with any settings passed in explicitly.
|
||||||
options[key] = int(values[-1])
|
parsed_config["OPTIONS"].update(settings.pop("OPTIONS", {}))
|
||||||
except (TypeError, ValueError):
|
parsed_config.update(settings)
|
||||||
options[key] = values[-1]
|
|
||||||
|
|
||||||
if ssl_require:
|
|
||||||
options["sslmode"] = "require"
|
|
||||||
|
|
||||||
# Support for Postgres Schema URLs
|
|
||||||
if "currentSchema" in options and engine in (
|
|
||||||
"django.contrib.gis.db.backends.postgis",
|
|
||||||
"django.db.backends.postgresql_psycopg2",
|
|
||||||
"django.db.backends.postgresql",
|
|
||||||
"django_redshift_backend",
|
|
||||||
"timescale.db.backends.postgresql",
|
|
||||||
"timescale.db.backends.postgis",
|
|
||||||
):
|
|
||||||
options["options"] = "-c search_path={0}".format(options.pop("currentSchema"))
|
|
||||||
|
|
||||||
if options:
|
|
||||||
parsed_config["OPTIONS"] = options
|
|
||||||
|
|
||||||
|
if not parsed_config["OPTIONS"]:
|
||||||
|
parsed_config.pop("OPTIONS")
|
||||||
return parsed_config
|
return parsed_config
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_option_values(values: list[str]) -> OptionType | list[OptionType]:
|
||||||
|
parsed_values = [_parse_value(v) for v in values]
|
||||||
|
return parsed_values[0] if len(parsed_values) == 1 else parsed_values
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_value(value: str) -> OptionType:
|
||||||
|
if value.isdigit():
|
||||||
|
return int(value)
|
||||||
|
if value.lower() in ("true", "false"):
|
||||||
|
return value.lower() == "true"
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _convert_to_settings(
|
||||||
|
engine: str | None,
|
||||||
|
conn_max_age: int | None,
|
||||||
|
conn_health_checks: bool,
|
||||||
|
disable_server_side_cursors: bool,
|
||||||
|
ssl_require: bool,
|
||||||
|
test_options: dict[str, Any] | None,
|
||||||
|
) -> DBConfig:
|
||||||
|
settings: DBConfig = {
|
||||||
|
"CONN_MAX_AGE": conn_max_age,
|
||||||
|
"CONN_HEALTH_CHECKS": conn_health_checks,
|
||||||
|
"DISABLE_SERVER_SIDE_CURSORS": disable_server_side_cursors,
|
||||||
|
}
|
||||||
|
if engine:
|
||||||
|
settings["ENGINE"] = engine
|
||||||
|
if ssl_require:
|
||||||
|
settings["OPTIONS"] = {}
|
||||||
|
settings["OPTIONS"]["sslmode"] = "require"
|
||||||
|
if test_options:
|
||||||
|
settings["TEST"] = test_options
|
||||||
|
return settings
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,79 @@
|
||||||
[tool.black]
|
[project]
|
||||||
skip-string-normalization = 1
|
name = "dj-database-url"
|
||||||
|
version = "0.0.0"
|
||||||
|
description = "Use Database URLs in your Django Application."
|
||||||
|
authors = [
|
||||||
|
{ name = "Jazzband community" }
|
||||||
|
]
|
||||||
|
readme = "README.rst"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
license = "BSD-3-Clause"
|
||||||
|
license-files = ["LICENSE"]
|
||||||
|
dependencies = [
|
||||||
|
"django>=4.2",
|
||||||
|
]
|
||||||
|
classifiers = [
|
||||||
|
"Environment :: Web Environment",
|
||||||
|
"Framework :: Django",
|
||||||
|
"Framework :: Django :: 4.2",
|
||||||
|
"Framework :: Django :: 5.2",
|
||||||
|
"Framework :: Django :: 6",
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"License :: OSI Approved :: BSD License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Programming Language :: Python",
|
||||||
|
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
||||||
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||||
|
"Programming Language :: Python",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Programming Language :: Python :: 3.14",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
Homepage = "https://jazzband.co/projects/dj-database-url"
|
||||||
|
Changelog = "https://github.com/jazzband/dj-database-url/blob/master/CHANGELOG.md"
|
||||||
|
Funding = "https://psfmember.org/civicrm/contribute/transact/?reset=1&id=34"
|
||||||
|
Bug = "https://github.com/jazzband/dj-database-url/issues"
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["uv_build>=0.9.17,<0.10.0"]
|
||||||
|
build-backend = "uv_build"
|
||||||
|
|
||||||
|
[tool.uv.build-backend]
|
||||||
|
module-name = "dj_database_url"
|
||||||
|
module-root = ""
|
||||||
|
source-include = ["dj_database_url/py.typed", "tests/**"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 88
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
extend-select = ["B", "I"]
|
||||||
|
|
||||||
|
[tool.ruff.format]
|
||||||
|
quote-style = "preserve"
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
show_error_codes=true
|
show_error_codes=true
|
||||||
disallow_untyped_defs=true
|
disallow_untyped_defs=true
|
||||||
disallow_untyped_calls=true
|
disallow_untyped_calls=true
|
||||||
warn_redundant_casts=true
|
warn_redundant_casts=true
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
typeCheckingMode = "strict"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"coverage>=7.13.0",
|
||||||
|
"mypy>=1.19.1",
|
||||||
|
"pyright>=1.1.407",
|
||||||
|
"pytest>=9.0.2",
|
||||||
|
"ruff>=0.14.10",
|
||||||
|
"setuptools>=80.9.0",
|
||||||
|
"twine>=6.2.0",
|
||||||
|
"wheel>=0.45.1",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
coverage
|
|
||||||
mypy
|
|
||||||
49
setup.py
49
setup.py
|
|
@ -1,49 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
readme = Path("README.rst").read_text()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="dj-database-url",
|
|
||||||
version="2.1.0",
|
|
||||||
url="https://github.com/jazzband/dj-database-url",
|
|
||||||
license="BSD",
|
|
||||||
author="Original Author: Kenneth Reitz, Maintained by: JazzBand Community",
|
|
||||||
description="Use Database URLs in your Django Application.",
|
|
||||||
long_description=readme,
|
|
||||||
long_description_content_type="text/x-rst",
|
|
||||||
packages=["dj_database_url"],
|
|
||||||
install_requires=["Django>=3.2", "typing_extensions >= 3.10.0.0"],
|
|
||||||
include_package_data=True,
|
|
||||||
package_data={
|
|
||||||
"dj_database_url": ["py.typed"],
|
|
||||||
},
|
|
||||||
platforms="any",
|
|
||||||
project_urls={
|
|
||||||
"GitHub": "https://github.com/jazzband/dj-database-url/",
|
|
||||||
"Release log": (
|
|
||||||
"https://github.com/jazzband/dj-database-url/blob/master/CHANGELOG.md"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
classifiers=[
|
|
||||||
"Environment :: Web Environment",
|
|
||||||
"Framework :: Django",
|
|
||||||
"Framework :: Django :: 3.2",
|
|
||||||
"Framework :: Django :: 4.0",
|
|
||||||
"Framework :: Django :: 4.1",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"License :: OSI Approved :: BSD License",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
"Programming Language :: Python",
|
|
||||||
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
||||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
||||||
"Programming Language :: Python",
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
|
||||||
"Programming Language :: Python :: 3.11",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
# pyright: reportTypedDictNotRequiredAccess=false
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
from urllib.parse import uses_netloc
|
||||||
|
|
||||||
import dj_database_url
|
import dj_database_url
|
||||||
|
|
||||||
|
|
@ -8,9 +12,10 @@ POSTGIS_URL = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compu
|
||||||
|
|
||||||
|
|
||||||
class DatabaseTestSuite(unittest.TestCase):
|
class DatabaseTestSuite(unittest.TestCase):
|
||||||
def test_postgres_parsing(self):
|
def test_postgres_parsing(self) -> None:
|
||||||
url = "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -19,9 +24,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_postgres_unix_socket_parsing(self):
|
def test_postgres_unix_socket_parsing(self) -> None:
|
||||||
url = "postgres://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -30,8 +36,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == ""
|
assert url["PASSWORD"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
url = "postgres://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["HOST"] == "/Users/postgres/RuN"
|
assert url["HOST"] == "/Users/postgres/RuN"
|
||||||
|
|
@ -39,9 +46,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == ""
|
assert url["PASSWORD"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
def test_postgres_google_cloud_parsing(self):
|
def test_postgres_google_cloud_parsing(self) -> None:
|
||||||
url = "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@%2Fcloudsql%2Fproject_id%3Aregion%3Ainstance_id/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@%2Fcloudsql%2Fproject_id%3Aregion%3Ainstance_id/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -50,9 +58,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
def test_ipv6_parsing(self):
|
def test_ipv6_parsing(self) -> None:
|
||||||
url = "postgres://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -61,9 +70,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_postgres_search_path_parsing(self):
|
def test_postgres_search_path_parsing(self) -> None:
|
||||||
url = "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||||
|
)
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||||
|
|
@ -73,9 +83,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_postgres_parsing_with_special_characters(self):
|
def test_postgres_parsing_with_special_characters(self) -> None:
|
||||||
url = "postgres://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
assert url["NAME"] == "#database"
|
assert url["NAME"] == "#database"
|
||||||
|
|
@ -84,9 +95,26 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "#password"
|
assert url["PASSWORD"] == "#password"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_postgis_parsing(self):
|
def test_postgres_parsing_with_int_bool_str_query_string(self) -> None:
|
||||||
url = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?server_side_binding=true&timeout=20&service=my_service&passfile=.my_pgpass"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||||
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||||
|
assert url["USER"] == "uf07k1i6d8ia0v"
|
||||||
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
|
assert url["PORT"] == 5431
|
||||||
|
assert url["OPTIONS"]["server_side_binding"] is True
|
||||||
|
assert url["OPTIONS"]["timeout"] == 20
|
||||||
|
assert url["OPTIONS"]["service"] == "my_service"
|
||||||
|
assert url["OPTIONS"]["passfile"] == ".my_pgpass"
|
||||||
|
|
||||||
|
def test_postgis_parsing(self) -> None:
|
||||||
|
url = dj_database_url.parse(
|
||||||
|
"postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.postgis"
|
assert url["ENGINE"] == "django.contrib.gis.db.backends.postgis"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -95,9 +123,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_postgis_search_path_parsing(self):
|
def test_postgis_search_path_parsing(self) -> None:
|
||||||
url = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||||
|
)
|
||||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.postgis"
|
assert url["ENGINE"] == "django.contrib.gis.db.backends.postgis"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||||
|
|
@ -107,9 +136,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_mysql_gis_parsing(self):
|
def test_mysql_gis_parsing(self) -> None:
|
||||||
url = "mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.mysql"
|
assert url["ENGINE"] == "django.contrib.gis.db.backends.mysql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -118,9 +148,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_mysql_connector_parsing(self):
|
def test_mysql_connector_parsing(self) -> None:
|
||||||
url = "mysql-connector://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"mysql-connector://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "mysql.connector.django"
|
assert url["ENGINE"] == "mysql.connector.django"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -129,7 +160,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_config_test_options(self):
|
def test_config_test_options(self) -> None:
|
||||||
with mock.patch.dict(
|
with mock.patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
{
|
{
|
||||||
|
|
@ -143,9 +174,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
|
|
||||||
assert url['TEST']['NAME'] == 'mytestdatabase'
|
assert url['TEST']['NAME'] == 'mytestdatabase'
|
||||||
|
|
||||||
def test_cleardb_parsing(self):
|
def test_cleardb_parsing(self) -> None:
|
||||||
url = "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.mysql"
|
assert url["ENGINE"] == "django.db.backends.mysql"
|
||||||
assert url["NAME"] == "heroku_97681db3eff7580"
|
assert url["NAME"] == "heroku_97681db3eff7580"
|
||||||
|
|
@ -154,7 +186,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "69772142"
|
assert url["PASSWORD"] == "69772142"
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
def test_database_url(self):
|
def test_database_url(self) -> None:
|
||||||
with mock.patch.dict(os.environ, clear=True):
|
with mock.patch.dict(os.environ, clear=True):
|
||||||
a = dj_database_url.config()
|
a = dj_database_url.config()
|
||||||
assert not a
|
assert not a
|
||||||
|
|
@ -174,28 +206,46 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_empty_sqlite_url(self):
|
def test_empty_sqlite_url(self) -> None:
|
||||||
url = "sqlite://"
|
url = dj_database_url.parse("sqlite://")
|
||||||
url = dj_database_url.parse(url)
|
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.sqlite3"
|
assert url["ENGINE"] == "django.db.backends.sqlite3"
|
||||||
assert url["NAME"] == ":memory:"
|
assert url["NAME"] == ":memory:"
|
||||||
|
|
||||||
def test_memory_sqlite_url(self):
|
def test_memory_sqlite_url(self) -> None:
|
||||||
url = "sqlite://:memory:"
|
url = dj_database_url.parse("sqlite://:memory:")
|
||||||
url = dj_database_url.parse(url)
|
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.sqlite3"
|
assert url["ENGINE"] == "django.db.backends.sqlite3"
|
||||||
assert url["NAME"] == ":memory:"
|
assert url["NAME"] == ":memory:"
|
||||||
|
|
||||||
def test_parse_engine_setting(self):
|
def test_sqlite_relative_url(self) -> None:
|
||||||
|
url = "sqlite:///db.sqlite3"
|
||||||
|
config = dj_database_url.parse(url)
|
||||||
|
|
||||||
|
assert config["ENGINE"] == "django.db.backends.sqlite3"
|
||||||
|
assert config["NAME"] == "db.sqlite3"
|
||||||
|
|
||||||
|
def test_sqlite_absolute_url(self) -> None:
|
||||||
|
# 4 slashes are needed:
|
||||||
|
# two are part of scheme
|
||||||
|
# one separates host:port from path
|
||||||
|
# and the fourth goes to "NAME" value
|
||||||
|
url = "sqlite:////db.sqlite3"
|
||||||
|
config = dj_database_url.parse(url)
|
||||||
|
|
||||||
|
assert config["ENGINE"] == "django.db.backends.sqlite3"
|
||||||
|
assert config["NAME"] == "/db.sqlite3"
|
||||||
|
|
||||||
|
def test_parse_engine_setting(self) -> None:
|
||||||
engine = "django_mysqlpool.backends.mysqlpool"
|
engine = "django_mysqlpool.backends.mysqlpool"
|
||||||
url = "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url, engine)
|
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true",
|
||||||
|
engine,
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == engine
|
assert url["ENGINE"] == engine
|
||||||
|
|
||||||
def test_config_engine_setting(self):
|
def test_config_engine_setting(self) -> None:
|
||||||
engine = "django_mysqlpool.backends.mysqlpool"
|
engine = "django_mysqlpool.backends.mysqlpool"
|
||||||
with mock.patch.dict(
|
with mock.patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
|
|
@ -207,15 +257,17 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
|
|
||||||
assert url["ENGINE"] == engine
|
assert url["ENGINE"] == engine
|
||||||
|
|
||||||
def test_parse_conn_max_age_setting(self):
|
def test_parse_conn_max_age_setting(self) -> None:
|
||||||
conn_max_age = 600
|
conn_max_age = 600
|
||||||
url = "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url, conn_max_age=conn_max_age)
|
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true",
|
||||||
|
conn_max_age=conn_max_age,
|
||||||
|
)
|
||||||
|
|
||||||
assert url["CONN_MAX_AGE"] == conn_max_age
|
assert url["CONN_MAX_AGE"] == conn_max_age
|
||||||
|
|
||||||
def test_config_conn_max_age_setting(self):
|
def test_config_conn_max_age_setting_none(self) -> None:
|
||||||
conn_max_age = 600
|
conn_max_age = None
|
||||||
with mock.patch.dict(
|
with mock.patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
{
|
{
|
||||||
|
|
@ -226,7 +278,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
|
|
||||||
assert url["CONN_MAX_AGE"] == conn_max_age
|
assert url["CONN_MAX_AGE"] == conn_max_age
|
||||||
|
|
||||||
def test_database_url_with_options(self):
|
def test_database_url_with_options(self) -> None:
|
||||||
# Test full options
|
# Test full options
|
||||||
with mock.patch.dict(
|
with mock.patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
|
|
@ -257,7 +309,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
url = dj_database_url.config()
|
url = dj_database_url.config()
|
||||||
assert "OPTIONS" not in url
|
assert "OPTIONS" not in url
|
||||||
|
|
||||||
def test_mysql_database_url_with_sslca_options(self):
|
def test_mysql_database_url_with_sslca_options(self) -> None:
|
||||||
with mock.patch.dict(
|
with mock.patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
{
|
{
|
||||||
|
|
@ -284,9 +336,8 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
url = dj_database_url.config()
|
url = dj_database_url.config()
|
||||||
assert "OPTIONS" not in url
|
assert "OPTIONS" not in url
|
||||||
|
|
||||||
def test_oracle_parsing(self):
|
def test_oracle_parsing(self) -> None:
|
||||||
url = "oracle://scott:tiger@oraclehost:1521/hr"
|
url = dj_database_url.parse("oracle://scott:tiger@oraclehost:1521/hr")
|
||||||
url = dj_database_url.parse(url)
|
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.oracle"
|
assert url["ENGINE"] == "django.db.backends.oracle"
|
||||||
assert url["NAME"] == "hr"
|
assert url["NAME"] == "hr"
|
||||||
|
|
@ -295,9 +346,8 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "tiger"
|
assert url["PASSWORD"] == "tiger"
|
||||||
assert url["PORT"] == "1521"
|
assert url["PORT"] == "1521"
|
||||||
|
|
||||||
def test_oracle_gis_parsing(self):
|
def test_oracle_gis_parsing(self) -> None:
|
||||||
url = "oraclegis://scott:tiger@oraclehost:1521/hr"
|
url = dj_database_url.parse("oraclegis://scott:tiger@oraclehost:1521/hr")
|
||||||
url = dj_database_url.parse(url)
|
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.oracle"
|
assert url["ENGINE"] == "django.contrib.gis.db.backends.oracle"
|
||||||
assert url["NAME"] == "hr"
|
assert url["NAME"] == "hr"
|
||||||
|
|
@ -306,14 +356,13 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "tiger"
|
assert url["PASSWORD"] == "tiger"
|
||||||
assert url["PORT"] == 1521
|
assert url["PORT"] == 1521
|
||||||
|
|
||||||
def test_oracle_dsn_parsing(self):
|
def test_oracle_dsn_parsing(self) -> None:
|
||||||
url = (
|
url = dj_database_url.parse(
|
||||||
"oracle://scott:tiger@/"
|
"oracle://scott:tiger@/"
|
||||||
"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)"
|
"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)"
|
||||||
"(HOST=oraclehost)(PORT=1521)))"
|
"(HOST=oraclehost)(PORT=1521)))"
|
||||||
"(CONNECT_DATA=(SID=hr)))"
|
"(CONNECT_DATA=(SID=hr)))"
|
||||||
)
|
)
|
||||||
url = dj_database_url.parse(url)
|
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.oracle"
|
assert url["ENGINE"] == "django.db.backends.oracle"
|
||||||
assert url["USER"] == "scott"
|
assert url["USER"] == "scott"
|
||||||
|
|
@ -329,9 +378,8 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
|
|
||||||
assert url["NAME"] == dsn
|
assert url["NAME"] == dsn
|
||||||
|
|
||||||
def test_oracle_tns_parsing(self):
|
def test_oracle_tns_parsing(self) -> None:
|
||||||
url = "oracle://scott:tiger@/tnsname"
|
url = dj_database_url.parse("oracle://scott:tiger@/tnsname")
|
||||||
url = dj_database_url.parse(url)
|
|
||||||
|
|
||||||
assert url["ENGINE"] == "django.db.backends.oracle"
|
assert url["ENGINE"] == "django.db.backends.oracle"
|
||||||
assert url["USER"] == "scott"
|
assert url["USER"] == "scott"
|
||||||
|
|
@ -340,9 +388,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["HOST"] == ""
|
assert url["HOST"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
def test_redshift_parsing(self):
|
def test_redshift_parsing(self) -> None:
|
||||||
url = "redshift://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5439/d8r82722r2kuvn?currentSchema=otherschema"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"redshift://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5439/d8r82722r2kuvn?currentSchema=otherschema"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "django_redshift_backend"
|
assert url["ENGINE"] == "django_redshift_backend"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -353,9 +402,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_mssql_parsing(self):
|
def test_mssql_parsing(self) -> None:
|
||||||
url = "mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "sql_server.pyodbc"
|
assert url["ENGINE"] == "sql_server.pyodbc"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -366,9 +416,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_mssql_instance_port_parsing(self):
|
def test_mssql_instance_port_parsing(self) -> None:
|
||||||
url = "mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com\\insnsnss:12345/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com\\insnsnss:12345/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "sql_server.pyodbc"
|
assert url["ENGINE"] == "sql_server.pyodbc"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -379,9 +430,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_cockroach(self):
|
def test_cockroach(self) -> None:
|
||||||
url = "cockroach://testuser:testpass@testhost:26257/cockroach?sslmode=verify-full&sslrootcert=/certs/ca.crt&sslcert=/certs/client.myprojectuser.crt&sslkey=/certs/client.myprojectuser.key"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"cockroach://testuser:testpass@testhost:26257/cockroach?sslmode=verify-full&sslrootcert=/certs/ca.crt&sslcert=/certs/client.myprojectuser.crt&sslkey=/certs/client.myprojectuser.key"
|
||||||
|
)
|
||||||
assert url['ENGINE'] == 'django_cockroachdb'
|
assert url['ENGINE'] == 'django_cockroachdb'
|
||||||
assert url['NAME'] == 'cockroach'
|
assert url['NAME'] == 'cockroach'
|
||||||
assert url['HOST'] == 'testhost'
|
assert url['HOST'] == 'testhost'
|
||||||
|
|
@ -393,9 +445,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url['OPTIONS']['sslcert'] == '/certs/client.myprojectuser.crt'
|
assert url['OPTIONS']['sslcert'] == '/certs/client.myprojectuser.crt'
|
||||||
assert url['OPTIONS']['sslkey'] == '/certs/client.myprojectuser.key'
|
assert url['OPTIONS']['sslkey'] == '/certs/client.myprojectuser.key'
|
||||||
|
|
||||||
def test_mssqlms_parsing(self):
|
def test_mssqlms_parsing(self) -> None:
|
||||||
url = "mssqlms://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"mssqlms://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "mssql"
|
assert url["ENGINE"] == "mssql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -406,9 +459,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_timescale_parsing(self):
|
def test_timescale_parsing(self) -> None:
|
||||||
url = "timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -417,9 +471,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_timescale_unix_socket_parsing(self):
|
def test_timescale_unix_socket_parsing(self) -> None:
|
||||||
url = "timescale://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescale://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -428,8 +483,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == ""
|
assert url["PASSWORD"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
url = "timescale://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescale://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||||
assert url["HOST"] == "/Users/postgres/RuN"
|
assert url["HOST"] == "/Users/postgres/RuN"
|
||||||
|
|
@ -437,9 +493,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == ""
|
assert url["PASSWORD"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
def test_timescale_ipv6_parsing(self):
|
def test_timescale_ipv6_parsing(self) -> None:
|
||||||
url = "timescale://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescale://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -448,9 +505,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_timescale_search_path_parsing(self):
|
def test_timescale_search_path_parsing(self) -> None:
|
||||||
url = "timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||||
|
)
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||||
|
|
@ -460,9 +518,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_timescale_parsing_with_special_characters(self):
|
def test_timescale_parsing_with_special_characters(self) -> None:
|
||||||
url = "timescale://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescale://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||||
assert url["NAME"] == "#database"
|
assert url["NAME"] == "#database"
|
||||||
|
|
@ -471,9 +530,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "#password"
|
assert url["PASSWORD"] == "#password"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_timescalegis_parsing(self):
|
def test_timescalegis_parsing(self) -> None:
|
||||||
url = "timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -482,9 +542,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_timescalegis_unix_socket_parsing(self):
|
def test_timescalegis_unix_socket_parsing(self) -> None:
|
||||||
url = "timescalegis://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescalegis://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -493,8 +554,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == ""
|
assert url["PASSWORD"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
url = "timescalegis://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescalegis://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||||
assert url["HOST"] == "/Users/postgres/RuN"
|
assert url["HOST"] == "/Users/postgres/RuN"
|
||||||
|
|
@ -502,9 +564,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == ""
|
assert url["PASSWORD"] == ""
|
||||||
assert url["PORT"] == ""
|
assert url["PORT"] == ""
|
||||||
|
|
||||||
def test_timescalegis_ipv6_parsing(self):
|
def test_timescalegis_ipv6_parsing(self) -> None:
|
||||||
url = "timescalegis://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescalegis://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
|
|
@ -513,9 +576,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_timescalegis_search_path_parsing(self):
|
def test_timescalegis_search_path_parsing(self) -> None:
|
||||||
url = "timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||||
|
)
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||||
assert url["NAME"] == "d8r82722r2kuvn"
|
assert url["NAME"] == "d8r82722r2kuvn"
|
||||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||||
|
|
@ -525,9 +589,10 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||||
assert "currentSchema" not in url["OPTIONS"]
|
assert "currentSchema" not in url["OPTIONS"]
|
||||||
|
|
||||||
def test_timescalegis_parsing_with_special_characters(self):
|
def test_timescalegis_parsing_with_special_characters(self) -> None:
|
||||||
url = "timescalegis://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
url = dj_database_url.parse(
|
||||||
url = dj_database_url.parse(url)
|
"timescalegis://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||||
|
)
|
||||||
|
|
||||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||||
assert url["NAME"] == "#database"
|
assert url["NAME"] == "#database"
|
||||||
|
|
@ -536,7 +601,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["PASSWORD"] == "#password"
|
assert url["PASSWORD"] == "#password"
|
||||||
assert url["PORT"] == 5431
|
assert url["PORT"] == 5431
|
||||||
|
|
||||||
def test_persistent_connection_variables(self):
|
def test_persistent_connection_variables(self) -> None:
|
||||||
url = dj_database_url.parse(
|
url = dj_database_url.parse(
|
||||||
"sqlite://myfile.db", conn_max_age=600, conn_health_checks=True
|
"sqlite://myfile.db", conn_max_age=600, conn_health_checks=True
|
||||||
)
|
)
|
||||||
|
|
@ -544,7 +609,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
assert url["CONN_MAX_AGE"] == 600
|
assert url["CONN_MAX_AGE"] == 600
|
||||||
assert url["CONN_HEALTH_CHECKS"] is True
|
assert url["CONN_HEALTH_CHECKS"] is True
|
||||||
|
|
||||||
def test_sqlite_memory_persistent_connection_variables(self):
|
def test_sqlite_memory_persistent_connection_variables(self) -> None:
|
||||||
# memory sqlite ignores connection.close(), so persistent connection
|
# memory sqlite ignores connection.close(), so persistent connection
|
||||||
# variables aren’t required
|
# variables aren’t required
|
||||||
url = dj_database_url.parse(
|
url = dj_database_url.parse(
|
||||||
|
|
@ -558,13 +623,13 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
os.environ,
|
os.environ,
|
||||||
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
||||||
)
|
)
|
||||||
def test_persistent_connection_variables_config(self):
|
def test_persistent_connection_variables_config(self) -> None:
|
||||||
url = dj_database_url.config(conn_max_age=600, conn_health_checks=True)
|
url = dj_database_url.config(conn_max_age=600, conn_health_checks=True)
|
||||||
|
|
||||||
assert url["CONN_MAX_AGE"] == 600
|
assert url["CONN_MAX_AGE"] == 600
|
||||||
assert url["CONN_HEALTH_CHECKS"] is True
|
assert url["CONN_HEALTH_CHECKS"] is True
|
||||||
|
|
||||||
def test_no_env_variable(self):
|
def test_no_env_variable(self) -> None:
|
||||||
with self.assertLogs() as cm:
|
with self.assertLogs() as cm:
|
||||||
with mock.patch.dict(os.environ, clear=True):
|
with mock.patch.dict(os.environ, clear=True):
|
||||||
url = dj_database_url.config()
|
url = dj_database_url.config()
|
||||||
|
|
@ -573,24 +638,60 @@ class DatabaseTestSuite(unittest.TestCase):
|
||||||
'WARNING:root:No DATABASE_URL environment variable set, and so no databases setup'
|
'WARNING:root:No DATABASE_URL environment variable set, and so no databases setup'
|
||||||
], cm.output
|
], cm.output
|
||||||
|
|
||||||
def test_bad_url_parsing(self):
|
def test_credentials_unquoted__raise_value_error(self) -> None:
|
||||||
with self.assertRaisesRegex(ValueError, "No support for 'foo'. We support: "):
|
expected_message = (
|
||||||
dj_database_url.parse("foo://bar")
|
"This string is not a valid url, possibly because some of its parts "
|
||||||
|
r"is not properly urllib.parse.quote()'ed."
|
||||||
|
)
|
||||||
|
with self.assertRaisesRegex(ValueError, re.escape(expected_message)):
|
||||||
|
dj_database_url.parse("postgres://user:passw#ord!@localhost/foobar")
|
||||||
|
|
||||||
|
def test_credentials_quoted__ok(self) -> None:
|
||||||
|
url = "postgres://user%40domain:p%23ssword!@localhost/foobar"
|
||||||
|
config = dj_database_url.parse(url)
|
||||||
|
assert config["USER"] == "user@domain"
|
||||||
|
assert config["PASSWORD"] == "p#ssword!"
|
||||||
|
|
||||||
|
def test_unknown_scheme__raise_value_error(self) -> None:
|
||||||
|
expected_message = (
|
||||||
|
"Scheme 'unknown-scheme://' is unknown. "
|
||||||
|
"Did you forget to register custom backend? Following schemes have registered backends:"
|
||||||
|
)
|
||||||
|
with self.assertRaisesRegex(ValueError, re.escape(expected_message)):
|
||||||
|
dj_database_url.parse("unknown-scheme://user:password@localhost/foobar")
|
||||||
|
|
||||||
|
def test_register_multiple_times__no_duplicates_in_uses_netloc(self) -> None:
|
||||||
|
# make sure that when register() function is misused,
|
||||||
|
# it won't pollute urllib.parse.uses_netloc list with duplicates.
|
||||||
|
# Otherwise, it might cause performance issue if some code assumes that
|
||||||
|
# that list is short and performs linear search on it.
|
||||||
|
dj_database_url.register("django.contrib.db.backends.bag_end", "bag-end")
|
||||||
|
dj_database_url.register("django.contrib.db.backends.bag_end", "bag-end")
|
||||||
|
assert len(uses_netloc) == len(set(uses_netloc))
|
||||||
|
|
||||||
@mock.patch.dict(
|
@mock.patch.dict(
|
||||||
os.environ,
|
os.environ,
|
||||||
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
||||||
)
|
)
|
||||||
def test_ssl_require(self):
|
def test_ssl_require(self) -> None:
|
||||||
url = dj_database_url.config(ssl_require=True)
|
url = dj_database_url.config(ssl_require=True)
|
||||||
assert url["OPTIONS"] == {'sslmode': 'require'}
|
assert url["OPTIONS"] == {'sslmode': 'require'}
|
||||||
|
|
||||||
def test_options_int_values(self):
|
def test_options_int_values(self) -> None:
|
||||||
"""Ensure that options with integer values are parsed correctly."""
|
"""Ensure that options with integer values are parsed correctly."""
|
||||||
url = dj_database_url.parse(
|
url = dj_database_url.parse(
|
||||||
"mysql://user:pw@127.0.0.1:15036/db?connect_timout=3"
|
"mysql://user:pw@127.0.0.1:15036/db?connect_timeout=3"
|
||||||
)
|
)
|
||||||
assert url["OPTIONS"] == {'connect_timout': 3}
|
assert url["OPTIONS"] == {'connect_timeout': 3}
|
||||||
|
|
||||||
|
@mock.patch.dict(
|
||||||
|
os.environ,
|
||||||
|
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
||||||
|
)
|
||||||
|
def test_server_side_cursors__config(self) -> None:
|
||||||
|
url = dj_database_url.config(disable_server_side_cursors=True)
|
||||||
|
|
||||||
|
assert url["DISABLE_SERVER_SIDE_CURSORS"] is True
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue