diff --git a/HISTORY.rst b/HISTORY.rst index 87a378b..0744ec6 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,13 @@ History ======= +2.5.0 +----- + +* Drop Python 3.8 support +* Add Python 3.13 support +* Add Django 5.1 support + 2.4.1 ----- diff --git a/README.rst b/README.rst index bb3493e..78f83d0 100644 --- a/README.rst +++ b/README.rst @@ -90,7 +90,7 @@ The codebase is targeted and tested against: * Django 3.2.x against Python 3.8, 3.9, 3.10 * Django 4.2.x against Python 3.8, 3.9, 3.10, 3.11, 3.12 -* Django 5.0.x against Python 3.10, 3.11, 3.12 +* Django 5.1.x against Python 3.10, 3.11, 3.12 To run the tests against all target environments, install `tox `_ and then execute the command:: diff --git a/docs/custom_usage.rst b/docs/custom_usage.rst index cbb44d8..96be703 100644 --- a/docs/custom_usage.rst +++ b/docs/custom_usage.rst @@ -171,7 +171,7 @@ with the contents of a freshly generated UUID. In the example accounts app you would create a file named `backends.py`.:: - from organizations.backends.defaults import InvitationBackend + from organizations.backends.defaults import InvitationBackend, make_random_password class CustomInvitations(InvitationBackend): @@ -179,8 +179,10 @@ In the example accounts app you would create a file named `backends.py`.:: try: user = self.user_model.objects.get(email=email) except self.user_model.DoesNotExist: - user = self.user_model.objects.create(email=email, - password=self.user_model.objects.make_random_password()) + user = self.user_model.objects.create( + email=email, + password=make_random_password(), + ) user.is_active = False user.save() self.send_invitation(user, sender, **kwargs) diff --git a/src/organizations/__init__.py b/src/organizations/__init__.py index 8824ff5..24304d2 100644 --- a/src/organizations/__init__.py +++ b/src/organizations/__init__.py @@ -2,4 +2,4 @@ __author__ = "Ben Lopatin" __email__ = "ben@benlopatin.com" -__version__ = "2.4.1" +__version__ = "2.5.0" diff --git a/src/organizations/backends/defaults.py b/src/organizations/backends/defaults.py index 18571f8..8ec9838 100644 --- a/src/organizations/backends/defaults.py +++ b/src/organizations/backends/defaults.py @@ -19,6 +19,7 @@ from django.template import loader from django.urls import path from django.urls import reverse +from django.utils.crypto import get_random_string from django.utils.translation import gettext as _ from organizations.backends.forms import UserRegistrationForm @@ -28,6 +29,20 @@ from organizations.utils import model_field_attr +def make_random_password( + length=10, + allowed_chars="abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789", +): + """ + Generate a random password with the given length and given + allowed_chars. The default value of allowed_chars does not have "I" or + "O" or letters and digits that look similar -- just to avoid confusion. + + (Pulled directly from since-deprecated manager method in Django source) + """ + return get_random_string(length, allowed_chars) + + class BaseBackend: """ Base backend class for registering and inviting users to an organization @@ -215,7 +230,7 @@ def register_by_email(self, email, sender=None, request=None, **kwargs): user = self.user_model.objects.create( username=self.get_username(), email=email, - password=self.user_model.objects.make_random_password(), + password=make_random_password(), ) user.is_active = False user.save() @@ -248,7 +263,7 @@ def create_view(self, request): user = self.user_model.objects.create( username=self.get_username(), email=form.cleaned_data["email"], - password=self.user_model.objects.make_random_password(), + password=make_random_password(), ) user.is_active = False user.save() @@ -317,11 +332,11 @@ def invite_by_email(self, email, sender=None, request=None, **kwargs): user = self.user_model.objects.create( username=self.get_username(), email=email, - password=self.user_model.objects.make_random_password(), + password=make_random_password(), ) else: user = self.user_model.objects.create( - email=email, password=self.user_model.objects.make_random_password() + email=email, password=make_random_password() ) user.is_active = False user.save() diff --git a/tox.ini b/tox.ini index 51dd1b3..2c86431 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,18 @@ [tox] envlist = flake8, - py{38,39,310}-django{32}, - py{38,39}-django{42}, - py{310,311,312}-django{42,50} + py{39,310}-django{32}, + py{39}-django{42}, + py{310,311,312}-django{42} + py{310,311,312,313}-django{51} [gh-actions] python = - 3.8: py38 3.9: py39 3.10: py310 3.11: py311 3.12: py312 + 3.13: py313 [build-system] build-backend = "hatchling.build" @@ -23,22 +24,22 @@ setenv = PYTHONPATH = {toxinidir}:{toxinidir}/organizations commands = pytest {posargs} --cov=organizations basepython = - py38: python3.8 py39: python3.9 py310: python3.10 py311: python3.11 py312: python3.12 + py313: python3.13 deps = hatch>=1.7.0 django32: Django>=3.2,<4 django42: Django>=4.2,<4.3 - django50: Django>=5.0,<5.1 + django51: Django>=5.1,<5.2 extras = tests [testenv:flake8] basepython=python3 deps= - flake8==3.8.3 + flake8==3.12.7 commands= flake8 src/organizations tests