diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe2edab..fdee337 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,6 +2,12 @@ Welcome to the team! We're excited to have you on board as part of our development team. To ensure a smooth and efficient workflow, we've put together some key rules and guidelines. Please take a moment to review them before getting started. +- [Backend](#backend) + - [First steps](#first-steps) + - [Testing](#testing) + - [Code quality](#code-qualtiy) +- [Version Control](#version-control) +- [Collaboration & Communication](#collaboration--communication) ## Backend ### First steps: We recommend using `uv` to manage Python versions, project dependencies, and virtual environments for this project. @@ -56,6 +62,52 @@ uv run pytest # Run a specific test uv run pytest tests/test_something.py ``` +### Code qualtiy +#### Linter - Ruff +```bash +# With uv +uv run ruff check + +# With an activated environment +ruff check + +# Pass the --fix option to automatically fix the fixable errors +uv run ruff check --fix +ruff check --fix +``` +If available you can also install the ruff extension to your IDE. +For more information refer to the [docs](https://docs.astral.sh/ruff/). +#### Formatter - Ruff & isort +```bash +# With uv +uv run ruff format +uv run isort . # you can also pass a file as an arugment + +# With an activated environment +ruff format +isort . # you can also pass a file as an arugment +``` +For more information refer to the [ruff](https://docs.astral.sh/ruff/) and [isort](https://pycqa.github.io/isort/) docs. +#### Pre-commit hooks +To simplify the process of maintaining coding quality standards, we've added a pre-commit configuration. This ensures that your code is automatically linted and reformatted before every commit, making it easier to keep everything clean and consistent. + +##### Install the git hook scripts +```bash +# With uv +uv run pre-commit install + +# With an activated environment +pre-commit install +``` +Now `pre-commit` will run automatically on `git commit`, checking the files in the staging area. If it finds any errors, it will abort the commit. +It is recommended to run the hooks against all of the files when adding new hooks. +```bash +# With uv +uv run pre-commit run --all-files + +# With an activated environment +pre-commit run --all-files +``` ## Version Control - Branching: For every task or feature, create a fresh branch. diff --git a/backend/.pre-commit-config.yaml b/backend/.pre-commit-config.yaml new file mode 100644 index 0000000..28ce2a0 --- /dev/null +++ b/backend/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.6.3 + hooks: + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format + + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort (python) \ No newline at end of file diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 8b2b423..12e5a80 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -21,6 +21,7 @@ dependencies = [ "httpx==0.27.2", "idna==3.8", "iniconfig==2.0.0", + "isort>=5.13.2", "jinja2==3.1.4", "markdown-it-py==3.0.0", "markupsafe==2.1.5", @@ -28,6 +29,7 @@ dependencies = [ "motor>=3.5.1", "packaging==24.1", "pluggy==1.5.0", + "pre-commit>=3.8.0", "pydantic-core==2.20.1", "pydantic-settings>=2.4.0", "pydantic==2.8.2", @@ -38,6 +40,7 @@ dependencies = [ "python-multipart==0.0.9", "pyyaml==6.0.2", "rich==13.8.0", + "ruff>=0.6.3", "shellingham==1.5.4", "sniffio==1.3.1", "starlette==0.38.4", diff --git a/backend/requirements.txt b/backend/requirements.txt index ffe96c8..75c3f56 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -15,6 +15,8 @@ certifi==2024.8.30 # quiz-app-backend (pyproject.toml) # httpcore # httpx +cfgv==3.4.0 + # via pre-commit click==8.1.7 # via # quiz-app-backend (pyproject.toml) @@ -26,6 +28,8 @@ colorama==0.4.6 # click # pytest # uvicorn +distlib==0.3.8 + # via virtualenv dnspython==2.6.1 # via # quiz-app-backend (pyproject.toml) @@ -39,6 +43,8 @@ fastapi==0.112.2 # via quiz-app-backend (pyproject.toml) fastapi-cli==0.0.5 # via quiz-app-backend (pyproject.toml) +filelock==3.15.4 + # via virtualenv h11==0.14.0 # via # quiz-app-backend (pyproject.toml) @@ -54,6 +60,8 @@ httptools==0.6.1 # uvicorn httpx==0.27.2 # via quiz-app-backend (pyproject.toml) +identify==2.6.0 + # via pre-commit idna==3.8 # via # quiz-app-backend (pyproject.toml) @@ -64,6 +72,8 @@ iniconfig==2.0.0 # via # quiz-app-backend (pyproject.toml) # pytest +isort==5.13.2 + # via quiz-app-backend (pyproject.toml) jinja2==3.1.4 # via quiz-app-backend (pyproject.toml) markdown-it-py==3.0.0 @@ -82,15 +92,21 @@ mdurl==0.1.2 # markdown-it-py motor==3.5.1 # via quiz-app-backend (pyproject.toml) +nodeenv==1.9.1 + # via pre-commit packaging==24.1 # via # quiz-app-backend (pyproject.toml) # marshmallow # pytest +platformdirs==4.2.2 + # via virtualenv pluggy==1.5.0 # via # quiz-app-backend (pyproject.toml) # pytest +pre-commit==3.8.0 + # via quiz-app-backend (pyproject.toml) pydantic==2.8.2 # via # quiz-app-backend (pyproject.toml) @@ -125,11 +141,14 @@ python-multipart==0.0.9 pyyaml==6.0.2 # via # quiz-app-backend (pyproject.toml) + # pre-commit # uvicorn rich==13.8.0 # via # quiz-app-backend (pyproject.toml) # typer +ruff==0.6.3 + # via quiz-app-backend (pyproject.toml) shellingham==1.5.4 # via # quiz-app-backend (pyproject.toml) @@ -158,6 +177,8 @@ uvicorn==0.30.6 # via # quiz-app-backend (pyproject.toml) # fastapi-cli +virtualenv==20.26.3 + # via pre-commit watchfiles==0.24.0 # via # quiz-app-backend (pyproject.toml) diff --git a/backend/uv.lock b/backend/uv.lock index 000e64b..b0448e0 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -36,6 +36,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, ] +[[package]] +name = "cfgv" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, +] + [[package]] name = "click" version = "8.1.7" @@ -57,6 +66,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, ] +[[package]] +name = "distlib" +version = "0.3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/91/e2df406fb4efacdf46871c25cde65d3c6ee5e173b7e5a4547a47bae91920/distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64", size = 609931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/41/9307e4f5f9976bc8b7fea0b66367734e8faf3ec84bc0d412d8cfabbb66cd/distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", size = 468850 }, +] + [[package]] name = "dnspython" version = "2.6.1" @@ -119,6 +137,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/24/ea/4b5011012ac925fe2f83b19d0e09cee9d324141ec7bf5e78bb2817f96513/fastapi_cli-0.0.5-py3-none-any.whl", hash = "sha256:e94d847524648c748a5350673546bbf9bcaeb086b33c24f2e82e021436866a46", size = 9489 }, ] +[[package]] +name = "filelock" +version = "3.15.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/08/dd/49e06f09b6645156550fb9aee9cc1e59aba7efbc972d665a1bd6ae0435d4/filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb", size = 18007 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/f0/48285f0262fe47103a4a45972ed2f9b93e4c80b8fd609fa98da78b2a5706/filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7", size = 16159 }, +] + [[package]] name = "h11" version = "0.14.0" @@ -172,6 +199,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395 }, ] +[[package]] +name = "identify" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/f4/8e8f7db397a7ce20fbdeac5f25adaf567fc362472432938d25556008e03a/identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf", size = 99116 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/6c/a4f39abe7f19600b74528d0c717b52fff0b300bb0161081510d39c53cb00/identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0", size = 98962 }, +] + [[package]] name = "idna" version = "3.8" @@ -190,6 +226,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, ] +[[package]] +name = "isort" +version = "5.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, +] + [[package]] name = "jinja2" version = "3.1.4" @@ -262,7 +307,15 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/06/5e/0fcf4bead928dd68fd2067ad0b8fd4764279f7609c778ac26cedaca4668b/motor-3.5.1.tar.gz", hash = "sha256:1622bd7b39c3e6375607c14736f6e1d498128eadf6f5f93f8786cf17d37062ac", size = 275180 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/f4/cba8351f0f16f6d73932092e926dd14fa309d604ca09aef0d08016de7237/motor-3.5.1-py3-none-any.whl", hash = "sha256:f95a9ea0f011464235e0bd72910baa291db3a6009e617ac27b82f57885abafb8", size = 74743 }, + { url = "https://files.pythonhosted.org/packages/ee/f4/cba8351f0f16f6d73932092e926dd14fa309d604ca09aef0d08016de7237/motor-3.5.1-py3-none-any.whl", hash = "sha256:f95a9ea0f011464235e0bd72910baa291db3a6009e617ac27b82f57885abafb8", size = 74743 },] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] [[package]] @@ -274,6 +327,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, ] +[[package]] +name = "platformdirs" +version = "4.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/52/0763d1d976d5c262df53ddda8d8d4719eedf9594d046f117c25a27261a19/platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3", size = 20916 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/13/2aa1f0e1364feb2c9ef45302f387ac0bd81484e9c9a4c5688a322fbdfd08/platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee", size = 18146 }, +] + [[package]] name = "pluggy" version = "1.5.0" @@ -283,6 +345,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, ] +[[package]] +name = "pre-commit" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cfgv" }, + { name = "identify" }, + { name = "nodeenv" }, + { name = "pyyaml" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/64/10/97ee2fa54dff1e9da9badbc5e35d0bbaef0776271ea5907eccf64140f72f/pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af", size = 177815 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/92/caae8c86e94681b42c246f0bca35c059a2f0529e5b92619f6aba4cf7e7b6/pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f", size = 204643 }, +] + [[package]] name = "pydantic" version = "2.8.2" @@ -466,6 +544,7 @@ dependencies = [ { name = "httpx" }, { name = "idna" }, { name = "iniconfig" }, + { name = "isort" }, { name = "jinja2" }, { name = "markdown-it-py" }, { name = "markupsafe" }, @@ -473,6 +552,7 @@ dependencies = [ { name = "motor" }, { name = "packaging" }, { name = "pluggy" }, + { name = "pre-commit" }, { name = "pydantic" }, { name = "pydantic-core" }, { name = "pydantic-settings" }, @@ -483,6 +563,7 @@ dependencies = [ { name = "python-multipart" }, { name = "pyyaml" }, { name = "rich" }, + { name = "ruff" }, { name = "shellingham" }, { name = "sniffio" }, { name = "starlette" }, @@ -511,6 +592,7 @@ requires-dist = [ { name = "httpx", specifier = "==0.27.2" }, { name = "idna", specifier = "==3.8" }, { name = "iniconfig", specifier = "==2.0.0" }, + { name = "isort", specifier = ">=5.13.2" }, { name = "jinja2", specifier = "==3.1.4" }, { name = "markdown-it-py", specifier = "==3.0.0" }, { name = "markupsafe", specifier = "==2.1.5" }, @@ -518,6 +600,7 @@ requires-dist = [ { name = "motor", specifier = ">=3.5.1" }, { name = "packaging", specifier = "==24.1" }, { name = "pluggy", specifier = "==1.5.0" }, + { name = "pre-commit", specifier = ">=3.8.0" }, { name = "pydantic", specifier = "==2.8.2" }, { name = "pydantic-core", specifier = "==2.20.1" }, { name = "pydantic-settings", specifier = ">=2.4.0" }, @@ -528,6 +611,7 @@ requires-dist = [ { name = "python-multipart", specifier = "==0.0.9" }, { name = "pyyaml", specifier = "==6.0.2" }, { name = "rich", specifier = "==13.8.0" }, + { name = "ruff", specifier = ">=0.6.3" }, { name = "shellingham", specifier = "==1.5.4" }, { name = "sniffio", specifier = "==1.3.1" }, { name = "starlette", specifier = "==0.38.4" }, @@ -551,6 +635,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/d9/c2a126eeae791e90ea099d05cb0515feea3688474b978343f3cdcfe04523/rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc", size = 241597 }, ] +[[package]] +name = "ruff" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5d/f9/0b32e5d1c6f957df49398cd882a011e9488fcbca0d6acfeeea50ccd37a4d/ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983", size = 2463514 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/68/1da6a1e39a03a229ea57c511691d6225072759cc7764206c3f0989521194/ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3", size = 9696928 }, + { url = "https://files.pythonhosted.org/packages/6e/59/3b8b1d3a4271c6eb6ceecd3cef19a6d881639a0f18ad651563d6f619aaae/ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc", size = 9448462 }, + { url = "https://files.pythonhosted.org/packages/35/4f/b942ecb8bbebe53aa9b33e9b96df88acd50b70adaaed3070f1d92131a1cb/ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1", size = 9176190 }, + { url = "https://files.pythonhosted.org/packages/a0/20/b0bcb29d4ee437f3567b73b6905c034e2e94d29b9b826c66daecc1cf6388/ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1", size = 10108892 }, + { url = "https://files.pythonhosted.org/packages/9c/e3/211bc759f424e8823a9937e0f678695ca02113c621dfde1fa756f9f26f6d/ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672", size = 9476471 }, + { url = "https://files.pythonhosted.org/packages/b2/a3/2ec35a2d7a554364864206f0e46812b92a074ad8a014b923d821ead532aa/ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1", size = 10294802 }, + { url = "https://files.pythonhosted.org/packages/03/8b/56ef687b3489c88886dea48c78fb4969b6b65f18007d0ac450070edd1f58/ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384", size = 11022372 }, + { url = "https://files.pythonhosted.org/packages/a5/21/327d147feb442adb88975e81e2263102789eba9ad2afa102c661912a482f/ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a", size = 10596596 }, + { url = "https://files.pythonhosted.org/packages/6c/86/ff386de63729da3e08c8099c57f577a00ec9f3eea711b23ac07cf3588dc5/ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500", size = 11572830 }, + { url = "https://files.pythonhosted.org/packages/38/5d/b33284c108e3f315ddd09b70296fd76bd28ecf8965a520bc93f3bbd8ac40/ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470", size = 10262577 }, + { url = "https://files.pythonhosted.org/packages/29/99/9cdfad0d7f460e66567236eddc691473791afd9aff93a0dfcdef0462a6c7/ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f", size = 10098751 }, + { url = "https://files.pythonhosted.org/packages/a8/9f/f801a1619f5549e552f1f722f1db57eb39e7e1d83d482133142781d450de/ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5", size = 9563859 }, + { url = "https://files.pythonhosted.org/packages/0b/4d/fb2424faf04ffdb960ae2b3a1d991c5183dd981003de727d2d5cc38abc98/ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351", size = 9914291 }, + { url = "https://files.pythonhosted.org/packages/2e/dd/94fddf002a8f6152e8ebfbb51d3f93febc415c1fe694345623c31ce8b33b/ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8", size = 10331549 }, + { url = "https://files.pythonhosted.org/packages/b4/73/ca9c2f9237a430ca423b6dca83b77e9a428afeb7aec80596e86c369123fe/ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521", size = 7962163 }, + { url = "https://files.pythonhosted.org/packages/55/ce/061c605b1dfb52748d59bc0c7a8507546c178801156415773d18febfd71d/ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb", size = 8800901 }, + { url = "https://files.pythonhosted.org/packages/63/28/ae4ffe7d3b6134ca6d31ebef07447ef70097c4a9e8fbbc519b374c5c1559/ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82", size = 8229171 }, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -643,6 +752,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/52/deb4be09060637ef4752adaa0b75bf770c20c823e8108705792f99cd4a6f/uvloop-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aea15c78e0d9ad6555ed201344ae36db5c63d428818b4b2a42842b3870127c00", size = 4115980 }, ] +[[package]] +name = "virtualenv" +version = "20.26.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/60/db9f95e6ad456f1872486769c55628c7901fb4de5a72c2f7bdd912abf0c1/virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a", size = 9057588 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/4d/410156100224c5e2f0011d435e477b57aed9576fc7fe137abcf14ec16e11/virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589", size = 5684792 }, +] + [[package]] name = "watchfiles" version = "0.24.0"