diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index c35604b..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,4 +0,0 @@
-[run]
-relative_files = True
-include =
- spinach/*
\ No newline at end of file
diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml
index 93e30e4..3e051df 100644
--- a/.github/workflows/python-publish.yml
+++ b/.github/workflows/python-publish.yml
@@ -18,11 +18,11 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install setuptools wheel twine
+ pip install hatch twine
- name: Build and publish
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
- python setup.py sdist bdist_wheel
+ hatch build
twine upload dist/*
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 6977cdf..0e17b27 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -28,15 +28,15 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install -e .[tests]
+ pip install hatch
- name: Lint
run: |
- pycodestyle --ignore=E252,W503,W504 spinach tests
+ hatch run pep8
- name: Test with pytest
run: |
- pytest -v --cov=spinach tests/
+ hatch run ci
- name: Coveralls
uses: AndreMiras/coveralls-python-action@develop
diff --git a/.gitignore b/.gitignore
index 5194427..756c307 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,7 @@
+# _version is generated by setuptools_scm and requests not to be under a VCS
+spinach/_version.py
+
+.hatch
.pyc
__pycache__
diff --git a/doc/hacking/contributing.rst b/doc/hacking/contributing.rst
index 5de8940..21ddc4d 100644
--- a/doc/hacking/contributing.rst
+++ b/doc/hacking/contributing.rst
@@ -30,26 +30,22 @@ Python sources
The code base follows `pep8 `_ guidelines with lines
wrapping at the 79th character. You can verify that the code follows the conventions with::
- $ pycodestyle --ignore=E252,W503,W504 spinach tests
+ $ hatch run pep8
Running tests is an invaluable help when adding a new feature or when refactoring. Try to add the
-proper test cases in ``tests/`` together with your patch. The test suite can be run with pytest::
+proper test cases in ``tests/`` together with your patch. Because the Redis broker tests require
+a running Redis server, there is a convenience `pyproject.toml` that runs all the tests and pep8
+checks for you after starting Redis in a container via docker-compose. Simply running::
- $ pytest tests
-
-Because the Redis broker tests require a running Redis server, there is also a convenience
-`tox.ini` that runs all the tests and pep8 checks for you after starting Redis in a container via
-docker-compose. Simply running::
-
- $ tox
+ $ hatch run py3
will build a virtualenv, install Spinach and its dependencies into it, start the Redis server in
-the container, and run tests and pycodestyle, tearing down the Redis server container when done.
+the container, and run tests and pep8, tearing down the Redis server container when done.
Compatibility
-------------
-Spinach runs on all versions of Python starting from 3.6. Tests are run via GitHub actions to
+Spinach runs on all versions of Python starting from 3.8. Tests are run via GitHub actions to
ensure that.
Documentation sources
@@ -61,7 +57,6 @@ with `Sphinx `_.
If you modify the docs, make sure it builds without errors::
- $ cd doc/
- $ make html
+ $ hatch run docs:build html
The generated HTML pages should land in ``doc/_build/html``.
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..0e22de0
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,131 @@
+[build-system]
+requires = ["hatchling", "hatch-vcs"]
+build-backend = "hatchling.build"
+
+[project]
+name = "spinach"
+dynamic = ["version"]
+description = "Modern Redis task queue for Python 3"
+readme = "README.rst"
+license-files = { paths = ["LICENSE"] }
+requires-python = ">=3.8.0,<4.0.0"
+authors = [
+ { name = "Nicolas Le Manchet", email = "nicolas@lemanchet.fr" },
+]
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Libraries",
+ "Topic :: System :: Distributed Computing",
+ "License :: OSI Approved :: BSD License",
+ "Natural Language :: English",
+ "Programming Language :: Python :: 3",
+ "Programming Language :: Python :: 3 :: Only",
+ "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",
+]
+keywords = ["task", "queue", "jobs", "redis"]
+urls.Source = "https://github.com/NicolasLM/spinach"
+
+dependencies = [
+ "redis",
+ "blinker"
+]
+
+[project.optional-dependencies]
+tests = [
+ "pytest",
+ "pytest-cov",
+ "pytest-threadleak",
+ "ruff",
+ "flask",
+ "django"
+]
+
+[tool.hatch.version]
+source = "vcs"
+
+[tool.hatch.build.hooks.vcs]
+version-file = "spinach/_version.py"
+
+[tool.hatch.build.targets.sdist]
+ignore-vcs = true
+include = ["/spinach", "/tests"]
+
+[tool.hatch.build.targets.wheel]
+packages = ["spinach"]
+
+[tool.hatch.build.targets.wheel.package-data]
+"spinach.brokers.redis_scripts" = [
+ "deregister.lua",
+ "enqueue_job.lua",
+ "enqueue_jobs_from_dead_broker.lua",
+ "flush.lua",
+ "get_jobs_from_queue.lua",
+ "move_future_jobs.lua",
+ "register_periodic_tasks.lua",
+ "remove_job_from_running.lua",
+ "set_concurrency_keys.lua",
+]
+
+[tool.hatch.envs.default]
+path = ".hatch"
+features = ["tests"]
+
+scripts.pep8 = ["ruff check spinach tests"]
+
+scripts.py3 = [
+ "docker-compose -f tests/docker-compose.yml up -d",
+ "pytest tests {args}",
+ "docker-compose -f tests/docker-compose.yml down",
+]
+
+# A minimalist pytest runner for Github to use
+scripts.ci = ["pytest -v --cov=spinach tests {args}"]
+
+[tool.hatch.envs.docs]
+detatched = true
+dependencies = [
+ "sphinx",
+ "blinker"
+]
+path = ".hatch"
+scripts.build = ["cd doc && make {args}"]
+
+[tool.ruff.lint]
+select = ["B", "C9", "D", "E", "F", "S", "W"]
+ignore = [
+ "B018", # Useless expression (seems to have false-positive bugs)
+ "S101", # Use of `assert` detected (conflicts with pytest)
+ "D10", # Missing docstring in public function / module / etc.
+ "D203", # 1 blank line required before class docstring
+ "D205", # 1 blank line required between summary line and description
+ "D213", # Multi-line docstring summary should start at the second line
+ "D40", # First line of docstring complaints
+ "D415", # First line should end with a period, question mark, or exclamation point
+ # TODO(nic): The remaining ignores point to actual issues in the code.
+ # Remove them and fix the issues later.
+ "B024",
+ "B027",
+ "B904",
+ "D204",
+ "F401",
+ "F841",
+ "S301",
+ "S311",
+]
+# Aggregating symbols into a convenient location for import is good practice
+# for libraries, we don't care if they look unused
+per-file-ignores = { "__init__.py" = ["F401"] }
+mccabe.max-complexity = 13
+
+[tool.coverage.run]
+relative_files = true
+include = ["spinach/*"]
+omit = ["spinach/_version.py"]
+
+[tool.pytest.ini_options]
+threadleak = true
diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index 4a1804a..0000000
--- a/pytest.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[pytest]
-threadleak = True
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 0f47e37..0000000
--- a/setup.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from setuptools import setup, find_packages
-from codecs import open
-from os import path
-
-here = path.abspath(path.dirname(__file__))
-
-with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
- long_description = f.read()
-
-with open(path.join(here, 'LICENSE'), encoding='utf-8') as f:
- long_description += f.read()
-
-with open(path.join(here, 'spinach', 'const.py'), encoding='utf-8') as fp:
- version = dict()
- exec(fp.read(), version)
- version = version['VERSION']
-
-setup(
- name='spinach',
- version=version,
- description='Modern Redis task queue for Python 3',
- long_description=long_description,
- url='https://github.com/NicolasLM/spinach',
- author='Nicolas Le Manchet',
- author_email='nicolas@lemanchet.fr',
- license='BSD 2-clause',
- # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
- classifiers=[
- 'Development Status :: 4 - Beta',
- 'Intended Audience :: Developers',
- 'Topic :: Software Development :: Libraries',
- 'Topic :: System :: Distributed Computing',
- 'License :: OSI Approved :: BSD License',
- 'Natural Language :: English',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3 :: Only',
- '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',
- ],
- keywords='task queue jobs redis',
-
- packages=find_packages(include=('spinach', 'spinach.*')),
- install_requires=[
- 'redis',
- 'blinker'
- ],
-
- extras_require={
- 'tests': [
- 'pytest',
- 'pytest-cov',
- 'pytest-threadleak',
- 'pycodestyle',
- 'flask',
- 'django'
- ],
- },
-
- package_data={
- 'spinach.brokers.redis_scripts': [
- 'deregister.lua',
- 'enqueue_job.lua',
- 'enqueue_jobs_from_dead_broker.lua',
- 'flush.lua',
- 'get_jobs_from_queue.lua',
- 'move_future_jobs.lua',
- 'register_periodic_tasks.lua',
- 'remove_job_from_running.lua',
- 'set_concurrency_keys.lua',
- ],
- },
-)
diff --git a/spinach/__init__.py b/spinach/__init__.py
index dc3fc3d..7c0ce7c 100644
--- a/spinach/__init__.py
+++ b/spinach/__init__.py
@@ -1,8 +1,6 @@
+from ._version import __version__
from .brokers.memory import MemoryBroker
from .brokers.redis import RedisBroker
-from .const import VERSION
from .engine import Engine
from .task import Tasks, Batch, RetryException, AbortException
from .worker import ThreadWorkers, AsyncioWorkers
-
-__version__ = VERSION
diff --git a/spinach/const.py b/spinach/const.py
index ab0d619..c758507 100644
--- a/spinach/const.py
+++ b/spinach/const.py
@@ -1,5 +1,3 @@
-VERSION = '0.0.25'
-
DEFAULT_QUEUE = 'spinach'
DEFAULT_NAMESPACE = 'spinach'
DEFAULT_MAX_RETRIES = 0
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 2a62ff8..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,28 +0,0 @@
-[tox]
-envlist = py3, pep8
-
-[testenv]
-basepython = python3
-envdir =
- py3: {toxworkdir}/py3
- pep8: {toxworkdir}/py3
-usedevelop = True
-allowlist_externals =
- docker-compose
-deps =
- pytest
- pytest-cov
- pytest-threadleak
- pycodestyle
- flake8
- flask
- django
-
-[testenv:pep8]
-commands =
- pycodestyle --ignore=E252,W503,W504 spinach tests
-
-[testenv:py3]
-commands_pre = docker-compose -f {toxinidir}/tests/docker-compose.yml up -d
-commands = pytest tests {posargs}
-commands_post = docker-compose -f {toxinidir}/tests/docker-compose.yml down