From c13e9c7f2da1a4325a40d22cc1e0897e4b6cc877 Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Sun, 23 Feb 2025 21:42:17 +0000 Subject: [PATCH 1/7] Update copyright year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 2c5097a..f82098c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2019-2024, Developer Society Limited +Copyright (c) 2019-2025, Developer Society Limited All rights reserved. Redistribution and use in source and binary forms, with or without From 56e2c88a4479e6720bc7f493f04422e108761104 Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Sun, 13 Apr 2025 20:46:31 +0100 Subject: [PATCH 2/7] Drop Django 2.2 support --- setup.py | 3 +-- tox.ini | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/setup.py b/setup.py index b5020b4..b6d0d3d 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def read(filename): packages=find_packages(exclude=["tests"]), include_package_data=True, python_requires=">=3.9", - install_requires=["Django>=2.2"], + install_requires=["Django>=3.2"], classifiers=[ "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", @@ -35,7 +35,6 @@ def read(filename): "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Framework :: Django", - "Framework :: Django :: 2.2", "Framework :: Django :: 3.2", "Framework :: Django :: 4.2", ], diff --git a/tox.ini b/tox.ini index 1893cd8..b00291d 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,6 @@ envlist = check lint - py39-django2.2 py{39,310}-django3.2 py{39,310,311,312}-django4.2 coverage @@ -11,10 +10,8 @@ no_package = true [testenv] deps = -rrequirements/testing.txt - django2.2: Django>=2.2,<3.0 django3.2: Django>=3.2,<4.0 django4.2: Django>=4.2,<5.0 - django2.2: psycopg2>=2.8,<2.9 django3.2: psycopg2>=2.9,<2.10 django4.2: psycopg<3.3 allowlist_externals = make From f2860a2ce7f671ac60f7b226cb043ea7ce989d0b Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Wed, 2 Oct 2024 21:25:14 +0100 Subject: [PATCH 3/7] Support Django 5.2 --- requirements/local.txt | 2 +- setup.py | 1 + tox.ini | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/requirements/local.txt b/requirements/local.txt index bc1c6a0..961812c 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -1,7 +1,7 @@ -r testing.txt bump2version==1.0.1 -Django>=4.2,<5.0 +Django>=5.2,<6.0 psycopg==3.2.3 tox==4.21.2 tox-uv==1.13.0 diff --git a/setup.py b/setup.py index b6d0d3d..05022e8 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ def read(filename): "Framework :: Django", "Framework :: Django :: 3.2", "Framework :: Django :: 4.2", + "Framework :: Django :: 5.2", ], license="BSD", ) diff --git a/tox.ini b/tox.ini index b00291d..76ecfc4 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,7 @@ envlist = lint py{39,310}-django3.2 py{39,310,311,312}-django4.2 + py{310,311,312,313}-django5.2 coverage no_package = true @@ -12,8 +13,10 @@ deps = -rrequirements/testing.txt django3.2: Django>=3.2,<4.0 django4.2: Django>=4.2,<5.0 + django5.2: Django>=5.2,<6.0 django3.2: psycopg2>=2.9,<2.10 django4.2: psycopg<3.3 + django5.2: psycopg<3.3 allowlist_externals = make commands = make test package = editable From d20c01ea6de98388fc82e7c2e9c81b4144a4aff8 Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Sat, 7 Jun 2025 18:42:35 +0100 Subject: [PATCH 4/7] Python package upgrades --- requirements/local.txt | 8 ++++---- requirements/testing.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements/local.txt b/requirements/local.txt index 961812c..f402921 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -2,7 +2,7 @@ bump2version==1.0.1 Django>=5.2,<6.0 -psycopg==3.2.3 -tox==4.21.2 -tox-uv==1.13.0 -twine==5.1.1 +psycopg==3.2.9 +tox==4.26.0 +tox-uv==1.26.0 +twine==6.1.0 diff --git a/requirements/testing.txt b/requirements/testing.txt index 49ddee3..dba0fca 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -1,3 +1,3 @@ -coverage==7.6.1 -pipdeptree==2.23.4 +coverage==7.9.1 +pipdeptree==2.26.1 ruff==0.6.9 From d520757fe1a4f4f86113d342d49ddaa3a7480d04 Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Sat, 7 Jun 2025 18:44:15 +0100 Subject: [PATCH 5/7] Upgrade ruff --- .github/matrix.py | 32 +++---- postgres_lock/lock.py | 2 +- .../management/commands/command_lock.py | 2 +- pyproject.toml | 89 +++++++++++++++---- requirements/testing.txt | 2 +- 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/.github/matrix.py b/.github/matrix.py index 17a63d3..34883f1 100644 --- a/.github/matrix.py +++ b/.github/matrix.py @@ -1,3 +1,4 @@ +# noqa:INP001 import fileinput import json import re @@ -9,21 +10,22 @@ def main(): actions_matrix = [] - for tox_env in fileinput.input(): - tox_env = tox_env.rstrip() - - if python_match := PY_VERSIONS_RE.match(tox_env): - version_tuple = python_match.groups() - else: - version_tuple = sys.version_info[0:2] - - python_version = "{}.{}".format(*version_tuple) - actions_matrix.append( - { - "python": python_version, - "tox_env": tox_env, - } - ) + with fileinput.input() as f: + for tox_env_line in f: + tox_env = tox_env_line.rstrip() + + if python_match := PY_VERSIONS_RE.match(tox_env): + version_tuple = python_match.groups() + else: + version_tuple = sys.version_info[0:2] + + python_version = "{}.{}".format(*version_tuple) + actions_matrix.append( + { + "python": python_version, + "tox_env": tox_env, + } + ) print(json.dumps(actions_matrix)) # noqa:T201 diff --git a/postgres_lock/lock.py b/postgres_lock/lock.py index 812f01e..c617c07 100644 --- a/postgres_lock/lock.py +++ b/postgres_lock/lock.py @@ -61,5 +61,5 @@ def release(self): def __enter__(self): return self.lock() - def __exit__(self, type, value, traceback): + def __exit__(self, exc_type, exc_value, traceback): self.release() diff --git a/postgres_lock/management/commands/command_lock.py b/postgres_lock/management/commands/command_lock.py index eea796c..893fe99 100644 --- a/postgres_lock/management/commands/command_lock.py +++ b/postgres_lock/management/commands/command_lock.py @@ -45,7 +45,7 @@ def execute(self, *args, **options): using=options["database"], ) as acquired: if acquired: - completed = subprocess.run(options["command"]) # noqa:S603 + completed = subprocess.run(options["command"], check=False) # noqa:S603 # Raise the return code of the child process if an error occurs if completed.returncode != 0: sys.exit(completed.returncode) diff --git a/pyproject.toml b/pyproject.toml index 9063b8f..8cfffe6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,29 +3,82 @@ line-length = 99 target-version = 'py39' [tool.ruff.lint] -select = [ - 'F', # pyflakes - 'E', # pycodestyle - 'W', # pycodestyle - 'I', # isort - 'N', # pep8-naming - 'UP', # pyupgrade - 'S', # flake8-bandit - 'BLE', # flake8-blind-except - 'C4', # flake8-comprehensions - 'EM', # flake8-errmsg - 'T20', # flake8-print - 'RET', # flake8-return - 'RUF', # ruff +extend-select = [ + 'ERA', # eradicate + 'YTT', # flake8-2020 + 'ASYNC', # flake8-async + 'S', # flake8-bandit + 'BLE', # flake8-blind-except + 'B', # flake8-bugbear + 'A', # flake8-builtins + 'COM', # flake8-commas + 'C4', # flake8-comprehensions + 'DTZ', # flake8-datetimez + 'T10', # flake8-debugger + 'DJ', # flake8-django + 'EM', # flake8-errmsg + 'EXE', # flake8-executable + 'FA', # flake8-future-annotations + 'INT', # flake8-gettext + 'ISC', # flake8-implicit-str-concat + 'ICN', # flake8-import-conventions + 'LOG', # flake8-logging + 'G', # flake8-logging-format + 'INP', # flake8-no-pep420 + 'PIE', # flake8-pie + 'T20', # flake8-print + 'PYI', # flake8-pyi + 'Q', # flake8-quotes + 'RSE', # flake8-raise + 'RET', # flake8-return + 'SLOT', # flake8-slots + 'SIM', # flake8-simplify + 'TID', # flake8-tidy-imports + 'TD', # flake8-todos + 'TCH', # flake8-type-checking + 'PTH', # flake8-use-pathlib + 'FLY', # flynt + 'I', # isort + 'NPY', # numpy-specific rules + 'PD', # pandas-vet + 'N', # pep8-naming + 'PERF', # perflint + 'E', # pycodestyle + 'W', # pycodestyle + 'F', # pyflakes + 'PGH', # pygrep-hooks + 'PLC', # pylint + 'PLE', # pylint + 'PLW', # pylint + 'UP', # pyupgrade + 'FURB', # refurb + 'RUF', # ruff-specific rules + 'TRY', # tryceratops ] ignore = [ - 'EM101', # flake8-errmsg: raw-string-in-exception + 'COM812', # flake8-commas: missing-trailing-comma + 'EM101', # flake8-errmsg: raw-string-in-exception + 'ISC001', # flake8-implicit-str-concat: single-line-implicit-string-concatenation + 'RUF012', # ruff-specific rules: mutable-class-default + 'SIM105', # flake8-simplify: suppressible-exception + 'SIM108', # flake8-simplify: if-else-block-instead-of-if-exp + 'TD002', # flake8-todos: missing-todo-author + 'TRY003', # tryceratops: raise-vanilla-args ] [tool.ruff.lint.isort] combine-as-imports = true +section-order = [ + 'future', + 'standard-library', + 'django', + 'third-party', + 'first-party', + 'local-folder', +] + +[tool.ruff.lint.isort.sections] +'django' = ['django'] [tool.ruff.lint.pep8-naming] -extend-ignore-names = [ - 'assert*', -] +extend-ignore-names = ['assert*'] diff --git a/requirements/testing.txt b/requirements/testing.txt index dba0fca..00ff0d1 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -1,3 +1,3 @@ coverage==7.9.1 pipdeptree==2.26.1 -ruff==0.6.9 +ruff==0.11.13 From 3dd83aa41d201f1a5dd2e0f30d5209ff89a64885 Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Sat, 7 Jun 2025 18:57:36 +0100 Subject: [PATCH 6/7] Use Markdown for README --- MANIFEST.in | 2 +- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++ README.rst | 55 ----------------------------------------------------- setup.py | 4 ++-- 4 files changed, 51 insertions(+), 58 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/MANIFEST.in b/MANIFEST.in index 2021bcd..cf3d63c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst +include README.md include LICENSE graft postgres_lock global-exclude *.py[co] diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c9e3ca --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Django Postgres Lock + +A [Django](https://www.djangoproject.com/) management command which will run a command inside a +Postgres lock, ensuring that only a single instance of the inner command will run. + +## Installation + +Using [pip](https://pip.pypa.io/): + +```console +$ pip install django-postgres-lock +``` + +Edit your Django project's settings module, and add the application to ``INSTALLED_APPS``: + +```python +INSTALLED_APPS = [ + # ... + "postgres_lock", + # ... +] +``` + +## Usage + +To run clearsessions with the default lock: + +```console +$ ./manage.py command_lock -- ./manage.py clearsessions +``` + +To use a unique lock for this task: + +```console +$ ./manage.py command_lock --name clearsessions -- ./manage.py clearsessions +``` + +To exit immediately if a lock can't be acquired: + +```console +$ ./manage.py command_lock --try --name clearsessions -- ./manage.py clearsessions +``` + +To ignore a lock failure and return a successful exit code: + +```console +$ ./manage.py command_lock --try --ignore-fail --name clearsessions -- ./manage.py clearsessions +``` diff --git a/README.rst b/README.rst deleted file mode 100644 index de5709a..0000000 --- a/README.rst +++ /dev/null @@ -1,55 +0,0 @@ -Django Postgres Lock -==================== - -A Django_ management command which will run a command inside a Postgres lock, ensuring that only a -single instance of the inner command will run. - -.. _Django: https://www.djangoproject.com/ - -Installation ------------- - -Using pip_: - -.. _pip: https://pip.pypa.io/ - -.. code-block:: console - - $ pip install django-postgres-lock - -Edit your Django project's settings module, and add the application to ``INSTALLED_APPS``: - -.. code-block:: python - - INSTALLED_APPS = [ - # ... - "postgres_lock", - # ... - ] - -Usage ------ - -To run clearsessions with the default lock: - -.. code-block:: console - - $ ./manage.py command_lock -- ./manage.py clearsessions - -To use a unique lock for this task: - -.. code-block:: console - - $ ./manage.py command_lock --name clearsessions -- ./manage.py clearsessions - -To exit immediately if a lock can't be acquired: - -.. code-block:: console - - $ ./manage.py command_lock --try --name clearsessions -- ./manage.py clearsessions - -To ignore a lock failure and return a successful exit code: - -.. code-block:: console - - $ ./manage.py command_lock --try --ignore-fail --name clearsessions -- ./manage.py clearsessions diff --git a/setup.py b/setup.py index 05022e8..f3a147a 100644 --- a/setup.py +++ b/setup.py @@ -13,8 +13,8 @@ def read(filename): name="django-postgres-lock", version="0.1.1", description="Django Postgres Lock", - long_description=read("README.rst"), - long_description_content_type="text/x-rst", + long_description=read("README.md"), + long_description_content_type="text/markdown", url="https://github.com/developersociety/django-postgres-lock", maintainer="The Developer Society", maintainer_email="studio@dev.ngo", From 22281291ecfa65928aa5384889268cbcb65a878d Mon Sep 17 00:00:00 2001 From: Alex Tomkins Date: Sat, 7 Jun 2025 19:07:16 +0100 Subject: [PATCH 7/7] Remove setup.py, use pyproject.toml --- .bumpversion.cfg | 13 ------------ .bumpversion.toml | 25 +++++++++++++++++++++++ MANIFEST.in | 7 +------ Makefile | 28 ++++++-------------------- pyproject.toml | 41 ++++++++++++++++++++++++++++++++++++++ requirements/local.txt | 3 +-- requirements/testing.txt | 3 +++ setup.cfg | 2 -- setup.py | 43 ---------------------------------------- 9 files changed, 77 insertions(+), 88 deletions(-) delete mode 100644 .bumpversion.cfg create mode 100644 .bumpversion.toml delete mode 100644 setup.cfg delete mode 100644 setup.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index f0838e4..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,13 +0,0 @@ -[bumpversion] -current_version = 0.1.1 -commit = True -tag = True -tag_name = {new_version} -serialize = - {major}.{minor}.{patch} - {major}.{minor} -parse = (?P\d+)\.(?P\d+)(\.(?P\d+))? - -[bumpversion:file:setup.py] - -[bumpversion:file:postgres_lock/__init__.py] diff --git a/.bumpversion.toml b/.bumpversion.toml new file mode 100644 index 0000000..ea84bb0 --- /dev/null +++ b/.bumpversion.toml @@ -0,0 +1,25 @@ +[tool.bumpversion] +current_version = "0.1.1" +parse = "(?P\\d+)\\.(?P\\d+)(\\.(?P\\d+))?" +serialize = [ + "{major}.{minor}.{patch}", + "{major}.{minor}", +] +search = "{current_version}" +replace = "{new_version}" +regex = false +ignore_missing_version = false +ignore_missing_files = false +tag = true +sign_tags = false +tag_name = "{new_version}" +tag_message = "Bump version: {current_version} → {new_version}" +allow_dirty = false +commit = true +message = "Bump version: {current_version} → {new_version}" +commit_args = "" + +[[tool.bumpversion.files]] +filename = "pyproject.toml" +search = "version = '{current_version}'" +replace = "version = '{new_version}'" diff --git a/MANIFEST.in b/MANIFEST.in index cf3d63c..1eeef06 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1 @@ -include README.md -include LICENSE -graft postgres_lock -global-exclude *.py[co] -global-exclude __pycache__ -global-exclude .DS_Store +prune tests diff --git a/Makefile b/Makefile index df0e566..e905ddf 100644 --- a/Makefile +++ b/Makefile @@ -46,11 +46,8 @@ test-report: coverage-clean test coverage-report test-lowest: ## Run tox with lowest (oldest) package dependencies. test-lowest: tox-test-lowest -dist: ## Builds source and wheel package -dist: clean build-dist - -release: ## Package and release this project to PyPI. -release: dist build-release +package: ## Builds source and wheel packages +package: clean build-package # --------------- @@ -67,23 +64,10 @@ build-clean: rm -rf .eggs find . -maxdepth 1 -name '*.egg-info' -exec rm -rf {} + -build-release: - @echo - @echo "This will package and release this project to PyPI." - @echo - @echo "A checklist before you continue:" - @echo - @echo " - have you ran 'bumpversion'?" - @echo " - have you pushed the commit and tag created by 'bumpversion'?" - @echo " - are you sure the project is in a state to be released?" - @echo - @read -p "Press to continue. Or -c to quit and address the above points." - twine upload dist/* - -build-dist: - python setup.py sdist - python setup.py bdist_wheel - ls -l dist +build-package: + python -m build + twine check --strict dist/* + check-wheel-contents dist/*.whl # Virtual Environments diff --git a/pyproject.toml b/pyproject.toml index 8cfffe6..2f7988c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,44 @@ +[project] +name = 'django-postgres-lock' +version = '0.1.1' +description = 'Django Postgres Lock' +readme = 'README.md' +maintainers = [{ name = 'The Developer Society', email = 'studio@dev.ngo' }] +requires-python = '>= 3.9' +dependencies = [ + 'Django>=3.2', +] +license = 'BSD-3-Clause' +classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Django', + 'Framework :: Django :: 3.2', + 'Framework :: Django :: 4.2', + 'Framework :: Django :: 5.2', + 'Intended Audience :: Developers', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + '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', +] + +[project.urls] +Homepage = 'https://github.com/developersociety/django-postgres-lock' + +[build-system] +requires = ['setuptools >= 77.0.3'] +build-backend = 'setuptools.build_meta' + +[tool.setuptools] +include-package-data = false + +[tool.setuptools.packages.find] +include = ['postgres_lock*'] + [tool.ruff] line-length = 99 target-version = 'py39' diff --git a/requirements/local.txt b/requirements/local.txt index f402921..eaaae2c 100644 --- a/requirements/local.txt +++ b/requirements/local.txt @@ -1,8 +1,7 @@ -r testing.txt -bump2version==1.0.1 +bump-my-version==1.2.0 Django>=5.2,<6.0 psycopg==3.2.9 tox==4.26.0 tox-uv==1.26.0 -twine==6.1.0 diff --git a/requirements/testing.txt b/requirements/testing.txt index 00ff0d1..de608ef 100644 --- a/requirements/testing.txt +++ b/requirements/testing.txt @@ -1,3 +1,6 @@ +build==1.2.2.post1 +check-wheel-contents==0.6.1 coverage==7.9.1 pipdeptree==2.26.1 ruff==0.11.13 +twine==6.1.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index dd38343..0000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -python-tag = py3 diff --git a/setup.py b/setup.py deleted file mode 100644 index f3a147a..0000000 --- a/setup.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python -import os - -from setuptools import find_packages, setup - - -def read(filename): - with open(os.path.join(os.path.dirname(__file__), filename)) as f: - return f.read() - - -setup( - name="django-postgres-lock", - version="0.1.1", - description="Django Postgres Lock", - long_description=read("README.md"), - long_description_content_type="text/markdown", - url="https://github.com/developersociety/django-postgres-lock", - maintainer="The Developer Society", - maintainer_email="studio@dev.ngo", - platforms=["any"], - packages=find_packages(exclude=["tests"]), - include_package_data=True, - python_requires=">=3.9", - install_requires=["Django>=3.2"], - classifiers=[ - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "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", - "Framework :: Django", - "Framework :: Django :: 3.2", - "Framework :: Django :: 4.2", - "Framework :: Django :: 5.2", - ], - license="BSD", -)