From eb6da27874b0e27984b099cda4aafcc2a494619c Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 07:42:47 +1200 Subject: [PATCH 1/9] Add deptry function which installs deptry via uv as a dev dependency. --- src/usethis/tool/deptry.py | 5 +++++ tests/test_nothing.py | 2 -- tests/usethis/tool/test_deptry.py | 37 +++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/usethis/tool/deptry.py delete mode 100644 tests/test_nothing.py create mode 100644 tests/usethis/tool/test_deptry.py diff --git a/src/usethis/tool/deptry.py b/src/usethis/tool/deptry.py new file mode 100644 index 0000000..026cd9a --- /dev/null +++ b/src/usethis/tool/deptry.py @@ -0,0 +1,5 @@ +import subprocess + + +def deptry() -> None: + subprocess.run(["uv", "add", "--dev", "deptry"], check=True) diff --git a/tests/test_nothing.py b/tests/test_nothing.py deleted file mode 100644 index a8a8219..0000000 --- a/tests/test_nothing.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_pass(): - pass diff --git a/tests/usethis/tool/test_deptry.py b/tests/usethis/tool/test_deptry.py new file mode 100644 index 0000000..0ef0229 --- /dev/null +++ b/tests/usethis/tool/test_deptry.py @@ -0,0 +1,37 @@ +import os +import subprocess +from contextlib import contextmanager +from pathlib import Path +from typing import Generator + +import tomlkit + +from usethis.tool.deptry import deptry + + +@contextmanager +def change_cwd(new_dir: Path) -> Generator[None, None, None]: + """Change the working directory temporarily.""" + old_dir = Path.cwd() + os.chdir(new_dir) + try: + yield + finally: + os.chdir(old_dir) + + +class TestDeptry: + def test_dependency_added(self, tmp_path: Path): + # Arrange + subprocess.run(["uv", "init"], cwd=tmp_path, check=True) + + # Act + with change_cwd(tmp_path): + deptry() + + # Assert + pyproject = tomlkit.parse((tmp_path / "pyproject.toml").read_text()) + dev_deps = pyproject["tool"]["uv"]["dev-dependencies"] + + assert len(dev_deps) == 1 + assert dev_deps[0].startswith("deptry>=") From 5b089d64b7e42158f37052e7450f1c5a7db951cc Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 07:56:31 +1200 Subject: [PATCH 2/9] Add progress message for usethis tool deptry. --- pyproject.toml | 1 + src/usethis/tool/deptry.py | 1 + tests/usethis/tool/test_deptry.py | 33 +++++++++---- uv.lock | 80 +++++++++++++++++++++++++++++-- 4 files changed, 102 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index bdd2b0b..9d09bc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,4 +15,5 @@ dev-dependencies = [ "pytest>=8.3.2", "pytest-md>=0.2.0", "pytest-emoji>=0.2.0", + "pydantic>=2.9.1", ] diff --git a/src/usethis/tool/deptry.py b/src/usethis/tool/deptry.py index 026cd9a..e9fdcfb 100644 --- a/src/usethis/tool/deptry.py +++ b/src/usethis/tool/deptry.py @@ -3,3 +3,4 @@ def deptry() -> None: subprocess.run(["uv", "add", "--dev", "deptry"], check=True) + print("✔ Adding deptry as a development dependency") diff --git a/tests/usethis/tool/test_deptry.py b/tests/usethis/tool/test_deptry.py index 0ef0229..db89a40 100644 --- a/tests/usethis/tool/test_deptry.py +++ b/tests/usethis/tool/test_deptry.py @@ -4,7 +4,9 @@ from pathlib import Path from typing import Generator +import pytest import tomlkit +from pydantic import TypeAdapter from usethis.tool.deptry import deptry @@ -20,18 +22,33 @@ def change_cwd(new_dir: Path) -> Generator[None, None, None]: os.chdir(old_dir) +@pytest.fixture +def uv_init_dir(tmp_path: Path) -> None: + subprocess.run(["uv", "init"], cwd=tmp_path, check=True) + return tmp_path + + class TestDeptry: - def test_dependency_added(self, tmp_path: Path): - # Arrange - subprocess.run(["uv", "init"], cwd=tmp_path, check=True) + def test_dependency_added(self, uv_init_dir: Path): + # Act + with change_cwd(uv_init_dir): + deptry() + # Assert + (dev_dep,) = _get_dev_deps(uv_init_dir) + assert dev_dep.startswith("deptry>=") + + def test_stdout(self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str]): # Act - with change_cwd(tmp_path): + with change_cwd(uv_init_dir): deptry() # Assert - pyproject = tomlkit.parse((tmp_path / "pyproject.toml").read_text()) - dev_deps = pyproject["tool"]["uv"]["dev-dependencies"] + out, _ = capfd.readouterr() + assert out == "✔ Adding deptry as a development dependency\n" + - assert len(dev_deps) == 1 - assert dev_deps[0].startswith("deptry>=") +def _get_dev_deps(proj_dir: Path) -> list[str]: + pyproject = tomlkit.parse((proj_dir / "pyproject.toml").read_text()) + dev_deps = pyproject["tool"]["uv"]["dev-dependencies"] + return TypeAdapter(list[str]).validate_python(dev_deps) diff --git a/uv.lock b/uv.lock index 74a71a4..4b0ae7c 100644 --- a/uv.lock +++ b/uv.lock @@ -1,8 +1,18 @@ version = 1 requires-python = ">=3.12" +resolution-markers = [ + "python_full_version < '3.13'", + "python_full_version >= '3.13'", +] -[options] -resolution-mode = "lowest-direct" +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] [[package]] name = "colorama" @@ -40,9 +50,58 @@ 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 = "pydantic" +version = "2.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/14/15/3d989541b9c8128b96d532cfd2dd10131ddcc75a807330c00feb3d42a5bd/pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2", size = 768511 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/28/fff23284071bc1ba419635c7e86561c8b9b8cf62a5bcb459b92d7625fd38/pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612", size = 434363 }, +] + +[[package]] +name = "pydantic-core" +version = "2.23.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/cc/07bec3fb337ff80eacd6028745bd858b9642f61ee58cfdbfb64451c1def0/pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690", size = 402277 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/35/6d81bc4aa7d06e716f39e2bffb0eabcbcebaf7bab94c2f8278e277ded0ea/pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305", size = 1845250 }, + { url = "https://files.pythonhosted.org/packages/18/42/0821cd46f76406e0fe57df7a89d6af8fddb22cce755bcc2db077773c7d1a/pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb", size = 1769993 }, + { url = "https://files.pythonhosted.org/packages/e5/55/b969088e48bd8ea588548a7194d425de74370b17b385cee4d28f5a79013d/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa", size = 1791250 }, + { url = "https://files.pythonhosted.org/packages/43/c1/1d460d09c012ac76b68b2a1fd426ad624724f93b40e24a9a993763f12c61/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162", size = 1802530 }, + { url = "https://files.pythonhosted.org/packages/70/8e/fd3c9eda00fbdadca726f17a0f863ecd871a65b3a381b77277ae386d3bcd/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801", size = 1997848 }, + { url = "https://files.pythonhosted.org/packages/f0/67/13fa22d7b09395e83721edc31bae2bd5c5e2c36a09d470c18f5d1de46958/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb", size = 2662790 }, + { url = "https://files.pythonhosted.org/packages/fa/1b/1d689c53d15ab67cb0df1c3a2b1df873b50409581e93e4848289dce57e2f/pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326", size = 2074114 }, + { url = "https://files.pythonhosted.org/packages/3d/d9/b565048609db77760b9a0900f6e0a3b2f33be47cd3c4a433f49653a0d2b5/pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c", size = 1918153 }, + { url = "https://files.pythonhosted.org/packages/41/94/8ee55c51333ed8df3a6f1e73c6530c724a9a37d326e114c9e3b24faacff9/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c", size = 1969019 }, + { url = "https://files.pythonhosted.org/packages/f7/49/0233bae5778a5526cef000447a93e8d462f4f13e2214c13c5b23d379cb25/pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab", size = 2121325 }, + { url = "https://files.pythonhosted.org/packages/42/a1/2f262db2fd6f9c2c9904075a067b1764cc6f71c014be5c6c91d9de52c434/pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c", size = 1725252 }, + { url = "https://files.pythonhosted.org/packages/9a/00/a57937080b49500df790c4853d3e7bc605bd0784e4fcaf1a159456f37ef1/pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b", size = 1920660 }, + { url = "https://files.pythonhosted.org/packages/e1/3c/32958c0a5d1935591b58337037a1695782e61261582d93d5a7f55441f879/pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f", size = 1845068 }, + { url = "https://files.pythonhosted.org/packages/92/a1/7e628e19b78e6ffdb2c92cccbb7eca84bfd3276cee4cafcae8833452f458/pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2", size = 1770095 }, + { url = "https://files.pythonhosted.org/packages/bb/17/d15fd8ce143cd1abb27be924eeff3c5c0fe3b0582f703c5a5273c11e67ce/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791", size = 1790964 }, + { url = "https://files.pythonhosted.org/packages/24/cc/37feff1792f09dc33207fbad3897373229279d1973c211f9562abfdf137d/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423", size = 1802384 }, + { url = "https://files.pythonhosted.org/packages/44/d8/ca9acd7f5f044d9ff6e43d7f35aab4b1d5982b4773761eabe3317fc68e30/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63", size = 1997824 }, + { url = "https://files.pythonhosted.org/packages/35/0f/146269dba21b10d5bf86f9a7a7bbeab4ce1db06f466a1ab5ec3dec68b409/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9", size = 2662907 }, + { url = "https://files.pythonhosted.org/packages/5a/7d/9573f006e39cd1a7b7716d1a264e3f4f353cf0a6042c04c01c6e31666f62/pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5", size = 2073953 }, + { url = "https://files.pythonhosted.org/packages/7e/a5/25200aaafd1e97e2ec3c1eb4b357669dd93911f2eba252bc60b6ba884fff/pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855", size = 1917822 }, + { url = "https://files.pythonhosted.org/packages/3e/b4/ac069c58e3cee70c69f03693222cc173fdf740d20d53167bceafc1efc7ca/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4", size = 1968838 }, + { url = "https://files.pythonhosted.org/packages/d1/3d/9f96bbd6212b4b0a6dc6d037e446208d3420baba2b2b81e544094b18a859/pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d", size = 2121468 }, + { url = "https://files.pythonhosted.org/packages/ac/50/7399d536d6600d69059a87fff89861332c97a7b3471327a3663c7576e707/pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8", size = 1725373 }, + { url = "https://files.pythonhosted.org/packages/24/ba/9ac8744ab636c1161c598cc5e8261379b6b0f1d63c31242bf9d5ed41ed32/pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1", size = 1920594 }, +] + [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -50,9 +109,9 @@ dependencies = [ { name = "packaging" }, { name = "pluggy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b4/8c/9862305bdcd6020bc7b45b1b5e7397a6caf1a33d3025b9a003b39075ffb2/pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce", size = 1439314 } +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/f9/cf155cf32ca7d6fa3601bc4c5dd19086af4b320b706919d48a4c79081cf9/pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5", size = 341802 }, + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, ] [[package]] @@ -79,6 +138,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/71/23d03f57c18116c6770141478e33b3500c4e92500cf4b49a396e9226733f/pytest_md-0.2.0-py3-none-any.whl", hash = "sha256:4c4cd16fea6d1485e87ee254558712c804a96d2aa9674b780e7eb8fb6526e1d1", size = 6117 }, ] +[[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 = "usethis" version = "0.1.0" @@ -86,6 +154,7 @@ source = { editable = "." } [package.dev-dependencies] dev = [ + { name = "pydantic" }, { name = "pytest" }, { name = "pytest-emoji" }, { name = "pytest-md" }, @@ -95,6 +164,7 @@ dev = [ [package.metadata.requires-dev] dev = [ + { name = "pydantic", specifier = ">=2.9.1" }, { name = "pytest", specifier = ">=8.3.2" }, { name = "pytest-emoji", specifier = ">=0.2.0" }, { name = "pytest-md", specifier = ">=0.2.0" }, From 9a76bb2980569f275027df0425e07f1bdead1cc0 Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 08:07:39 +1200 Subject: [PATCH 3/9] Add tests for running deptry after calling `usethis tool deptry`. --- tests/usethis/tool/test_deptry.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/usethis/tool/test_deptry.py b/tests/usethis/tool/test_deptry.py index db89a40..a6fae6c 100644 --- a/tests/usethis/tool/test_deptry.py +++ b/tests/usethis/tool/test_deptry.py @@ -47,6 +47,31 @@ def test_stdout(self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str]): out, _ = capfd.readouterr() assert out == "✔ Adding deptry as a development dependency\n" + def test_run_deptry_fail(self, uv_init_dir: Path): + # Arrange + f = uv_init_dir / "bad.py" + f.write_text("import broken_dependency") + + # Act + with change_cwd(uv_init_dir): + deptry() + + # Assert + with pytest.raises(subprocess.CalledProcessError): + subprocess.run(["deptry", "."], cwd=uv_init_dir, check=True) + + def test_run_deptry_pass(self, uv_init_dir: Path): + # Arrange + f = uv_init_dir / "good.py" + f.write_text("import sys") + + # Act + with change_cwd(uv_init_dir): + deptry() + + # Assert + subprocess.run(["deptry", "."], cwd=uv_init_dir, check=True) + def _get_dev_deps(proj_dir: Path) -> list[str]: pyproject = tomlkit.parse((proj_dir / "pyproject.toml").read_text()) From cb6e8d5831e026c6d7806fba6789a887f53b407e Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 08:16:28 +1200 Subject: [PATCH 4/9] Remove hello function. --- src/usethis/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/usethis/__init__.py b/src/usethis/__init__.py index f17db58..e69de29 100644 --- a/src/usethis/__init__.py +++ b/src/usethis/__init__.py @@ -1,2 +0,0 @@ -def hello() -> str: - return "Hello from usethis!" From d7a77ea35886ad0daf5a949febde36472c4fb9ea Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 08:37:32 +1200 Subject: [PATCH 5/9] Configure the package as a CLI app using typer. --- pyproject.toml | 9 +- src/usethis/__main__.py | 12 ++ src/usethis/tool.py | 13 ++ src/usethis/tool/deptry.py | 6 - .../{tool/test_deptry.py => test_tool.py} | 5 +- uv.lock | 114 ++++++++++++++++++ 6 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 src/usethis/__main__.py create mode 100644 src/usethis/tool.py delete mode 100644 src/usethis/tool/deptry.py rename tests/usethis/{tool/test_deptry.py => test_tool.py} (92%) diff --git a/pyproject.toml b/pyproject.toml index 9d09bc8..4f0ae37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,12 @@ version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" -dependencies = [] +dependencies = [ + "typer>=0.12.5", +] + +[project.scripts] +usethis = "usethis.__main__:app" [build-system] requires = ["hatchling"] @@ -16,4 +21,6 @@ dev-dependencies = [ "pytest-md>=0.2.0", "pytest-emoji>=0.2.0", "pydantic>=2.9.1", + "tomlkit>=0.13.2", + "deptry>=0.20.0", ] diff --git a/src/usethis/__main__.py b/src/usethis/__main__.py new file mode 100644 index 0000000..950760c --- /dev/null +++ b/src/usethis/__main__.py @@ -0,0 +1,12 @@ +import typer + +import usethis.tool + +app = typer.Typer( + help=( + "🤖 Automate Python package and project setup tasks that are otherwise " + "performed manually." + ) +) +app.add_typer(usethis.tool.app, name="tool") +app(prog_name="usethis") diff --git a/src/usethis/tool.py b/src/usethis/tool.py new file mode 100644 index 0000000..7097562 --- /dev/null +++ b/src/usethis/tool.py @@ -0,0 +1,13 @@ +import subprocess + +import typer + +app = typer.Typer(help="Add and configure development tools, e.g. linters") + + +@app.command( + help="Use the deptry linter: avoid missing or superfluous dependency declarations." +) +def deptry() -> None: + print("✔ Adding deptry as a development dependency") + subprocess.run(["uv", "add", "--dev", "--quiet", "deptry"], check=True) diff --git a/src/usethis/tool/deptry.py b/src/usethis/tool/deptry.py deleted file mode 100644 index e9fdcfb..0000000 --- a/src/usethis/tool/deptry.py +++ /dev/null @@ -1,6 +0,0 @@ -import subprocess - - -def deptry() -> None: - subprocess.run(["uv", "add", "--dev", "deptry"], check=True) - print("✔ Adding deptry as a development dependency") diff --git a/tests/usethis/tool/test_deptry.py b/tests/usethis/test_tool.py similarity index 92% rename from tests/usethis/tool/test_deptry.py rename to tests/usethis/test_tool.py index a6fae6c..633b27c 100644 --- a/tests/usethis/tool/test_deptry.py +++ b/tests/usethis/test_tool.py @@ -8,7 +8,7 @@ import tomlkit from pydantic import TypeAdapter -from usethis.tool.deptry import deptry +from usethis.tool import deptry @contextmanager @@ -72,6 +72,9 @@ def test_run_deptry_pass(self, uv_init_dir: Path): # Assert subprocess.run(["deptry", "."], cwd=uv_init_dir, check=True) + def test_cli(self, uv_init_dir: Path): + subprocess.run(["usethis", "tool", "deptry"], cwd=uv_init_dir, check=True) + def _get_dev_deps(proj_dir: Path) -> list[str]: pyproject = tomlkit.parse((proj_dir / "pyproject.toml").read_text()) diff --git a/uv.lock b/uv.lock index 4b0ae7c..b3ef71d 100644 --- a/uv.lock +++ b/uv.lock @@ -14,6 +14,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, ] +[[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" @@ -23,6 +35,24 @@ 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 = "deptry" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/9e/7a976d923d3ae18d7dc4ace8e0c83e20a847828196e7f4b13a4bf6b03b50/deptry-0.20.0.tar.gz", hash = "sha256:62e9aaf3aea9e2ca66c85da98a0ba0290b4d3daea4e1d0ad937d447bd3c36402", size = 129936 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/da/c94ebc2192a29a6f45acb5b87fdb31d1b84843154572d9b88100b7047eda/deptry-0.20.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:41434d95124851b83cb05524d1a09ad6fea62006beafed2ef90a6b501c1b237f", size = 1624964 }, + { url = "https://files.pythonhosted.org/packages/98/8e/08f7b33b384a7981b27de5aa3def41b6fa691aa692904910dc1f5bd1fc02/deptry-0.20.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:b3b4b22d1406147de5d606a24042126cd74d52fdfdb0232b9c5fd0270d601610", size = 1545726 }, + { url = "https://files.pythonhosted.org/packages/55/47/8e813609a4ba6c75032bd3468f9edcad31e11906eafd0a1e5a3f3f837fba/deptry-0.20.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012fb106dbea6ca95196cdcd75ac90c516c8f01292f7934f2e802a7cf025a660", size = 1676818 }, + { url = "https://files.pythonhosted.org/packages/b4/70/456d976912c6026252034c0cdb37a3cbad34ac0ce815763466720c63aece/deptry-0.20.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ce3920e2bd6d2b4427ab31ab8efb94bbef897001c2d395782bc30002966d12d", size = 1708051 }, + { url = "https://files.pythonhosted.org/packages/ff/66/95e04a84120861b0c0ac980999e6172612509d5ff9a84b41e2f71cc3c3c0/deptry-0.20.0-cp38-abi3-win_amd64.whl", hash = "sha256:0c90ce64e637d0e902bc97c5a020adecfee9e9f09ee0bf4c61554994139bebdb", size = 1493281 }, + { url = "https://files.pythonhosted.org/packages/53/c9/9d7d86b5fdc452b522ef16df9e27c8404dc6f231fa865a3af31c1dab7563/deptry-0.20.0-cp38-abi3-win_arm64.whl", hash = "sha256:6886ff44aaf26fd83093f14f844ebc84589d90df9bbad9a1625e8a080e6f1be2", size = 1420087 }, +] + [[package]] name = "iniconfig" version = "2.0.0" @@ -32,6 +62,27 @@ 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 = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + [[package]] name = "packaging" version = "24.1" @@ -99,6 +150,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/24/ba/9ac8744ab636c1161c598cc5e8261379b6b0f1d63c31242bf9d5ed41ed32/pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1", size = 1920594 }, ] +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + [[package]] name = "pytest" version = "8.3.3" @@ -138,6 +198,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/71/23d03f57c18116c6770141478e33b3500c4e92500cf4b49a396e9226733f/pytest_md-0.2.0-py3-none-any.whl", hash = "sha256:4c4cd16fea6d1485e87ee254558712c804a96d2aa9674b780e7eb8fb6526e1d1", size = 6117 }, ] +[[package]] +name = "rich" +version = "13.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/76/40f084cb7db51c9d1fa29a7120717892aeda9a7711f6225692c957a93535/rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a", size = 222080 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/11/dadb85e2bd6b1f1ae56669c3e1f0410797f9605d752d68fb47b77f525b31/rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06", size = 241608 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[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 = "typer" +version = "0.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/58/a79003b91ac2c6890fc5d90145c662fd5771c6f11447f116b63300436bc9/typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722", size = 98953 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/2b/886d13e742e514f704c33c4caa7df0f3b89e5a25ef8db02aa9ca3d9535d5/typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b", size = 47288 }, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -151,21 +257,29 @@ wheels = [ name = "usethis" version = "0.1.0" source = { editable = "." } +dependencies = [ + { name = "typer" }, +] [package.dev-dependencies] dev = [ + { name = "deptry" }, { name = "pydantic" }, { name = "pytest" }, { name = "pytest-emoji" }, { name = "pytest-md" }, + { name = "tomlkit" }, ] [package.metadata] +requires-dist = [{ name = "typer", specifier = ">=0.12.5" }] [package.metadata.requires-dev] dev = [ + { name = "deptry", specifier = ">=0.20.0" }, { name = "pydantic", specifier = ">=2.9.1" }, { name = "pytest", specifier = ">=8.3.2" }, { name = "pytest-emoji", specifier = ">=0.2.0" }, { name = "pytest-md", specifier = ">=0.2.0" }, + { name = "tomlkit", specifier = ">=0.13.2" }, ] From 7eb078b553ff13fb995a8907eab458980c1e2f92 Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 08:39:16 +1200 Subject: [PATCH 6/9] Reword message for usethis tool deptry --- tests/usethis/test_tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/usethis/test_tool.py b/tests/usethis/test_tool.py index 633b27c..352ffec 100644 --- a/tests/usethis/test_tool.py +++ b/tests/usethis/test_tool.py @@ -45,7 +45,7 @@ def test_stdout(self, uv_init_dir: Path, capfd: pytest.CaptureFixture[str]): # Assert out, _ = capfd.readouterr() - assert out == "✔ Adding deptry as a development dependency\n" + assert out == "✔ Ensuring deptry is a development dependency\n" def test_run_deptry_fail(self, uv_init_dir: Path): # Arrange From 0d0d235074433c87ab8d4d2cd7427a1ecb13cb31 Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 08:41:28 +1200 Subject: [PATCH 7/9] Reword message for usethis tool deptry --- src/usethis/tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usethis/tool.py b/src/usethis/tool.py index 7097562..c80bf98 100644 --- a/src/usethis/tool.py +++ b/src/usethis/tool.py @@ -9,5 +9,5 @@ help="Use the deptry linter: avoid missing or superfluous dependency declarations." ) def deptry() -> None: - print("✔ Adding deptry as a development dependency") + print("✔ Ensuring deptry is a development dependency") subprocess.run(["uv", "add", "--dev", "--quiet", "deptry"], check=True) From 54bfed1b2a9e5e7e0da5129a8ab123ee8d5978ca Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 09:13:29 +1200 Subject: [PATCH 8/9] Use rich for console output --- pyproject.toml | 1 + src/usethis/__init__.py | 3 +++ src/usethis/tool.py | 4 +++- uv.lock | 6 +++++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4f0ae37..9db7284 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [ + "rich>=13.8.1", "typer>=0.12.5", ] diff --git a/src/usethis/__init__.py b/src/usethis/__init__.py index e69de29..a9463af 100644 --- a/src/usethis/__init__.py +++ b/src/usethis/__init__.py @@ -0,0 +1,3 @@ +from rich.console import Console + +console = Console() diff --git a/src/usethis/tool.py b/src/usethis/tool.py index c80bf98..28f76e8 100644 --- a/src/usethis/tool.py +++ b/src/usethis/tool.py @@ -2,6 +2,8 @@ import typer +from usethis import console + app = typer.Typer(help="Add and configure development tools, e.g. linters") @@ -9,5 +11,5 @@ help="Use the deptry linter: avoid missing or superfluous dependency declarations." ) def deptry() -> None: - print("✔ Ensuring deptry is a development dependency") + console.print("✔ Ensuring deptry is a development dependency", style="green") subprocess.run(["uv", "add", "--dev", "--quiet", "deptry"], check=True) diff --git a/uv.lock b/uv.lock index b3ef71d..461173b 100644 --- a/uv.lock +++ b/uv.lock @@ -258,6 +258,7 @@ name = "usethis" version = "0.1.0" source = { editable = "." } dependencies = [ + { name = "rich" }, { name = "typer" }, ] @@ -272,7 +273,10 @@ dev = [ ] [package.metadata] -requires-dist = [{ name = "typer", specifier = ">=0.12.5" }] +requires-dist = [ + { name = "rich", specifier = ">=13.8.1" }, + { name = "typer", specifier = ">=0.12.5" }, +] [package.metadata.requires-dev] dev = [ From f873ec40bbbe35a6ec667aef7f71b6138fd2e50f Mon Sep 17 00:00:00 2001 From: Nathan McDougall Date: Fri, 13 Sep 2024 09:46:29 +1200 Subject: [PATCH 9/9] Set PYTHONIOENCODING explicitly in CI config. --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41ce261..c36cec6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,8 @@ on: jobs: tests: runs-on: ${{ matrix.os }} + env: + PYTHONIOENCODING: utf-8 steps: - name: Checkout code uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7