diff --git a/.github/workflows/pr-format.yml b/.github/workflows/pr-format.yml index df75e8848..49bbdc9bf 100644 --- a/.github/workflows/pr-format.yml +++ b/.github/workflows/pr-format.yml @@ -26,18 +26,19 @@ jobs: # submodule: 'recursive' fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 - id: setup_python + - name: Get uv version + id: get_uv_version + if: github.event_name != 'pull_request' || github.event.action != 'closed' + run: echo "uv_version=$(sed -e 's/uv==//g' requirements.txt)" >> "$GITHUB_OUTPUT" + - name: Set up uv + uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4.2.0 if: github.event_name != 'pull_request' || github.event.action != 'closed' with: - python-version-file: .python-version - cache: pipenv - - if: github.event_name != 'pull_request' || github.event.action != 'closed' - run: sed -i -e "s/python_version = \".*\"/python_version = \"$(echo ${{ steps.setup_python.outputs.python-version }} | sed -e 's/\([0-9]*\.[0-9]*\).*/\1/g')\"/g" Pipfile + version: ${{steps.get_uv_version.outputs.uv_version}} + enable-cache: true - name: Install dependencies if: github.event_name != 'pull_request' || github.event.action != 'closed' - run: bash "${GITHUB_WORKSPACE}/scripts/pipenv_install.sh" + run: bash "${GITHUB_WORKSPACE}/scripts/uv_install.sh" # formatする # --exit-codeをつけることで、autopep8内でエラーが起きれば1、差分があれば2のエラーステータスコードが返ってくる。正常時は0が返る - name: Format files diff --git a/.github/workflows/pr-test.yml b/.github/workflows/pr-test.yml index d6945256e..6b10ff0e2 100644 --- a/.github/workflows/pr-test.yml +++ b/.github/workflows/pr-test.yml @@ -14,13 +14,16 @@ jobs: with: submodules: "recursive" fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + - name: Get uv version + id: get_uv_version + run: echo "uv_version=$(sed -e 's/uv==//g' requirements.txt)" >> "$GITHUB_OUTPUT" + - name: Set up uv + uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4.2.0 with: - python-version-file: .python-version - cache: pipenv - - name: Install pipenv - run: bash "${GITHUB_WORKSPACE}/scripts/pipenv_install.sh" + version: ${{steps.get_uv_version.outputs.uv_version}} + enable-cache: true + - name: Install uv + run: bash "${GITHUB_WORKSPACE}/scripts/uv_install.sh" - name: Set venv path env: DEST_PATH: "/home/runner/work/_temp/_github_workflow/.venv" diff --git a/.github/workflows/pr-update-readme-sudden-death.yml b/.github/workflows/pr-update-readme-sudden-death.yml index aef4132c1..d9faae98c 100644 --- a/.github/workflows/pr-update-readme-sudden-death.yml +++ b/.github/workflows/pr-update-readme-sudden-death.yml @@ -24,7 +24,7 @@ jobs: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} - if: github.event_name != 'pull_request' || github.event.action != 'closed' - run: sed -i -e "s/Python [0-9.]*/Python $(cat .python-version)/g" README.md + run: sed -i -e "s/Python [0-9.]*/Python $(yq '.project.requires-python' pyproject.toml | sed -e 's/==//g')/g" README.md - uses: dev-hato/actions-diff-pr-management@8d475e5bc78f61c40024d4859b79134b66f788d2 # v1.2.1 with: github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 8f4dc5cfc..000000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -Pipfile.lock diff --git a/.python-version b/.python-version deleted file mode 100644 index c10780c62..000000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.13.1 diff --git a/MANIFEST.in b/MANIFEST.in index 7fbdfb023..bec201fc8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include Pipfile +include pyproject.toml diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 483f1a8a8..000000000 --- a/Pipfile +++ /dev/null @@ -1,21 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] -autopep8 = "==2.3.1" -pylint = "==3.3.2" -mypy = "==1.13.0" -flake8 = "==7.1.1" -importlib-metadata = "==8.5.0" -toml = "==0.10.2" -types-toml = "==0.10.8.20240310" -setuptools = "==75.6.0" - -[packages] -pyperclip = ">=1.5.27" -click = "==8.1.7" - -[requires] -python_version = "3.13" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 634166345..000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,228 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "2ae3cd671e93e2accef54cd7adf2b60667207ae55ea5bea94478ba40dc2be507" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.13" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "click": { - "hashes": [ - "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", - "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" - ], - "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==8.1.7" - }, - "pyperclip": { - "hashes": [ - "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310" - ], - "index": "pypi", - "version": "==1.9.0" - } - }, - "develop": { - "astroid": { - "hashes": [ - "sha256:6aaea045f938c735ead292204afdb977a36e989522b7833ef6fea94de743f442", - "sha256:db676dc4f3ae6bfe31cda227dc60e03438378d7a896aec57422c95634e8d722f" - ], - "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.6" - }, - "autopep8": { - "hashes": [ - "sha256:8d6c87eba648fdcfc83e29b788910b8643171c395d9c4bcf115ece035b9c9dda", - "sha256:a203fe0fcad7939987422140ab17a930f684763bf7335bdb6709991dd7ef6c2d" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.3.1" - }, - "dill": { - "hashes": [ - "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", - "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" - ], - "markers": "python_version >= '3.8'", - "version": "==0.3.9" - }, - "flake8": { - "hashes": [ - "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", - "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213" - ], - "index": "pypi", - "markers": "python_full_version >= '3.8.1'", - "version": "==7.1.1" - }, - "importlib-metadata": { - "hashes": [ - "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", - "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==8.5.0" - }, - "isort": { - "hashes": [ - "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", - "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.13.2" - }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "mypy": { - "hashes": [ - "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc", - "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", - "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f", - "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74", - "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a", - "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", - "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b", - "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73", - "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", - "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d", - "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d", - "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6", - "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca", - "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d", - "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", - "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", - "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a", - "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc", - "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7", - "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb", - "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", - "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732", - "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80", - "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", - "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", - "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", - "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", - "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24", - "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", - "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b", - "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372", - "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.13.0" - }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, - "platformdirs": { - "hashes": [ - "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", - "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" - ], - "markers": "python_version >= '3.8'", - "version": "==4.3.6" - }, - "pycodestyle": { - "hashes": [ - "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", - "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521" - ], - "markers": "python_version >= '3.8'", - "version": "==2.12.1" - }, - "pyflakes": { - "hashes": [ - "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", - "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a" - ], - "markers": "python_version >= '3.8'", - "version": "==3.2.0" - }, - "pylint": { - "hashes": [ - "sha256:77f068c287d49b8683cd7c6e624243c74f92890f767f106ffa1ddf3c0a54cb7a", - "sha256:9ec054ec992cd05ad30a6df1676229739a73f8feeabf3912c995d17601052b01" - ], - "index": "pypi", - "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.2" - }, - "setuptools": { - "hashes": [ - "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", - "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" - ], - "index": "pypi", - "markers": "python_version >= '3.9'", - "version": "==75.6.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "index": "pypi", - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", - "version": "==0.10.2" - }, - "tomlkit": { - "hashes": [ - "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", - "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79" - ], - "markers": "python_version >= '3.8'", - "version": "==0.13.2" - }, - "types-toml": { - "hashes": [ - "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", - "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==0.10.8.20240310" - }, - "typing-extensions": { - "hashes": [ - "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", - "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" - ], - "markers": "python_version >= '3.8'", - "version": "==4.12.2" - }, - "zipp": { - "hashes": [ - "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", - "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931" - ], - "markers": "python_version >= '3.9'", - "version": "==3.21.0" - } - } -} diff --git a/README.md b/README.md index 4f2d3bd5d..26dda8a2f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Requirements - Python 3.13.1 -- `pipenv install` +- `uv sync` - `npm install` ## Usage @@ -17,7 +17,7 @@ sd.pyを実行することで次に入力した文字が吹き出しになって出力されます。また、同時に出力された吹き出しはクリップボードにもコピーされます。 ```sh -pipenv run python sd.py メッセージ +uv run sd.py メッセージ ``` ## lintをかける方法 diff --git a/package.json b/package.json index 2c85ecc2f..171200b43 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "lint": "npm run lint:markdown && npm run lint:text && npm run lint:python && npm run lint:python-type && npm run lint:secret", "lint:markdown": "markdownlint -c .markdown-lint.yml -i node_modules .", "lint:text": "textlint -c .textlintrc $(find . -name '*.md' | grep -v node_modules)", - "lint:python": "pipenv run pylint --rcfile .python-lint *.py", - "lint:python-type": "pipenv run mypy --config-file .mypy.ini --install-types --non-interactive *.py", + "lint:python": "uv tool run pylint --rcfile .python-lint *.py", + "lint:python-type": "uv tool run mypy --config-file .mypy.ini --install-types --non-interactive *.py", "lint:secret": "docker run --rm --platform=linux/amd64 -v $(pwd):/path -w /path zricethezav/gitleaks protect --verbose --redact" }, "devDependencies": { diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..cf68799cf --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "sudden_death" +version = "0.0.1" +description = "突然の死(ハリフキダシ)を生成するツール" +requires-python = "==3.13.1" +dependencies = [ "click==8.1.7", "pyperclip>=1.5.27",] + +[dependency-groups] +dev = [ "autopep8==2.3.1", "pylint==3.3.2", "mypy==1.13.0", "flake8==7.1.1", "importlib-metadata==8.5.0", "toml==0.10.2", "types-toml==0.10.8.20240310", "setuptools==75.6.0", "toml==0.10.2",] + +[tool.uv] +[[tool.uv.index]] +name = "pypi" +url = "https://pypi.org/simple" + diff --git a/requirements.txt b/requirements.txt index 7ca1918f0..14b158370 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -pipenv==2024.4.0 +uv==0.5.9 diff --git a/scripts/pipenv_install.sh b/scripts/pipenv_install.sh deleted file mode 100755 index 2f7b478c5..000000000 --- a/scripts/pipenv_install.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -pip install -r requirements.txt -pipenv --version -pipenv install --dev diff --git a/scripts/pr_copy_ci_sudden_death/pr_copy_ci/copy_ci.sh b/scripts/pr_copy_ci_sudden_death/pr_copy_ci/copy_ci.sh index d6ff279af..b1d666c11 100755 --- a/scripts/pr_copy_ci_sudden_death/pr_copy_ci/copy_ci.sh +++ b/scripts/pr_copy_ci_sudden_death/pr_copy_ci/copy_ci.sh @@ -16,11 +16,9 @@ for f in $(find hato-bot/scripts -type f | grep -v hato_bot | sed -e "s:hato-bot cp "hato-bot/${f}" "sudden-death/${f}" done -for f in .markdown-lint.yml .python-lint .textlintrc .gitleaks.toml .mypy.ini .pre-commit-config.yaml .python-version .pep8 .flake8 .python-black .isort.cfg .prettierignore renovate.json requirements.txt; do +for f in .markdown-lint.yml .python-lint .textlintrc .gitleaks.toml .mypy.ini .pre-commit-config.yaml .pep8 .flake8 .python-black .isort.cfg .prettierignore renovate.json requirements.txt; do rm -f "sudden-death/${f}" cp hato-bot/${f} sudden-death/ done -PATTERN_BEFORE="$(grep '^click' sudden-death/Pipfile)" -PATTERN_AFTER="$(grep '^click' hato-bot/Pipfile)" -PATTERN="s/${PATTERN_BEFORE}/${PATTERN_AFTER}/g" -sed -i -e "${PATTERN}" sudden-death/Pipfile +PATTERN_AFTER="$(grep click hato-bot/pyproject.toml | sed -e 's/^.*click==\([0-9.]*\)".*$/\1/g')" +sed -i -e "s/click==[0-9.]*\"/click==${PATTERN_AFTER}\"/g" sudden-death/pyproject.toml diff --git a/scripts/pr_format/pr_format/fix_pipfile.py b/scripts/pr_format/pr_format/fix_pipfile.py deleted file mode 100644 index 7fc13d64f..000000000 --- a/scripts/pr_format/pr_format/fix_pipfile.py +++ /dev/null @@ -1,253 +0,0 @@ -""" -Pipfileに対して以下の修正を行う。 - -* Pipfileでのバージョン指定が「*」となっているパッケージについて、バージョン指定を実際にインストールされるものに修正する -* プロジェクト内のPythonファイルでimportされているがPipfile内には存在しないパッケージをPipfileの「packages」セクションに追加する -""" - -import importlib.util -import re -import sys -from pathlib import Path -from typing import NoReturn, TypeGuard - -import importlib_metadata -import toml - -# Pipfileの「packages」「dev-packages」セクションのデータ型 -PipfilePackages = dict[str, str | dict[str, str | list[str]]] - -# Pipfileのいずれかのセクションのデータ型 -PipfileValue = PipfilePackages | list[dict[str, str | bool]] - -# Pipfileのデータ型 -Pipfile = dict[str, PipfileValue] - - -def is_pipfile_packages(pipfile_value: PipfileValue) -> TypeGuard[PipfilePackages]: - """ - PipfileValue型 のデータがPipfilePackages型であるかを判定する - :param pipfile_value: 判定対象のデータ - :return: PipfilePackages型であるか - """ - if not isinstance(pipfile_value, dict): - return False - - for k, v in pipfile_value.items(): - if not isinstance(k, str): - return False - - if isinstance(v, str): - continue - - for k2, v2 in v.items(): - if not isinstance(k2, str): - return False - - if isinstance(v2, list): - for v3 in v2: - if not isinstance(v3, str): - return False - elif not isinstance(v2, str): - return False - - return True - - -def get_package_version(package_name: str) -> str: - """ - メタデータからパッケージのバージョンを取得する - :param package_name: 取得対象のパッケージ名 - :return: パッケージのバージョン (「==X.Y.Z」形式)。バージョンを取得できなかった場合は「*」を返す。 - """ - try: - dist = importlib_metadata.distribution(package_name) - except importlib_metadata.PackageNotFoundError: - return "*" - - return f"=={dist.version}" - - -def fix_package_version(packages: PipfilePackages) -> PipfilePackages: - """ - Pipfileでのバージョン指定が「*」となっているパッケージについて、バージョン指定を実際にインストールされるものに修正する - :param packages: パッケージ一覧 - :return: パッケージ一覧 - """ - for package_name in packages.keys(): - if packages[package_name] == "*": - packages[package_name] = get_package_version(package_name) - - return packages - - -def is_std_or_local_lib(project_root: Path, package_name: str) -> bool: - """ - 与えられたパッケージが標準パッケージ or 独自に定義したものであるかを判定する - :param project_root: プロジェクトのルートディレクトリのパス - :param package_name: 判定対象のパッケージ名 - :return: 与えられたパッケージが標準パッケージ or 独自に定義したものであるか - """ - # 与えられたパッケージがビルドインのモジュールならば標準パッケージと判定する - if package_name in sys.builtin_module_names: - return True - - package_spec = None - - # Finderを使ってパッケージ情報 (Spec) を取得する - for finder in sys.meta_path: - try: - package_spec = finder.find_spec(package_name, ".") - except (AttributeError, ValueError, ModuleNotFoundError): - pass - - if package_spec: - break - - if package_spec is None: - try: - package_spec = importlib.util.find_spec(package_name) - except (AttributeError, ValueError, ModuleNotFoundError): - pass - - # パッケージ情報がないならばPipfileによってインストールされたものと判定する - if package_spec is None: - return False - - # パッケージのファイルパス - package_origin = package_spec.origin - - # パッケージのファイルパスが取得できないならばPipfileによってインストールされたものと判定する - if not package_origin: - return False - - # パッケージのファイルパスがプロジェクト内のものであれば独自に定義したものと判定する - if project_root.resolve() in Path(package_origin).resolve().parents: - return True - - # パッケージのファイルパスがPythonのシステムのパスと一致するならば標準パッケージと判定する - if package_origin.startswith(sys.base_prefix): - return True - - return False - - -def get_imported_packages(project_root: Path) -> set[str]: - """ - プロジェクト内のPythonファイルからimportされているパッケージの一覧 (標準パッケージや独自に定義したものを除く) を取得する - :param project_root: プロジェクトのルートディレクトリのパス - :return: プロジェクト内のPythonファイル内でimportされているパッケージの一覧 (標準パッケージや独自に定義したものを除く) - """ - imported_packages: set[str] = set() - - for file in project_root.glob("**/*.py"): - if str(file).endswith("setup.py") or "node_modules" in str(file): - continue - - with open(str(file), "r") as python_file: - for imported_package in re.findall( - r"^(?:import|from)\s+(\w+)", python_file.read(), re.MULTILINE - ): - if imported_package != "sudden_death" and not is_std_or_local_lib( - project_root, imported_package - ): - imported_packages.add(imported_package) - - return imported_packages - - -def get_pipfile_packages(pipfile: Pipfile) -> set[str] | NoReturn: - """ - Pipfileからパッケージ一覧を取得する - :param pipfile: Pipfileの中身 - :return: Pipfile内のパッケージ一覧 - """ - pipfile_packages: set[str] = set() - - for key in ["packages", "dev-packages"]: - pipfile_value = pipfile[key] - - if not is_pipfile_packages(pipfile_value): - raise TypeError("Failed to cast to PipfilePackages: " + str(pipfile_value)) - - for package_name in pipfile_value.keys(): - pipfile_packages.add(package_name.lower().replace("_", "-")) - - return pipfile_packages - - -def exist_package_in_pipfile(packages: list[str], pipfile_packages: set[str]) -> bool: - """ - 与えられたパッケージ群のいずれかがPipfile内に存在するかを判定する - :param packages: パッケージ群 - :param pipfile_packages: Pipfile内のパッケージ一覧 - :return: 与えられたパッケージ群のいずれかがPipfile内に存在するか - """ - for package_name in packages: - if package_name.lower().replace("_", "-") in pipfile_packages: - return True - - return False - - -def get_missing_packages( - imported_packages: set[str], pipfile_packages: set[str] -) -> dict[str, str] | NoReturn: - """ - プロジェクト内のPythonファイルでimportされているがPipfile内には存在しないパッケージ一覧を取得する - :param imported_packages: プロジェクト内のPythonファイルからimportされているパッケージ一覧 - :param pipfile_packages: Pipfile内のパッケージ一覧 - :return: プロジェクト内のPythonファイルでimportされているがPipfile内には存在しないパッケージ一覧。Pipfile内でのパッケージ名がkey、バージョンがvalueになっている。 - """ - # import時のパッケージ名とPipfile内でのパッケージ名の対応表 - distributions = importlib_metadata.packages_distributions() - - missing_packages: dict[str, str] = dict() - - for imported_package in imported_packages: - if imported_package not in distributions: - raise ModuleNotFoundError( - f"Package {imported_package} is not found. It maybe not installed." - ) - - packages = distributions[imported_package] - - if len(packages) == 0: - raise ModuleNotFoundError( - f"Package {imported_package} is not found. It maybe not installed." - ) - - if not exist_package_in_pipfile(packages, pipfile_packages): - package_name: str = packages[0] - missing_packages[package_name] = get_package_version(package_name) - - return missing_packages - - -def main(): - project_root = Path.cwd() - pipfile_path = project_root / "Pipfile" - - if not pipfile_path.exists(): - raise FileNotFoundError("Pipfile not found.") - - pipfile = toml.load(pipfile_path) - - for key in ["packages", "dev-packages"]: - if not is_pipfile_packages(pipfile[key]): - raise TypeError("Failed to cast to PipfilePackages: " + str(pipfile[key])) - - pipfile[key] = fix_package_version(pipfile[key]) - - # プロジェクト内のPythonファイルでimportされているがPipfile内には存在しないパッケージをPipfileの「packages」セクションに追加する - if key == "packages": - pipfile[key] |= get_missing_packages( - get_imported_packages(project_root), get_pipfile_packages(pipfile) - ) - - with open(pipfile_path, "w") as f: - toml.dump(pipfile, f) - - -if __name__ == "__main__": - main() diff --git a/scripts/pr_format/pr_format/fix_pyproject.py b/scripts/pr_format/pr_format/fix_pyproject.py new file mode 100644 index 000000000..f66c072c0 --- /dev/null +++ b/scripts/pr_format/pr_format/fix_pyproject.py @@ -0,0 +1,267 @@ +""" +pyproject.tomlに対して以下の修正を行う。 + +* pyproject.tomlでのバージョン指定がないパッケージについて、バージョン指定を実際にインストールされるものに修正する +* プロジェクト内のPythonファイルでimportされているがpyproject.toml内には存在しないパッケージをpyproject.tomlの「project.dependencies」セクションに追加する +""" + +import importlib.util +import re +import sys +from pathlib import Path +from typing import NoReturn, TypeGuard + +import importlib_metadata +import toml + +# pyproject.tomlのproject.dependenciesやdependency-groups.devのデータ型 +PyProjectDependencies = list[str] + +# pyproject.tomlのデータ型 +PyProject = dict[str, dict[str, PyProjectDependencies]] + +version_operator_pattern = re.compile(r"[\\[<=>@]") + + +def is_pyproject_dependencies( + pyproject_dependencies, +) -> TypeGuard[PyProjectDependencies]: + """ + データがPyProjectDependencies型であるかを判定する + :param pyproject_dependencies: 判定対象のデータ + :return: PyProjectDependencies型であるか + """ + if not isinstance(pyproject_dependencies, list): + return False + + for v in pyproject_dependencies: + if not isinstance(v, str): + return False + + return True + + +def get_package_version(package_name: str) -> str: + """ + メタデータからパッケージのバージョンを取得する + :param package_name: 取得対象のパッケージ名 + :return: パッケージのバージョン (「package_name==X.Y.Z」形式)。バージョンを取得できなかった場合はpackage_nameを返す。 + """ + try: + package_data = version_operator_pattern.split(package_name) + dist = importlib_metadata.distribution(package_data[0]) + except importlib_metadata.PackageNotFoundError: + return package_name + + return f"{package_name}=={dist.version}" + + +def fix_package_version(packages: PyProjectDependencies) -> PyProjectDependencies: + """ + project.tomlでのバージョン指定がないパッケージについて、バージョン指定を実際にインストールされるものに修正する + :param packages: パッケージ一覧 + :return: パッケージ一覧 + """ + for i in range(len(packages)): + if not re.findall(r"[<=>@]", packages[i]): + packages[i] = get_package_version(packages[i]) + + return packages + + +def is_std_or_local_lib(project_root: Path, package_name: str) -> bool: + """ + 与えられたパッケージが標準パッケージ or 独自に定義したものであるかを判定する + :param project_root: プロジェクトのルートディレクトリのパス + :param package_name: 判定対象のパッケージ名 + :return: 与えられたパッケージが標準パッケージ or 独自に定義したものであるか + """ + # 与えられたパッケージがビルドインのモジュールならば標準パッケージと判定する + if package_name in sys.builtin_module_names: + return True + + package_spec = None + + # Finderを使ってパッケージ情報 (Spec) を取得する + for finder in sys.meta_path: + try: + package_spec = finder.find_spec(package_name, ".") + except (AttributeError, ValueError, ModuleNotFoundError): + pass + + if package_spec: + break + + if package_spec is None: + try: + package_spec = importlib.util.find_spec(package_name) + except (AttributeError, ValueError, ModuleNotFoundError): + pass + + # パッケージ情報がないならばpyproject.tomlによってインストールされたものと判定する + if package_spec is None: + return False + + # パッケージのファイルパス + package_origin = package_spec.origin + + # 次のいずれかならばpyproject.tomlによってインストールされたものと判定する + # * パッケージのファイルパスが取得できない + # * パッケージのファイルパス内に「.venv」を含む + if not package_origin or ".venv" in package_origin: + return False + + # パッケージのファイルパスがプロジェクト内のものであれば独自に定義したものと判定する + if project_root.resolve() in Path(package_origin).resolve().parents: + return True + + # パッケージのファイルパスがPythonのシステムのパスと一致するならば標準パッケージと判定する + if package_origin.startswith(sys.base_prefix): + return True + + return False + + +def get_imported_packages(project_root: Path) -> set[str]: + """ + プロジェクト内のPythonファイルからimportされているパッケージの一覧 (標準パッケージや独自に定義したものを除く) を取得する + :param project_root: プロジェクトのルートディレクトリのパス + :return: プロジェクト内のPythonファイル内でimportされているパッケージの一覧 (標準パッケージや独自に定義したものを除く) + """ + imported_packages: set[str] = set() + + for file in project_root.glob("**/*.py"): + if ( + str(file).endswith("setup.py") + or "node_modules" in str(file) + or ".venv" in str(file) + ): + continue + + with open(str(file), "r") as python_file: + for imported_package in re.findall( + r"^(?:import|from)\s+(\w+)", python_file.read(), re.MULTILINE + ): + if imported_package != "sudden_death" and not is_std_or_local_lib( + project_root, imported_package + ): + imported_packages.add(imported_package) + + return imported_packages + + +def get_pyproject_packages(pyproject: PyProject) -> set[str] | NoReturn: + """ + pyproject.tomlからパッケージ一覧を取得する + :param pyproject: pyproject.tomlの中身 + :return: pyproject.toml内のパッケージ一覧 + """ + pyproject_packages: set[str] = set() + + for pyproject_dependencies in [ + pyproject["project"]["dependencies"], + pyproject["dependency-groups"]["dev"], + ]: + if not is_pyproject_dependencies(pyproject_dependencies): + raise TypeError( + "Failed to cast to PyProjectDependencies: " + + str(pyproject_dependencies) + ) + + for pyproject_dependency in pyproject_dependencies: + package_data = version_operator_pattern.split(pyproject_dependency) + pyproject_packages.add(package_data[0].strip().lower().replace("_", "-")) + + return pyproject_packages + + +def exist_package_in_pyproject( + packages: list[str], pyproject_packages: set[str] +) -> bool: + """ + 与えられたパッケージ群のいずれかがpyproject.toml内に存在するかを判定する + :param packages: パッケージ群 + :param pyproject_packages: pyproject.toml内のパッケージ一覧 + :return: 与えられたパッケージ群のいずれかがpyproject.toml内に存在するか + """ + for package_name in packages: + if package_name.lower().replace("_", "-") in pyproject_packages: + return True + + return False + + +def get_missing_packages( + imported_packages: set[str], pyproject_packages: set[str] +) -> PyProjectDependencies | NoReturn: + """ + プロジェクト内のPythonファイルでimportされているがpyproject.toml内には存在しないパッケージ一覧を取得する + :param imported_packages: プロジェクト内のPythonファイルからimportされているパッケージ一覧 + :param pyproject_packages: pyproject.toml内のパッケージ一覧 + :return: プロジェクト内のPythonファイルでimportされているがpyproject.toml内には存在しないパッケージ一覧。pyproject.toml内でのパッケージ名がkey、バージョンがvalueになっている。 + """ + # import時のパッケージ名とpyproject.toml内でのパッケージ名の対応表 + distributions = importlib_metadata.packages_distributions() + + missing_packages: PyProjectDependencies = list() + + for imported_package in imported_packages: + if imported_package not in distributions: + raise ModuleNotFoundError( + f"Package {imported_package} is not found. It maybe not installed." + ) + + packages = distributions[imported_package] + + if len(packages) == 0: + raise ModuleNotFoundError( + f"Package {imported_package} is not found. It maybe not installed." + ) + + if not exist_package_in_pyproject(packages, pyproject_packages): + package_name: str = packages[0] + missing_packages.append(get_package_version(package_name)) + + return missing_packages + + +def main(): + project_root = Path.cwd() + pyproject_path = project_root / "pyproject.toml" + + if not pyproject_path.exists(): + raise FileNotFoundError("pyproject.toml not found.") + + pyproject = toml.load(pyproject_path) + + if not is_pyproject_dependencies(pyproject["project"]["dependencies"]): + raise TypeError( + "Failed to cast to PyProjectDependencies: " + + str(pyproject["project"]["dependencies"]) + ) + + pyproject["project"]["dependencies"] = fix_package_version( + pyproject["project"]["dependencies"] + ) + + # プロジェクト内のPythonファイルでimportされているがpyproject.toml内には存在しないパッケージをpyproject.tomlの「project.dependencies」セクションに追加する + pyproject["project"]["dependencies"] += get_missing_packages( + get_imported_packages(project_root), get_pyproject_packages(pyproject) + ) + + if not is_pyproject_dependencies(pyproject["dependency-groups"]["dev"]): + raise TypeError( + "Failed to cast to PyProjectDependencies: " + + str(pyproject["dependency-groups"]["dev"]) + ) + + pyproject["dependency-groups"]["dev"] = fix_package_version( + pyproject["dependency-groups"]["dev"] + ) + + with open(pyproject_path, "w") as f: + toml.dump(pyproject, f) + + +if __name__ == "__main__": + main() diff --git a/scripts/pr_format/pr_format/format.sh b/scripts/pr_format/pr_format/format.sh index 20245491d..3cfb07bc1 100755 --- a/scripts/pr_format/pr_format/format.sh +++ b/scripts/pr_format/pr_format/format.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash -pipenv run python "${GITHUB_WORKSPACE}/scripts/pr_format/pr_format/fix_pipfile.py" +uv run "${GITHUB_WORKSPACE}/scripts/pr_format/pr_format/fix_pyproject.py" tag_name="$(yq '.jobs.pr-super-lint.steps[-1].uses' .github/workflows/pr-test.yml | sed -e 's;/slim@.*;:slim;g')" tag_version="$(yq '.jobs.pr-super-lint.steps[-1].uses | line_comment' .github/workflows/pr-test.yml)" pyink_version="$(docker run --rm --entrypoint '' "ghcr.io/${tag_name}-${tag_version}" /bin/sh -c 'pyink --version' | grep pyink | awk '{ print $2 }')" -sed -i -e "s/pyink = .*/pyink = \"==${pyink_version}\"/g" Pipfile -pipenv install --dev -pipenv run autopep8 --exit-code --in-place --recursive . -pipenv run pyink --config .python-black . -pipenv run isort --sp .isort.cfg . +sed -i -e "s/pyink==.*\"/pyink==${pyink_version}\"/g" pyproject.toml +uv sync --dev +uv tool run autopep8 --exit-code --in-place --recursive . +uv tool run pyink --config .python-black . +uv tool run isort --sp .isort.cfg . diff --git a/scripts/pr_format/pr_format/get_version.sh b/scripts/pr_format/pr_format/get_version.sh deleted file mode 100755 index 6b7bbcc45..000000000 --- a/scripts/pr_format/pr_format/get_version.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -echo "PYTHON_VERSION=$(cat .python-version)" >>"${GITHUB_ENV}" -sed -i -e "s/python_version = \".*\"/python_version = \"$(sed -e 's/\([0-9]*\.[0-9]*\).*/\1/g' .python-version)\"/g" Pipfile diff --git a/scripts/pr_format/pr_format/install_pipenv.sh b/scripts/pr_format/pr_format/install_pipenv.sh deleted file mode 100755 index c235e4a10..000000000 --- a/scripts/pr_format/pr_format/install_pipenv.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -file_name=Dockerfile -package_name=pipenv - -if [ -f ${file_name} ]; then - PATTERN="${package_name}[^ ]+" - package_name_with_version=$(grep -oE "${PATTERN}" ${file_name}) -else - package_name_with_version=${package_name} -fi - -pip install "${package_name_with_version}" - -if [ -f ${file_name} ]; then - new_version="$(pip list --outdated | grep pipenv || true)" - new_version="$(echo -e "${new_version}" | awk '{print $3}')" - if [ -n "${new_version}" ]; then - PATTERN_BEFORE="${package_name}[^ ]+" - PATTERN_AFTER="${package_name}==${new_version}" - sed -i -E "s/${PATTERN_BEFORE}/${PATTERN_AFTER}/g" ${file_name} - pip install "${package_name}==${new_version}" - exit 1 - fi -fi diff --git a/scripts/pr_format/pr_format/pipenv_install.sh b/scripts/pr_format/pr_format/pipenv_install.sh deleted file mode 100755 index f325620c9..000000000 --- a/scripts/pr_format/pr_format/pipenv_install.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -pipenv --version -pipenv install --dev diff --git a/scripts/pr_test/pr_super_lint/get_python_version.py b/scripts/pr_test/pr_super_lint/get_python_version.py new file mode 100644 index 000000000..32342308a --- /dev/null +++ b/scripts/pr_test/pr_super_lint/get_python_version.py @@ -0,0 +1,3 @@ +import sys + +print(".".join(map(str, sys.version_info[0:2]))) diff --git a/scripts/pr_test/pr_super_lint/install_pipenv.sh b/scripts/pr_test/pr_super_lint/install_pipenv.sh deleted file mode 100755 index 973179ac6..000000000 --- a/scripts/pr_test/pr_super_lint/install_pipenv.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash - -file_name=Dockerfile -package_name=pipenv - -if [ -f ${file_name} ]; then - package_name_v=$(grep -oE "${package_name}[^ ]+" ${file_name}) -else - package_name_v=${package_name} -fi - -pip install "${package_name_v}" -pipenv --version -pipenv install --dev - -# 環境ファイルを使ってenvにsetしている -# 参考URL: https://bit.ly/2KJhjqk -venv_path=$(pipenv --venv) -echo "${venv_path}" -VENV_PATH="${venv_path}" - -# https://github.com/github/super-linter/issues/157#issuecomment-648850330 -# -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -# ここに cp -r することで、super-linterのなかに.venvを配置できる -# また、元ディレクトリにも残っているので、キャッシュが作られる -cp -r "${VENV_PATH}" "${DEST_PATH}" diff --git a/scripts/pr_test/pr_super_lint/npm_ci.sh b/scripts/pr_test/pr_super_lint/npm_ci.sh index d1dad548e..425cca6e6 100755 --- a/scripts/pr_test/pr_super_lint/npm_ci.sh +++ b/scripts/pr_test/pr_super_lint/npm_ci.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash bash "${GITHUB_WORKSPACE}/scripts/npm_ci.sh" -echo "PYTHONPATH=/github/workspace/:/github/workflow/.venv/lib/python$(echo 'import sys; print(".".join(map(str, sys.version_info[0:2])))' | python)/site-packages" >>"${GITHUB_ENV}" +echo "PYTHONPATH=/github/workspace/:/github/workflow/.venv/lib/python$(uv run "${GITHUB_WORKSPACE}/scripts/pr_test/pr_super_lint/get_python_version.py")/site-packages" >>"${GITHUB_ENV}" action="$(yq '.jobs.pr-super-lint.steps[-1].uses | line_comment' .github/workflows/pr-test.yml)" PATH="$(docker run --rm --entrypoint '' "ghcr.io/super-linter/super-linter:slim-${action}" /bin/sh -c 'echo $PATH')" echo "PATH=/github/workspace/node_modules/.bin:${PATH}" >>"$GITHUB_ENV" diff --git a/scripts/pr_test/pr_super_lint/set_venv_path.sh b/scripts/pr_test/pr_super_lint/set_venv_path.sh index 6c08a1cec..04f5406b7 100755 --- a/scripts/pr_test/pr_super_lint/set_venv_path.sh +++ b/scripts/pr_test/pr_super_lint/set_venv_path.sh @@ -2,9 +2,7 @@ # 環境ファイルを使ってenvにsetしている # 参考URL: https://bit.ly/2KJhjqk -venv_path=$(pipenv --venv) -echo "${venv_path}" -VENV_PATH="${venv_path}" +VENV_PATH=".venv" # https://github.com/github/super-linter/issues/157#issuecomment-648850330 # -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" diff --git a/scripts/uv_install.sh b/scripts/uv_install.sh new file mode 100755 index 000000000..2c9f5c666 --- /dev/null +++ b/scripts/uv_install.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +uv --version +uv python install +uv sync --dev diff --git a/setup.py b/setup.py index 8f79fdef2..1d42496d8 100644 --- a/setup.py +++ b/setup.py @@ -2,26 +2,14 @@ パッケージインストール用スクリプト """ +import toml from setuptools import setup def _requires_from_file(filename): - is_in_packages = False - requires = [] - with open(filename, encoding="UTF-8") as _f: - for _r in _f: - _r = _r.strip() - if _r == "[packages]": - is_in_packages = True - elif _r.startswith("["): - is_in_packages = False - elif _r and is_in_packages: - requires.append( - _r.replace('"', "").replace(" ", "").replace("=", "", 1) - ) - - return requires + pyproject = toml.load(_f) + return pyproject["project"]["dependencies"] setup( @@ -31,7 +19,7 @@ def _requires_from_file(filename): description="突然の死(ハリフキダシ)を生成するツール", author="koluku", url="https://github.com/koluku/sudden-death", - install_requires=_requires_from_file("Pipfile"), + install_requires=_requires_from_file("pyproject.toml"), packages=["sudden_death"], package_data={ "sudden_death": ["py.typed"], diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..1e066c9bf --- /dev/null +++ b/uv.lock @@ -0,0 +1,268 @@ +version = 1 +requires-python = "==3.13.1" + +[[package]] +name = "astroid" +version = "3.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/40/e028137cb19ed577001c76b91c5c50fee5a9c85099f45820b69385574ac5/astroid-3.3.6.tar.gz", hash = "sha256:6aaea045f938c735ead292204afdb977a36e989522b7833ef6fea94de743f442", size = 397452 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/d2/82c8ccef22ea873a2b0da9636e47d45137eeeb2fb9320c5dbbdd3627bab0/astroid-3.3.6-py3-none-any.whl", hash = "sha256:db676dc4f3ae6bfe31cda227dc60e03438378d7a896aec57422c95634e8d722f", size = 274644 }, +] + +[[package]] +name = "autopep8" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycodestyle" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/52/65556a5f917a4b273fd1b705f98687a6bd721dbc45966f0f6687e90a18b0/autopep8-2.3.1.tar.gz", hash = "sha256:8d6c87eba648fdcfc83e29b788910b8643171c395d9c4bcf115ece035b9c9dda", size = 92064 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/9e/f0beffe45b507dca9d7540fad42b316b2fd1076dc484c9b1f23d9da570d7/autopep8-2.3.1-py2.py3-none-any.whl", hash = "sha256:a203fe0fcad7939987422140ab17a930f684763bf7335bdb6709991dd7ef6c2d", size = 45667 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[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 } +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 = "dill" +version = "0.3.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/43/86fe3f9e130c4137b0f1b50784dd70a5087b911fe07fa81e53e0c4c47fea/dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c", size = 187000 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/d1/e73b6ad76f0b1fb7f23c35c6d95dbc506a9c8804f43dda8cb5b0fa6331fd/dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", size = 119418 }, +] + +[[package]] +name = "flake8" +version = "7.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mccabe" }, + { name = "pycodestyle" }, + { name = "pyflakes" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/37/72/e8d66150c4fcace3c0a450466aa3480506ba2cae7b61e100a2613afc3907/flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", size = 48054 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/42/65004373ac4617464f35ed15931b30d764f53cdd30cc78d5aea349c8c050/flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213", size = 57731 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, +] + +[[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 = "mccabe" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, +] + +[[package]] +name = "mypy" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 }, + { url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 }, + { url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 }, + { url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 }, + { url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 }, + { url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 }, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pycodestyle" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/aa/210b2c9aedd8c1cbeea31a50e42050ad56187754b34eb214c46709445801/pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521", size = 39232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/d8/a211b3f85e99a0daa2ddec96c949cac6824bd305b040571b82a03dd62636/pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3", size = 31284 }, +] + +[[package]] +name = "pyflakes" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/f9/669d8c9c86613c9d568757c7f5824bd3197d7b1c6c27553bc5618a27cce2/pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f", size = 63788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/d7/f1b7db88d8e4417c5d47adad627a93547f44bdc9028372dbd2313f34a855/pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a", size = 62725 }, +] + +[[package]] +name = "pylint" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astroid" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "dill" }, + { name = "isort" }, + { name = "mccabe" }, + { name = "platformdirs" }, + { name = "tomlkit" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/d8/4471b2cb4ad18b4af717918c468209bd2bd5a02c52f60be5ee8a71b5af2c/pylint-3.3.2.tar.gz", hash = "sha256:9ec054ec992cd05ad30a6df1676229739a73f8feeabf3912c995d17601052b01", size = 1516485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/55/5eaf6c415f6ddb09b9b039278823a8e27fb81ea7a34ec80c6d9223b17f2e/pylint-3.3.2-py3-none-any.whl", hash = "sha256:77f068c287d49b8683cd7c6e624243c74f92890f767f106ffa1ddf3c0a54cb7a", size = 521873 }, +] + +[[package]] +name = "pyperclip" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/23/2f0a3efc4d6a32f3b63cdff36cd398d9701d26cda58e3ab97ac79fb5e60d/pyperclip-1.9.0.tar.gz", hash = "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310", size = 20961 } + +[[package]] +name = "setuptools" +version = "75.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/54/292f26c208734e9a7f067aea4a7e282c080750c4546559b58e2e45413ca0/setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", size = 1337429 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/21/47d163f615df1d30c094f6c8bbb353619274edccf0327b185cc2493c2c33/setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d", size = 1224032 }, +] + +[[package]] +name = "sudden-death" +version = "0.0.1" +source = { virtual = "." } +dependencies = [ + { name = "click" }, + { name = "pyperclip" }, +] + +[package.dev-dependencies] +dev = [ + { name = "autopep8" }, + { name = "flake8" }, + { name = "importlib-metadata" }, + { name = "mypy" }, + { name = "pylint" }, + { name = "setuptools" }, + { name = "toml" }, + { name = "types-toml" }, +] + +[package.metadata] +requires-dist = [ + { name = "click", specifier = "==8.1.7" }, + { name = "pyperclip", specifier = ">=1.5.27" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "autopep8", specifier = "==2.3.1" }, + { name = "flake8", specifier = "==7.1.1" }, + { name = "importlib-metadata", specifier = "==8.5.0" }, + { name = "mypy", specifier = "==1.13.0" }, + { name = "pylint", specifier = "==3.3.2" }, + { name = "setuptools", specifier = "==75.6.0" }, + { name = "toml", specifier = "==0.10.2" }, + { name = "types-toml", specifier = "==0.10.8.20240310" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "tomlkit" +version = "0.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 }, +] + +[[package]] +name = "types-toml" +version = "0.10.8.20240310" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/47/3e4c75042792bff8e90d7991aa5c51812cc668828cc6cce711e97f63a607/types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331", size = 4392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/a2/d32ab58c0b216912638b140ab2170ee4b8644067c293b170e19fba340ccc/types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d", size = 4777 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "zipp" +version = "3.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e9468ebd0bcd6505de3b275e06f202c2cb016e3ff56f/zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4", size = 24545 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, +]