Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-django-js-asset for openSUSE:Factory checked in at 2022-09-03 23:18:59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-django-js-asset (Old) and /work/SRC/openSUSE:Factory/.python-django-js-asset.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-js-asset" Sat Sep 3 23:18:59 2022 rev:4 rq:1000978 version:2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-django-js-asset/python-django-js-asset.changes 2022-01-07 12:48:06.735917578 +0100 +++ /work/SRC/openSUSE:Factory/.python-django-js-asset.new.2083/python-django-js-asset.changes 2022-09-03 23:19:14.495841296 +0200 @@ -1,0 +2,11 @@ +Sat Sep 3 02:25:21 UTC 2022 - John Vandenberg <jay...@gmail.com> + +- Remove unneeded changes-in-Django.patch +- Update to v2.0 + * Raised the minimum supported versions of Python to 3.6, Django to 2.2. + * Replaced the explicit configuration of whether static() should be used + or not with automatic configuration. The static argument is still + accepted but ignored and will be removed at a later time. + * Added support for boolean attributes when using Django 4.1 or better. + +------------------------------------------------------------------- Old: ---- 1.2.2.tar.gz changes-in-Django.patch New: ---- 2.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-django-js-asset.spec ++++++ --- /var/tmp/diff_new_pack.mcciNR/_old 2022-09-03 23:19:14.903842368 +0200 +++ /var/tmp/diff_new_pack.mcciNR/_new 2022-09-03 23:19:14.915842400 +0200 @@ -17,16 +17,16 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} +%define skip_python2 1 %define skip_python36 1 Name: python-django-js-asset -Version: 1.2.2 +Version: 2.0 Release: 0 Summary: Script tag with additional attributes for django.formsMedia License: BSD-3-Clause Group: Development/Languages/Python URL: https://github.com/matthiask/django-js-asset/ Source: https://github.com/matthiask/django-js-asset/archive/%{version}.tar.gz -Patch0: changes-in-Django.patch BuildRequires: %{python_module Django} BuildRequires: %{python_module pytz} BuildRequires: %{python_module setuptools} @@ -43,7 +43,6 @@ %prep %setup -q -n django-js-asset-%{version} -%patch0 -p1 %build %python_build @@ -58,6 +57,7 @@ %files %{python_files} %doc README.rst %license LICENSE -%{python_sitelib}/* +%{python_sitelib}/js_asset/ +%{python_sitelib}/*django_js_asset*/ %changelog ++++++ 1.2.2.tar.gz -> 2.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/.editorconfig new/django-js-asset-2.0/.editorconfig --- old/django-js-asset-1.2.2/.editorconfig 1970-01-01 01:00:00.000000000 +0100 +++ new/django-js-asset-2.0/.editorconfig 2022-02-10 10:05:07.000000000 +0100 @@ -0,0 +1,13 @@ +# top-most EditorConfig file +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.py] +indent_size = 4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/.github/workflows/tests.yml new/django-js-asset-2.0/.github/workflows/tests.yml --- old/django-js-asset-1.2.2/.github/workflows/tests.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/django-js-asset-2.0/.github/workflows/tests.yml 2022-02-10 10:05:07.000000000 +0100 @@ -0,0 +1,35 @@ +name: Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tests: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.6" + - "3.7" + - "3.8" + - "3.9" + - "3.10" + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip wheel setuptools tox + - name: Run tox targets for ${{ matrix.python-version }} + run: | + ENV_PREFIX=$(tr -C -d "0-9" <<< "${{ matrix.python-version }}") + TOXENV=$(tox --listenvs | grep "^py$ENV_PREFIX" | tr '\n' ',') python -m tox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/.pre-commit-config.yaml new/django-js-asset-2.0/.pre-commit-config.yaml --- old/django-js-asset-1.2.2/.pre-commit-config.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/django-js-asset-2.0/.pre-commit-config.yaml 2022-02-10 10:05:07.000000000 +0100 @@ -0,0 +1,38 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: check-added-large-files + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [--py36-plus] + - repo: https://github.com/adamchainz/django-upgrade + rev: 1.4.0 + hooks: + - id: django-upgrade + args: [--target-version, "2.2"] + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + args: [--profile=black, --lines-after-imports=2, --combine-as] + - repo: https://github.com/psf/black + rev: 22.1.0 + hooks: + - id: black + - repo: https://github.com/pycqa/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + args: ["--ignore=E203,E501,W503"] + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.5.1 + hooks: + - id: prettier + args: [--list-different, --no-semi] + exclude: "^conf/|.*\\.html$" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/.travis.yml new/django-js-asset-2.0/.travis.yml --- old/django-js-asset-1.2.2/.travis.yml 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,61 +0,0 @@ -dist: xenial -sudo: false -language: python -python: - - 3.7 -matrix: - fast_finish: true - include: - - python: 3.7 - env: REQ="https://github.com/django/django/archive/master.zip#egg=Django" - - python: 3.7 - env: REQ="Django>=2.2a1,<3.0" - - python: 3.6 - env: REQ="Django>=2.2a1,<3.0" - - python: 3.5 - env: REQ="Django>=2.2a1,<3.0" - - python: 3.7 - env: REQ="Django>=2.1,<2.2" - - python: 3.7 - env: REQ="Django>=2.0,<2.1" - - python: 3.7 - env: REQ="Django>=1.11,<2.0" - - python: 3.6 - env: REQ="Django>=1.11,<2.0" - - python: 3.5 - env: REQ="Django>=1.11,<2.0" - - python: 3.4 - env: REQ="Django>=1.11,<2.0" - - python: 2.7 - env: REQ="Django>=1.11,<2.0" - - python: 3.5 - env: REQ="Django>=1.10,<1.11" - - python: 2.7 - env: REQ="Django>=1.10,<1.11" - - python: 3.5 - env: REQ="Django>=1.9,<1.10" - - python: 2.7 - env: REQ="Django>=1.9,<1.10" - - python: 3.5 - env: REQ="Django>=1.8,<1.9" - - python: 3.4 - env: REQ="Django>=1.8,<1.9" - - python: 2.7 - env: REQ="Django>=1.8,<1.9" - - python: 3.4 - env: REQ="Django>=1.7,<1.8" - - python: 2.7 - env: REQ="Django>=1.7,<1.8" - - python: 3.7 - env: REQ="black flake8" - install: - - pip install black flake8 - script: - - flake8 . - - black --check js_asset tests - allow_failures: - - env: REQ="https://github.com/django/django/archive/master.zip#egg=Django" -install: - - pip install $REQ coverage pytz - - python setup.py install -script: cd tests && ./manage.py test -v2 testapp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/CHANGELOG.rst new/django-js-asset-2.0/CHANGELOG.rst --- old/django-js-asset-1.2.2/CHANGELOG.rst 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/CHANGELOG.rst 2022-02-10 10:05:07.000000000 +0100 @@ -7,8 +7,35 @@ `Next version`_ ~~~~~~~~~~~~~~~ +.. _Next version: https://github.com/matthiask/django-js-asset/compare/2.0...master + + +`2.0`_ (2022-02-10) +~~~~~~~~~~~~~~~~~~~ + +.. _2.0: https://github.com/matthiask/django-js-asset/compare/1.2...2.0 + +- Raised the minimum supported versions of Python to 3.6, Django to 2.2. +- Added pre-commit. +- Replaced the explicit configuration of whether ``static()`` should be used or + not with automatic configuration. The ``static`` argument is still accepted + but ignored and will be removed at a later time. +- Added support for boolean attributes when using Django 4.1 or better. + + +Released as 1.2.1 and 1.2.2: +---------------------------- + - Made ``JS()`` objects hashable so that they can be put into sets in preparation for a possible fix for media ordering in Django #30179. +- Confirmed support for Django 3.0 and 3.1a1. +- Django dropped ``type="text/javascript"`` in 3.1, changed our tests to + pass again. +- Switched from Travis CI to GitHub actions. +- Dropped Django 1.7 from the CI jobs list because it somehow didn't + discover our tests. +- Renamed the main branch to ``main``. +- Added CI testing for Django 3.2. `1.2`_ (2019-02-08) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/README.rst new/django-js-asset-2.0/README.rst --- old/django-js-asset-1.2.2/README.rst 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/README.rst 2022-02-10 10:05:07.000000000 +0100 @@ -2,8 +2,8 @@ django-js-asset -- script tag with additional attributes for django.forms.Media =============================================================================== -.. image:: https://travis-ci.org/matthiask/django-js-asset.svg?branch=master - :target: https://travis-ci.org/matthiask/django-js-asset +.. image:: https://github.com/matthiask/django-js-asset/workflows/Tests/badge.svg + :target: https://github.com/matthiask/django-js-asset Usage ===== @@ -44,12 +44,14 @@ ``js_asset.static`` function is provided which does the right thing automatically. -When adding external script assets, you should pass ``static=False`` to -the ``JS`` object to avoid passing the script URL through ``static()``. -In this case, you probably want to add ``defer`` or ``async``, and maybe -also ``integrity`` and ``crossorigin`` attributes. Please note that -boolean attributes are not properly supported, so specify them as -follows:: +When adding external script assets, you should pass ``static=False`` to the +``JS`` object to avoid passing the script URL through ``static()``. In this +case, you probably want to add ``defer`` or ``async``, and maybe also +``integrity`` and ``crossorigin`` attributes. Please note that boolean +attributes are not properly supported when using Django before 4.1 so specify +them as follows: + +.. code-block:: python JS( "https://cdn.example.com/script.js", @@ -61,7 +63,8 @@ Compatibility ============= -At the time of writing this app is compatible with Django 1.7 and better +At the time of writing this app is compatible with Django 1.8 and better (up to and including the Django master branch), but have a look at the -`Travis CI build <https://travis-ci.org/matthiask/django-js-asset>`_ for +`tox configuration +<https://github.com/matthiask/django-js-asset/blob/main/tox.ini>`_ for definitive answers. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/js_asset/__init__.py new/django-js-asset-2.0/js_asset/__init__.py --- old/django-js-asset-1.2.2/js_asset/__init__.py 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/js_asset/__init__.py 2022-02-10 10:05:07.000000000 +0100 @@ -1,7 +1,4 @@ -from __future__ import absolute_import, unicode_literals - - -VERSION = (1, 2, 2) +VERSION = (2, 0, 0) __version__ = ".".join(map(str, VERSION)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/js_asset/js.py new/django-js-asset-2.0/js_asset/js.py --- old/django-js-asset-1.2.2/js_asset/js.py 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/js_asset/js.py 2022-02-10 10:05:07.000000000 +0100 @@ -1,29 +1,19 @@ -from __future__ import unicode_literals - import json +import warnings from django import VERSION -from django.apps import apps from django.forms.utils import flatatt from django.templatetags.static import static -from django.utils.html import format_html, mark_safe +from django.utils.html import format_html, html_safe, mark_safe __all__ = ("JS", "static") -if VERSION < (1, 10): # pragma: no cover - _static = static - - def static(path): - if apps.is_installed("django.contrib.staticfiles"): - from django.contrib.staticfiles.storage import staticfiles_storage +_sentinel = object() - return staticfiles_storage.url(path) - return _static(path) - -class JS(object): +class JS: """ Use this to insert a script tag via ``forms.Media`` containing additional attributes (such as ``id`` and ``data-*`` for CSP-compatible data @@ -48,36 +38,57 @@ var answer = document.querySelector('#asset-script').dataset.answer; """ - def __init__(self, js, attrs=None, static=True): + def __init__(self, js, attrs=None, static=_sentinel): self.js = js self.attrs = attrs or {} - self.static = static + if static is not _sentinel: + warnings.warn( + "JS automatically determines whether it received an absolute" + " path or not. Stop passing the 'static' argument please.", + DeprecationWarning, + stacklevel=2, + ) def startswith(self, _): # Masquerade as absolute path so that we are returned as-is. return True def __repr__(self): - return "JS({}, {}, static={})".format( - self.js, json.dumps(self.attrs, sort_keys=True), self.static - ) - - def __html__(self): - js = static(self.js) if self.static else self.js - return ( - format_html('{}"{}', js, mark_safe(flatatt(self.attrs)))[:-1] - if self.attrs - else js - ) + return f"JS({self.js}, {json.dumps(self.attrs, sort_keys=True)})" - def __eq__(self, other): - if isinstance(other, JS): + if VERSION >= (4, 1): + + def __str__(self): + return format_html( + '<script src="{}"{}></script>', + self.js + if self.js.startswith(("http://", "https://", "/")) + else static(self.js), + mark_safe(flatatt(self.attrs)), + ) + + else: + + def __html__(self): + js = ( + self.js + if self.js.startswith(("http://", "https://", "/")) + else static(self.js) + ) return ( - self.js == other.js - and self.attrs == other.attrs - and self.static == other.static + format_html('{}"{}', js, mark_safe(flatatt(self.attrs)))[:-1] + if self.attrs + else js ) - return self.js == other and not self.attrs and self.static + + def __eq__(self, other): + if isinstance(other, JS): + return self.js == other.js and self.attrs == other.attrs + return self.js == other and not self.attrs def __hash__(self): - return hash((self.js, json.dumps(self.attrs, sort_keys=True), self.static)) + return hash((self.js, json.dumps(self.attrs, sort_keys=True))) + + +if VERSION >= (4, 1): + JS = html_safe(JS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/setup.cfg new/django-js-asset-2.0/setup.cfg --- old/django-js-asset-1.2.2/setup.cfg 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/setup.cfg 2022-02-10 10:05:07.000000000 +0100 @@ -1,11 +1,51 @@ -[flake8] -exclude=venv,build,docs,.tox -ignore=E203,W503 -max-complexity=10 -max-line-length=88 +[metadata] +name = django_js_asset +version = attr: js_asset.__version__ +description = script tag with additional attributes for django.forms.Media +long_description = file: README.rst +long_description_content_type = text/x-rst +url = https://github.com/matthiask/django-js-asset/ +author = Matthias Kestenholz +author_email = m...@feinheit.ch +license = BSD-3-Clause +license_file = LICENSE +platforms = OS Independent +classifiers = + Environment :: Web Environment + Framework :: Django + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Topic :: Internet :: WWW/HTTP :: Dynamic Content + Topic :: Software Development + Topic :: Software Development :: Libraries :: Application Frameworks -[bdist_wheel] -universal = 1 +[options] +packages = find: +install_requires = + Django>=2.2 +python_requires = >=3.6 +include_package_data = True +tests_require = + Django + coverage + pytz +zip_safe = False + +[options.extras_require] +tests = + coverage + +[options.packages.find] +exclude = tests [coverage:run] branch = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/setup.py new/django-js-asset-2.0/setup.py --- old/django-js-asset-1.2.2/setup.py 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/setup.py 2022-02-10 10:05:07.000000000 +0100 @@ -1,42 +1,5 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 +from setuptools import setup -from io import open -import os -from setuptools import setup, find_packages - -def read(filename): - path = os.path.join(os.path.dirname(__file__), filename) - with open(path, encoding="utf-8") as handle: - return handle.read() - - -setup( - name="django-js-asset", - version=__import__("js_asset").__version__, - description="script tag with additional attributes for django.forms.Media", - long_description=read("README.rst"), - author="Matthias Kestenholz", - author_email="m...@feinheit.ch", - url="https://github.com/matthiask/django-js-asset/", - license="BSD License", - platforms=["OS Independent"], - packages=find_packages(exclude=["tests"]), - include_package_data=True, - classifiers=[ - # 'Development Status :: 5 - Production/Stable', - "Environment :: Web Environment", - "Framework :: Django", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 3", - "Topic :: Internet :: WWW/HTTP :: Dynamic Content", - "Topic :: Software Development", - "Topic :: Software Development :: Libraries :: Application Frameworks", - ], - zip_safe=False, - tests_require=["Django", "coverage", "pytz"], -) +setup() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/tests/testapp/test_js_asset.py new/django-js-asset-2.0/tests/testapp/test_js_asset.py --- old/django-js-asset-1.2.2/tests/testapp/test_js_asset.py 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/tests/testapp/test_js_asset.py 2022-02-10 10:05:07.000000000 +0100 @@ -1,11 +1,16 @@ -from __future__ import unicode_literals +from unittest import skipIf +import django from django.forms import Media from django.test import TestCase from js_asset.js import JS +CSS_TYPE = ' type="text/css"' if django.VERSION < (4, 1) else "" +JS_TYPE = ' type="text/javascript"' if django.VERSION < (3, 1) else "" + + class AssetTest(TestCase): def test_asset(self): media = Media( @@ -21,19 +26,21 @@ # print(html) self.assertInHTML( - '<link href="/static/app/print.css" type="text/css" media="print" rel="stylesheet" />', # noqa + f'<link href="/static/app/print.css"{CSS_TYPE} media="print" rel="stylesheet" />', # noqa html, ) self.assertInHTML( - '<script type="text/javascript" src="/static/app/test.js"></script>', # noqa + f'<script{JS_TYPE} src="/static/app/test.js"></script>', # noqa html, ) self.assertInHTML( - '<script type="text/javascript" src="/static/app/asset.js" data-the-answer="42" id="asset-script"></script>', # noqa + '<script{} src="/static/app/asset.js" data-the-answer="42" id="asset-script"></script>'.format( # noqa + JS_TYPE + ), html, ) self.assertInHTML( - '<script type="text/javascript" src="/static/app/asset-without.js"></script>', # noqa + f'<script{JS_TYPE} src="/static/app/asset-without.js"></script>', html, ) @@ -42,7 +49,9 @@ html = "%s" % media self.assertInHTML( - '<script type="text/javascript" src="https://cdn.example.org/script.js"></script>', # noqa + '<script{} src="https://cdn.example.org/script.js"></script>'.format( + JS_TYPE + ), html, ) @@ -59,7 +68,7 @@ repr( JS("app/asset.js", {"id": "asset-script", "data-the-answer": 42}) ).lstrip("u"), - 'JS(app/asset.js, {"data-the-answer": 42, "id": "asset-script"}, static=True)', # noqa + 'JS(app/asset.js, {"data-the-answer": 42, "id": "asset-script"})', # noqa ) def test_set(self): @@ -70,3 +79,13 @@ ] self.assertEqual(len(set(media)), 2) + + @skipIf( + django.VERSION < (4, 1), + "django-js-asset doesn't support boolean attributes yet", + ) + def test_boolean_attributes(self): + self.assertEqual( + str(JS("app/asset.js", {"bool": True, "cool": False})), + '<script src="/static/app/asset.js" bool></script>', + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-js-asset-1.2.2/tox.ini new/django-js-asset-2.0/tox.ini --- old/django-js-asset-1.2.2/tox.ini 2019-02-21 08:47:34.000000000 +0100 +++ new/django-js-asset-2.0/tox.ini 2022-02-10 10:05:07.000000000 +0100 @@ -1,39 +1,16 @@ -[testenv] -basepython = python3 - -[testenv:style] -deps = - black - flake8 -changedir = {toxinidir} -commands = - black setup.py js_asset tests - flake8 . -skip_install = true - -# [testenv:docs] -# deps = Sphinx -# changedir = docs -# commands = make html -# skip_install = true -# whitelist_externals = make +[tox] +envlist = + py{36,37,38,39}-dj{22,32} + py{38,39,310}-dj{32,40,main} -[testenv:tests] -deps = - Django - coverage -changedir = {toxinidir} -skip_install = true +[testenv] +usedevelop = true +extras = tests commands = - coverage run tests/manage.py test -v 2 testapp - coverage html - - -[testenv:tests-old] -basepython = python2 + python -Wd {envbindir}/coverage run tests/manage.py test -v2 --keepdb {posargs:testapp} + coverage report -m deps = - Django<1.8 -changedir = {toxinidir} -skip_install = true -commands = - python tests/manage.py test -v 2 testapp + dj22: Django>=2.2,<3.0 + dj32: Django>=3.2,<4.0 + dj40: Django>=4.0,<4.1 + djmain: https://github.com/django/django/archive/main.tar.gz