Compare commits

..

No commits in common. "master" and "v3.0.1" have entirely different histories.

12 changed files with 187 additions and 1233 deletions

5
.flake8 Normal file
View file

@ -0,0 +1,5 @@
[flake8]
max-line-length = 88
extend-ignore = E203
per-file-ignores=
tests/test_dj_database_url.py: E501, E265

View file

@ -1,8 +1,9 @@
name: Release
on:
release:
types: [published]
push:
tags:
- '*'
jobs:
build:
@ -10,29 +11,28 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
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@v5
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
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: jazzband

View file

@ -1,74 +1,56 @@
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.9", "3.10", "3.11", "3.12", "3.13"]
django-version: ["4.2", "5.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.11 is not compatible with 3.2
- python-version: "3.11"
django-version: "6.0"
django-version: "3.2"
- python-version: "3.12"
django-version: "3.2"
- python-version: "3.13"
django-version: "3.2"
# django 5.x is not compatible with python 3.9 or lower
- python-version: "3.9"
django-version: "5.1"
steps:
- uses: actions/checkout@v4
- 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@v5
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 type checking
run: |
python -m mypy dj_database_url
python -m pyright dj_database_url
- 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 -v
coverage report
coverage xml
- uses: codecov/codecov-action@v4
- name: Check types installation
run: |
pip install .
cd tests
python -m mypy .
python -m pyright .

2
.isort.cfg Normal file
View file

@ -0,0 +1,2 @@
[settings]
profile = black

View file

@ -1,21 +1,23 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: v5.0.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: "6.0.1"
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: 25.1.0
hooks:
- id: typos
- id: black
args: [--target-version=py38]
- repo: https://github.com/pycqa/flake8
rev: '7.2.0'
hooks:
- id: flake8

View file

@ -1,17 +1,11 @@
# 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.
> Bumping to version 3; changes to code do break some API compatability.
* Implement a new decorator registry pattern to impement 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.
@ -24,46 +18,54 @@
## 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
* Update django 5.0 python compatability 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.
* 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
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)
* 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 +85,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 +112,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 +140,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 +152,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
🐍 ✨

View file

@ -1,33 +1,32 @@
import logging
import os
import urllib.parse as urlparse
from collections.abc import Callable
from typing import Any, TypedDict
from typing import Any, Callable, Dict, List, Optional, TypedDict, Union
DEFAULT_ENV = "DATABASE_URL"
ENGINE_SCHEMES: dict[str, "Engine"] = {}
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_MAX_AGE: Optional[int]
CONN_HEALTH_CHECKS: bool
DISABLE_SERVER_SIDE_CURSORS: bool
ENGINE: str
HOST: str
NAME: str
OPTIONS: dict[str, Any]
OPTIONS: Dict[str, Any]
PASSWORD: str
PORT: str | int
TEST: dict[str, Any]
PORT: Union[str, int]
TEST: Dict[str, Any]
TIME_ZONE: str
USER: str
PostprocessCallable = Callable[[DBConfig], None]
OptionType = int | str | bool
OptionType = Union[int, str, bool]
class ParseError(ValueError):
@ -39,7 +38,7 @@ class ParseError(ValueError):
class UnknownSchemeError(ValueError):
def __init__(self, scheme: str):
def __init__(self, scheme: str) -> None:
self.scheme = scheme
def __str__(self) -> str:
@ -60,7 +59,7 @@ class Engine:
self,
backend: str,
postprocess: PostprocessCallable = default_postprocess,
):
) -> None:
self.backend = backend
self.postprocess = postprocess
@ -126,13 +125,13 @@ def apply_current_schema(parsed_config: DBConfig) -> None:
def config(
env: str = DEFAULT_ENV,
default: str | None = None,
engine: str | None = None,
conn_max_age: int | None = 0,
default: Optional[str] = None,
engine: Optional[str] = None,
conn_max_age: int = 0,
conn_health_checks: bool = False,
disable_server_side_cursors: bool = False,
ssl_require: bool = False,
test_options: dict[str, Any] | None = None,
test_options: Optional[Dict[str, Any]] = None,
) -> DBConfig:
"""Returns configured DATABASE dictionary from DATABASE_URL."""
s = os.environ.get(env, default)
@ -158,12 +157,12 @@ def config(
def parse(
url: str,
engine: str | None = None,
conn_max_age: int | None = 0,
engine: Optional[str] = None,
conn_max_age: int = 0,
conn_health_checks: bool = False,
disable_server_side_cursors: bool = False,
ssl_require: bool = False,
test_options: dict[str, Any] | None = None,
test_options: Optional[Dict[str, Any]] = None,
) -> DBConfig:
"""Parses a database URL and returns configured DATABASE dictionary."""
settings = _convert_to_settings(
@ -217,7 +216,7 @@ def parse(
return parsed_config
def _parse_option_values(values: list[str]) -> OptionType | list[OptionType]:
def _parse_option_values(values: List[str]) -> Union[OptionType, List[OptionType]]:
parsed_values = [_parse_value(v) for v in values]
return parsed_values[0] if len(parsed_values) == 1 else parsed_values
@ -231,12 +230,12 @@ def _parse_value(value: str) -> OptionType:
def _convert_to_settings(
engine: str | None,
conn_max_age: int | None,
engine: Optional[str],
conn_max_age: int,
conn_health_checks: bool,
disable_server_side_cursors: bool,
ssl_require: bool,
test_options: dict[str, Any] | None,
test_options: Optional[dict[str, Any]],
) -> DBConfig:
settings: DBConfig = {
"CONN_MAX_AGE": conn_max_age,

View file

@ -1,61 +1,5 @@
[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
@ -65,15 +9,3 @@ 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",
]

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
coverage
mypy
pyright

50
setup.py Normal file
View file

@ -0,0 +1,50 @@
from pathlib import Path
from setuptools import setup # pyright: ignore[reportUnknownVariableType]
readme = Path("README.rst").read_text()
setup(
name="dj-database-url",
version="3.0.1",
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>=4.2"],
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 :: 4.2",
"Framework :: Django :: 5.0",
"Framework :: Django :: 5.1",
"Framework :: Django :: 5.2",
"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",
],
)

View file

@ -266,8 +266,8 @@ class DatabaseTestSuite(unittest.TestCase):
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) -> None:
conn_max_age = 600
with mock.patch.dict(
os.environ,
{
@ -680,9 +680,9 @@ class DatabaseTestSuite(unittest.TestCase):
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"
"mysql://user:pw@127.0.0.1:15036/db?connect_timout=3"
)
assert url["OPTIONS"] == {'connect_timeout': 3}
assert url["OPTIONS"] == {'connect_timout': 3}
@mock.patch.dict(
os.environ,

1033
uv.lock

File diff suppressed because it is too large Load diff