From c0dedfc1b2c4bc69c3fa5079842d1db833060b2a Mon Sep 17 00:00:00 2001 From: Bas van Oostveen Date: Fri, 9 Sep 2022 13:10:06 +0200 Subject: [PATCH 01/19] bumped version number --- django_extensions/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_extensions/__init__.py b/django_extensions/__init__.py index 8f4c64ef1..f2518d20b 100644 --- a/django_extensions/__init__.py +++ b/django_extensions/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -VERSION = (3, 2, 1) +VERSION = (3, 2, 2, 'DEV') def get_version(version): From b48561548abc99c31c3198b948fd7b7e3aad739e Mon Sep 17 00:00:00 2001 From: Hameed Gifford Date: Sun, 25 Dec 2022 16:00:57 -0500 Subject: [PATCH 02/19] Fix error with lack of PosixPath support (#1785) --- django_extensions/import_subclasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_extensions/import_subclasses.py b/django_extensions/import_subclasses.py index 4e6140ca5..ad0f3dbe1 100644 --- a/django_extensions/import_subclasses.py +++ b/django_extensions/import_subclasses.py @@ -39,7 +39,7 @@ def collect_subclasses(self): # type: () -> Dict[str, List[Tuple[str, str]]] but in future functionality of aliasing subclasses can be added. """ result = {} # type: Dict[str, List[Tuple[str, str]]] - for loader, module_name, is_pkg in walk_packages(path=[settings.BASE_DIR]): + for loader, module_name, is_pkg in walk_packages(path=[str(settings.BASE_DIR)]): subclasses_from_module = self._collect_classes_from_module(module_name) if subclasses_from_module: result[module_name] = subclasses_from_module From b3c3e895e2210c2dc0c902fc1bb5ea9015c736b1 Mon Sep 17 00:00:00 2001 From: Bas van Oostveen Date: Sun, 25 Dec 2022 22:04:08 +0100 Subject: [PATCH 03/19] silence imported but unused F401 at flake8 --- django_extensions/management/commands/runserver_plus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_extensions/management/commands/runserver_plus.py b/django_extensions/management/commands/runserver_plus.py index e8f104c58..996ebbc14 100644 --- a/django_extensions/management/commands/runserver_plus.py +++ b/django_extensions/management/commands/runserver_plus.py @@ -7,7 +7,7 @@ import traceback import webbrowser import functools -from typing import List, Set +from typing import List, Set # NOQA import django from django.conf import settings From c27377c83c5cfd028071926b2bf74d92addbd322 Mon Sep 17 00:00:00 2001 From: Jelmer Draaijer Date: Tue, 13 Dec 2022 10:25:30 +0100 Subject: [PATCH 04/19] feat: force pip to use pkg_resources as backend for resolving distributions --- django_extensions/management/commands/pipchecker.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/django_extensions/management/commands/pipchecker.py b/django_extensions/management/commands/pipchecker.py index e3563ee2c..0e0cbf482 100644 --- a/django_extensions/management/commands/pipchecker.py +++ b/django_extensions/management/commands/pipchecker.py @@ -39,20 +39,16 @@ def get_installed_distributions( """Return a list of installed Distribution objects. Left for compatibility until direct pkg_resources uses are refactored out. """ - from pip._internal.metadata import get_default_environment, get_environment - from pip._internal.metadata.pkg_resources import Distribution as _Dist - if paths is None: - env = get_default_environment() - else: - env = get_environment(paths) - dists = env.iter_installed_distributions( + from pip._internal.metadata import pkg_resources + + dists = pkg_resources.Environment.from_paths(paths).iter_installed_distributions( local_only=local_only, include_editables=include_editables, editables_only=editables_only, user_only=user_only, ) - return [cast(_Dist, dist)._dist for dist in dists] + return [cast(pkg_resources.Distribution, dist)._dist for dist in dists] except ImportError: # pip < 10 try: From d422fb796bd9bcfc679dc9f6353ca20024e0a33a Mon Sep 17 00:00:00 2001 From: Josh Michael Karamuth Date: Sat, 29 Oct 2022 18:28:47 +0400 Subject: [PATCH 05/19] Add REMOTE_USER to werkzeug environment Django RemoteUserMiddleware and RemoteUserBackend are able to authenticate users by looking at the REMOTE_USER request header. In Werkzeug, this header isn't set unlike in Django's default WSGI server. This patch add the variable to the Werkzeug environ if it's set. Fixes #1708 --- django_extensions/management/commands/runserver_plus.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/django_extensions/management/commands/runserver_plus.py b/django_extensions/management/commands/runserver_plus.py index 996ebbc14..1ea99362c 100644 --- a/django_extensions/management/commands/runserver_plus.py +++ b/django_extensions/management/commands/runserver_plus.py @@ -326,6 +326,9 @@ def make_environ(self): environ = super().make_environ() if not options['keep_meta_shutdown_func'] and 'werkzeug.server.shutdown' in environ: del environ['werkzeug.server.shutdown'] + remote_user = os.getenv('REMOTE_USER') + if remote_user is not None: + environ['REMOTE_USER'] = remote_user return environ threaded = options['threaded'] From 868b8a83386d3bde103924eeeb088937e10505c9 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sun, 25 Dec 2022 22:14:22 +0100 Subject: [PATCH 06/19] feat: send the file_changed event when a reload is triggered, this way other receivers can consume it (#1775) Co-authored-by: trbs <65552+trbs@users.noreply.github.com> --- .../management/commands/runserver_plus.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/django_extensions/management/commands/runserver_plus.py b/django_extensions/management/commands/runserver_plus.py index 1ea99362c..39b6c542b 100644 --- a/django_extensions/management/commands/runserver_plus.py +++ b/django_extensions/management/commands/runserver_plus.py @@ -7,6 +7,7 @@ import traceback import webbrowser import functools +from pathlib import Path from typing import List, Set # NOQA import django @@ -15,7 +16,7 @@ from django.core.management.color import color_style from django.core.servers.basehttp import get_internal_wsgi_application from django.dispatch import Signal -from django.utils.autoreload import get_reloader +from django.utils.autoreload import file_changed, get_reloader from django.views import debug as django_views_debug try: @@ -86,12 +87,18 @@ def __init__(self, *args, **kwargs): @property def extra_files(self): - return self._extra_files.union(_error_files) + return self._extra_files.union(_error_files, {"*.html"}) @extra_files.setter def extra_files(self, extra_files): self._extra_files = extra_files + def trigger_reload(self, filename: str) -> None: + path = Path(filename) + results = file_changed.send(sender=self, file_path=path) + if not any(res[1] for res in results): + super().trigger_reload(filename) + _reloader.reloader_loops[name] = WrappedReloaderLoop From 9cc676b98596fc7f716be752bb89f943ea81f234 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Tue, 27 Dec 2022 11:29:07 +0100 Subject: [PATCH 07/19] feat: add Python 3.11 to tox and actions to formally support python 3.11 (#1786) --- .github/workflows/pytest.yml | 1 + setup.py | 1 + tox.ini | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 3abc67892..91c17a562 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -18,6 +18,7 @@ jobs: - 3.8 - 3.9 - "3.10" + - "3.11" - "pypy3.9" tox-django-version: - "32" diff --git a/setup.py b/setup.py index 6a2232f8d..917433e33 100644 --- a/setup.py +++ b/setup.py @@ -127,6 +127,7 @@ def fullsplit(path, result=None): 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', diff --git a/tox.ini b/tox.ini index 51c777bf5..7f967b60d 100644 --- a/tox.ini +++ b/tox.ini @@ -8,10 +8,10 @@ envlist = precommit safety mypy - {py37,py38,py39,py310}-flake8 + {py37,py38,py39,py310,py311}-flake8 {py36,py37,py38,py39,py310,pypy}-dj32 {py38,py39,py310,pypy}-dj40 - {py38,py39,py310,pypy}-dj41 + {py38,py39,py310,py311,pypy}-dj41 {py38,py39,py310,pypy}-djmaster py310-dj32-postgres py310-dj40-postgres From 86b13d6b9f10844f675e437687448ccd603e75f9 Mon Sep 17 00:00:00 2001 From: Michael K Date: Sat, 29 Apr 2023 19:40:17 +0000 Subject: [PATCH 08/19] Run tests againts Django 4.2 and add trove classifier (#1812) * fix: test_should_highlight_bash_syntax_without_name to include whitespace spans * Run tests againts Django 4.2 and add trove classifier --------- Co-authored-by: Jelmer Draaijer --- .github/workflows/pytest.yml | 7 +++++++ setup.py | 1 + tests/templatetags/test_highlighting.py | 2 +- tox.ini | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 91c17a562..cfb2b23c7 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -24,6 +24,7 @@ jobs: - "32" - "40" - "41" + - "42" # GH Actions don't support something like allow-failure ? # - "master" exclude: @@ -37,6 +38,12 @@ jobs: tox-django-version: "41" - python-version: "pypy3.9" tox-django-version: "41" + - python-version: "3.6" + tox-django-version: "42" + - python-version: "3.7" + tox-django-version: "42" + - python-version: "pypy3.9" + tox-django-version: "42" steps: - name: Checkout uses: actions/checkout@v3 diff --git a/setup.py b/setup.py index 917433e33..26ea97b34 100644 --- a/setup.py +++ b/setup.py @@ -116,6 +116,7 @@ def fullsplit(path, result=None): 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', 'Framework :: Django :: 4.1', + 'Framework :: Django :: 4.2', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', diff --git a/tests/templatetags/test_highlighting.py b/tests/templatetags/test_highlighting.py index 40ec2bd2b..2115e85aa 100644 --- a/tests/templatetags/test_highlighting.py +++ b/tests/templatetags/test_highlighting.py @@ -44,7 +44,7 @@ def test_should_highlight_bash_syntax_without_name(self): {% highlight 'bash' %} echo "Hello $1" {% endhighlight %}""" - expected_result = '''
echo "Hello $1"
+        expected_result = '''
echo "Hello $1"
 
''' result = Template(content).render(self.ctx) diff --git a/tox.ini b/tox.ini index 7f967b60d..59638e661 100644 --- a/tox.ini +++ b/tox.ini @@ -12,13 +12,16 @@ envlist = {py36,py37,py38,py39,py310,pypy}-dj32 {py38,py39,py310,pypy}-dj40 {py38,py39,py310,py311,pypy}-dj41 + {py38,py39,py310,py311,pypy}-dj42 {py38,py39,py310,pypy}-djmaster py310-dj32-postgres py310-dj40-postgres py310-dj41-postgres + py310-dj42-postgres py310-dj32-mysql py310-dj40-mysql py310-dj41-mysql + py310-dj42-mysql py310-djmaster-postgres [testenv] @@ -44,6 +47,7 @@ deps = dj32: Django>=3.2,<4.0 dj40: Django>=4.0,<4.1 dj41: Django>=4.1,<4.2 + dj42: Django>=4.2,<5.0 djmaster: https://github.com/django/django/archive/refs/heads/main.zip postgres: psycopg2-binary mysql: mysqlclient From 601e96c4859b1427815784528d06012b94cde1f1 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sat, 29 Apr 2023 21:41:45 +0200 Subject: [PATCH 09/19] fix: test_should_highlight_bash_syntax_without_name to include whitespace spans (#1797) From 9484207d1e8ab256d825ffe43eeadfae3784e901 Mon Sep 17 00:00:00 2001 From: Jelmer Date: Sat, 29 Apr 2023 21:48:33 +0200 Subject: [PATCH 10/19] runserver_plus autoreload on template change (#1796) * fix: walk all the template dirs manually since *.html is not supported by the StatLoaderLoop * feat: reset loaders when new files are added --- .../management/commands/runserver_plus.py | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/django_extensions/management/commands/runserver_plus.py b/django_extensions/management/commands/runserver_plus.py index 39b6c542b..6440b6503 100644 --- a/django_extensions/management/commands/runserver_plus.py +++ b/django_extensions/management/commands/runserver_plus.py @@ -16,6 +16,7 @@ from django.core.management.color import color_style from django.core.servers.basehttp import get_internal_wsgi_application from django.dispatch import Signal +from django.template.autoreload import get_template_directories, reset_loaders from django.utils.autoreload import file_changed, get_reloader from django.views import debug as django_views_debug @@ -77,17 +78,43 @@ _error_files = set() # type: Set[str] +def get_all_template_files() -> Set[str]: + template_list = set() + + for template_dir in get_template_directories(): + for base_dir, _, filenames in os.walk(template_dir): + for filename in filenames: + template_list.add(os.path.join(base_dir, filename)) + + return template_list + + if HAS_WERKZEUG: # Monkey patch the reloader to support adding more files to extra_files for name, reloader_loop_klass in _reloader.reloader_loops.items(): class WrappedReloaderLoop(reloader_loop_klass): # type: ignore def __init__(self, *args, **kwargs): + self._template_files: Set[str] = get_all_template_files() super().__init__(*args, **kwargs) self._extra_files = self.extra_files @property def extra_files(self): - return self._extra_files.union(_error_files, {"*.html"}) + template_files = get_all_template_files() + + # reset loaders if there are new files detected + if len(self._template_files) != len(template_files): + + changed = template_files.difference(self._template_files) + for filename in changed: + _log("info", f" * New file {filename} added, reset template loaders") + self.register_file_changed(filename) + + reset_loaders() + + self._template_files = template_files + + return self._extra_files.union(_error_files, template_files) @extra_files.setter def extra_files(self, extra_files): @@ -98,6 +125,14 @@ def trigger_reload(self, filename: str) -> None: results = file_changed.send(sender=self, file_path=path) if not any(res[1] for res in results): super().trigger_reload(filename) + else: + _log("info", f" * Detected change in {filename!r}, reset template loaders") + self.register_file_changed(filename) + + def register_file_changed(self, filename): + if hasattr(self, "mtimes"): + mtime = os.stat(filename).st_mtime + self.mtimes[filename] = mtime _reloader.reloader_loops[name] = WrappedReloaderLoop From bc437932d1ea8de29e067f9803ce31c08322310b Mon Sep 17 00:00:00 2001 From: Scott Rubin Date: Mon, 15 May 2023 07:07:48 -0400 Subject: [PATCH 11/19] Add support for psycopg3 (#1814) closes #1811 --- .../management/commands/drop_test_database.py | 7 ++++++- .../management/commands/reset_db.py | 7 ++++++- docs/sqldsn.rst | 2 +- .../commands/test_drop_test_database.py | 19 ++++++++++++++++--- tests/management/commands/test_pipchecker.py | 1 + tests/management/commands/test_reset_db.py | 13 +++++++++++-- tox.ini | 5 +++++ 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/django_extensions/management/commands/drop_test_database.py b/django_extensions/management/commands/drop_test_database.py index 5021ab5b3..936a13c6f 100644 --- a/django_extensions/management/commands/drop_test_database.py +++ b/django_extensions/management/commands/drop_test_database.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import importlib.util from itertools import count import os import logging @@ -167,7 +168,11 @@ def format_filename(name, number): cursor.execute(drop_query) elif engine in POSTGRESQL_ENGINES: - import psycopg2 as Database # NOQA + has_psycopg3 = importlib.util.find_spec("psycopg") + if has_psycopg3: + import psycopg as Database # NOQA + else: + import psycopg2 as Database # NOQA conn_params = {'database': 'template1'} if user: diff --git a/django_extensions/management/commands/reset_db.py b/django_extensions/management/commands/reset_db.py index 09bc1d43a..83d486c39 100644 --- a/django_extensions/management/commands/reset_db.py +++ b/django_extensions/management/commands/reset_db.py @@ -4,6 +4,7 @@ originally from http://www.djangosnippets.org/snippets/828/ by dnordberg """ +import importlib.util import os import logging import warnings @@ -141,7 +142,11 @@ def handle(self, *args, **options): logging.info('Executing... "%s"', create_query) connection.query(create_query.strip()) elif engine in POSTGRESQL_ENGINES: - import psycopg2 as Database # NOQA + has_psycopg3 = importlib.util.find_spec("psycopg") + if has_psycopg3: + import psycopg as Database # NOQA + else: + import psycopg2 as Database # NOQA conn_params = {'database': 'template1'} if user: diff --git a/docs/sqldsn.rst b/docs/sqldsn.rst index b02fa0fcc..2f1866bd5 100644 --- a/docs/sqldsn.rst +++ b/docs/sqldsn.rst @@ -10,7 +10,7 @@ Supported Databases Currently the following databases are supported: -* PostgreSQL (psycopg2 or postgis) +* PostgreSQL (psycopg2, psycopg3, or postgis) * Sqlite3 * MySQL diff --git a/tests/management/commands/test_drop_test_database.py b/tests/management/commands/test_drop_test_database.py index ed8d9b111..5017dcc10 100644 --- a/tests/management/commands/test_drop_test_database.py +++ b/tests/management/commands/test_drop_test_database.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import importlib.util from io import StringIO from unittest.mock import MagicMock, Mock, PropertyMock, call, patch @@ -251,7 +252,11 @@ def test_postgresql_should_drop_database(self, m_stdout): # Indicate that no clone databases exist type(m_cursor).rowcount = PropertyMock(side_effect=(1, 0)) - with patch.dict("sys.modules", psycopg2=m_database): + mock_kwargs = {"psycopg2": m_database} + has_psycopg3 = importlib.util.find_spec("psycopg") is not None + if has_psycopg3: + mock_kwargs = {"psycopg": m_database} + with patch.dict("sys.modules", **mock_kwargs): call_command('drop_test_database', '--noinput', verbosity=2) with self.subTest('Should check for and remove test database names until failure'): @@ -277,7 +282,11 @@ def test_postgresql_should_drop_all_existing_cloned_databases(self): # Indicate that clone databases exist up to test_test_2 type(m_cursor).rowcount = PropertyMock(side_effect=(1, 1, 1, 0)) - with patch.dict("sys.modules", psycopg2=m_database): + mock_kwargs = {"psycopg2": m_database} + has_psycopg3 = importlib.util.find_spec("psycopg") is not None + if has_psycopg3: + mock_kwargs = {"psycopg": m_database} + with patch.dict("sys.modules", **mock_kwargs): call_command('drop_test_database', '--noinput') exists_query = "SELECT datname FROM pg_catalog.pg_database WHERE datname=" @@ -303,7 +312,11 @@ def test_postgresql_should_not_print_Reset_successful_when_exception_occured(sel m_cursor.execute.side_effect = m_database.ProgrammingError m_database.connect.return_value.cursor.return_value = m_cursor - with patch.dict("sys.modules", psycopg2=m_database): + mock_kwargs = {"psycopg2": m_database} + has_psycopg3 = importlib.util.find_spec("psycopg") is not None + if has_psycopg3: + mock_kwargs = {"psycopg": m_database} + with patch.dict("sys.modules", **mock_kwargs): call_command('drop_test_database', '--noinput', verbosity=2) self.assertNotIn("Reset successful.", m_stdout.getvalue()) diff --git a/tests/management/commands/test_pipchecker.py b/tests/management/commands/test_pipchecker.py index 0575b6af8..b6a625526 100644 --- a/tests/management/commands/test_pipchecker.py +++ b/tests/management/commands/test_pipchecker.py @@ -140,6 +140,7 @@ def test_pipchecker_with_long_up_to_date_requirements(self): f.write('Pillow') f.write('pluggy') f.write('psycopg2-binary') + f.write('psycopg[binary,pool]') f.write('py') f.write('pyparsing') f.write('pytest') diff --git a/tests/management/commands/test_reset_db.py b/tests/management/commands/test_reset_db.py index 4ffc660a4..73b113c17 100644 --- a/tests/management/commands/test_reset_db.py +++ b/tests/management/commands/test_reset_db.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import importlib.util import os from io import StringIO @@ -166,7 +167,11 @@ def test_should_drop_and_create_database_and_print_success_messsage(self, m_stdo mock.call('CREATE DATABASE "test_db" WITH OWNER = "foo" ENCODING = \'UTF8\';'), ] - with mock.patch.dict("sys.modules", psycopg2=m_database): + mock_kwargs = {"psycopg2": m_database} + has_psycopg3 = importlib.util.find_spec("psycopg") is not None + if has_psycopg3: + mock_kwargs = {"psycopg": m_database} + with mock.patch.dict("sys.modules", **mock_kwargs): call_command('reset_db', '--noinput', verbosity=2) m_database.connect.assert_called_once_with(database='template1', host='127.0.0.1', password='bar', port='5432', user='foo') @@ -186,7 +191,11 @@ def test_should_drop_create_database_close_sessions_and_print_success_messsage(s mock.call('CREATE DATABASE "test_db" WITH OWNER = "foo" ENCODING = \'UTF8\' TABLESPACE = TEST_TABLESPACE;'), ] - with mock.patch.dict("sys.modules", psycopg2=m_database): + mock_kwargs = {"psycopg2": m_database} + has_psycopg3 = importlib.util.find_spec("psycopg") is not None + if has_psycopg3: + mock_kwargs = {"psycopg": m_database} + with mock.patch.dict("sys.modules", **mock_kwargs): call_command('reset_db', '--noinput', '--close-sessions', verbosity=2) m_database.connect.assert_called_once_with(database='template1', host='127.0.0.1', password='bar', port='5432', user='foo') diff --git a/tox.ini b/tox.ini index 59638e661..696de184c 100644 --- a/tox.ini +++ b/tox.ini @@ -18,11 +18,13 @@ envlist = py310-dj40-postgres py310-dj41-postgres py310-dj42-postgres + py310-dj42-postgres3 py310-dj32-mysql py310-dj40-mysql py310-dj41-mysql py310-dj42-mysql py310-djmaster-postgres + py310-djmaster-postgres3 [testenv] commands = make test @@ -38,6 +40,8 @@ passenv = setenv = postgres: DJANGO_EXTENSIONS_DATABASE_ENGINE = {env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.postgresql} postgres: DJANGO_EXTENSIONS_DATABASE_NAME = {env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test} + postgres3: DJANGO_EXTENSIONS_DATABASE_ENGINE = {env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.postgresql} + postgres3: DJANGO_EXTENSIONS_DATABASE_NAME = {env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test} mysql: DJANGO_EXTENSIONS_DATABASE_ENGINE = {env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.mysql} mysql: DJANGO_EXTENSIONS_DATABASE_NAME = {env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test} @@ -50,6 +54,7 @@ deps = dj42: Django>=4.2,<5.0 djmaster: https://github.com/django/django/archive/refs/heads/main.zip postgres: psycopg2-binary + postgres3: psycopg[binary,pool] mysql: mysqlclient [testenv:precommit] From 4fc31cb266534163bc9740c3233d5432f0a491d0 Mon Sep 17 00:00:00 2001 From: jannh Date: Wed, 24 May 2023 15:11:28 +0200 Subject: [PATCH 12/19] Fixed drop test database command with psycopg 3 (#1818) Co-authored-by: Jann Haber --- .../management/commands/drop_test_database.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/django_extensions/management/commands/drop_test_database.py b/django_extensions/management/commands/drop_test_database.py index 936a13c6f..66d45b73a 100644 --- a/django_extensions/management/commands/drop_test_database.py +++ b/django_extensions/management/commands/drop_test_database.py @@ -174,7 +174,7 @@ def format_filename(name, number): else: import psycopg2 as Database # NOQA - conn_params = {'database': 'template1'} + conn_params = {'dbname': 'template1'} if user: conn_params['user'] = user if password: @@ -185,7 +185,10 @@ def format_filename(name, number): conn_params['port'] = database_port connection = Database.connect(**conn_params) - connection.set_isolation_level(0) # autocommit false + if has_psycopg3: + connection.autocommit = True + else: + connection.set_isolation_level(0) # autocommit false cursor = connection.cursor() for db_name in get_database_names('{}_{}'.format): From 8c7fae179f7981411056aba4e5f6960a984859ed Mon Sep 17 00:00:00 2001 From: trbs Date: Mon, 5 Jun 2023 17:57:54 +0200 Subject: [PATCH 13/19] disabled pypy3.9 due to asgiref typing error --- .github/workflows/pytest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index cfb2b23c7..f10aeee6e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -19,7 +19,8 @@ jobs: - 3.9 - "3.10" - "3.11" - - "pypy3.9" + # 2023-06-05: disabled pypy3.9 due to asgiref typing error + # - "pypy3.9" tox-django-version: - "32" - "40" From de7dfac7eccab913bde5bade691f6834fb610680 Mon Sep 17 00:00:00 2001 From: jannh Date: Mon, 5 Jun 2023 18:01:35 +0200 Subject: [PATCH 14/19] Fixed reset_db with psycopg3 (same patch like for drop_test_database) (#1821) * Fixed reset_db with psycopg3 (same patch like for drop_test_database) * Fixed tests --------- Co-authored-by: Jann Haber --- django_extensions/management/commands/reset_db.py | 7 +++++-- tests/management/commands/test_reset_db.py | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/django_extensions/management/commands/reset_db.py b/django_extensions/management/commands/reset_db.py index 83d486c39..3734dd498 100644 --- a/django_extensions/management/commands/reset_db.py +++ b/django_extensions/management/commands/reset_db.py @@ -148,7 +148,7 @@ def handle(self, *args, **options): else: import psycopg2 as Database # NOQA - conn_params = {'database': 'template1'} + conn_params = {'dbname': 'template1'} if user: conn_params['user'] = user if password: @@ -159,7 +159,10 @@ def handle(self, *args, **options): conn_params['port'] = database_port connection = Database.connect(**conn_params) - connection.set_isolation_level(0) # autocommit false + if has_psycopg3: + connection.autocommit = True + else: + connection.set_isolation_level(0) # autocommit false cursor = connection.cursor() if options['close_sessions']: diff --git a/tests/management/commands/test_reset_db.py b/tests/management/commands/test_reset_db.py index 73b113c17..3c232adaa 100644 --- a/tests/management/commands/test_reset_db.py +++ b/tests/management/commands/test_reset_db.py @@ -174,7 +174,7 @@ def test_should_drop_and_create_database_and_print_success_messsage(self, m_stdo with mock.patch.dict("sys.modules", **mock_kwargs): call_command('reset_db', '--noinput', verbosity=2) - m_database.connect.assert_called_once_with(database='template1', host='127.0.0.1', password='bar', port='5432', user='foo') + m_database.connect.assert_called_once_with(dbname='template1', host='127.0.0.1', password='bar', port='5432', user='foo') m_cursor.execute.assert_has_calls(expected_calls, any_order=False) self.assertEqual("Reset successful.\n", m_stdout.getvalue()) @@ -198,7 +198,7 @@ def test_should_drop_create_database_close_sessions_and_print_success_messsage(s with mock.patch.dict("sys.modules", **mock_kwargs): call_command('reset_db', '--noinput', '--close-sessions', verbosity=2) - m_database.connect.assert_called_once_with(database='template1', host='127.0.0.1', password='bar', port='5432', user='foo') + m_database.connect.assert_called_once_with(dbname='template1', host='127.0.0.1', password='bar', port='5432', user='foo') m_cursor.execute.assert_has_calls(expected_calls, any_order=False) self.assertEqual("Reset successful.\n", m_stdout.getvalue()) From 6d93223e1017e6459f7a7bb7d5f8debe6f3df3fb Mon Sep 17 00:00:00 2001 From: Gabriel Augendre Date: Mon, 5 Jun 2023 18:08:02 +0200 Subject: [PATCH 15/19] Cleanup http: links (#1798) Move http: links to https: where possible and update old links. Also removed broken links. --- .editorconfig | 2 +- README.rst | 6 +++--- django_extensions/db/fields/__init__.py | 4 ++-- django_extensions/locale/fr/LC_MESSAGES/django.po | 2 +- .../management/commands/clear_cache.py | 2 +- .../management/commands/dumpscript.py | 2 +- .../management/commands/list_model_info.py | 2 +- .../management/commands/pipchecker.py | 4 ++-- django_extensions/management/commands/reset_db.py | 2 +- .../management/commands/runserver_plus.py | 4 ++-- .../management/commands/shell_plus.py | 2 +- .../management/commands/show_template_tags.py | 2 +- django_extensions/management/commands/sqldiff.py | 4 ++-- django_extensions/management/debug_cursor.py | 1 - django_extensions/management/modelviz.py | 6 +++--- django_extensions/mongodb/fields/__init__.py | 4 ++-- .../django_extensions/js/jquery.ajaxQueue.js | 3 --- .../static/django_extensions/js/jquery.bgiframe.js | 2 +- django_extensions/templatetags/highlighting.py | 3 --- django_extensions/templatetags/syntax_color.py | 2 +- docs/AUTHORS | 2 +- docs/command_extensions.rst | 4 ++-- docs/export_emails.rst | 2 +- docs/graph_models.rst | 4 ++-- docs/index.rst | 2 +- docs/runprofileserver.rst | 10 ++++------ docs/runserver_plus.rst | 14 +++++++------- docs/shell_plus.rst | 2 +- setup.py | 6 +++--- tox.ini | 2 +- 30 files changed, 49 insertions(+), 58 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5c99f6582..736c9d3e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# http://editorconfig.org +# https://editorconfig.org # Source: pydanny cookiecutter-django repo root = true diff --git a/README.rst b/README.rst index 36d5f866e..ca5bf09e2 100644 --- a/README.rst +++ b/README.rst @@ -110,7 +110,7 @@ Open Source projects can always use more help. Fixing a problem, documenting a f translation in your language. If you have some time to spare and like to help us, here are the places to do so: - GitHub: https://github.com/django-extensions/django-extensions -- Mailing list: http://groups.google.com/group/django-extensions +- Mailing list: https://groups.google.com/group/django-extensions - Translations: https://www.transifex.com/projects/p/django-extensions/ @@ -134,6 +134,6 @@ Please remember that nobody is paid directly to develop or maintain Django Exten between putting food on the table, family, this project and the rest of life :-) -__ http://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ -__ http://vimeo.com/1720508 +__ https://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ +__ https://vimeo.com/1720508 __ https://www.youtube.com/watch?v=1F6G3ONhr4k diff --git a/django_extensions/db/fields/__init__.py b/django_extensions/db/fields/__init__.py index 5d1b657aa..bc524f14f 100644 --- a/django_extensions/db/fields/__init__.py +++ b/django_extensions/db/fields/__init__.py @@ -143,7 +143,7 @@ def slugify_function(self, content): slug = AutoSlugField(populate_from='title') Inspired by SmileyChris' Unique Slugify snippet: - http://www.djangosnippets.org/snippets/690/ + https://www.djangosnippets.org/snippets/690/ """ def __init__(self, *args, **kwargs): @@ -484,7 +484,7 @@ class UUIDFieldMixin: By default uses UUID version 4 (randomly generated UUID). The field support all uuid versions which are natively supported by the uuid python module, except version 2. - For more information see: http://docs.python.org/lib/module-uuid.html + For more information see: https://docs.python.org/lib/module-uuid.html """ DEFAULT_MAX_LENGTH = 36 diff --git a/django_extensions/locale/fr/LC_MESSAGES/django.po b/django_extensions/locale/fr/LC_MESSAGES/django.po index 0e9afbe19..7dce17f79 100644 --- a/django_extensions/locale/fr/LC_MESSAGES/django.po +++ b/django_extensions/locale/fr/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgstr "" "POT-Creation-Date: 2011-02-02 11:42+0100\n" "PO-Revision-Date: 2014-01-11 11:14+0000\n" "Last-Translator: mathiasuk\n" -"Language-Team: French (http://www.transifex.com/projects/p/django-extensions/language/fr/)\n" +"Language-Team: French (https://www.transifex.com/projects/p/django-extensions/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/django_extensions/management/commands/clear_cache.py b/django_extensions/management/commands/clear_cache.py index b022c61dc..dcfcde112 100644 --- a/django_extensions/management/commands/clear_cache.py +++ b/django_extensions/management/commands/clear_cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Author: AxiaCore S.A.S. http://axiacore.com +# Author: AxiaCore S.A.S. https://axiacore.com from django.conf import settings from django.core.cache import DEFAULT_CACHE_ALIAS, caches from django.core.cache.backends.base import InvalidCacheBackendError diff --git a/django_extensions/management/commands/dumpscript.py b/django_extensions/management/commands/dumpscript.py index d31ddcbd7..384549c13 100644 --- a/django_extensions/management/commands/dumpscript.py +++ b/django_extensions/management/commands/dumpscript.py @@ -2,7 +2,7 @@ """ Title: Dumpscript management command Project: Hardytools (queryset-refactor version) - Author: Will Hardy (http://willhardy.com.au) + Author: Will Hardy Date: June 2008 Usage: python manage.py dumpscript appname > scripts/scriptname.py $Revision: 217 $ diff --git a/django_extensions/management/commands/list_model_info.py b/django_extensions/management/commands/list_model_info.py index 15e067f0e..c0a027314 100644 --- a/django_extensions/management/commands/list_model_info.py +++ b/django_extensions/management/commands/list_model_info.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Author: OmenApps. http://www.omenapps.com +# Author: OmenApps. https://omenapps.com import inspect from django.apps import apps as django_apps diff --git a/django_extensions/management/commands/pipchecker.py b/django_extensions/management/commands/pipchecker.py index 0e0cbf482..9f4a84b71 100644 --- a/django_extensions/management/commands/pipchecker.py +++ b/django_extensions/management/commands/pipchecker.py @@ -216,8 +216,8 @@ def check_github(self): curl -u 'rizumu' -d '{"scopes":["repo"], "note":"pipchecker"}' https://api.github.com/authorizations For more info on github api tokens: - https://help.github.com/articles/creating-an-oauth-token-for-command-line-use - http://developer.github.com/v3/oauth/#oauth-authorizations-api + https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token + https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps Requirement Format ------------------ diff --git a/django_extensions/management/commands/reset_db.py b/django_extensions/management/commands/reset_db.py index 3734dd498..e35273c97 100644 --- a/django_extensions/management/commands/reset_db.py +++ b/django_extensions/management/commands/reset_db.py @@ -2,7 +2,7 @@ """ reset_db command -originally from http://www.djangosnippets.org/snippets/828/ by dnordberg +originally from https://www.djangosnippets.org/snippets/828/ by dnordberg """ import importlib.util import os diff --git a/django_extensions/management/commands/runserver_plus.py b/django_extensions/management/commands/runserver_plus.py index 6440b6503..6ed2174ce 100644 --- a/django_extensions/management/commands/runserver_plus.py +++ b/django_extensions/management/commands/runserver_plus.py @@ -353,7 +353,7 @@ def application(env, start_response): def inner_run(self, options): if not HAS_WERKZEUG: - raise CommandError("Werkzeug is required to use runserver_plus. Please visit http://werkzeug.pocoo.org/ or install via pip. (pip install Werkzeug)") + raise CommandError("Werkzeug is required to use runserver_plus. Please visit https://werkzeug.palletsprojects.com/ or install via pip. (pip install Werkzeug)") # Set colored output if settings.DEBUG: @@ -428,7 +428,7 @@ def make_environ(self): if self.show_startup_messages: print("\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)) print("Development server is running at %s" % (bind_url,)) - print("Using the Werkzeug debugger (http://werkzeug.pocoo.org/)") + print("Using the Werkzeug debugger (https://werkzeug.palletsprojects.com/)") print("Quit the server with %s." % quit_command) if open_browser: diff --git a/django_extensions/management/commands/shell_plus.py b/django_extensions/management/commands/shell_plus.py index 8229918fe..b4ad275cf 100644 --- a/django_extensions/management/commands/shell_plus.py +++ b/django_extensions/management/commands/shell_plus.py @@ -484,7 +484,7 @@ def set_application_name(self, options): Use the fallback_application_name to let the user override it with PGAPPNAME env variable - http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS # noqa + https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS # noqa """ supported_backends = ( 'django.db.backends.postgresql', diff --git a/django_extensions/management/commands/show_template_tags.py b/django_extensions/management/commands/show_template_tags.py index 399dd262e..b0ebc75f3 100644 --- a/django_extensions/management/commands/show_template_tags.py +++ b/django_extensions/management/commands/show_template_tags.py @@ -38,7 +38,7 @@ def format_block(block, nlspaces=0): The purpose is to let us list a code block as a multiline, triple-quoted Python string, taking care of indentation concerns. - http://code.activestate.com/recipes/145672/ + https://code.activestate.com/recipes/145672/ """ # separate block into lines lines = smart_str(block).split('\n') diff --git a/django_extensions/management/commands/sqldiff.py b/django_extensions/management/commands/sqldiff.py index 20849dadb..d1dc8e015 100644 --- a/django_extensions/management/commands/sqldiff.py +++ b/django_extensions/management/commands/sqldiff.py @@ -265,7 +265,7 @@ def sql_to_dict(self, query, param): sql_to_dict(query, param) -> list of dicts - code from snippet at http://www.djangosnippets.org/snippets/1383/ + code from snippet at https://www.djangosnippets.org/snippets/1383/ """ cursor = connection.cursor() cursor.execute(query, param) @@ -947,7 +947,7 @@ def load_null(self): # sqlite does not support tablespaces tablespace = "public" # index, column_name, column_type, nullable, default_value - # see: http://www.sqlite.org/pragma.html#pragma_table_info + # see: https://www.sqlite.org/pragma.html#pragma_table_info for table_info in self.sql_to_dict("PRAGMA table_info('%s');" % table_name, []): key = (tablespace, table_name, table_info['name']) self.null[key] = not table_info['notnull'] diff --git a/django_extensions/management/debug_cursor.py b/django_extensions/management/debug_cursor.py index 24f79172d..d161bd8d8 100644 --- a/django_extensions/management/debug_cursor.py +++ b/django_extensions/management/debug_cursor.py @@ -18,7 +18,6 @@ def monkey_patch_cursordebugwrapper(print_sql=None, print_sql_location=False, tr if truncate is None: truncate = getattr(settings, '%s_PRINT_SQL_TRUNCATE' % confprefix, DEFAULT_PRINT_SQL_TRUNCATE_CHARS) - # Code orginally from http://gist.github.com/118990 sqlparse = None if getattr(settings, '%s_SQLPARSE_ENABLED' % confprefix, True): try: diff --git a/django_extensions/management/modelviz.py b/django_extensions/management/modelviz.py index 81804a3a5..b9d1cddea 100644 --- a/django_extensions/management/modelviz.py +++ b/django_extensions/management/modelviz.py @@ -31,9 +31,9 @@ __license__ = "Python" __author__ = "Bas van Oostveen ", __contributors__ = [ - "Antonio Cavedoni " - "Stefano J. Attardi ", - "limodou ", + "Antonio Cavedoni " + "Stefano J. Attardi ", + "limodou", "Carlo C8E Miron", "Andre Campos ", "Justin Findlay ", diff --git a/django_extensions/mongodb/fields/__init__.py b/django_extensions/mongodb/fields/__init__.py index ee28484d9..e87cace8b 100644 --- a/django_extensions/mongodb/fields/__init__.py +++ b/django_extensions/mongodb/fields/__init__.py @@ -56,7 +56,7 @@ class AutoSlugField(SlugField): If set to True, overwrites the slug on every save (default: False) Inspired by SmileyChris' Unique Slugify snippet: - http://www.djangosnippets.org/snippets/690/ + https://www.djangosnippets.org/snippets/690/ """ def __init__(self, *args, **kwargs): @@ -216,7 +216,7 @@ class UUIDField(StringField): By default uses UUID version 1 (generate from host ID, sequence number and current time) The field support all uuid versions which are natively supported by the uuid python module. - For more information see: http://docs.python.org/lib/module-uuid.html + For more information see: https://docs.python.org/lib/module-uuid.html """ def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs): diff --git a/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js b/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js index cd4492c13..aca15d9a6 100644 --- a/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js +++ b/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js @@ -1,8 +1,5 @@ /** * Ajax Queue Plugin - * - * Homepage: http://jquery.com/plugins/project/ajaxqueue - * Documentation: http://docs.jquery.com/AjaxQueue */ /** diff --git a/django_extensions/static/django_extensions/js/jquery.bgiframe.js b/django_extensions/static/django_extensions/js/jquery.bgiframe.js index 5c3735d68..1a452ba00 100644 --- a/django_extensions/static/django_extensions/js/jquery.bgiframe.js +++ b/django_extensions/static/django_extensions/js/jquery.bgiframe.js @@ -1,4 +1,4 @@ -/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) +/*! Copyright (c) 2010 Brandon Aaron (http://brandon.aaron.sh/) * Licensed under the MIT License (LICENSE.txt). * * Version 2.1.2 diff --git a/django_extensions/templatetags/highlighting.py b/django_extensions/templatetags/highlighting.py index a38d39d73..f172fd3df 100644 --- a/django_extensions/templatetags/highlighting.py +++ b/django_extensions/templatetags/highlighting.py @@ -4,14 +4,11 @@ copy+paste actual code into your Django templates without needing to escape or anything crazy. -http://lobstertech.com/2008/aug/30/django_syntax_highlight_template_tag/ - Example: {% load highlighting %}