diff --git a/.github/workflows/define-labels.yml b/.github/workflows/define-labels.yml index fe1a2ae..6a33061 100644 --- a/.github/workflows/define-labels.yml +++ b/.github/workflows/define-labels.yml @@ -10,7 +10,7 @@ jobs: define: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: micnncim/action-label-syncer@v1.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml index 3ff67ad..5bb8ffa 100644 --- a/.github/workflows/publish-package.yml +++ b/.github/workflows/publish-package.yml @@ -11,12 +11,12 @@ jobs: runs-on: ubuntu-22.04 environment: publish steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build sdist run: pipx run build --sdist --wheel - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: path: dist/* @@ -31,7 +31,7 @@ jobs: id-token: write steps: - name: Download artifact from build step - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: artifact path: dist diff --git a/.github/workflows/run-project-tests.yml b/.github/workflows/run-project-tests.yml index cb01814..5e00517 100644 --- a/.github/workflows/run-project-tests.yml +++ b/.github/workflows/run-project-tests.yml @@ -22,26 +22,35 @@ jobs: strategy: matrix: os: [ubuntu-22.04, windows-2022] - django-version: ["4.2"] + python-version: ["3.12", "3.14"] + django-version: ["4.2", "5.2"] project: ["extension", "replacement"] + exclude: + # Django 4.2 only supports Python up to 3.12 + - python-version: "3.14" + django-version: "4.2" + # Test Django 5.2 with latest Python only + - python-version: "3.12" + django-version: "5.2" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - name: Set up Python - uses: actions/setup-python@v5 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: ${{ matrix.python-version }} - name: Upgrade pip run: python3 -m pip install -U distlib pip setuptools wheel - name: Get pip cache dir id: pip-cache - run: echo "::set-output name=dir::$(pip cache dir)" + shell: bash + run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ steps.pip-cache.outputs.dir }} key: @@ -61,8 +70,9 @@ jobs: - name: Python and Django versions run: | + echo "Python ${{ matrix.python-version }} & Django ${{ matrix.django-version }}" python3 --version - echo "Django ${{ matrix.django-version }}: $(django-admin --version)" + echo "Django: $(django-admin --version)" - name: Run tests working-directory: ./example_${{ matrix.project }}_project diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index def66d7..90d93b2 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -18,14 +18,20 @@ jobs: strategy: matrix: os: [ubuntu-22.04, windows-2022] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] - django-version: ["4.2"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + django-version: ["4.2", "5.2"] + exclude: + # Django 4.2 only supports Python up to 3.12 + - python-version: "3.13" + django-version: "4.2" + - python-version: "3.14" + django-version: "4.2" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -34,10 +40,11 @@ jobs: - name: Get pip cache dir id: pip-cache - run: echo "::set-output name=dir::$(pip cache dir)" + shell: bash + run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ steps.pip-cache.outputs.dir }} key: @@ -68,8 +75,7 @@ jobs: coverage xml - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: - token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true verbose: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8c1a03..e7ba10b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks default_language_version: - python: "python3.9" + python: "python3.10" repos: # - repo: meta # hooks: diff --git a/docs/index.rst b/docs/index.rst index 17776fa..86ecb55 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,7 @@ If you're new and want to see what your options are, please read :doc:`select_configuration_method`. The documentation you are viewing covers Django Improved User 2.1, -compatible with Django 2.2, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2. +compatible with Django 4.2 and 5.2 (Python 3.10 to 3.14). .. toctree:: :caption: Contents: diff --git a/pyproject.toml b/pyproject.toml index 9726577..5c68742 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ { name = "Andrew Pinkham" }, ] readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.10" dependencies = ["django>=4.2"] license = { file = "LICENSE"} classifiers = [ @@ -20,14 +20,15 @@ classifiers = [ "Operating System :: OS Independent", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "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", "Programming Language :: Python :: 3 :: Only", "Framework :: Django", "Framework :: Django :: 4.2", + "Framework :: Django :: 5.2", ] [project.urls] @@ -79,5 +80,5 @@ exclude = [ [tool.black] line-length = 79 -target-version = ['py38'] +target-version = ['py310'] include = '\.pyi?$' diff --git a/src/improved_user/admin.py b/src/improved_user/admin.py index 2fba14b..9659783 100644 --- a/src/improved_user/admin.py +++ b/src/improved_user/admin.py @@ -40,3 +40,4 @@ class UserAdmin(BaseUserAdmin): list_display = ("email", "full_name", "short_name", "is_staff") search_fields = ("email", "full_name", "short_name") ordering = ("email",) + readonly_fields = ("last_login", "date_joined") diff --git a/src/improved_user/managers.py b/src/improved_user/managers.py index 73e1ed4..72edddd 100644 --- a/src/improved_user/managers.py +++ b/src/improved_user/managers.py @@ -1,6 +1,7 @@ """User Manager used by Improved User; may be extended""" from django.contrib.auth.models import BaseUserManager +from django.utils.crypto import get_random_string class UserManager(BaseUserManager): @@ -54,3 +55,16 @@ def create_superuser(self, email, password, **extra_fields): if extra_fields.get("is_superuser") is not True: raise ValueError("Superuser must have is_superuser=True.") return self._create_user(email, password, **extra_fields) + + def make_random_password( + self, + length=10, + allowed_chars="abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", + ): + """Generate a random password with the given length and allowed_chars. + + The default value of allowed_chars excludes visually ambiguous + characters: lowercase 'i', 'l', 'o'; uppercase 'I', 'O'; and + digits '0', '1'. + """ + return get_random_string(length, allowed_chars) diff --git a/tests/test_admin.py b/tests/test_admin.py index 2ed7faf..979244c 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -55,7 +55,7 @@ def login(self, username="testclient@example.com", password="password"): def logout(self): """Logout the user; helper function""" - response = self.client.get("/admin/logout/") + response = self.client.post("/admin/logout/") self.assertEqual(response.status_code, 200) self.assertNotIn(SESSION_KEY, self.client.session) diff --git a/tox.ini b/tox.ini index 76d0738..0ea26e6 100644 --- a/tox.ini +++ b/tox.ini @@ -3,8 +3,10 @@ isolated_build = True envlist = py312-django42-pkgcheck, py312-docs, - py{38,39,310,311,312}-django42-unit, - py312-django42-{extension,replacement} + py{310,311,312}-django42-unit, + py{310,311,312,313,314}-django52-unit, + py312-django42-{extension,replacement}, + py314-django52-{extension,replacement} [testenv] changedir = @@ -25,6 +27,7 @@ deps = extension: -r{toxinidir}/example_extension_project/requirements.txt replacement: -r{toxinidir}/example_replacement_project/requirements.txt django42: Django>=4.2,<4.3 + django52: Django>=5.2,<6.0 commands = docs: sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html docs: python -msphinx -b linkcheck . build/linkcheck