mirror of
https://github.com/jazzband/dj-database-url.git
synced 2026-03-17 06:30:25 +00:00
Compare commits
No commits in common. "master" and "v1.3.0-alpha" have entirely different histories.
master
...
v1.3.0-alp
17 changed files with 463 additions and 1783 deletions
5
.flake8
Normal file
5
.flake8
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[flake8]
|
||||
max-line-length = 88
|
||||
extend-ignore = E203
|
||||
per-file-ignores=
|
||||
test_dj_database_url.py: E501, E265
|
||||
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
|
|
@ -1,8 +1,9 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
|
@ -10,30 +11,29 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set version from release/tag
|
||||
id: version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/*/}
|
||||
VERSION=${VERSION#v}
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: astral-sh/setup-uv@v7
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.12
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U pip
|
||||
python -m pip install -U setuptools wheel twine
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
uv version ${{ env.VERSION }}
|
||||
uv build
|
||||
uvx twine check dist/*
|
||||
python setup.py --version
|
||||
python setup.py sdist --format=gztar bdist_wheel
|
||||
twine check dist/*
|
||||
|
||||
- name: Upload packages to Jazzband
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
user: jazzband
|
||||
password: ${{ secrets.JAZZBAND_RELEASE_KEY }}
|
||||
|
|
|
|||
79
.github/workflows/test.yml
vendored
79
.github/workflows/test.yml
vendored
|
|
@ -1,74 +1,49 @@
|
|||
name: test
|
||||
on: [push, pull_request]
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [formatting, typecheck]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
django-version: ["4.2", "5.2", "6.0"]
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||
django-version: ["3.2", "4.0", "4.1"]
|
||||
exclude:
|
||||
# django 4.x is not compatible with python 3.13 or higher
|
||||
- python-version: "3.13"
|
||||
django-version: "4.2"
|
||||
- python-version: "3.14"
|
||||
django-version: "4.2"
|
||||
# django 6.x is not compatible with python 3.11 or lower
|
||||
- python-version: "3.10"
|
||||
django-version: "6.0"
|
||||
# Python 3.7 is not compatible with 4.0
|
||||
- python-version: "3.7"
|
||||
django-version: "4.0"
|
||||
# Python 3.7 is not compatible with 4.1
|
||||
- python-version: "3.7"
|
||||
django-version: "4.1"
|
||||
# Python 3.11 is not compatible with 3.2
|
||||
- python-version: "3.11"
|
||||
django-version: "6.0"
|
||||
|
||||
django-version: "3.2"
|
||||
# Python 3.11 is not compatible with 4.0
|
||||
- python-version: "3.11"
|
||||
django-version: "4.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install uv and set the Python version
|
||||
uses: astral-sh/setup-uv@v7
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
activate-environment: 'true'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv pip install "Django~=${{ matrix.django-version }}.0"
|
||||
pip install -r requirements.txt
|
||||
pip install "Django~=${{ matrix.django-version }}.0" .
|
||||
|
||||
- name: Run mypy
|
||||
run: |
|
||||
python -m mypy dj_database_url.py
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
echo "$(python --version) / Django $(django-admin --version)"
|
||||
uvx coverage run --source=dj_database_url --branch -m unittest discover -v
|
||||
uvx coverage report
|
||||
uvx coverage xml
|
||||
coverage run --source=dj_database_url --branch -m unittest discover
|
||||
coverage report
|
||||
coverage xml
|
||||
|
||||
- uses: codecov/codecov-action@v4
|
||||
- uses: codecov/codecov-action@v3
|
||||
|
|
|
|||
2
.isort.cfg
Normal file
2
.isort.cfg
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[settings]
|
||||
profile = black
|
||||
|
|
@ -1,21 +1,23 @@
|
|||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.4
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: "5.12.0"
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args:
|
||||
- --fix
|
||||
- id: ruff-format
|
||||
args:
|
||||
- --quiet
|
||||
- id: isort
|
||||
args: ["--profile", "black"]
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
hooks:
|
||||
- id: typos
|
||||
- id: black
|
||||
args: [--target-version=py38]
|
||||
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: '6.0.0'
|
||||
hooks:
|
||||
- id: flake8
|
||||
|
|
|
|||
87
CHANGELOG.md
87
CHANGELOG.md
|
|
@ -1,69 +1,38 @@
|
|||
# CHANGELOG
|
||||
|
||||
## 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.
|
||||
|
||||
## v2.0.0 (2023-04-27)
|
||||
* Update project setup such that we now install it as a package.
|
||||
|
||||
_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.
|
||||
|
||||
## v1.3.0 (2023-03-27)
|
||||
|
||||
* Cosmetic changes to the generation of schemes.
|
||||
* Bump isort version - 5.11.5.
|
||||
* raise a warning message if `database_url` is not set.
|
||||
* `CONN_MAX_AGE` fix type - `Optional[int]`.
|
||||
* raise warning message if database_url is not set.
|
||||
* CONN_MAX_AGE fix type - Optional[int].
|
||||
|
||||
## v1.2.0 (2022-12-13)
|
||||
|
||||
* Add the ability to add test databases.
|
||||
* 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)
|
||||
|
||||
* Option for connection health checks parameter.
|
||||
* Update supported version python 3.11.
|
||||
* Code changes, various improvements.
|
||||
* Add project links to `setup.py`.
|
||||
* Code changes, various improvments.
|
||||
* Add project links to setup.py
|
||||
|
||||
## 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 the official MSSQL connector.
|
||||
* Add support for the offical MSSQL connector.
|
||||
* Update License to be compatible with Jazzband.
|
||||
* Remove support for Python < 3.5 including Python 2.7
|
||||
* Update source code to Black format.
|
||||
* Update CI using pre-commit
|
||||
|
||||
## v0.5.0 (2018-03-01)
|
||||
|
||||
- Use str port for mssql
|
||||
- Added license
|
||||
- Add mssql to readme
|
||||
|
|
@ -83,15 +52,17 @@ Initial release of code, dj-database-urls is now part of [Jazzband](https://jazz
|
|||
- Added SpatiaLite in README.rst
|
||||
|
||||
## v0.4.1 (2016-04-06)
|
||||
|
||||
- Enable CA providing for MySQL URIs
|
||||
- Update Readme
|
||||
- Update trove classifiers
|
||||
- Updated setup.py description
|
||||
|
||||
## v0.4.0 (2016-02-04)
|
||||
|
||||
- Update readme
|
||||
- Fix for python3
|
||||
- Handle search path config in connection url for postgres
|
||||
- Handle search path config in connect url for postgres
|
||||
- Add tox config to ease testing against multiple Python versions
|
||||
- Simplified the querystring parse logic
|
||||
- Cleaned up querystring parsing
|
||||
|
|
@ -108,25 +79,27 @@ Initial release of code, dj-database-urls is now part of [Jazzband](https://jazz
|
|||
- Added support for python mysql-connector
|
||||
|
||||
## v0.3.0 (2014-03-10)
|
||||
- Add `.gitignore` file
|
||||
- Remove `.pyc` file
|
||||
- Remove travis-ci unsupported python versions (Travis CI supports Python versions 2.6, 2.7, 3.2 and 3.3)
|
||||
|
||||
- Add .gitignore file
|
||||
- Remove .pyc file
|
||||
- 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
|
||||
- Add `setup.cfg` for wheel support
|
||||
- Add setup.cfg for wheel support
|
||||
- Add trove classifiers for python versions
|
||||
- Replace Python 3.1 with Python 3.3
|
||||
- Add MySQL (GIS) support
|
||||
- Ability to set different engine
|
||||
|
||||
## v0.2.2 (2013-07-17)
|
||||
|
||||
- Added spatialite to uses_netloc too
|
||||
- Added spatialite backend
|
||||
- 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
|
||||
- Fixing test to actually use the result of the parse
|
||||
- Adding in tests to ensure sqlite in-memory databases work
|
||||
- Fixed a too-short title underline
|
||||
- Fixed too-short title underline
|
||||
- Added :target: attribute to Travis status image in README
|
||||
- Added docs for default argument to config
|
||||
- Add "pgsql" as a PostgreSQL URL scheme.
|
||||
|
|
@ -134,6 +107,7 @@ Initial release of code, dj-database-urls is now part of [Jazzband](https://jazz
|
|||
- fixed url
|
||||
|
||||
## v0.2.1 (2012-06-19)
|
||||
|
||||
- Add python3 support
|
||||
- Adding travis status and tests
|
||||
- Adding test environment variables
|
||||
|
|
@ -145,23 +119,28 @@ Initial release of code, dj-database-urls is now part of [Jazzband](https://jazz
|
|||
- RedHat's OpenShift platform uses the 'postgresql' scheme
|
||||
- Registered 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)
|
||||
|
||||
- Fix parse(s)
|
||||
|
||||
## v0.1.4 (2012-05-30)
|
||||
|
||||
- Add defaults for env
|
||||
- Set the DATABASES dict rather than assigning to it
|
||||
|
||||
## 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
|
||||
- Clean dependencies
|
||||
|
||||
## v0.1.2 (2012-04-30)
|
||||
|
||||
- Update readme
|
||||
- Refactor config and use a new parse function
|
||||
- Refactor config and use new parse function
|
||||
|
||||
## v0.1.1 (2012-04-30) First release
|
||||
|
||||
🐍 ✨
|
||||
|
|
|
|||
1
MANIFEST.in
Normal file
1
MANIFEST.in
Normal file
|
|
@ -0,0 +1 @@
|
|||
include py.typed
|
||||
64
README.rst
64
README.rst
|
|
@ -22,6 +22,12 @@ 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
|
||||
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
|
||||
------------
|
||||
|
||||
|
|
@ -142,63 +148,6 @@ and should instead be passed as:
|
|||
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
|
||||
----------
|
||||
|
||||
|
|
@ -206,7 +155,6 @@ URL schema
|
|||
| Engine | Django Backend | URL |
|
||||
+======================+===============================================+==================================================+
|
||||
| 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`` |
|
||||
+----------------------+-----------------------------------------------+--------------------------------------------------+
|
||||
|
|
|
|||
184
dj_database_url.py
Normal file
184
dj_database_url.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import logging
|
||||
import os
|
||||
import urllib.parse as urlparse
|
||||
from typing import Any, Dict, Optional, Union
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
DEFAULT_ENV = "DATABASE_URL"
|
||||
|
||||
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
|
||||
class DBConfig(TypedDict, total=False):
|
||||
ATOMIC_REQUESTS: bool
|
||||
AUTOCOMMIT: bool
|
||||
CONN_MAX_AGE: Optional[int]
|
||||
CONN_HEALTH_CHECKS: bool
|
||||
DISABLE_SERVER_SIDE_CURSORS: bool
|
||||
ENGINE: str
|
||||
HOST: str
|
||||
NAME: str
|
||||
OPTIONS: Optional[Dict[str, Any]]
|
||||
PASSWORD: str
|
||||
PORT: Union[str, int]
|
||||
TEST: Dict[str, Any]
|
||||
TIME_ZONE: str
|
||||
USER: str
|
||||
|
||||
|
||||
def config(
|
||||
env: str = DEFAULT_ENV,
|
||||
default: Optional[str] = None,
|
||||
engine: Optional[str] = None,
|
||||
conn_max_age: Optional[int] = 0,
|
||||
conn_health_checks: bool = False,
|
||||
ssl_require: bool = False,
|
||||
test_options: Optional[Dict] = None,
|
||||
) -> DBConfig:
|
||||
"""Returns configured DATABASE dictionary from DATABASE_URL."""
|
||||
s = os.environ.get(env, default)
|
||||
|
||||
if s is None:
|
||||
logging.warning(
|
||||
"No %s environment variable set, and so no databases setup" % env
|
||||
)
|
||||
|
||||
if s:
|
||||
return parse(
|
||||
s, engine, conn_max_age, conn_health_checks, ssl_require, test_options
|
||||
)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def parse(
|
||||
url: str,
|
||||
engine: Optional[str] = None,
|
||||
conn_max_age: Optional[int] = 0,
|
||||
conn_health_checks: bool = False,
|
||||
ssl_require: bool = False,
|
||||
test_options: Optional[dict] = None,
|
||||
) -> DBConfig:
|
||||
"""Parses a database URL."""
|
||||
if url == "sqlite://:memory:":
|
||||
# this is a special case, because if we pass this URL into
|
||||
# urlparse, urlparse will choke trying to interpret "memory"
|
||||
# as a port number
|
||||
return {"ENGINE": SCHEMES["sqlite"], "NAME": ":memory:"}
|
||||
# note: no other settings are required for sqlite
|
||||
|
||||
# otherwise parse the url as normal
|
||||
parsed_config: DBConfig = {}
|
||||
|
||||
if test_options is None:
|
||||
test_options = {}
|
||||
|
||||
spliturl = urlparse.urlsplit(url)
|
||||
|
||||
# Split query strings from path.
|
||||
path = spliturl.path[1:]
|
||||
query = urlparse.parse_qs(spliturl.query)
|
||||
|
||||
# If we are using sqlite and we have no path, then assume we
|
||||
# want an in-memory database (this is the behaviour of sqlalchemy)
|
||||
if spliturl.scheme == "sqlite" and path == "":
|
||||
path = ":memory:"
|
||||
|
||||
# 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,
|
||||
}
|
||||
)
|
||||
if test_options:
|
||||
parsed_config.update(
|
||||
{
|
||||
'TEST': test_options,
|
||||
}
|
||||
)
|
||||
|
||||
# Pass the query string into OPTIONS.
|
||||
options: Dict[str, Any] = {}
|
||||
for key, values in query.items():
|
||||
if spliturl.scheme == "mysql" and key == "ssl-ca":
|
||||
options["ssl"] = {"ca": values[-1]}
|
||||
continue
|
||||
|
||||
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
|
||||
|
||||
return parsed_config
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
import logging
|
||||
import os
|
||||
import urllib.parse as urlparse
|
||||
from collections.abc import Callable
|
||||
from typing import Any, TypedDict
|
||||
|
||||
DEFAULT_ENV = "DATABASE_URL"
|
||||
ENGINE_SCHEMES: dict[str, "Engine"] = {}
|
||||
|
||||
|
||||
# From https://docs.djangoproject.com/en/stable/ref/settings/#databases
|
||||
class DBConfig(TypedDict, total=False):
|
||||
ATOMIC_REQUESTS: bool
|
||||
AUTOCOMMIT: bool
|
||||
CONN_MAX_AGE: int | None
|
||||
CONN_HEALTH_CHECKS: bool
|
||||
DISABLE_SERVER_SIDE_CURSORS: bool
|
||||
ENGINE: str
|
||||
HOST: str
|
||||
NAME: str
|
||||
OPTIONS: dict[str, Any]
|
||||
PASSWORD: str
|
||||
PORT: str | int
|
||||
TEST: dict[str, Any]
|
||||
TIME_ZONE: 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(
|
||||
env: str = DEFAULT_ENV,
|
||||
default: str | None = None,
|
||||
engine: str | None = None,
|
||||
conn_max_age: int | None = 0,
|
||||
conn_health_checks: bool = False,
|
||||
disable_server_side_cursors: bool = False,
|
||||
ssl_require: bool = False,
|
||||
test_options: dict[str, Any] | None = None,
|
||||
) -> DBConfig:
|
||||
"""Returns configured DATABASE dictionary from DATABASE_URL."""
|
||||
s = os.environ.get(env, default)
|
||||
|
||||
if s is None:
|
||||
logging.warning(
|
||||
"No %s environment variable set, and so no databases setup", env
|
||||
)
|
||||
|
||||
if s:
|
||||
return parse(
|
||||
s,
|
||||
engine,
|
||||
conn_max_age,
|
||||
conn_health_checks,
|
||||
disable_server_side_cursors,
|
||||
ssl_require,
|
||||
test_options,
|
||||
)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def parse(
|
||||
url: str,
|
||||
engine: str | None = None,
|
||||
conn_max_age: int | None = 0,
|
||||
conn_health_checks: bool = False,
|
||||
disable_server_side_cursors: bool = False,
|
||||
ssl_require: bool = False,
|
||||
test_options: dict[str, Any] | None = None,
|
||||
) -> DBConfig:
|
||||
"""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:":
|
||||
# this is a special case, because if we pass this URL into
|
||||
# urlparse, urlparse will choke trying to interpret "memory"
|
||||
# as a port number
|
||||
return {"ENGINE": ENGINE_SCHEMES["sqlite"].backend, "NAME": ":memory:"}
|
||||
# note: no other settings are required for sqlite
|
||||
|
||||
try:
|
||||
split_result = urlparse.urlsplit(url)
|
||||
engine_obj = ENGINE_SCHEMES.get(split_result.scheme)
|
||||
if engine_obj is None:
|
||||
raise UnknownSchemeError(split_result.scheme)
|
||||
path = split_result.path[1:]
|
||||
query = urlparse.parse_qs(split_result.query)
|
||||
options = {k: _parse_option_values(v) for k, v in query.items()}
|
||||
parsed_config: DBConfig = {
|
||||
"ENGINE": engine_obj.backend,
|
||||
"USER": urlparse.unquote(split_result.username or ""),
|
||||
"PASSWORD": urlparse.unquote(split_result.password or ""),
|
||||
"HOST": urlparse.unquote(split_result.hostname or ""),
|
||||
"PORT": split_result.port or "",
|
||||
"NAME": urlparse.unquote(path),
|
||||
"OPTIONS": options,
|
||||
}
|
||||
except UnknownSchemeError:
|
||||
raise
|
||||
except ValueError:
|
||||
raise ParseError() from None
|
||||
|
||||
# Guarantee that config has options, possibly empty, when postprocess() is called
|
||||
assert isinstance(parsed_config["OPTIONS"], dict)
|
||||
engine_obj.postprocess(parsed_config)
|
||||
|
||||
# Update the final config with any settings passed in explicitly.
|
||||
parsed_config["OPTIONS"].update(settings.pop("OPTIONS", {}))
|
||||
parsed_config.update(settings)
|
||||
|
||||
if not parsed_config["OPTIONS"]:
|
||||
parsed_config.pop("OPTIONS")
|
||||
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,79 +1,8 @@
|
|||
[project]
|
||||
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.black]
|
||||
skip-string-normalization = 1
|
||||
|
||||
[tool.mypy]
|
||||
show_error_codes=true
|
||||
disallow_untyped_defs=true
|
||||
disallow_untyped_calls=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",
|
||||
]
|
||||
|
|
|
|||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
coverage
|
||||
mypy
|
||||
47
setup.py
Normal file
47
setup.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
from pathlib import Path
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
readme = Path("README.rst").read_text()
|
||||
|
||||
setup(
|
||||
name="dj-database-url",
|
||||
version="1.3.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",
|
||||
py_modules=["dj_database_url"],
|
||||
install_requires=["Django>=3.2", "typing_extensions >= 3.10.0.0"],
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
platforms="any",
|
||||
project_links={
|
||||
"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,10 +1,6 @@
|
|||
# pyright: reportTypedDictNotRequiredAccess=false
|
||||
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from urllib.parse import uses_netloc
|
||||
|
||||
import dj_database_url
|
||||
|
||||
|
|
@ -12,10 +8,9 @@ POSTGIS_URL = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compu
|
|||
|
||||
|
||||
class DatabaseTestSuite(unittest.TestCase):
|
||||
def test_postgres_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_postgres_parsing(self):
|
||||
url = "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -24,10 +19,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_postgres_unix_socket_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgres://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||
)
|
||||
def test_postgres_unix_socket_parsing(self):
|
||||
url = "postgres://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -36,9 +30,8 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
url = dj_database_url.parse(
|
||||
"postgres://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||
)
|
||||
url = "postgres://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["HOST"] == "/Users/postgres/RuN"
|
||||
|
|
@ -46,10 +39,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
def test_postgres_google_cloud_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@%2Fcloudsql%2Fproject_id%3Aregion%3Ainstance_id/d8r82722r2kuvn"
|
||||
)
|
||||
def test_postgres_google_cloud_parsing(self):
|
||||
url = "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@%2Fcloudsql%2Fproject_id%3Aregion%3Ainstance_id/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -58,10 +50,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == ""
|
||||
|
||||
def test_ipv6_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgres://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_ipv6_parsing(self):
|
||||
url = "postgres://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -70,10 +61,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_postgres_search_path_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
)
|
||||
def test_postgres_search_path_parsing(self):
|
||||
url = "postgres://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
url = dj_database_url.parse(url)
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||
|
|
@ -83,10 +73,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_postgres_parsing_with_special_characters(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgres://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||
)
|
||||
def test_postgres_parsing_with_special_characters(self):
|
||||
url = "postgres://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.postgresql"
|
||||
assert url["NAME"] == "#database"
|
||||
|
|
@ -95,26 +84,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "#password"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_postgres_parsing_with_int_bool_str_query_string(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"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"
|
||||
)
|
||||
def test_postgis_parsing(self):
|
||||
url = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.postgis"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -123,10 +95,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_postgis_search_path_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
)
|
||||
def test_postgis_search_path_parsing(self):
|
||||
url = "postgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
url = dj_database_url.parse(url)
|
||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.postgis"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||
|
|
@ -136,10 +107,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_mysql_gis_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_mysql_gis_parsing(self):
|
||||
url = "mysqlgis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.mysql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -148,10 +118,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_mysql_connector_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"mysql-connector://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_mysql_connector_parsing(self):
|
||||
url = "mysql-connector://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "mysql.connector.django"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -160,7 +129,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_config_test_options(self) -> None:
|
||||
def test_config_test_options(self):
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
|
|
@ -174,10 +143,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
|
||||
assert url['TEST']['NAME'] == 'mytestdatabase'
|
||||
|
||||
def test_cleardb_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
||||
)
|
||||
def test_cleardb_parsing(self):
|
||||
url = "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.mysql"
|
||||
assert url["NAME"] == "heroku_97681db3eff7580"
|
||||
|
|
@ -186,7 +154,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "69772142"
|
||||
assert url["PORT"] == ""
|
||||
|
||||
def test_database_url(self) -> None:
|
||||
def test_database_url(self):
|
||||
with mock.patch.dict(os.environ, clear=True):
|
||||
a = dj_database_url.config()
|
||||
assert not a
|
||||
|
|
@ -206,46 +174,28 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_empty_sqlite_url(self) -> None:
|
||||
url = dj_database_url.parse("sqlite://")
|
||||
def test_empty_sqlite_url(self):
|
||||
url = "sqlite://"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.sqlite3"
|
||||
assert url["NAME"] == ":memory:"
|
||||
|
||||
def test_memory_sqlite_url(self) -> None:
|
||||
url = dj_database_url.parse("sqlite://:memory:")
|
||||
def test_memory_sqlite_url(self):
|
||||
url = "sqlite://:memory:"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.sqlite3"
|
||||
assert url["NAME"] == ":memory:"
|
||||
|
||||
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:
|
||||
def test_parse_engine_setting(self):
|
||||
engine = "django_mysqlpool.backends.mysqlpool"
|
||||
url = dj_database_url.parse(
|
||||
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true",
|
||||
engine,
|
||||
)
|
||||
url = "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
||||
url = dj_database_url.parse(url, engine)
|
||||
|
||||
assert url["ENGINE"] == engine
|
||||
|
||||
def test_config_engine_setting(self) -> None:
|
||||
def test_config_engine_setting(self):
|
||||
engine = "django_mysqlpool.backends.mysqlpool"
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
|
|
@ -257,17 +207,15 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
|
||||
assert url["ENGINE"] == engine
|
||||
|
||||
def test_parse_conn_max_age_setting(self) -> None:
|
||||
def test_parse_conn_max_age_setting(self):
|
||||
conn_max_age = 600
|
||||
url = dj_database_url.parse(
|
||||
"mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true",
|
||||
conn_max_age=conn_max_age,
|
||||
)
|
||||
url = "mysql://bea6eb025ca0d8:69772142@us-cdbr-east.cleardb.com/heroku_97681db3eff7580?reconnect=true"
|
||||
url = dj_database_url.parse(url, conn_max_age=conn_max_age)
|
||||
|
||||
assert url["CONN_MAX_AGE"] == conn_max_age
|
||||
|
||||
def test_config_conn_max_age_setting_none(self) -> None:
|
||||
conn_max_age = None
|
||||
def test_config_conn_max_age_setting(self):
|
||||
conn_max_age = 600
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
|
|
@ -278,7 +226,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
|
||||
assert url["CONN_MAX_AGE"] == conn_max_age
|
||||
|
||||
def test_database_url_with_options(self) -> None:
|
||||
def test_database_url_with_options(self):
|
||||
# Test full options
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
|
|
@ -309,7 +257,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
url = dj_database_url.config()
|
||||
assert "OPTIONS" not in url
|
||||
|
||||
def test_mysql_database_url_with_sslca_options(self) -> None:
|
||||
def test_mysql_database_url_with_sslca_options(self):
|
||||
with mock.patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
|
|
@ -336,8 +284,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
url = dj_database_url.config()
|
||||
assert "OPTIONS" not in url
|
||||
|
||||
def test_oracle_parsing(self) -> None:
|
||||
url = dj_database_url.parse("oracle://scott:tiger@oraclehost:1521/hr")
|
||||
def test_oracle_parsing(self):
|
||||
url = "oracle://scott:tiger@oraclehost:1521/hr"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.oracle"
|
||||
assert url["NAME"] == "hr"
|
||||
|
|
@ -346,8 +295,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "tiger"
|
||||
assert url["PORT"] == "1521"
|
||||
|
||||
def test_oracle_gis_parsing(self) -> None:
|
||||
url = dj_database_url.parse("oraclegis://scott:tiger@oraclehost:1521/hr")
|
||||
def test_oracle_gis_parsing(self):
|
||||
url = "oraclegis://scott:tiger@oraclehost:1521/hr"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.contrib.gis.db.backends.oracle"
|
||||
assert url["NAME"] == "hr"
|
||||
|
|
@ -356,13 +306,14 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "tiger"
|
||||
assert url["PORT"] == 1521
|
||||
|
||||
def test_oracle_dsn_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
def test_oracle_dsn_parsing(self):
|
||||
url = (
|
||||
"oracle://scott:tiger@/"
|
||||
"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)"
|
||||
"(HOST=oraclehost)(PORT=1521)))"
|
||||
"(CONNECT_DATA=(SID=hr)))"
|
||||
)
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.oracle"
|
||||
assert url["USER"] == "scott"
|
||||
|
|
@ -378,8 +329,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
|
||||
assert url["NAME"] == dsn
|
||||
|
||||
def test_oracle_tns_parsing(self) -> None:
|
||||
url = dj_database_url.parse("oracle://scott:tiger@/tnsname")
|
||||
def test_oracle_tns_parsing(self):
|
||||
url = "oracle://scott:tiger@/tnsname"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django.db.backends.oracle"
|
||||
assert url["USER"] == "scott"
|
||||
|
|
@ -388,10 +340,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["HOST"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
def test_redshift_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"redshift://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5439/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
)
|
||||
def test_redshift_parsing(self):
|
||||
url = "redshift://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5439/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "django_redshift_backend"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -402,10 +353,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_mssql_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
||||
)
|
||||
def test_mssql_parsing(self):
|
||||
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)
|
||||
|
||||
assert url["ENGINE"] == "sql_server.pyodbc"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -416,10 +366,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_mssql_instance_port_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"mssql://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com\\insnsnss:12345/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
||||
)
|
||||
def test_mssql_instance_port_parsing(self):
|
||||
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)
|
||||
|
||||
assert url["ENGINE"] == "sql_server.pyodbc"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -430,10 +379,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_cockroach(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"cockroach://testuser:testpass@testhost:26257/cockroach?sslmode=verify-full&sslrootcert=/certs/ca.crt&sslcert=/certs/client.myprojectuser.crt&sslkey=/certs/client.myprojectuser.key"
|
||||
)
|
||||
def test_cockroach(self):
|
||||
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)
|
||||
assert url['ENGINE'] == 'django_cockroachdb'
|
||||
assert url['NAME'] == 'cockroach'
|
||||
assert url['HOST'] == 'testhost'
|
||||
|
|
@ -445,10 +393,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url['OPTIONS']['sslcert'] == '/certs/client.myprojectuser.crt'
|
||||
assert url['OPTIONS']['sslkey'] == '/certs/client.myprojectuser.key'
|
||||
|
||||
def test_mssqlms_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"mssqlms://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com/d8r82722r2kuvn?driver=ODBC Driver 13 for SQL Server"
|
||||
)
|
||||
def test_mssqlms_parsing(self):
|
||||
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)
|
||||
|
||||
assert url["ENGINE"] == "mssql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -459,10 +406,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["driver"] == "ODBC Driver 13 for SQL Server"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_timescale_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_timescale_parsing(self):
|
||||
url = "timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -471,10 +417,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_timescale_unix_socket_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescale://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||
)
|
||||
def test_timescale_unix_socket_parsing(self):
|
||||
url = "timescale://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -483,9 +428,8 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
url = dj_database_url.parse(
|
||||
"timescale://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||
)
|
||||
url = "timescale://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||
assert url["HOST"] == "/Users/postgres/RuN"
|
||||
|
|
@ -493,10 +437,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
def test_timescale_ipv6_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescale://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_timescale_ipv6_parsing(self):
|
||||
url = "timescale://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -505,10 +448,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_timescale_search_path_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
)
|
||||
def test_timescale_search_path_parsing(self):
|
||||
url = "timescale://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
url = dj_database_url.parse(url)
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||
|
|
@ -518,10 +460,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_timescale_parsing_with_special_characters(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescale://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||
)
|
||||
def test_timescale_parsing_with_special_characters(self):
|
||||
url = "timescale://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgresql"
|
||||
assert url["NAME"] == "#database"
|
||||
|
|
@ -530,10 +471,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "#password"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_timescalegis_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_timescalegis_parsing(self):
|
||||
url = "timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -542,10 +482,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_timescalegis_unix_socket_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescalegis://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||
)
|
||||
def test_timescalegis_unix_socket_parsing(self):
|
||||
url = "timescalegis://%2Fvar%2Frun%2Fpostgresql/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -554,9 +493,8 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
url = dj_database_url.parse(
|
||||
"timescalegis://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||
)
|
||||
url = "timescalegis://%2FUsers%2Fpostgres%2FRuN/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||
assert url["HOST"] == "/Users/postgres/RuN"
|
||||
|
|
@ -564,10 +502,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == ""
|
||||
assert url["PORT"] == ""
|
||||
|
||||
def test_timescalegis_ipv6_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescalegis://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||
)
|
||||
def test_timescalegis_ipv6_parsing(self):
|
||||
url = "timescalegis://ieRaekei9wilaim7:wegauwhgeuioweg@[2001:db8:1234::1234:5678:90af]:5431/d8r82722r2kuvn"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
|
|
@ -576,10 +513,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "wegauwhgeuioweg"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_timescalegis_search_path_parsing(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
)
|
||||
def test_timescalegis_search_path_parsing(self):
|
||||
url = "timescalegis://uf07k1i6d8ia0v:wegauwhgeuioweg@ec2-107-21-253-135.compute-1.amazonaws.com:5431/d8r82722r2kuvn?currentSchema=otherschema"
|
||||
url = dj_database_url.parse(url)
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||
assert url["NAME"] == "d8r82722r2kuvn"
|
||||
assert url["HOST"] == "ec2-107-21-253-135.compute-1.amazonaws.com"
|
||||
|
|
@ -589,10 +525,9 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["OPTIONS"]["options"] == "-c search_path=otherschema"
|
||||
assert "currentSchema" not in url["OPTIONS"]
|
||||
|
||||
def test_timescalegis_parsing_with_special_characters(self) -> None:
|
||||
url = dj_database_url.parse(
|
||||
"timescalegis://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||
)
|
||||
def test_timescalegis_parsing_with_special_characters(self):
|
||||
url = "timescalegis://%23user:%23password@ec2-107-21-253-135.compute-1.amazonaws.com:5431/%23database"
|
||||
url = dj_database_url.parse(url)
|
||||
|
||||
assert url["ENGINE"] == "timescale.db.backends.postgis"
|
||||
assert url["NAME"] == "#database"
|
||||
|
|
@ -601,7 +536,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["PASSWORD"] == "#password"
|
||||
assert url["PORT"] == 5431
|
||||
|
||||
def test_persistent_connection_variables(self) -> None:
|
||||
def test_persistent_connection_variables(self):
|
||||
url = dj_database_url.parse(
|
||||
"sqlite://myfile.db", conn_max_age=600, conn_health_checks=True
|
||||
)
|
||||
|
|
@ -609,7 +544,7 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
assert url["CONN_MAX_AGE"] == 600
|
||||
assert url["CONN_HEALTH_CHECKS"] is True
|
||||
|
||||
def test_sqlite_memory_persistent_connection_variables(self) -> None:
|
||||
def test_sqlite_memory_persistent_connection_variables(self):
|
||||
# memory sqlite ignores connection.close(), so persistent connection
|
||||
# variables aren’t required
|
||||
url = dj_database_url.parse(
|
||||
|
|
@ -623,13 +558,13 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
os.environ,
|
||||
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
||||
)
|
||||
def test_persistent_connection_variables_config(self) -> None:
|
||||
def test_persistent_connection_variables_config(self):
|
||||
url = dj_database_url.config(conn_max_age=600, conn_health_checks=True)
|
||||
|
||||
assert url["CONN_MAX_AGE"] == 600
|
||||
assert url["CONN_HEALTH_CHECKS"] is True
|
||||
|
||||
def test_no_env_variable(self) -> None:
|
||||
def test_no_env_variable(self):
|
||||
with self.assertLogs() as cm:
|
||||
with mock.patch.dict(os.environ, clear=True):
|
||||
url = dj_database_url.config()
|
||||
|
|
@ -638,61 +573,18 @@ class DatabaseTestSuite(unittest.TestCase):
|
|||
'WARNING:root:No DATABASE_URL environment variable set, and so no databases setup'
|
||||
], cm.output
|
||||
|
||||
def test_credentials_unquoted__raise_value_error(self) -> None:
|
||||
expected_message = (
|
||||
"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))
|
||||
def test_bad_url_parsing(self):
|
||||
with self.assertRaisesRegex(ValueError, "No support for 'foo'. We support: "):
|
||||
dj_database_url.parse("foo://bar")
|
||||
|
||||
@mock.patch.dict(
|
||||
os.environ,
|
||||
{"DATABASE_URL": "postgres://user:password@instance.amazonaws.com:5431/d8r8?"},
|
||||
)
|
||||
def test_ssl_require(self) -> None:
|
||||
def test_ssl_require(self):
|
||||
url = dj_database_url.config(ssl_require=True)
|
||||
assert url["OPTIONS"] == {'sslmode': 'require'}
|
||||
|
||||
def test_options_int_values(self) -> None:
|
||||
"""Ensure that options with integer values are parsed correctly."""
|
||||
url = dj_database_url.parse(
|
||||
"mysql://user:pw@127.0.0.1:15036/db?connect_timeout=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__":
|
||||
unittest.main()
|
||||
Loading…
Reference in a new issue