diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..e656eab --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @Coding-Cuddles/kata-maintainers diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2b49354 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 + +updates: + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 798d5cf..7343182 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -11,49 +11,40 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - - name: Install poetry - run: pipx install poetry - - - name: Set up Python - uses: actions/setup-python@v4 + - uses: actions/setup-python@v7 with: - python-version: '3.8' - cache: 'poetry' + python-version: "3.11" - - name: Install dependencies - run: poetry install + - uses: astral-sh/setup-uv@v9.0.0 + with: + version: "latest" + prune-cache: true - name: Check formatting - run: | - source $(poetry env info --path)/bin/activate - make format-check + run: make format-check test: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v7 - - name: Install poetry - run: pipx install poetry - - - name: Set up Python - uses: actions/setup-python@v4 + - uses: actions/setup-python@v7 with: - python-version: '3.8' - cache: 'poetry' + python-version: "3.11" - - name: Install dependencies - run: poetry install + - uses: astral-sh/setup-uv@v9.0.0 + with: + version: "latest" + prune-cache: true - name: Run main run: make run - name: Test - run: | - source $(poetry env info --path)/bin/activate - make test + run: make test diff --git a/.replit b/.replit deleted file mode 100644 index ddce839..0000000 --- a/.replit +++ /dev/null @@ -1,19 +0,0 @@ -run = ["make", "test"] -language = "python3" -entrypoint = "main.py" -hidden = ["LICENSE", "venv", ".config", ".gitignore", "**/__pycache__", "**/.pytest_cache", "**/*.pyc"] - -[nix] -channel = "stable-21_11" - -[languages] - -[languages.python3] -pattern = "**/*.py" - -[languages.python3.languageServer] -start = "pylsp" - -[debugger] -support = false - diff --git a/Makefile b/Makefile index c7c588a..e05c904 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,43 @@ -all: test +COLOR_CYAN := \033[36m +COLOR_RESET := \033[0m -SRCS := $(shell git ls-files *.py) +SRCS := $(shell git ls-files '*.py') + +.DEFAULT_GOAL := help + +.PHONY: all +all: test ## Run tests + +.PHONY: help +help: ## Show this help message + @awk 'BEGIN {FS = ":.*##"; printf "Usage: make [options] $(COLOR_CYAN)[target] ...$(COLOR_RESET)\n\n"} \ + /^[a-zA-Z_-]+:.*##/ {printf " $(COLOR_CYAN)%-20s$(COLOR_RESET) %s\n", $$1, $$2}' \ + $(MAKEFILE_LIST) .PHONY: run -run: - ./main.py +run: ## Run the main entry point + uv run python main.py .PHONY: test -test: - pytest test_*.py +test: ## Run tests + uv run pytest test_*.py .PHONY: format -format: - yapf -i $(SRCS) +format: ## Format Python sources in place + uv run ruff format $(SRCS) .PHONY: format-check -format-check: - yapf --diff $(SRCS) \ +format-check: ## Fail if Python sources require formatting + uv run ruff format --check $(SRCS) \ || (echo "Some files require formatting. Run 'make format' to fix." && exit 1) .PHONY: clean -clean: - git clean -Xfd +clean: ## Remove generated caches + find . \( -path ./.git -o -path ./.venv -o -path ./venv \) -prune \ + -o -type d -name __pycache__ -exec rm -rf {} + + rm -rf .pytest_cache .ruff_cache + rm -f .coverage .coverage.* -ifndef VERBOSE +ifneq ($(VERBOSE),1) .SILENT: endif diff --git a/README.md b/README.md index ddbb649..86ecc1d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Roman numerals kata in Python [![CI](https://github.com/Coding-Cuddles/roman-numerals-python-kata/actions/workflows/main.yml/badge.svg)](https://github.com/Coding-Cuddles/roman-numerals-python-kata/actions/workflows/main.yml) -[![Replit](https://img.shields.io/badge/Try%20with%20Replit-black?logo=replit)](https://replit.com/new/github/Coding-Cuddles/roman-numerals-python-kata) +[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ## Overview @@ -61,24 +62,97 @@ their corresponding Arabic digits. * If you do know an algorithm, evaluate if it can be implemented using strict TDD principles. -## Usage +## Prerequisites -You can import this project into [Replit](https://replit.com), and it will -handle all dependencies automatically. +Required: -### Prerequisites +- [Git](https://git-scm.com/downloads) +- [uv](https://docs.astral.sh/uv/getting-started/installation/) -* [Python 3.8+](https://www.python.org/) -* [pytest](https://pytest.org) +Optional: -### Run main +- [GNU Make](https://www.gnu.org/software/make/), for shorter commands. Every required task also + has a direct `uv` command. + +You do not need to install Python or pytest separately. `uv` installs a compatible Python version +and the locked project dependencies when needed. + +## Set up the kata + +1. Clone the repository: + + ```console + git clone https://github.com/Coding-Cuddles/roman-numerals-python-kata.git + ``` + +2. Enter the repository directory: + + ```console + cd roman-numerals-python-kata + ``` + +3. Run the existing test. Use Make when it is installed: + + ```console + make test + ``` + + Otherwise, run pytest through `uv` directly: + + ```console + uv run pytest test_*.py + ``` + + The first run may install Python and the project dependencies. Setup is complete when pytest + reports `1 passed`. + + If the command fails with `uv: command not found`, install + [uv](https://docs.astral.sh/uv/getting-started/installation/) and repeat this step. + +## Work on the kata + +1. Add the next behavior to `test_roman_numerals.py`, then implement it in `roman_numerals.py`. + +2. Run the tests after each change. Use Make when it is installed: + + ```console + make test + ``` + + Otherwise, run pytest through `uv` directly: + + ```console + uv run pytest test_*.py + ``` + + Continue when the test run passes. + +## Run the entry point + +The repository retains `main.py` as its command-line entry point. Use Make when it is installed: ```console make run ``` -### Run tests +Otherwise, run it through `uv` directly: ```console -make test +uv run python main.py ``` + +The command prints `Hello World!`. + +## Make command reference + +Make is optional. Run `make` or `make help` to list these commands in the terminal. + +| Command | Result | +| ------------------- | --------------------------------------- | +| `make all` | Run the test suite | +| `make help` | List public Make targets | +| `make run` | Run `main.py` | +| `make test` | Run the test suite | +| `make format` | Format tracked Python files | +| `make format-check` | Check formatting without changing files | +| `make clean` | Remove generated caches | diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index f1500b8..0000000 --- a/poetry.lock +++ /dev/null @@ -1,276 +0,0 @@ -[[package]] -name = "attrs" -version = "22.1.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" - -[[package]] -name = "exceptiongroup" -version = "1.0.3" -description = "Backport of PEP 654 (exception groups)" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "jedi" -version = "0.18.1" -description = "An autocompletion tool for Python that can be used for text editors." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "packaging" -version = "21.3" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - -[[package]] -name = "parso" -version = "0.8.3" -description = "A Python Parser" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "dev" -optional = false -python-versions = ">=3.6.8" - -[package.extras] -diagrams = ["railroad-diagrams", "jinja2"] - -[[package]] -name = "pytest" -version = "7.2.0" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "python-lsp-jsonrpc" -version = "1.0.0" -description = "JSON RPC 2.0 server library" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -ujson = ">=3.0.0" - -[package.extras] -test = ["pylint", "pycodestyle", "pyflakes", "pytest", "pytest-cov", "coverage"] - -[[package]] -name = "pytoolconfig" -version = "1.2.2" -description = "Python tool configuration" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -packaging = ">=21.3" -tomli = {version = ">=2.0", markers = "python_version < \"3.11\""} - -[package.extras] -doc = ["tabulate (>=0.8.9)", "sphinx (>=4.5.0)"] -gen_docs = ["sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)", "pytoolconfig"] -global = ["appdirs (>=1.4.4)"] -validation = ["pydantic (>=1.7.4)"] - -[[package]] -name = "replit-python-lsp-server" -version = "1.15.9" -description = "Python Language Server for the Language Server Protocol" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -jedi = ">=0.17.2,<0.19.0" -pluggy = ">=1.0.0" -pyflakes = {version = ">=2.5.0,<2.6.0", optional = true, markers = "extra == \"pyflakes\""} -python-lsp-jsonrpc = ">=1.0.0" -rope = {version = ">0.10.5", optional = true, markers = "extra == \"rope\""} -toml = ">=0.10.2" -ujson = ">=3.0.0" -whatthepatch = {version = ">=1.0.2,<2.0.0", optional = true, markers = "extra == \"yapf\""} -yapf = {version = "*", optional = true, markers = "extra == \"yapf\""} - -[package.extras] -all = ["autopep8 (>=1.6.0,<1.7.0)", "flake8 (>=5.0.0,<5.1.0)", "mccabe (>=0.7.0,<0.8.0)", "pycodestyle (>=2.9.0,<2.10.0)", "pydocstyle (>=2.0.0)", "pyflakes (>=2.5.0,<2.6.0)", "pylint (>=2.5.0)", "rope (>=0.10.5)", "yapf", "whatthepatch"] -autopep8 = ["autopep8 (>=1.6.0,<1.7.0)"] -flake8 = ["flake8 (>=5.0.0,<5.1.0)"] -mccabe = ["mccabe (>=0.7.0,<0.8.0)"] -pycodestyle = ["pycodestyle (>=2.9.0,<2.10.0)"] -pydocstyle = ["pydocstyle (>=2.0.0)"] -pyflakes = ["pyflakes (>=2.5.0,<2.6.0)"] -pylint = ["pylint (>=2.5.0)"] -rope = ["rope (>0.10.5)"] -test = ["pylint (>=2.5.0)", "pytest", "pytest-cov", "coverage", "numpy (<1.23)", "pandas", "matplotlib", "pyqt5", "flaky"] -websockets = ["websockets (>=10.3)"] -yapf = ["yapf", "whatthepatch (>=1.0.2,<2.0.0)"] - -[[package]] -name = "rope" -version = "1.4.0" -description = "a python refactoring library..." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -pytoolconfig = ">=1.2.2" - -[package.extras] -dev = ["pytest (>=7.0.1)", "pytest-timeout (>=2.1.0)", "build (>=0.7.0)"] -doc = ["pytoolconfig", "sphinx (>=4.5.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx-rtd-theme (>=1.0.0)"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "ujson" -version = "5.5.0" -description = "Ultra fast JSON encoder and decoder for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "whatthepatch" -version = "1.0.3" -description = "A patch parsing and application library." -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "yapf" -version = "0.32.0" -description = "A formatter for Python code." -category = "dev" -optional = false -python-versions = "*" - -[metadata] -lock-version = "1.1" -python-versions = ">=3.8.0" -content-hash = "34b2796708ecda4150baa477583b7a57bfe007fd248124afc12c8976c0546e19" - -[metadata.files] -attrs = [] -colorama = [] -exceptiongroup = [] -iniconfig = [] -jedi = [] -packaging = [] -parso = [] -pluggy = [] -pyflakes = [] -pyparsing = [] -pytest = [] -python-lsp-jsonrpc = [] -pytoolconfig = [] -replit-python-lsp-server = [] -rope = [] -toml = [] -tomli = [] -ujson = [] -whatthepatch = [] -yapf = [] diff --git a/pyproject.toml b/pyproject.toml index 1bf25bc..dbaa064 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,23 @@ -[tool.poetry] +[project] name = "roman-numerals-python-kata" version = "1.0.0" description = "Roman numerals kata in Python" -authors = ["Codding Cuddles"] +authors = [{ name = "Codding Cuddles" }] +requires-python = ">=3.11" -[tool.poetry.dependencies] -python = ">=3.8.0" +dependencies = [ + "pytest>=9.1.1" +] -[tool.poetry.dev-dependencies] -pytest = "^7.2.0" -replit-python-lsp-server = {extras = ["yapf", "rope", "pyflakes"], version = "^1.15.9"} +[dependency-groups] +dev = [ + "ruff>=0.16.1" +] -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.ruff] +target-version = "py311" +line-length = 100 + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" diff --git a/replit.nix b/replit.nix deleted file mode 100644 index 56778e4..0000000 --- a/replit.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ pkgs }: { - deps = [ - pkgs.python38Full - pkgs.python38Packages.pytest - ]; - env = { - PYTHON_LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ - # Needed for pandas / numpy - pkgs.stdenv.cc.cc.lib - pkgs.zlib - # Needed for matplotlib - pkgs.xorg.libX11 - ]; - PYTHONBIN = "${pkgs.python38Full}/bin/python3.8"; - LANG = "en_US.UTF-8"; - }; -} diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..7e29de8 --- /dev/null +++ b/uv.lock @@ -0,0 +1,108 @@ +version = 1 +revision = 3 +requires-python = ">=3.11" + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +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 = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "roman-numerals-python-kata" +version = "1.0.0" +source = { virtual = "." } +dependencies = [ + { name = "pytest" }, +] + +[package.dev-dependencies] +dev = [ + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [{ name = "pytest", specifier = ">=9.1.1" }] + +[package.metadata.requires-dev] +dev = [{ name = "ruff", specifier = ">=0.16.1" }] + +[[package]] +name = "ruff" +version = "0.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/25/7113f6d5498888c5fb7db34081cba7d5971c4cb1bfb26819966eee68f003/ruff-0.16.1.tar.gz", hash = "sha256:fedad7c801dabd3fb9741d76aca39246e6ddd9ca446a015875207bf19f1e6bc7", size = 4877500, upload-time = "2026-07-30T19:37:01.379Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/bd/694da69368e0973de65df2ddc73ab18d43c469d5963d9b150911de6bc513/ruff-0.16.1-py3-none-linux_armv6l.whl", hash = "sha256:58edb313b88f0c5460a26adf5f39a37a3be789494a15e3e411e35fa78b89f9a0", size = 10839126, upload-time = "2026-07-30T19:36:13.697Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f0/b626e5d5bd0dd9576263658ef12885e2288afd1029a48e26ffed65ec1ac1/ruff-0.16.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fde5a99e2f97479af66edd6622c6d5a2a7592c77cf4153d9e4428f5eeb55b60c", size = 11070253, upload-time = "2026-07-30T19:36:17.14Z" }, + { url = "https://files.pythonhosted.org/packages/83/63/f40acfb6b35b88623e71684942b552c3edd96035f5d98f313815f7b277de/ruff-0.16.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e0d4c20532fca4f7fa609369161d968dd28f65d83dabbd61d8e9c7edbf7001f6", size = 10561425, upload-time = "2026-07-30T19:36:20.04Z" }, + { url = "https://files.pythonhosted.org/packages/aa/dd/14ec0e9c2b4d315547dd38765004b4863e354e1b52cb308272215d9f6f6d/ruff-0.16.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30affbcedf59ad5703d9c91f82266e02b47739f797e1a7b6e158e5526a6dae38", size = 10948879, upload-time = "2026-07-30T19:36:22.476Z" }, + { url = "https://files.pythonhosted.org/packages/33/e9/9d870cbae575030fdef595f04b4b97573c525b5497cce4f4498cf2f85446/ruff-0.16.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24e9c631573cbca9d20f1283f8f479b2afa4a8503504822bd71a293889f16743", size = 10643691, upload-time = "2026-07-30T19:36:24.914Z" }, + { url = "https://files.pythonhosted.org/packages/c4/09/12743d544e2173f53ecd27217c65f90d2bc0f8424a66a60339e56bbc0457/ruff-0.16.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b41bdd48fb420987a9b5212e4957c26ad4abce401fa9ea9d4d85843727945f4f", size = 11435354, upload-time = "2026-07-30T19:36:28.447Z" }, + { url = "https://files.pythonhosted.org/packages/7f/89/a1652b2daee52083c9554a6333b678a8b01d0400f976827bb87857f9449a/ruff-0.16.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0d1e1393b7648079e13669de1c1f4fde06d4583e84d8fd5c1551e0a77a2aa75", size = 12259033, upload-time = "2026-07-30T19:36:31.326Z" }, + { url = "https://files.pythonhosted.org/packages/16/96/ecdcb8c54ee7b123b487f807eb014e6e019155a0b81dfb669acd52f28ce3/ruff-0.16.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07bf434b1c95f4e093be4532068ef4fcf00924eb2ade8796075980902d6fd54a", size = 11667981, upload-time = "2026-07-30T19:36:34.394Z" }, + { url = "https://files.pythonhosted.org/packages/cd/90/c52e12e0d862e9572f2a33aa227409143520abe53111e9a6babbac7b4af8/ruff-0.16.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39897739f112253ee4fdd2e8aa9a4f9ded99fb2be367d5f31dfa4ded6025584c", size = 11468183, upload-time = "2026-07-30T19:36:37.339Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6b/4ffb7ad1d83eb16cf8cbb3c8815d3f11c88460fd162d4b372a2059be1c2a/ruff-0.16.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:82ae3c0c0d74daf17b968a10b7b3bb3ef297ab7de0c1f749646b25e690ccb150", size = 11470071, upload-time = "2026-07-30T19:36:39.91Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/32ae7db4c0b5e32ab611787caa19d1546800676d79f7483b7100a3561bf4/ruff-0.16.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4d5f2ed10f8242d83fc08d521301089364e3375375705356f20c0e31606ef3ef", size = 10919503, upload-time = "2026-07-30T19:36:42.65Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ca/3d901ba6ad6fc38da39c3448fc6c59ac945679293a17c3ceb6d6c1cba13e/ruff-0.16.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a4665b309891f83f3e3c25447935f1213e9abbd4b5640af7a1f2def9f8d413c1", size = 10649861, upload-time = "2026-07-30T19:36:45.18Z" }, + { url = "https://files.pythonhosted.org/packages/92/79/894ef1ced26552d5f8c9cf6d85b0687840e1128c55aeab7b9c2d54a0d880/ruff-0.16.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26e9ca5c9bc3971f20d3cf18a957f52ffd6a5f6564ff15c4912a144dcac22494", size = 11148137, upload-time = "2026-07-30T19:36:47.936Z" }, + { url = "https://files.pythonhosted.org/packages/2d/69/3609a09fa1cb46cc28b762363e440a354204e5dff01bd0c8d7437874d6b9/ruff-0.16.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:67e1e1e3fa4f0c82f0e36d4cd61e661f6e7a6196cb1aa92fe0828fa7b8f257cd", size = 11559211, upload-time = "2026-07-30T19:36:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8a/fb22af2fd78a736e241fabf67e30ce1799a64244026377a49e133af90762/ruff-0.16.1-py3-none-win32.whl", hash = "sha256:d31765e131295b8445caf301e3e8a85b34d1b9b211b4109b7ba457888b051806", size = 10838258, upload-time = "2026-07-30T19:36:53.298Z" }, + { url = "https://files.pythonhosted.org/packages/d4/35/e57fd9fb5d423961df087a00b12d42c0a830288dc2f3b45ecca299158b4f/ruff-0.16.1-py3-none-win_amd64.whl", hash = "sha256:09b05e8b90c2cb06ad63464350e7a45e8e44a2dfe52072ebfba6666ca8d3f596", size = 11961111, upload-time = "2026-07-30T19:36:56.107Z" }, + { url = "https://files.pythonhosted.org/packages/cb/46/240ea004bf6dc4feb40e9832f2205a476a47dd5b8a3f8211a5fc5f95e20e/ruff-0.16.1-py3-none-win_arm64.whl", hash = "sha256:dbaadaac38c70239f056d306b7476f246b0bf000fa6b3876402acbf5b227eaf8", size = 11309414, upload-time = "2026-07-30T19:36:58.79Z" }, +]