diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 121ac04f..7b82f668 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,68 +6,90 @@ concurrency: group: test-${{ github.head_ref }} cancel-in-progress: true -env: - PYTHONUNBUFFERED: "1" - FORCE_COLOR: "1" - jobs: - - static_tests: - name: Static Tests + ruff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.11" - - name: Install latest pip and Hatch - run: pip install --upgrade pip hatch - - name: Lint - run: hatch run lint:all - - name: Docs - run: hatch run docs:build - - name: Build - run: hatch build + - uses: chartboost/ruff-action@v1 tests_matrix: - name: Python ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: - fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: [3.8, 3.9, "3.10", 3.11, 3.12] + django-version: [3.2, 4.1, 4.2, 5.0, "main"] + exclude: + # Django 3.2 + - python-version: "3.10" + django-version: 3.2 + - python-version: 3.11 + django-version: 3.2 + - python-version: 3.12 + django-version: 3.2 - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} + # Django 4.1 + - python-version: 3.12 + django-version: 4.1 - - name: Install GDAL binaries - run: sudo apt-get install binutils libproj-dev gdal-bin + # Django 4.2 + - python-version: 3.12 + django-version: 4.2 - - name: Install latest pip, Hatch and coveralls - run: pip install --upgrade pip hatch coveralls + # Django 5.0 + - python-version: 3.8 + django-version: 5.0 + - python-version: 3.9 + django-version: 5.0 - - name: Run tests - run: hatch run test-cov + # Django main + - python-version: 3.8 + django-version: "main" + - python-version: 3.9 + django-version: "main" - - name: Coverage combine - run: coverage combine + steps: + - uses: actions/checkout@v4 + - name: Install GDAL binaries + run: sudo apt-get install binutils libproj-dev gdal-bin + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + cache-dependency-path: requirements-test.txt + - run: python -m pip install -r requirements-test.txt + - run: python -m pip install -U Django==${{ matrix.django-version }} + if: matrix.django-version != 'main' + - run: python -m pip install -U https://github.com/django/django/archive/master.tar.gz + if: matrix.django-version == 'main' + - run: python -m pip install -e . + - run: coverage run manage.py test + - run: python -m pip install -U coveralls + - name: Upload coveralls (parallel) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + run: coveralls --service=github - - name: Upload coveralls (parallel) - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true - run: coveralls --service=github + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: 'pip' + cache-dependency-path: docs/requirements.txt + - name: Install and build + run: | + python -m pip install -r docs/requirements.txt + make docs tests: if: always() runs-on: ubuntu-latest - needs: [ static_tests, tests_matrix ] + needs: [ tests_matrix, ruff, docs ] steps: - name: Check tests matrix status if: needs.tests_matrix.result != 'success' diff --git a/.readthedocs.yml b/.readthedocs.yml index 636b846f..a34d6434 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,6 +5,7 @@ build: tools: python: "3.11" commands: - - pip install -U pip hatch - - hatch run docs:build + - pip install -U pip + - pip install -U -r docs/requirements.txt + - make docs - mv docs/_build $READTHEDOCS_OUTPUT diff --git a/CHANGELOG.md b/CHANGELOG.md index 708a49c4..a4804f66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - Drop support for Python 3.7 in test matrix (#533). - Fix support for Django 4.2 in test matrix (#533). - Pass "horizontal_field_offset_class" to child renderers (#391, #521). +- Add support for Django 5.0 (#538). +- Add support for Python 3.12 (#538). +- Revert packaging tools to setuptools, build, tox and twine (#538). ## 23.3 (2023-06-03) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 920504a8..c68dde9a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,8 +45,6 @@ Ready to contribute? Here\'s how to set up `django-bootstrap5` for local develop You will need some knowledge of git, github, and Python/Django development. Using a Python virtual environment is advised. -This project uses [Hatch](https://github.com/pypa/hatch) for environments and builds. - ### Local installation This section assumes you know about local Python versions and virtual environments. @@ -56,7 +54,7 @@ To clone the repository and install the requirements for local development: ```console git clone git://github.com/zostera/django-bootstrap5.git cd django-bootstrap5 -pip install -U pip hatch +pip install -U pip -r requirements-dev.txt pip install -e . ``` diff --git a/Makefile b/Makefile index 449eb848..72062bc2 100644 --- a/Makefile +++ b/Makefile @@ -1,35 +1,37 @@ -VERSION := $(shell hatch version) +VERSION := $(shell sed -n 's/^ *version.*=.*"\([^"]*\)".*/\1/p' pyproject.toml) .PHONY: test test: - hatch run test + coverage run manage.py test + coverage report .PHONY: tests tests: - hatch run all:test + tox .PHONY: reformat reformat: - hatch run lint:fmt + ruff format . + ruff --fix . .PHONY: lint lint: - hatch run lint:style + ruff . .PHONY: docs -docs: - hatch run docs:build +docs: clean + cd docs && sphinx-build -b html -d _build/doctrees . _build/html .PHONY: example example: - hatch run example:runserver + cd example && python manage.py runserver .PHONY: porcelain porcelain: ifeq ($(shell git status --porcelain),) @echo "Working directory is clean." else - @echo "Error - working directory is dirty. Commit those changes!"; + @echo "Error - working directory is dirty. Commit your changes."; @exit 1; endif @@ -38,17 +40,25 @@ branch: ifeq ($(shell git rev-parse --abbrev-ref HEAD),main) @echo "On branch main." else - @echo "Error - Not on branch main!" + @echo "Error - Not on branch main." @exit 1; endif .PHONY: build build: docs - rm -rf build dist src/*.egg-info - hatch build + python -m build .PHONY: publish publish: porcelain branch build - hatch publish + twine check dist/* + twine upload dist/* git tag -a v${VERSION} -m "Release ${VERSION}" git push origin --tags + +.PHONY: clean +clean: docs + rm -rf build dist src/*.egg-info .coverage* + +.PHONY: version +version: + @echo ${VERSION} diff --git a/docs/conf.py b/docs/conf.py index 00b46e05..46810fd5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -import importlib from datetime import datetime import tomllib @@ -7,15 +6,7 @@ pyproject = tomllib.load(f) project = pyproject["project"]["name"] -conf = {"module": project.replace("-", "_")} - -try: - conf.update(pyproject["tool"]["sphinx"]["x-conf"]) -except KeyError: - pass - -module = importlib.import_module(conf["module"]) -release = module.__version__ +release = pyproject["project"]["version"] version = ".".join(release.split(".")[:2]) author = ", ".join(author["name"] for author in pyproject["project"]["authors"]) year = datetime.now().year diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..cb2e9af4 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +sphinx>=7.2.6 +sphinx-mdinclude>=0.5.3 +furo>=2023.05.20 diff --git a/pyproject.toml b/pyproject.toml index 9fb69119..57cbc276 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -build-backend = "hatchling.build" -requires = ["hatchling"] +build-backend = "setuptools.build_meta" +requires = ["setuptools"] [project] authors = [ @@ -13,6 +13,7 @@ classifiers = [ "Framework :: Django :: 3.2", "Framework :: Django :: 4.1", "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", @@ -21,20 +22,17 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ] -dependencies = [ - "Django>=3.2", -] +dependencies = ["Django>=3.2"] description = "Bootstrap 5 for Django" -dynamic = [ - "version", -] -license = "BSD-3-Clause" +license = {file = "LICENSE"} name = "django-bootstrap5" readme = "README.md" requires-python = ">=3.8" +version = "23.7.dev0" [project.urls] Changelog = "https://github.com/zostera/django-bootstrap5/blob/main/CHANGELOG.md" @@ -43,96 +41,6 @@ Homepage = "https://github.com/zostera/django-bootstrap5" Issues = "https://github.com/zostera/django-bootstrap5/issues" Source = "https://github.com/zostera/django-bootstrap5" -[tool.hatch.version] -path = "src/django_bootstrap5/__about__.py" - -[tool.hatch.envs.default] -dependencies = [ - "coverage[toml]>=7.2.6", - "pillow>=9.5.0", - "beautifulsoup4>=4.12.2", -] -matrix-name-format = "dj{value}" - -[tool.hatch.envs.default.scripts] -cov = [ - "test-cov", - "cov-report", -] -cov-report = [ - "- coverage combine", - "coverage report", -] -test = "python manage.py test {args:tests}" -test-cov = "coverage run manage.py test {args:tests}" - -[[tool.hatch.envs.all.matrix]] -django = ["3.2", "4.1", "4.2"] -python = ["3.8", "3.9"] - -[[tool.hatch.envs.all.matrix]] -django = ["3.2", "4.1", "4.2", "main"] -python = ["3.10"] - -[[tool.hatch.envs.all.matrix]] -django = ["4.1", "4.2", "main"] -python = ["3.11"] - -[tool.hatch.envs.all.overrides] -matrix.django.dependencies = [ - {value = "django~=3.2.0", if = ["3.2"]}, - {value = "django~=4.1.0", if = ["4.1"]}, - {value = "django~=4.2.0", if = ["4.2"]}, - {value = "django @ git+https://github.com/django/django.git", if = ["main"]}, -] - -[tool.hatch.envs.example] -dependencies = [ - "django>=3.2", -] -python = "3.11" -template = "example" - -[tool.hatch.envs.example.scripts] -runserver = [ - "cd example && python manage.py migrate && python manage.py runserver", -] - -[tool.hatch.envs.docs] -dependencies = [ - "sphinx>=7.0.1", - "sphinx-mdinclude>=0.5.3", - "furo>=2023.05.20", -] -python = "3.11" -template = "docs" - -[tool.hatch.envs.docs.scripts] -build = [ - "clean", - "cd docs && sphinx-build -b html -d _build/doctrees . _build/html", -] -clean = "rm -rf docs/_build" - -[tool.hatch.envs.lint] -dependencies = [ - "ruff>0.1", -] -detached = true - -[tool.hatch.envs.lint.scripts] -all = [ - "style", -] -fmt = [ - "ruff --fix {args:.}", - "ruff format {args:.}", - "style", -] -style = [ - "ruff {args:.}", -] - [tool.ruff] fix = false fixable = [ @@ -156,22 +64,21 @@ select = [ "UP", # pyupgrade ] src = ["src"] -target-version = "py37" +target-version = "py38" unfixable = [ "F8", # names in flake8, such as defined but unused variables ] [tool.ruff.isort] -known-first-party = ["django_bootstrap5", "app"] +known-first-party = ["django_marina", "app"] known-third-party = ["django"] [tool.coverage.run] branch = true -parallel = true -source = ["src"] +source = ["src", "tests"] [tool.coverage.paths] -package = ["src/django_bootstrap5", "*/django_bootstrap5/src/django_bootstrap5"] +package = ["src/django_marina", "*/django_marina/src/django_marina"] [tool.coverage.report] show_missing = true diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..9c8d1209 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +-r requirements-test.txt +-r docs/requirements.txt +twine==4.0.2 diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 00000000..9a1574c5 --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,5 @@ +tox==4.11.4 +coverage==7.3.4 +ruff==0.1.9 +pillow>=10.1.0 +beautifulsoup4>=4.12.2 diff --git a/src/django_bootstrap5/__about__.py b/src/django_bootstrap5/__about__.py index fdd1c655..2c02b644 100644 --- a/src/django_bootstrap5/__about__.py +++ b/src/django_bootstrap5/__about__.py @@ -1 +1,3 @@ -__version__ = "23.4.dev0" +import importlib.metadata + +__version__ = importlib.metadata.version(__package__ or __name__) diff --git a/tests/test_bootstrap_field.py b/tests/test_bootstrap_field.py index bf5de1e2..c6377649 100644 --- a/tests/test_bootstrap_field.py +++ b/tests/test_bootstrap_field.py @@ -34,7 +34,9 @@ def test_placeholder(self): self.assertIn('placeholder="placeholdertest"', html) def test_field_class(self): - html = self.render("{% bootstrap_field form.subject field_class='field-class-test' %}", {"form": SubjectTestForm()}) + html = self.render( + "{% bootstrap_field form.subject field_class='field-class-test' %}", {"form": SubjectTestForm()} + ) self.assertIn('class="form-control field-class-test"', html) def test_xss_field(self): diff --git a/tests/test_bootstrap_field_input_checkbox.py b/tests/test_bootstrap_field_input_checkbox.py index f304b5cc..00408167 100644 --- a/tests/test_bootstrap_field_input_checkbox.py +++ b/tests/test_bootstrap_field_input_checkbox.py @@ -1,6 +1,6 @@ from django import forms -from tests.base import BootstrapTestCase +from tests.base import DJANGO_VERSION, BootstrapTestCase class CheckboxTestForm(forms.Form): @@ -24,8 +24,14 @@ def test_input_type_checkbox(self): def test_input_type_checkbox_is_invalid(self): """Test field with checkbox widget.""" + html = self.render( + "{% bootstrap_field form.test %}", + context={"form": CheckboxTestForm(data={})}, + ) + if DJANGO_VERSION >= "5": + html = html.replace(' aria-invalid="true"', "") self.assertHTMLEqual( - self.render("{% bootstrap_field form.test %}", context={"form": CheckboxTestForm(data={})}), + html, ( '
' '
' @@ -40,7 +46,10 @@ def test_input_type_checkbox_is_invalid(self): def test_input_type_checkbox_is_valid(self): """Test field with checkbox widget.""" self.assertHTMLEqual( - self.render("{% bootstrap_field form.test %}", context={"form": CheckboxTestForm(data={"test": "on"})}), + self.render( + "{% bootstrap_field form.test %}", + context={"form": CheckboxTestForm(data={"test": "on"})}, + ), ( '
' '
' @@ -55,7 +64,8 @@ def test_input_type_checkbox_style_switch(self): """Test field with checkbox widget, style switch.""" self.assertHTMLEqual( self.render( - '{% bootstrap_field form.test checkbox_style="switch" %}', context={"form": CheckboxTestForm()} + '{% bootstrap_field form.test checkbox_style="switch" %}', + context={"form": CheckboxTestForm()}, ), ( '
' @@ -70,7 +80,10 @@ def test_input_type_checkbox_style_switch(self): def test_bootstrap_field_checkbox_horizontal(self): """Test field with checkbox widget, layout horizontal.""" self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='horizontal' %}", context={"form": CheckboxTestForm()}), + self.render( + "{% bootstrap_field form.test layout='horizontal' %}", + context={"form": CheckboxTestForm()}, + ), ( '
' '
' diff --git a/tests/test_bootstrap_field_input_text.py b/tests/test_bootstrap_field_input_text.py index 69252d37..c12abd2a 100644 --- a/tests/test_bootstrap_field_input_text.py +++ b/tests/test_bootstrap_field_input_text.py @@ -1,7 +1,7 @@ from django import forms from django.forms import TextInput -from tests.base import BootstrapTestCase +from tests.base import DJANGO_VERSION, BootstrapTestCase class CharFieldTestForm(forms.Form): @@ -30,8 +30,12 @@ def test_input_type_text(self): ) form = CharFieldTestForm(data={}) + html = self.render("{% bootstrap_field form.test %}", context={"form": form}) + if DJANGO_VERSION >= "5": + html = html.replace(' aria-invalid="true"', "") + self.assertHTMLEqual( - self.render("{% bootstrap_field form.test %}", context={"form": form}), + html, ( '
' '' @@ -47,7 +51,10 @@ def test_input_type_text_more(self): form = CharFieldTestForm() self.assertHTMLEqual( - self.render('{% bootstrap_field form.test addon_before="foo" %}', context={"form": form}), + self.render( + '{% bootstrap_field form.test addon_before="foo" %}', + context={"form": form}, + ), ( '
' '' @@ -124,7 +131,10 @@ def test_input_type_text_more(self): ) self.assertHTMLEqual( - self.render('{% bootstrap_field form.test addon_before="foo" layout="floating" %}', context={"form": form}), + self.render( + '{% bootstrap_field form.test addon_before="foo" layout="floating" %}', + context={"form": form}, + ), ( '
' '' @@ -136,21 +146,29 @@ def test_input_type_text_more(self): ), ) - self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='horizontal' %}", context={"form": form}), - ( - '
' - '
' - '' - "
" - "
" + ( + self.assertHTMLEqual( + self.render( + "{% bootstrap_field form.test layout='horizontal' %}", + context={"form": form}, + ), + ( + '
' + '
' + '' + "
" + "
" + ), ), - ), + ) self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='floating' %}", context={"form": form}), + self.render( + "{% bootstrap_field form.test layout='floating' %}", + context={"form": form}, + ), ( '
' '' @@ -164,8 +182,12 @@ def test_input_validation_failure(self): form = CharFieldRequiredTestForm(data={"test": ""}) self.assertFalse(form.is_valid()) + html = self.render('{% bootstrap_field form.test addon_before="foo" %}', context={"form": form}) + if DJANGO_VERSION >= "5": + html = html.replace(' aria-invalid="true"', "") + self.assertHTMLEqual( - self.render('{% bootstrap_field form.test addon_before="foo" %}', context={"form": form}), + html, ( '
' '' @@ -183,7 +205,10 @@ def test_input_validation_failure(self): self.assertTrue(form.is_valid()) self.assertHTMLEqual( - self.render('{% bootstrap_field form.test addon_before="foo" %}', context={"form": form}), + self.render( + '{% bootstrap_field form.test addon_before="foo" %}', + context={"form": form}, + ), ( '
' '' @@ -232,12 +257,18 @@ class InputTypeTestForm(forms.Form): ) self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='horizontal' %}", context={"form": form}), + self.render( + "{% bootstrap_field form.test layout='horizontal' %}", + context={"form": form}, + ), horizontal_html, ) self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='floating' %}", context={"form": form}), + self.render( + "{% bootstrap_field form.test layout='floating' %}", + context={"form": form}, + ), floating_html, ) @@ -270,21 +301,29 @@ class PasswordTestForm(forms.Form): ), ) - self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='horizontal' %}", context={"form": form}), - ( - '
' - '
' - '' - "
" - "
" + ( + self.assertHTMLEqual( + self.render( + "{% bootstrap_field form.test layout='horizontal' %}", + context={"form": form}, + ), + ( + '
' + '
' + '' + "
" + "
" + ), ), - ), + ) self.assertHTMLEqual( - self.render("{% bootstrap_field form.test layout='floating' %}", context={"form": form}), + self.render( + "{% bootstrap_field form.test layout='floating' %}", + context={"form": form}, + ), ( '
' '' diff --git a/tests/test_bootstrap_form.py b/tests/test_bootstrap_form.py index 9bacda8c..52e67078 100644 --- a/tests/test_bootstrap_form.py +++ b/tests/test_bootstrap_form.py @@ -2,7 +2,7 @@ from django import forms from django.forms import formset_factory -from tests.base import BootstrapTestCase +from tests.base import DJANGO_VERSION, BootstrapTestCase class FormTestForm(forms.Form): @@ -32,7 +32,12 @@ def test_illegal_form(self): self.render("{% bootstrap_form form %}", {"form": "illegal"}) def test_exclude(self): - html = self.render('{% bootstrap_form form exclude="optional_text" %}', {"form": FormTestForm()}) + html = self.render( + '{% bootstrap_form form exclude="optional_text" %}', + {"form": FormTestForm()}, + ) + if DJANGO_VERSION >= "5": + html = html.replace(' aria-describedby="id_required_text_helptext"', "") self.assertHTMLEqual( html, ( @@ -51,7 +56,10 @@ def test_error_css_class(self): html = self.render("{% bootstrap_form form %}", {"form": form}) self.assertIn("django_bootstrap5-err", html) - html = self.render('{% bootstrap_form form error_css_class="custom-error-class" %}', {"form": form}) + html = self.render( + '{% bootstrap_form form error_css_class="custom-error-class" %}', + {"form": form}, + ) self.assertIn("custom-error-class", html) html = self.render('{% bootstrap_form form error_css_class="" %}', {"form": form}) @@ -62,7 +70,10 @@ def test_required_css_class(self): html = self.render("{% bootstrap_form form %}", {"form": form}) self.assertIn("django_bootstrap5-req", html) - html = self.render('{% bootstrap_form form required_css_class="custom-required-class" %}', {"form": form}) + html = self.render( + '{% bootstrap_form form required_css_class="custom-required-class" %}', + {"form": form}, + ) self.assertIn("custom-required-class", html) html = self.render('{% bootstrap_form form required_css_class="" %}', {"form": form}) @@ -76,7 +87,10 @@ def test_success_css_class(self): form = FormTestForm({"subject": "subject"}) - html = self.render('{% bootstrap_form form success_css_class="successful-test" %}', {"form": form}) + html = self.render( + '{% bootstrap_form form success_css_class="successful-test" %}', + {"form": form}, + ) self.assertIn("successful-test", html) form = FormTestForm({"subject": "subject"}) @@ -128,7 +142,10 @@ class ShowLabelTestCase(BootstrapTestCase): def test_show_label_false(self): self.assertInHTML( '', - self.render("{% bootstrap_form form show_label=False %}", {"form": ShowLabelTestForm()}), + self.render( + "{% bootstrap_form form show_label=False %}", + {"form": ShowLabelTestForm()}, + ), ) def test_show_label_sr_only(self): @@ -140,14 +157,20 @@ def test_show_label_sr_only(self): def test_show_label_skip(self): self.assertNotIn( "label", - self.render("{% bootstrap_form form show_label='skip' %}", {"form": ShowLabelTestForm()}), + self.render( + "{% bootstrap_form form show_label='skip' %}", + {"form": ShowLabelTestForm()}, + ), ) def test_show_label_false_in_formset(self): TestFormSet = formset_factory(ShowLabelTestForm, extra=1) self.assertInHTML( '', - self.render("{% bootstrap_formset formset show_label=False %}", {"formset": TestFormSet()}), + self.render( + "{% bootstrap_formset formset show_label=False %}", + {"formset": TestFormSet()}, + ), ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..2501070a --- /dev/null +++ b/tox.ini @@ -0,0 +1,50 @@ +[tox] +args_are_paths = false +envlist = + py38-{3.2,4.1,4.2}, + py39-{3.2,4.1,4.2}, + py310-{3.2,4.1,4.2,5.0,main}, + py311-{4.1,4.2,5.0,main}, + py312-{4.2,5.0,main}, + docs, + lint, + +[testenv] +basepython = + py37: python3.7 + py38: python3.8 + py39: python3.9 + py310: python3.10 + py311: python3.11 + py312: python3.12 +usedevelop = true +pip_pre = true +setenv = + PYTHONPATH={toxinidir} + PYTHONWARNINGS=all +commands = + python manage.py test {posargs} +deps = + 3.2: Django==3.2.* + 4.0: Django==4.0.* + 4.1: Django==4.1.* + 4.2: Django==4.2.* + 5.0: Django==5.0.* + main: https://github.com/django/django/archive/main.tar.gz + -r{toxinidir}/requirements-test.txt + +[testenv:ruff] +basepython = python3.11 +allowlist_externals = ruff +deps = ruff +commands = ruff . + +[testenv:docs] +basepython = python3.11 +allowlist_externals = make +setenv = + PYTHONWARNINGS=default +commands = + make docs +deps = + -r{toxinidir}/docs/requirements.txt