diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 1e989ec..b1a4af0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -34,7 +34,7 @@ jobs: - name: Run linters run: | - make check-linting + make lint test: runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..32b7157 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,36 @@ +repos: + # --------------------------------------------------------------------------- + # Lint: Ruff + Black + mypy + # --------------------------------------------------------------------------- + - repo: local + hooks: + - id: lint + name: lint + entry: make lint + language: system + pass_filenames: false + stages: [pre-commit] + + # --------------------------------------------------------------------------- + # Run tests: fast feedback on commits + # --------------------------------------------------------------------------- + - repo: local + hooks: + - id: test + name: test + entry: make test + language: system + pass_filenames: false + stages: [pre-commit] + + # --------------------------------------------------------------------------- + # Run documentation tests: fast feedback on commits + # --------------------------------------------------------------------------- + - repo: local + hooks: + - id: test-docs + name: test-docs + entry: make test-docs + language: system + pass_filenames: false + stages: [pre-commit] diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11ab56d..7426c3a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,9 @@ uv --version Create and sync the virtual environment (including dev dependencies): ```bash -uv sync --all-groups +uv sync --all-groups --all-extras + +uv run pre-commit install ``` This will: @@ -56,6 +58,8 @@ This will: - create a local `.venv/` - install dependencies according to `uv.lock` - keep the environment reproducible +- setup pre-commit hooks + ## Running tests @@ -141,13 +145,13 @@ We use: Run all linters: ```bash -make check-linting +make lint ``` Auto-fix formatting where possible: ```bash -make fix-linting +make lint-fix ``` ## Building the package diff --git a/Makefile b/Makefile index 0efddaf..36551b0 100644 --- a/Makefile +++ b/Makefile @@ -1,32 +1,81 @@ -check-linting: - uv run ruff check typeid/ tests/ - uv run black --check --diff typeid/ tests/ --line-length 119 - uv run mypy typeid/ --pretty +# ============================================================================== +# Project configuration +# ============================================================================== +PACKAGE := typeid +TESTS := tests +DIST_DIR := dist +VENV_DIR := .venv -fix-linting: - uv run ruff check --fix typeid/ tests/ - uv run black typeid/ tests/ --line-length 119 +UV := uv run +PYTEST := $(UV) pytest +RUFF := $(UV) ruff +BLACK := $(UV) black +MYPY := $(UV) mypy +# ============================================================================== +# Phony targets +# ============================================================================== -.PHONY: build-sdist -build-sdist: - @rm -rf dist build *.egg-info .venv - @uv build --sdist -o dist - @ls -la dist +.PHONY: help lint lint-fix test test-docs docs docs-build build-sdist clean +# ============================================================================== +# Help +# ============================================================================== -test: - uv run pytest -v +help: + @echo "Available targets:" + @echo "" + @echo " lint Run all linters (ruff, black, mypy)" + @echo " lint-fix Automatically fix linting issues" + @echo " test Run test suite" + @echo " test-docs Run documentation tests" + @echo " docs Serve documentation locally" + @echo " docs-build Build documentation" + @echo " build-sdist Build source distribution" + @echo " clean Remove build artifacts" + @echo "" + +# ============================================================================== +# Linting +# ============================================================================== + +lint: + $(RUFF) check $(PACKAGE)/ $(TESTS)/ + $(BLACK) --check --diff $(PACKAGE)/ $(TESTS)/ + $(MYPY) $(PACKAGE)/ + +lint-fix: + $(RUFF) check --fix $(PACKAGE)/ $(TESTS)/ + $(BLACK) $(PACKAGE)/ $(TESTS)/ + +# ============================================================================== +# Testing +# ============================================================================== +test: + $(PYTEST) -v $(TESTS) test-docs: - uv run pytest README.md docs/ --markdown-docs + $(PYTEST) README.md docs/ --markdown-docs +# ============================================================================== +# Documentation +# ============================================================================== docs: mkdocs serve - -docs-build: +docs-build: mkdocs build + +# ============================================================================== +# Build & cleanup +# ============================================================================== + +build-sdist: clean + @uv build --sdist -o $(DIST_DIR) + @ls -la $(DIST_DIR) + +clean: + @rm -rf $(DIST_DIR) build *.egg-info $(VENV_DIR) diff --git a/pyproject.toml b/pyproject.toml index 445f57b..4e076cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,8 +49,19 @@ dev = [ "pytest-markdown-docs>=0.9.0", "pytest-benchmark>=5.0.1", "maturin>=1.5; platform_system != 'Windows'", + "pre-commit>=4.5.1", ] +[tool.black] +line-length = 119 + +[tool.ruff] +line-length = 119 +target-version = "py311" + +[tool.mypy] +pretty = true + [build-system] requires = ["maturin>=1.5"] build-backend = "maturin" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 2dc824e..0000000 --- a/pytest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[pytest] -testpaths = - tests -pythonpath = src \ No newline at end of file diff --git a/uv.lock b/uv.lock index d2ea01e..5392cd7 100644 --- a/uv.lock +++ b/uv.lock @@ -126,6 +126,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, ] +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + [[package]] name = "charset-normalizer" version = "3.4.4" @@ -285,6 +294,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/af/72cd6ef29f9c5f731251acadaeb821559fe25f10852f44a63374c9ca08c1/cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9", size = 4409447, upload-time = "2025-10-15T23:18:24.209Z" }, ] +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + [[package]] name = "docutils" version = "0.22.3" @@ -306,6 +324,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] +[[package]] +name = "filelock" +version = "3.20.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" }, +] + [[package]] name = "ghp-import" version = "2.1.0" @@ -366,6 +393,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9f/cb/18326d2d89ad3b0dd143da971e77afd1e6ca6674f1b1c3df4b6bec6279fc/id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658", size = 13611, upload-time = "2024-12-04T19:53:03.02Z" }, ] +[[package]] +name = "identify" +version = "2.6.15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -883,6 +919,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/7e/a96255f63b7aef032cbee8fc4d6e37def72e3aaedc1f72759235e8f13cb1/nh3-0.3.2-cp38-abi3-win_arm64.whl", hash = "sha256:cf5964d54edd405e68583114a7cba929468bcd7db5e676ae38ee954de1cfc104", size = 584162, upload-time = "2025-10-30T11:17:44.96Z" }, ] +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -928,6 +973,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "pre-commit" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, +] + [[package]] name = "py-cpuinfo" version = "9.0.0" @@ -1324,6 +1385,7 @@ dev = [ { name = "mkdocs-section-index" }, { name = "mkdocstrings", extra = ["python"] }, { name = "mypy" }, + { name = "pre-commit" }, { name = "pytest" }, { name = "pytest-benchmark" }, { name = "pytest-markdown-docs" }, @@ -1352,6 +1414,7 @@ dev = [ { name = "mkdocs-section-index", specifier = ">=0.3.10" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.0" }, { name = "mypy", specifier = ">=1.3.0,<2" }, + { name = "pre-commit", specifier = ">=4.5.1" }, { name = "pytest", specifier = ">=7.3.2,<8" }, { name = "pytest-benchmark", specifier = ">=5.0.1" }, { name = "pytest-markdown-docs", specifier = ">=0.9.0" }, @@ -1417,6 +1480,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6b/c7/e3f3ce05c5af2bf86a0938d22165affe635f4dcbfd5687b1dacc042d3e0e/uuid_utils-0.12.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:84e5c0eba209356f7f389946a3a47b2cc2effd711b3fc7c7f155ad9f7d45e8a3", size = 360693, upload-time = "2025-12-01T17:29:54.558Z" }, ] +[[package]] +name = "virtualenv" +version = "20.35.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, +] + [[package]] name = "watchdog" version = "6.0.0"