From c70003020cce8fd07b81531f1689fb2f43d27e5a Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 17 Feb 2025 11:00:18 -0600 Subject: [PATCH 01/26] tests(ci) Check runtime deps import correctly --- .github/workflows/tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 44c1a4ce6..affbc0b0e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,6 +23,15 @@ jobs: - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} + - name: Test runtime dependencies + run: | + uv run --no-dev -p python${{ matrix.python-version }} -- python -c ' + from libtmux import common, constants, exc, formats, neo, pane, server, session, window, __version__ + server = server.Server() + print("libtmux version:", __version__) + print("libtmux Server:", server) + ' + - name: Install dependencies run: uv sync --all-extras --dev From f66abaf6837d7455e341e888a1cf0e203c837aa3 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 17 Feb 2025 11:19:41 -0600 Subject: [PATCH 02/26] docs(CHANGES) Note CI checking for runtime deps --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 320670151..eb74dc809 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,13 @@ $ pip install --user --upgrade --pre libtmux - _Future release notes will be placed here_ +### Development + +- CI: Check for runtime dependencies (#574) + + Kudos @ppentchev for inspiration on the command + ([comment](https://github.com/tmux-python/libtmux/pull/572#issuecomment-2663642923)). + ## libtmux 0.44.2 (2025-02-17) ### Bug fix From 572d4a48ead7e866ca6412208e2680bd4a7bb768 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 17 Feb 2025 15:02:55 -0600 Subject: [PATCH 03/26] .tool-versions(uv) uv 0.6.0 -> 0.6.1 See also: - uv: - https://github.com/astral-sh/uv/releases/tag/0.6.1 - https://github.com/astral-sh/uv/blob/0.6.1/CHANGELOG.md --- .tool-versions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.tool-versions b/.tool-versions index 5a3a79a5a..ff78fd6a6 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -uv 0.6.0 +uv 0.6.1 python 3.13.2 3.12.9 3.11.11 3.10.16 3.9.21 3.8.20 3.7.17 From 3f8d1454ffc761f52c5e0e6c3d6facdd9311b5f9 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Fri, 21 Feb 2025 20:02:18 -0600 Subject: [PATCH 04/26] py(deps[dev]) Bump dev packages --- uv.lock | 180 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 125 insertions(+), 55 deletions(-) diff --git a/uv.lock b/uv.lock index 10fc21123..44541399d 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,8 @@ version = 1 revision = 1 requires-python = ">=3.9, <4.0" resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] @@ -23,7 +24,8 @@ name = "alabaster" version = "1.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } wheels = [ @@ -294,7 +296,8 @@ dependencies = [ { name = "beautifulsoup4" }, { name = "pygments" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-basic-ng" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a0/e2/d351d69a9a9e4badb4a5be062c2d0e87bd9e6c23b5e57337fef14bef34c8/furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01", size = 1661506 } @@ -404,10 +407,12 @@ dev = [ { name = "pytest-xdist" }, { name = "ruff" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-autobuild" }, { name = "sphinx-autodoc-typehints", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx-autodoc-typehints", version = "3.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-copybutton" }, { name = "sphinx-inline-tabs" }, { name = "sphinxext-opengraph" }, @@ -421,10 +426,12 @@ docs = [ { name = "myst-parser", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-autobuild" }, { name = "sphinx-autodoc-typehints", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx-autodoc-typehints", version = "3.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-copybutton" }, { name = "sphinx-inline-tabs" }, { name = "sphinxext-opengraph" }, @@ -694,7 +701,8 @@ name = "myst-parser" version = "4.0.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "docutils", marker = "python_full_version >= '3.10'" }, @@ -702,7 +710,8 @@ dependencies = [ { name = "markdown-it-py", marker = "python_full_version >= '3.10'" }, { name = "mdit-py-plugins", marker = "python_full_version >= '3.10'" }, { name = "pyyaml", marker = "python_full_version >= '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985 } wheels = [ @@ -885,29 +894,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, ] +[[package]] +name = "roman-numerals-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/78/9491ab144c9cb2d97aa74d6f632bd6f4be67957de03f945a23a67415d859/roman_numerals_py-3.0.0.tar.gz", hash = "sha256:91199c4373658c03d87d9fe004f4a5120a20f6cb192be745c2377cce274ef41c", size = 8970 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/d0/a3a2fed015e95b9e81619182adc472540f9786183febfaef8b7c5e909418/roman_numerals_py-3.0.0-py3-none-any.whl", hash = "sha256:a1421ce66b3eab7e8735065458de3fa5c4a46263d50f9f4ac8f0e5e7701dd125", size = 4416 }, +] + [[package]] name = "ruff" -version = "0.9.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/e1/e265aba384343dd8ddd3083f5e33536cd17e1566c41453a5517b5dd443be/ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9", size = 3639454 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/e3/3d2c022e687e18cf5d93d6bfa2722d46afc64eaa438c7fbbdd603b3597be/ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba", size = 11714128 }, - { url = "https://files.pythonhosted.org/packages/e1/22/aff073b70f95c052e5c58153cba735748c9e70107a77d03420d7850710a0/ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504", size = 11682539 }, - { url = "https://files.pythonhosted.org/packages/75/a7/f5b7390afd98a7918582a3d256cd3e78ba0a26165a467c1820084587cbf9/ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83", size = 11132512 }, - { url = "https://files.pythonhosted.org/packages/a6/e3/45de13ef65047fea2e33f7e573d848206e15c715e5cd56095589a7733d04/ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc", size = 11929275 }, - { url = "https://files.pythonhosted.org/packages/7d/f2/23d04cd6c43b2e641ab961ade8d0b5edb212ecebd112506188c91f2a6e6c/ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b", size = 11466502 }, - { url = "https://files.pythonhosted.org/packages/b5/6f/3a8cf166f2d7f1627dd2201e6cbc4cb81f8b7d58099348f0c1ff7b733792/ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e", size = 12676364 }, - { url = "https://files.pythonhosted.org/packages/f5/c4/db52e2189983c70114ff2b7e3997e48c8318af44fe83e1ce9517570a50c6/ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666", size = 13335518 }, - { url = "https://files.pythonhosted.org/packages/66/44/545f8a4d136830f08f4d24324e7db957c5374bf3a3f7a6c0bc7be4623a37/ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5", size = 12823287 }, - { url = "https://files.pythonhosted.org/packages/c5/26/8208ef9ee7431032c143649a9967c3ae1aae4257d95e6f8519f07309aa66/ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5", size = 14592374 }, - { url = "https://files.pythonhosted.org/packages/31/70/e917781e55ff39c5b5208bda384fd397ffd76605e68544d71a7e40944945/ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217", size = 12500173 }, - { url = "https://files.pythonhosted.org/packages/84/f5/e4ddee07660f5a9622a9c2b639afd8f3104988dc4f6ba0b73ffacffa9a8c/ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6", size = 11906555 }, - { url = "https://files.pythonhosted.org/packages/f1/2b/6ff2fe383667075eef8656b9892e73dd9b119b5e3add51298628b87f6429/ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897", size = 11538958 }, - { url = "https://files.pythonhosted.org/packages/3c/db/98e59e90de45d1eb46649151c10a062d5707b5b7f76f64eb1e29edf6ebb1/ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08", size = 12117247 }, - { url = "https://files.pythonhosted.org/packages/ec/bc/54e38f6d219013a9204a5a2015c09e7a8c36cedcd50a4b01ac69a550b9d9/ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656", size = 12554647 }, - { url = "https://files.pythonhosted.org/packages/a5/7d/7b461ab0e2404293c0627125bb70ac642c2e8d55bf590f6fce85f508f1b2/ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d", size = 9949214 }, - { url = "https://files.pythonhosted.org/packages/ee/30/c3cee10f915ed75a5c29c1e57311282d1a15855551a64795c1b2bbe5cf37/ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa", size = 10999914 }, - { url = "https://files.pythonhosted.org/packages/e8/a8/d71f44b93e3aa86ae232af1f2126ca7b95c0f515ec135462b3e1f351441c/ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a", size = 10177499 }, +version = "0.9.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/8b/a86c300359861b186f18359adf4437ac8e4c52e42daa9eedc731ef9d5b53/ruff-0.9.7.tar.gz", hash = "sha256:643757633417907510157b206e490c3aa11cab0c087c912f60e07fbafa87a4c6", size = 3669813 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/f3/3a1d22973291226df4b4e2ff70196b926b6f910c488479adb0eeb42a0d7f/ruff-0.9.7-py3-none-linux_armv6l.whl", hash = "sha256:99d50def47305fe6f233eb8dabfd60047578ca87c9dcb235c9723ab1175180f4", size = 11774588 }, + { url = "https://files.pythonhosted.org/packages/8e/c9/b881f4157b9b884f2994fd08ee92ae3663fb24e34b0372ac3af999aa7fc6/ruff-0.9.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:d59105ae9c44152c3d40a9c40d6331a7acd1cdf5ef404fbe31178a77b174ea66", size = 11746848 }, + { url = "https://files.pythonhosted.org/packages/14/89/2f546c133f73886ed50a3d449e6bf4af27d92d2f960a43a93d89353f0945/ruff-0.9.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f313b5800483770bd540cddac7c90fc46f895f427b7820f18fe1822697f1fec9", size = 11177525 }, + { url = "https://files.pythonhosted.org/packages/d7/93/6b98f2c12bf28ab9def59c50c9c49508519c5b5cfecca6de871cf01237f6/ruff-0.9.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042ae32b41343888f59c0a4148f103208bf6b21c90118d51dc93a68366f4e903", size = 11996580 }, + { url = "https://files.pythonhosted.org/packages/8e/3f/b3fcaf4f6d875e679ac2b71a72f6691a8128ea3cb7be07cbb249f477c061/ruff-0.9.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:87862589373b33cc484b10831004e5e5ec47dc10d2b41ba770e837d4f429d721", size = 11525674 }, + { url = "https://files.pythonhosted.org/packages/f0/48/33fbf18defb74d624535d5d22adcb09a64c9bbabfa755bc666189a6b2210/ruff-0.9.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a17e1e01bee0926d351a1ee9bc15c445beae888f90069a6192a07a84af544b6b", size = 12739151 }, + { url = "https://files.pythonhosted.org/packages/63/b5/7e161080c5e19fa69495cbab7c00975ef8a90f3679caa6164921d7f52f4a/ruff-0.9.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7c1f880ac5b2cbebd58b8ebde57069a374865c73f3bf41f05fe7a179c1c8ef22", size = 13416128 }, + { url = "https://files.pythonhosted.org/packages/4e/c8/b5e7d61fb1c1b26f271ac301ff6d9de5e4d9a9a63f67d732fa8f200f0c88/ruff-0.9.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e63fc20143c291cab2841dbb8260e96bafbe1ba13fd3d60d28be2c71e312da49", size = 12870858 }, + { url = "https://files.pythonhosted.org/packages/da/cb/2a1a8e4e291a54d28259f8fc6a674cd5b8833e93852c7ef5de436d6ed729/ruff-0.9.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91ff963baed3e9a6a4eba2a02f4ca8eaa6eba1cc0521aec0987da8d62f53cbef", size = 14786046 }, + { url = "https://files.pythonhosted.org/packages/ca/6c/c8f8a313be1943f333f376d79724260da5701426c0905762e3ddb389e3f4/ruff-0.9.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88362e3227c82f63eaebf0b2eff5b88990280fb1ecf7105523883ba8c3aaf6fb", size = 12550834 }, + { url = "https://files.pythonhosted.org/packages/9d/ad/f70cf5e8e7c52a25e166bdc84c082163c9c6f82a073f654c321b4dff9660/ruff-0.9.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0372c5a90349f00212270421fe91874b866fd3626eb3b397ede06cd385f6f7e0", size = 11961307 }, + { url = "https://files.pythonhosted.org/packages/52/d5/4f303ea94a5f4f454daf4d02671b1fbfe2a318b5fcd009f957466f936c50/ruff-0.9.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d76b8ab60e99e6424cd9d3d923274a1324aefce04f8ea537136b8398bbae0a62", size = 11612039 }, + { url = "https://files.pythonhosted.org/packages/eb/c8/bd12a23a75603c704ce86723be0648ba3d4ecc2af07eecd2e9fa112f7e19/ruff-0.9.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0c439bdfc8983e1336577f00e09a4e7a78944fe01e4ea7fe616d00c3ec69a3d0", size = 12168177 }, + { url = "https://files.pythonhosted.org/packages/cc/57/d648d4f73400fef047d62d464d1a14591f2e6b3d4a15e93e23a53c20705d/ruff-0.9.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:115d1f15e8fdd445a7b4dc9a30abae22de3f6bcabeb503964904471691ef7606", size = 12610122 }, + { url = "https://files.pythonhosted.org/packages/49/79/acbc1edd03ac0e2a04ae2593555dbc9990b34090a9729a0c4c0cf20fb595/ruff-0.9.7-py3-none-win32.whl", hash = "sha256:e9ece95b7de5923cbf38893f066ed2872be2f2f477ba94f826c8defdd6ec6b7d", size = 9988751 }, + { url = "https://files.pythonhosted.org/packages/6d/95/67153a838c6b6ba7a2401241fd8a00cd8c627a8e4a0491b8d853dedeffe0/ruff-0.9.7-py3-none-win_amd64.whl", hash = "sha256:3770fe52b9d691a15f0b87ada29c45324b2ace8f01200fb0c14845e499eb0c2c", size = 11002987 }, + { url = "https://files.pythonhosted.org/packages/63/6a/aca01554949f3a401991dc32fe22837baeaccb8a0d868256cbb26a029778/ruff-0.9.7-py3-none-win_arm64.whl", hash = "sha256:b075a700b2533feb7a01130ff656a4ec0d5f340bb540ad98759b8401c32c2037", size = 10177763 }, ] [[package]] @@ -974,25 +992,25 @@ name = "sphinx" version = "8.1.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version == '3.10.*'", ] dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "babel", marker = "python_full_version >= '3.10'" }, - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.10'" }, - { name = "imagesize", marker = "python_full_version >= '3.10'" }, - { name = "jinja2", marker = "python_full_version >= '3.10'" }, - { name = "packaging", marker = "python_full_version >= '3.10'" }, - { name = "pygments", marker = "python_full_version >= '3.10'" }, - { name = "requests", marker = "python_full_version >= '3.10'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.10'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.10'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.10'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.10'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.10'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.10'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.10'" }, + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "babel", marker = "python_full_version == '3.10.*'" }, + { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version == '3.10.*'" }, + { name = "imagesize", marker = "python_full_version == '3.10.*'" }, + { name = "jinja2", marker = "python_full_version == '3.10.*'" }, + { name = "packaging", marker = "python_full_version == '3.10.*'" }, + { name = "pygments", marker = "python_full_version == '3.10.*'" }, + { name = "requests", marker = "python_full_version == '3.10.*'" }, + { name = "snowballstemmer", marker = "python_full_version == '3.10.*'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*'" }, { name = "tomli", marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } @@ -1000,6 +1018,37 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, ] +[[package]] +name = "sphinx" +version = "8.2.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "babel", marker = "python_full_version >= '3.11'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "imagesize", marker = "python_full_version >= '3.11'" }, + { name = "jinja2", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "requests", marker = "python_full_version >= '3.11'" }, + { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/4b/95bdb36eaee30698f2d244d52e1b9e58642af56525d4b02fcd0f7312c27c/sphinx-8.2.1.tar.gz", hash = "sha256:e4b932951b9c18b039f73b72e4e63afe967d90408700ec222b981ac24647c01e", size = 8321376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/aa/282768cff0039b227a923cb65686539bb606e448c594d4fdee4d2c7765a1/sphinx-8.2.1-py3-none-any.whl", hash = "sha256:b5d2bb3cdf6207fcacde9f92085d2b97667b05b9c346eaec426ca4be8af505e9", size = 3589415 }, +] + [[package]] name = "sphinx-autobuild" version = "2024.10.3" @@ -1007,7 +1056,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama" }, { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "starlette" }, { name = "uvicorn" }, { name = "watchfiles" }, @@ -1038,23 +1088,39 @@ name = "sphinx-autodoc-typehints" version = "3.0.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version == '3.10.*'", ] dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282 } wheels = [ { url = "https://files.pythonhosted.org/packages/3c/dc/dc46c5c7c566b7ec5e8f860f9c89533bf03c0e6aadc96fb9b337867e4460/sphinx_autodoc_typehints-3.0.1-py3-none-any.whl", hash = "sha256:4b64b676a14b5b79cefb6628a6dc8070e320d4963e8ff640a2f3e9390ae9045a", size = 20245 }, ] +[[package]] +name = "sphinx-autodoc-typehints" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/cc/d38e7260b1bd3af0c84ad8285dfd78236584b74544510584e07963e000ec/sphinx_autodoc_typehints-3.1.0.tar.gz", hash = "sha256:a6b7b0b6df0a380783ce5b29150c2d30352746f027a3e294d37183995d3f23ed", size = 36528 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/bc5bed0677ae00b9ca7919968ea675e2f696b6b20f1648262f26a7a6c6b4/sphinx_autodoc_typehints-3.1.0-py3-none-any.whl", hash = "sha256:67bdee7e27ba943976ce92ebc5647a976a7a08f9f689a826c54617b96a423913", size = 20404 }, +] + [[package]] name = "sphinx-basic-ng" version = "1.0.0b2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/0b/a866924ded68efec7a1759587a4e478aec7559d8165fac8b2ad1c0e774d6/sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9", size = 20736 } wheels = [ @@ -1067,7 +1133,8 @@ version = "0.5.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039 } wheels = [ @@ -1080,7 +1147,8 @@ version = "2023.4.21" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/48/f5/f8a2be63ed7be9f91a4c2bea0e25bcb56aa4c5cc37ec4d8ead8065f926b1/sphinx_inline_tabs-2023.4.21.tar.gz", hash = "sha256:5df2f13f602c158f3f5f6c509e008aeada199a8c76d97ba3aa2822206683bebc", size = 42664 } wheels = [ @@ -1147,7 +1215,8 @@ version = "0.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1c/5b/4302fe33c88dbfb572e2c1cad26735164c23f16fb8dba94ddb1867d0ef06/sphinxext-opengraph-0.9.1.tar.gz", hash = "sha256:dd2868a1e7c9497977fbbf44cc0844a42af39ca65fe1bb0272518af225d06fc5", size = 1034511 } wheels = [ @@ -1160,7 +1229,8 @@ version = "0.2.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "sphinx", version = "8.2.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/b4/e5fbb493f796430230189a1ce5f9beff1ac1b98619fc71ed35deca6059a5/sphinxext-rediraffe-0.2.7.tar.gz", hash = "sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d", size = 8735 } wheels = [ From fcecec5b9d345baa18b6860f165c3ba6e92dc515 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 22 Feb 2025 06:28:27 -0600 Subject: [PATCH 05/26] py(deps[dev]) Bump dev packages --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 44541399d..591c0fe58 100644 --- a/uv.lock +++ b/uv.lock @@ -896,11 +896,11 @@ wheels = [ [[package]] name = "roman-numerals-py" -version = "3.0.0" +version = "3.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/78/9491ab144c9cb2d97aa74d6f632bd6f4be67957de03f945a23a67415d859/roman_numerals_py-3.0.0.tar.gz", hash = "sha256:91199c4373658c03d87d9fe004f4a5120a20f6cb192be745c2377cce274ef41c", size = 8970 } +sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017 } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/d0/a3a2fed015e95b9e81619182adc472540f9786183febfaef8b7c5e909418/roman_numerals_py-3.0.0-py3-none-any.whl", hash = "sha256:a1421ce66b3eab7e8735065458de3fa5c4a46263d50f9f4ac8f0e5e7701dd125", size = 4416 }, + { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742 }, ] [[package]] From 77a79f87aea984de917462a57a9f0432de5c3e81 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 22 Feb 2025 17:23:31 -0600 Subject: [PATCH 06/26] py(deps[dev]) Bump dev packages --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 591c0fe58..766dcad5f 100644 --- a/uv.lock +++ b/uv.lock @@ -1239,15 +1239,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.45.3" +version = "0.46.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/fb/2984a686808b89a6781526129a4b51266f678b2d2b97ab2d325e56116df8/starlette-0.45.3.tar.gz", hash = "sha256:2cbcba2a75806f8a41c722141486f37c28e30a0921c5f6fe4346cb0dcee1302f", size = 2574076 } +sdist = { url = "https://files.pythonhosted.org/packages/44/b6/fb9a32e3c5d59b1e383c357534c63c2d3caa6f25bf3c59dd89d296ecbaec/starlette-0.46.0.tar.gz", hash = "sha256:b359e4567456b28d473d0193f34c0de0ed49710d75ef183a74a5ce0499324f50", size = 2575568 } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/61/f2b52e107b1fc8944b33ef56bf6ac4ebbe16d91b94d2b87ce013bf63fb84/starlette-0.45.3-py3-none-any.whl", hash = "sha256:dfb6d332576f136ec740296c7e8bb8c8a7125044e7c6da30744718880cdd059d", size = 71507 }, + { url = "https://files.pythonhosted.org/packages/41/94/8af675a62e3c91c2dee47cf92e602cfac86e8767b1a1ac3caf1b327c2ab0/starlette-0.46.0-py3-none-any.whl", hash = "sha256:913f0798bd90ba90a9156383bcf1350a17d6259451d0d8ee27fc0cf2db609038", size = 71991 }, ] [[package]] From 6236cbe813f7388b2ee16f29892122cd11f29661 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sat, 22 Feb 2025 17:46:45 -0600 Subject: [PATCH 07/26] py(deps[dev]) Bump dev packages --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 766dcad5f..58b73a9ea 100644 --- a/uv.lock +++ b/uv.lock @@ -307,16 +307,16 @@ wheels = [ [[package]] name = "gp-libs" -version = "0.0.10" +version = "0.0.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docutils" }, { name = "myst-parser", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/27/8f/02465fe771a79649ec3b01f13cbd5cc980438a7c59e5c8c6a0203cc5706e/gp_libs-0.0.10.tar.gz", hash = "sha256:83e5bf7107465fdba10369e63de90eed0577b5c80c0f21044499f2e703ba225c", size = 14497 } +sdist = { url = "https://files.pythonhosted.org/packages/a6/e2/262a8af11c5a4435506822cf63662365a9ea40de110cdb76fa8ec00ea64b/gp_libs-0.0.11.tar.gz", hash = "sha256:0a5c43f47c9d459bd9ba80cacecaede2f47a17b6821e4f222b52513158b7e429", size = 14712 } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/33/80adaf9da81532a7b3ee45fe0eaaa9ed96fb25eddaae75267406dd24f49d/gp_libs-0.0.10-py3-none-any.whl", hash = "sha256:0ad6cfdddc0974454456fb1bcaec55212f1b95fe6c2c4383ac4665fb47541094", size = 15565 }, + { url = "https://files.pythonhosted.org/packages/bd/6d/e91e25fbe57cb2e09bbb4f89a113a9e96dacc7a74fb42f4a25cfab55aaba/gp_libs-0.0.11-py3-none-any.whl", hash = "sha256:c7699af49907d5c7a4cb96093c9f6e192e566abbfd5dbc6d3f58bbac2ac731b3", size = 15699 }, ] [[package]] From b33cb46358eca1f647ce42d982b42db8380d2099 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 05:44:47 -0600 Subject: [PATCH 08/26] docs(cursor[rules]) Add dev-loop rule See also: https://docs.cursor.com/context/rules-for-ai --- .cursor/rules/dev-loop.mdc | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .cursor/rules/dev-loop.mdc diff --git a/.cursor/rules/dev-loop.mdc b/.cursor/rules/dev-loop.mdc new file mode 100644 index 000000000..1886aa702 --- /dev/null +++ b/.cursor/rules/dev-loop.mdc @@ -0,0 +1,37 @@ +--- +description: QA every edit +globs: *.py +--- + +# First: QA Every edit + +Run these commands between edits: + +Check typings: + +``` +uv run mypy +``` + +Lint: + +``` +uv run ruff check . --fix; uv run ruff format .; +``` + +Check tests: + +``` +uv run py.test +``` + +Between every edit, rerun: +- Type checks +- Lint +- Tests + +If there's any failures *due to the edits*, fix them first, then: + +# When your edit is complete: Commit it + +Make an atomic commit for the edit, using conventional commits. From 30edca2a542712efef8cc07555ff7b559d7f176f Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 05:44:59 -0600 Subject: [PATCH 09/26] docs(cursor[rules]): Add git commit message standards why: Establish clear conventions for commit messages and AI assistance what: - Created git-commits.mdc with commit format guidelines - Added commit type definitions and examples - Included instructions for using Cursor's AI commit features - Documented best practices for commit message structure See also: https://docs.cursor.com/context/rules-for-ai --- .cursor/rules/git-commits.mdc | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .cursor/rules/git-commits.mdc diff --git a/.cursor/rules/git-commits.mdc b/.cursor/rules/git-commits.mdc new file mode 100644 index 000000000..0a5fa1184 --- /dev/null +++ b/.cursor/rules/git-commits.mdc @@ -0,0 +1,82 @@ +--- +description: git-commits: Git commit message standards and AI assistance +globs: git-commits: Git commit message standards and AI assistance | *.git/* .gitignore .github/* CHANGELOG.md CHANGES.md +--- +# Git Commit Standards + +## Format +``` +type(scope[component]): concise description + +why: explanation of necessity/impact +what: +- technical changes made +- keep focused on single topic + +refs: #issue-number, breaking changes, links +``` + +## Commit Types +- `feat`: New features/enhancements +- `fix`: Bug fixes +- `refactor`: Code restructuring +- `docs`: Documentation changes +- `chore`: Maintenance tasks (deps, tooling) +- `test`: Test-related changes +- `style`: Code style/formatting + +## Guidelines +- Subject line: max 50 chars +- Body lines: max 72 chars +- Use imperative mood ("Add" not "Added") +- Single topic per commit +- Blank line between subject and body +- Mark breaking changes with "BREAKING:" +- Use "See also:" for external links + +## AI Assistance in Cursor +- Stage changes with `git add` +- Use `@commit` to generate initial message +- Review and adjust the generated message +- Ensure it follows format above + +## Examples + +Good commit: +``` +feat(subprocess[run]): Switch to unicode-only text handling + +why: Improve consistency and type safety in subprocess handling +what: +- BREAKING: Changed run() to use text=True by default +- Removed console_to_str() helper and encoding logic +- Simplified output handling +- Updated type hints for better safety + +refs: #485 +See also: https://docs.python.org/3/library/subprocess.html +``` + +Bad commit: +``` +updated some stuff and fixed bugs +``` + +Cursor Rules: Add development QA and git commit standards (#cursor-rules) + +- Add dev-loop.mdc: QA process for code edits + - Type checking with mypy + - Linting with ruff + - Test validation with pytest + - Ensures edits are validated before commits + +- Add git-commits.mdc: Commit message standards + - Structured format with why/what sections + - Defined commit types and guidelines + - Examples of good/bad commits + - AI assistance instructions + +Note: These rules help maintain code quality and commit history +consistency across the project. + +See also: https://docs.cursor.com/context/rules-for-ai \ No newline at end of file From 32e600b9d5f97cc34ca888a457a751cbdd4c6c52 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 06:21:14 -0600 Subject: [PATCH 10/26] ci(coverage) Omit tests/(*/)?test_*.py --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index a1752d1c2..a6a014676 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,6 +134,8 @@ parallel = true omit = [ "*/_compat.py", "docs/conf.py", + "tests/test_*.py", + "tests/*/test_*.py", ] [tool.coverage.report] From 11748a91956294c9981a30b7d0ea5c2b49d00ce3 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 13:00:37 -0600 Subject: [PATCH 11/26] docs(MIGRATION) Formatting via prettier --- MIGRATION | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/MIGRATION b/MIGRATION index 2c88341b9..fcba337e9 100644 --- a/MIGRATION +++ b/MIGRATION @@ -64,13 +64,11 @@ _Detailed migration steps for the next version will be posted here._ ### Renamings (#527) - `Session.attached_window` renamed to {meth}`Session.active_window` - - `Session.attached_window` deprecated + - `Session.attached_window` deprecated - `Session.attached_pane` renamed to {meth}`Session.active_pane` - - `Session.attached_pane` deprecated + - `Session.attached_pane` deprecated - `Window.attached_pane` renamed to {meth}`Window.active_pane` - - `Window.attached_pane` deprecated - - + - `Window.attached_pane` deprecated ## 0.28.0: Resizing and detached by default (2024-02-15) @@ -88,6 +86,7 @@ _Detailed migration steps for the next version will be posted here._ - `Pane.resize_pane()` renamed to {meth}`Pane.resize()` (via #523) This convention will be more consistent with {meth}`Window.resize()`. + - {meth}`Pane.resize_pane()`'s params changed (#523) - No longer accepts `-U`, `-D`, `-L`, `-R` directly, instead accepts @@ -129,9 +128,11 @@ _Detailed migration steps for the next version will be posted here._ - 0.16 and below: `window['id']` 0.17 and after: `window.id` + - 0.16 and below: `window.get('id')` 0.17 and after: `window.id` + - 0.16 and below: `window.get('id', None)` 0.17 and after: `getattr(window, 'id', None)` From 3ca0bf91aa38f271cb2131063debdf5002957a83 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:39:13 -0600 Subject: [PATCH 12/26] pyproject(ruff[lint.flake8-builtins]) Skip `random` src/libtmux/test/random.py:1:1: A005 Module `random` shadows a Python standard-library module See also: https://docs.astral.sh/ruff/settings/#lint_flake8-builtins_builtins-allowed-modules --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index a6a014676..a95f12034 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -197,6 +197,7 @@ required-imports = [ [tool.ruff.lint.flake8-builtins] builtins-allowed-modules = [ "dataclasses", + "random", "types", ] From c3021fd08d3cbc47a957762711c701e5698d8a27 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:27:04 -0600 Subject: [PATCH 13/26] refactor(test) test.py -> test/__init__.py --- src/libtmux/{test.py => test/__init__.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/libtmux/{test.py => test/__init__.py} (99%) diff --git a/src/libtmux/test.py b/src/libtmux/test/__init__.py similarity index 99% rename from src/libtmux/test.py rename to src/libtmux/test/__init__.py index 2bbaa2e53..937c16250 100644 --- a/src/libtmux/test.py +++ b/src/libtmux/test/__init__.py @@ -10,7 +10,7 @@ import time import typing as t -from .exc import WaitTimeout +from libtmux.exc import WaitTimeout logger = logging.getLogger(__name__) From 044431a3ee3027d2de1ba4fe175157dbafaf6f83 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:29:07 -0600 Subject: [PATCH 14/26] refactor! EnvironmentVarGuard to `tests/environment.py` --- src/libtmux/test/__init__.py | 54 ------------------------- src/libtmux/test/environment.py | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 54 deletions(-) create mode 100644 src/libtmux/test/environment.py diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index 937c16250..1797ebefe 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -300,57 +300,3 @@ def temp_window( if len(session.windows.filter(window_id=window_id)) > 0: window.kill() return - - -class EnvironmentVarGuard: - """Mock environmental variables safely. - - Helps rotect the environment variable properly. Can be used as context - manager. - - Notes - ----- - Vendorized to fix issue with Anaconda Python 2 not including test module, - see #121 [1]_ - - References - ---------- - .. [1] Just installed, "ImportError: cannot import name test_support". - GitHub issue for tmuxp. https://github.com/tmux-python/tmuxp/issues/121. - Created October 12th, 2015. Accessed April 7th, 2018. - """ - - def __init__(self) -> None: - self._environ = os.environ - self._unset: set[str] = set() - self._reset: dict[str, str] = {} - - def set(self, envvar: str, value: str) -> None: - """Set environment variable.""" - if envvar not in self._environ: - self._unset.add(envvar) - else: - self._reset[envvar] = self._environ[envvar] - self._environ[envvar] = value - - def unset(self, envvar: str) -> None: - """Unset environment variable.""" - if envvar in self._environ: - self._reset[envvar] = self._environ[envvar] - del self._environ[envvar] - - def __enter__(self) -> Self: - """Return context for for context manager.""" - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - exc_tb: types.TracebackType | None, - ) -> None: - """Cleanup to run after context manager finishes.""" - for envvar, value in self._reset.items(): - self._environ[envvar] = value - for unset in self._unset: - del self._environ[unset] diff --git a/src/libtmux/test/environment.py b/src/libtmux/test/environment.py new file mode 100644 index 000000000..9023c1f83 --- /dev/null +++ b/src/libtmux/test/environment.py @@ -0,0 +1,72 @@ +"""Helper methods for libtmux and downstream libtmux libraries.""" + +from __future__ import annotations + +import logging +import os +import typing as t + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import sys + import types + + if sys.version_info >= (3, 11): + from typing import Self + else: + from typing_extensions import Self + + +class EnvironmentVarGuard: + """Mock environmental variables safely. + + Helps rotect the environment variable properly. Can be used as context + manager. + + Notes + ----- + Vendorized to fix issue with Anaconda Python 2 not including test module, + see #121 [1]_ + + References + ---------- + .. [1] Just installed, "ImportError: cannot import name test_support". + GitHub issue for tmuxp. https://github.com/tmux-python/tmuxp/issues/121. + Created October 12th, 2015. Accessed April 7th, 2018. + """ + + def __init__(self) -> None: + self._environ = os.environ + self._unset: set[str] = set() + self._reset: dict[str, str] = {} + + def set(self, envvar: str, value: str) -> None: + """Set environment variable.""" + if envvar not in self._environ: + self._unset.add(envvar) + else: + self._reset[envvar] = self._environ[envvar] + self._environ[envvar] = value + + def unset(self, envvar: str) -> None: + """Unset environment variable.""" + if envvar in self._environ: + self._reset[envvar] = self._environ[envvar] + del self._environ[envvar] + + def __enter__(self) -> Self: + """Return context for for context manager.""" + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + """Cleanup to run after context manager finishes.""" + for envvar, value in self._reset.items(): + self._environ[envvar] = value + for unset in self._unset: + del self._environ[unset] From d3ea0f96ca05cacb6a92aff26f7884eef1910511 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:35:50 -0600 Subject: [PATCH 15/26] refactor!(test[constants]) Move to test.constants --- src/libtmux/pytest_plugin.py | 3 ++- src/libtmux/test/__init__.py | 10 +++++----- src/libtmux/test/constants.py | 9 +++++++++ tests/legacy_api/test_session.py | 3 ++- tests/legacy_api/test_tmuxobject.py | 3 ++- tests/test_session.py | 3 ++- tests/test_tmuxobject.py | 3 ++- 7 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 src/libtmux/test/constants.py diff --git a/src/libtmux/pytest_plugin.py b/src/libtmux/pytest_plugin.py index 320d31ca2..dcef4d9c2 100644 --- a/src/libtmux/pytest_plugin.py +++ b/src/libtmux/pytest_plugin.py @@ -13,7 +13,8 @@ from libtmux import exc from libtmux.server import Server -from libtmux.test import TEST_SESSION_PREFIX, get_test_session_name, namer +from libtmux.test import get_test_session_name, namer +from libtmux.test.constants import TEST_SESSION_PREFIX if t.TYPE_CHECKING: import pathlib diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index 1797ebefe..fd40e9e0c 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -11,6 +11,11 @@ import typing as t from libtmux.exc import WaitTimeout +from libtmux.test.constants import ( + RETRY_INTERVAL_SECONDS, + RETRY_TIMEOUT_SECONDS, + TEST_SESSION_PREFIX, +) logger = logging.getLogger(__name__) @@ -29,11 +34,6 @@ from typing_extensions import Self -TEST_SESSION_PREFIX = "libtmux_" -RETRY_TIMEOUT_SECONDS = int(os.getenv("RETRY_TIMEOUT_SECONDS", 8)) -RETRY_INTERVAL_SECONDS = float(os.getenv("RETRY_INTERVAL_SECONDS", 0.05)) - - class RandomStrSequence: """Factory to generate random string.""" diff --git a/src/libtmux/test/constants.py b/src/libtmux/test/constants.py new file mode 100644 index 000000000..63d644da3 --- /dev/null +++ b/src/libtmux/test/constants.py @@ -0,0 +1,9 @@ +"""Constants for libtmux test helpers.""" + +from __future__ import annotations + +import os + +TEST_SESSION_PREFIX = "libtmux_" +RETRY_TIMEOUT_SECONDS = int(os.getenv("RETRY_TIMEOUT_SECONDS", 8)) +RETRY_INTERVAL_SECONDS = float(os.getenv("RETRY_INTERVAL_SECONDS", 0.05)) diff --git a/tests/legacy_api/test_session.py b/tests/legacy_api/test_session.py index ffe115ae2..770c77255 100644 --- a/tests/legacy_api/test_session.py +++ b/tests/legacy_api/test_session.py @@ -12,7 +12,8 @@ from libtmux.common import has_gte_version, has_lt_version from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import TEST_SESSION_PREFIX, namer +from libtmux.test import namer +from libtmux.test.constants import TEST_SESSION_PREFIX from libtmux.window import Window if t.TYPE_CHECKING: diff --git a/tests/legacy_api/test_tmuxobject.py b/tests/legacy_api/test_tmuxobject.py index 790b8cebb..c1cb2fdaa 100644 --- a/tests/legacy_api/test_tmuxobject.py +++ b/tests/legacy_api/test_tmuxobject.py @@ -7,7 +7,8 @@ from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import TEST_SESSION_PREFIX, namer +from libtmux.test import namer +from libtmux.test.constants import TEST_SESSION_PREFIX from libtmux.window import Window if t.TYPE_CHECKING: diff --git a/tests/test_session.py b/tests/test_session.py index 33b82dd72..c88686f5e 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -13,7 +13,8 @@ from libtmux.constants import WindowDirection from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import TEST_SESSION_PREFIX, namer +from libtmux.test import namer +from libtmux.test.constants import TEST_SESSION_PREFIX from libtmux.window import Window if t.TYPE_CHECKING: diff --git a/tests/test_tmuxobject.py b/tests/test_tmuxobject.py index 5c77e29e4..a72b97fb9 100644 --- a/tests/test_tmuxobject.py +++ b/tests/test_tmuxobject.py @@ -7,7 +7,8 @@ from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import TEST_SESSION_PREFIX, namer +from libtmux.test import namer +from libtmux.test.constants import TEST_SESSION_PREFIX from libtmux.window import Window if t.TYPE_CHECKING: From e07ebf99baa734cf133b9d2ab70b9e0f3c7d68cd Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:38:13 -0600 Subject: [PATCH 16/26] refactor!(test[random]) Move helpers to test.random --- src/libtmux/pytest_plugin.py | 3 +- src/libtmux/test/__init__.py | 31 ++----------------- src/libtmux/test/random.py | 46 +++++++++++++++++++++++++++++ tests/legacy_api/test_session.py | 2 +- tests/legacy_api/test_tmuxobject.py | 2 +- tests/test_session.py | 2 +- tests/test_tmuxobject.py | 2 +- 7 files changed, 54 insertions(+), 34 deletions(-) create mode 100644 src/libtmux/test/random.py diff --git a/src/libtmux/pytest_plugin.py b/src/libtmux/pytest_plugin.py index dcef4d9c2..2d5c11704 100644 --- a/src/libtmux/pytest_plugin.py +++ b/src/libtmux/pytest_plugin.py @@ -13,8 +13,9 @@ from libtmux import exc from libtmux.server import Server -from libtmux.test import get_test_session_name, namer +from libtmux.test import get_test_session_name from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.random import namer if t.TYPE_CHECKING: import pathlib diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index fd40e9e0c..52070760a 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -17,6 +17,8 @@ TEST_SESSION_PREFIX, ) +from .random import namer + logger = logging.getLogger(__name__) if t.TYPE_CHECKING: @@ -34,35 +36,6 @@ from typing_extensions import Self -class RandomStrSequence: - """Factory to generate random string.""" - - def __init__( - self, - characters: str = "abcdefghijklmnopqrstuvwxyz0123456789_", - ) -> None: - """Create a random letter / number generator. 8 chars in length. - - >>> rng = RandomStrSequence() - >>> next(rng) - '...' - >>> len(next(rng)) - 8 - >>> type(next(rng)) - - """ - self.characters: str = characters - - def __iter__(self) -> RandomStrSequence: - """Return self.""" - return self - - def __next__(self) -> str: - """Return next random string.""" - return "".join(random.sample(self.characters, k=8)) - - -namer = RandomStrSequence() current_dir = pathlib.Path(__file__) example_dir = current_dir.parent / "examples" fixtures_dir = current_dir / "fixtures" diff --git a/src/libtmux/test/random.py b/src/libtmux/test/random.py new file mode 100644 index 000000000..e874bd3c7 --- /dev/null +++ b/src/libtmux/test/random.py @@ -0,0 +1,46 @@ +"""Random helpers for libtmux and downstream libtmux libraries.""" + +from __future__ import annotations + +import logging +import random +import typing as t + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import sys + + if sys.version_info >= (3, 11): + pass + + +class RandomStrSequence: + """Factory to generate random string.""" + + def __init__( + self, + characters: str = "abcdefghijklmnopqrstuvwxyz0123456789_", + ) -> None: + """Create a random letter / number generator. 8 chars in length. + + >>> rng = RandomStrSequence() + >>> next(rng) + '...' + >>> len(next(rng)) + 8 + >>> type(next(rng)) + + """ + self.characters: str = characters + + def __iter__(self) -> RandomStrSequence: + """Return self.""" + return self + + def __next__(self) -> str: + """Return next random string.""" + return "".join(random.sample(self.characters, k=8)) + + +namer = RandomStrSequence() diff --git a/tests/legacy_api/test_session.py b/tests/legacy_api/test_session.py index 770c77255..c756999ea 100644 --- a/tests/legacy_api/test_session.py +++ b/tests/legacy_api/test_session.py @@ -12,8 +12,8 @@ from libtmux.common import has_gte_version, has_lt_version from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import namer from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.random import namer from libtmux.window import Window if t.TYPE_CHECKING: diff --git a/tests/legacy_api/test_tmuxobject.py b/tests/legacy_api/test_tmuxobject.py index c1cb2fdaa..dc023d24b 100644 --- a/tests/legacy_api/test_tmuxobject.py +++ b/tests/legacy_api/test_tmuxobject.py @@ -7,8 +7,8 @@ from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import namer from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.random import namer from libtmux.window import Window if t.TYPE_CHECKING: diff --git a/tests/test_session.py b/tests/test_session.py index c88686f5e..88d5f79e6 100644 --- a/tests/test_session.py +++ b/tests/test_session.py @@ -13,8 +13,8 @@ from libtmux.constants import WindowDirection from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import namer from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.random import namer from libtmux.window import Window if t.TYPE_CHECKING: diff --git a/tests/test_tmuxobject.py b/tests/test_tmuxobject.py index a72b97fb9..e8c8be9ec 100644 --- a/tests/test_tmuxobject.py +++ b/tests/test_tmuxobject.py @@ -7,8 +7,8 @@ from libtmux.pane import Pane from libtmux.session import Session -from libtmux.test import namer from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.random import namer from libtmux.window import Window if t.TYPE_CHECKING: From 5e87bc094f5d378eb7d2741be9f563c4305f7dc4 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:43:48 -0600 Subject: [PATCH 17/26] refactor!(test[random]) Move more helpers to test.random --- src/libtmux/pytest_plugin.py | 3 +- src/libtmux/test/__init__.py | 72 ----------------------------- src/libtmux/test/random.py | 88 ++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 74 deletions(-) diff --git a/src/libtmux/pytest_plugin.py b/src/libtmux/pytest_plugin.py index 2d5c11704..92da4676d 100644 --- a/src/libtmux/pytest_plugin.py +++ b/src/libtmux/pytest_plugin.py @@ -13,9 +13,8 @@ from libtmux import exc from libtmux.server import Server -from libtmux.test import get_test_session_name from libtmux.test.constants import TEST_SESSION_PREFIX -from libtmux.test.random import namer +from libtmux.test.random import get_test_session_name, namer if t.TYPE_CHECKING: import pathlib diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index 52070760a..52f72cbbd 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -90,78 +90,6 @@ def retry_until( return True -def get_test_session_name(server: Server, prefix: str = TEST_SESSION_PREFIX) -> str: - """ - Faker to create a session name that doesn't exist. - - Parameters - ---------- - server : :class:`libtmux.Server` - libtmux server - prefix : str - prefix for sessions (e.g. ``libtmux_``). Defaults to - ``TEST_SESSION_PREFIX``. - - Returns - ------- - str - Random session name guaranteed to not collide with current ones. - - Examples - -------- - >>> get_test_session_name(server=server) - 'libtmux_...' - - Never the same twice: - >>> get_test_session_name(server=server) != get_test_session_name(server=server) - True - """ - while True: - session_name = prefix + next(namer) - if not server.has_session(session_name): - break - return session_name - - -def get_test_window_name( - session: Session, - prefix: str | None = TEST_SESSION_PREFIX, -) -> str: - """ - Faker to create a window name that doesn't exist. - - Parameters - ---------- - session : :class:`libtmux.Session` - libtmux session - prefix : str - prefix for windows (e.g. ``libtmux_``). Defaults to - ``TEST_SESSION_PREFIX``. - - ATM we reuse the test session prefix here. - - Returns - ------- - str - Random window name guaranteed to not collide with current ones. - - Examples - -------- - >>> get_test_window_name(session=session) - 'libtmux_...' - - Never the same twice: - >>> get_test_window_name(session=session) != get_test_window_name(session=session) - True - """ - assert prefix is not None - while True: - window_name = prefix + next(namer) - if len(session.windows.filter(window_name=window_name)) == 0: - break - return window_name - - @contextlib.contextmanager def temp_session( server: Server, diff --git a/src/libtmux/test/random.py b/src/libtmux/test/random.py index e874bd3c7..abcb95bce 100644 --- a/src/libtmux/test/random.py +++ b/src/libtmux/test/random.py @@ -6,6 +6,22 @@ import random import typing as t +from libtmux.test.constants import ( + TEST_SESSION_PREFIX, +) + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import sys + + from libtmux.server import Server + from libtmux.session import Session + + if sys.version_info >= (3, 11): + pass + + logger = logging.getLogger(__name__) if t.TYPE_CHECKING: @@ -44,3 +60,75 @@ def __next__(self) -> str: namer = RandomStrSequence() + + +def get_test_session_name(server: Server, prefix: str = TEST_SESSION_PREFIX) -> str: + """ + Faker to create a session name that doesn't exist. + + Parameters + ---------- + server : :class:`libtmux.Server` + libtmux server + prefix : str + prefix for sessions (e.g. ``libtmux_``). Defaults to + ``TEST_SESSION_PREFIX``. + + Returns + ------- + str + Random session name guaranteed to not collide with current ones. + + Examples + -------- + >>> get_test_session_name(server=server) + 'libtmux_...' + + Never the same twice: + >>> get_test_session_name(server=server) != get_test_session_name(server=server) + True + """ + while True: + session_name = prefix + next(namer) + if not server.has_session(session_name): + break + return session_name + + +def get_test_window_name( + session: Session, + prefix: str | None = TEST_SESSION_PREFIX, +) -> str: + """ + Faker to create a window name that doesn't exist. + + Parameters + ---------- + session : :class:`libtmux.Session` + libtmux session + prefix : str + prefix for windows (e.g. ``libtmux_``). Defaults to + ``TEST_SESSION_PREFIX``. + + ATM we reuse the test session prefix here. + + Returns + ------- + str + Random window name guaranteed to not collide with current ones. + + Examples + -------- + >>> get_test_window_name(session=session) + 'libtmux_...' + + Never the same twice: + >>> get_test_window_name(session=session) != get_test_window_name(session=session) + True + """ + assert prefix is not None + while True: + window_name = prefix + next(namer) + if len(session.windows.filter(window_name=window_name)) == 0: + break + return window_name From 8751eb283df321c78c20446751fe99a8a07ee3ea Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:46:15 -0600 Subject: [PATCH 18/26] refactor!(test[temporary]) Move from `test` to `test.temporary` --- src/libtmux/test/__init__.py | 113 ---------------------------- src/libtmux/test/temporary.py | 135 ++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 113 deletions(-) create mode 100644 src/libtmux/test/temporary.py diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index 52f72cbbd..006b2f579 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -88,116 +88,3 @@ def retry_until( return False time.sleep(interval) return True - - -@contextlib.contextmanager -def temp_session( - server: Server, - *args: t.Any, - **kwargs: t.Any, -) -> Generator[Session, t.Any, t.Any]: - """ - Return a context manager with a temporary session. - - If no ``session_name`` is entered, :func:`get_test_session_name` will make - an unused session name. - - The session will destroy itself upon closing with :meth:`Session.session()`. - - Parameters - ---------- - server : :class:`libtmux.Server` - - Other Parameters - ---------------- - args : list - Arguments passed into :meth:`Server.new_session` - kwargs : dict - Keyword arguments passed into :meth:`Server.new_session` - - Yields - ------ - :class:`libtmux.Session` - Temporary session - - Examples - -------- - >>> with temp_session(server) as session: - ... session.new_window(window_name='my window') - Window(@3 2:my window, Session($... ...)) - """ - if "session_name" in kwargs: - session_name = kwargs.pop("session_name") - else: - session_name = get_test_session_name(server) - - session = server.new_session(session_name, *args, **kwargs) - - try: - yield session - finally: - if server.has_session(session_name): - session.kill() - return - - -@contextlib.contextmanager -def temp_window( - session: Session, - *args: t.Any, - **kwargs: t.Any, -) -> Generator[Window, t.Any, t.Any]: - """ - Return a context manager with a temporary window. - - The window will destroy itself upon closing with :meth:`window. - kill()`. - - If no ``window_name`` is entered, :func:`get_test_window_name` will make - an unused window name. - - Parameters - ---------- - session : :class:`libtmux.Session` - - Other Parameters - ---------------- - args : list - Arguments passed into :meth:`Session.new_window` - kwargs : dict - Keyword arguments passed into :meth:`Session.new_window` - - Yields - ------ - :class:`libtmux.Window` - temporary window - - Examples - -------- - >>> with temp_window(session) as window: - ... window - Window(@2 2:... Session($1 libtmux_...)) - - - >>> with temp_window(session) as window: - ... window.split() - Pane(%4 Window(@3 2:libtmux_..., Session($1 libtmux_...))) - """ - if "window_name" not in kwargs: - window_name = get_test_window_name(session) - else: - window_name = kwargs.pop("window_name") - - window = session.new_window(window_name, *args, **kwargs) - - # Get ``window_id`` before returning it, it may be killed within context. - window_id = window.window_id - assert window_id is not None - assert isinstance(window_id, str) - - try: - yield window - finally: - if len(session.windows.filter(window_id=window_id)) > 0: - window.kill() - return diff --git a/src/libtmux/test/temporary.py b/src/libtmux/test/temporary.py new file mode 100644 index 000000000..cc2106edd --- /dev/null +++ b/src/libtmux/test/temporary.py @@ -0,0 +1,135 @@ +"""Temporary object helpers for libtmux and downstream libtmux libraries.""" + +from __future__ import annotations + +import contextlib +import logging +import typing as t + +from libtmux.test.random import get_test_session_name, get_test_window_name + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import sys + from collections.abc import Generator + + from libtmux.server import Server + from libtmux.session import Session + from libtmux.window import Window + + if sys.version_info >= (3, 11): + pass + + +@contextlib.contextmanager +def temp_session( + server: Server, + *args: t.Any, + **kwargs: t.Any, +) -> Generator[Session, t.Any, t.Any]: + """ + Return a context manager with a temporary session. + + If no ``session_name`` is entered, :func:`get_test_session_name` will make + an unused session name. + + The session will destroy itself upon closing with :meth:`Session.session()`. + + Parameters + ---------- + server : :class:`libtmux.Server` + + Other Parameters + ---------------- + args : list + Arguments passed into :meth:`Server.new_session` + kwargs : dict + Keyword arguments passed into :meth:`Server.new_session` + + Yields + ------ + :class:`libtmux.Session` + Temporary session + + Examples + -------- + >>> with temp_session(server) as session: + ... session.new_window(window_name='my window') + Window(@3 2:my window, Session($... ...)) + """ + if "session_name" in kwargs: + session_name = kwargs.pop("session_name") + else: + session_name = get_test_session_name(server) + + session = server.new_session(session_name, *args, **kwargs) + + try: + yield session + finally: + if server.has_session(session_name): + session.kill() + return + + +@contextlib.contextmanager +def temp_window( + session: Session, + *args: t.Any, + **kwargs: t.Any, +) -> Generator[Window, t.Any, t.Any]: + """ + Return a context manager with a temporary window. + + The window will destroy itself upon closing with :meth:`window. + kill()`. + + If no ``window_name`` is entered, :func:`get_test_window_name` will make + an unused window name. + + Parameters + ---------- + session : :class:`libtmux.Session` + + Other Parameters + ---------------- + args : list + Arguments passed into :meth:`Session.new_window` + kwargs : dict + Keyword arguments passed into :meth:`Session.new_window` + + Yields + ------ + :class:`libtmux.Window` + temporary window + + Examples + -------- + >>> with temp_window(session) as window: + ... window + Window(@2 2:... Session($1 libtmux_...)) + + + >>> with temp_window(session) as window: + ... window.split() + Pane(%4 Window(@3 2:libtmux_..., Session($1 libtmux_...))) + """ + if "window_name" not in kwargs: + window_name = get_test_window_name(session) + else: + window_name = kwargs.pop("window_name") + + window = session.new_window(window_name, *args, **kwargs) + + # Get ``window_id`` before returning it, it may be killed within context. + window_id = window.window_id + assert window_id is not None + assert isinstance(window_id, str) + + try: + yield window + finally: + if len(session.windows.filter(window_id=window_id)) > 0: + window.kill() + return From ef55d418e500a1e5c06024eeffed8ff3120539f2 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:50:11 -0600 Subject: [PATCH 19/26] refactor!(test[retry]) Move from `test` to `test.retry` --- src/libtmux/test/__init__.py | 49 ------------------------- src/libtmux/test/retry.py | 71 ++++++++++++++++++++++++++++++++++++ tests/test_pane.py | 2 +- tests/test_test.py | 2 +- 4 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 src/libtmux/test/retry.py diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index 006b2f579..4bc7627df 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -39,52 +39,3 @@ current_dir = pathlib.Path(__file__) example_dir = current_dir.parent / "examples" fixtures_dir = current_dir / "fixtures" - - -def retry_until( - fun: Callable[[], bool], - seconds: float = RETRY_TIMEOUT_SECONDS, - *, - interval: float = RETRY_INTERVAL_SECONDS, - raises: bool | None = True, -) -> bool: - """ - Retry a function until a condition meets or the specified time passes. - - Parameters - ---------- - fun : callable - A function that will be called repeatedly until it returns ``True`` or - the specified time passes. - seconds : float - Seconds to retry. Defaults to ``8``, which is configurable via - ``RETRY_TIMEOUT_SECONDS`` environment variables. - interval : float - Time in seconds to wait between calls. Defaults to ``0.05`` and is - configurable via ``RETRY_INTERVAL_SECONDS`` environment variable. - raises : bool - Whether or not to raise an exception on timeout. Defaults to ``True``. - - Examples - -------- - >>> def fn(): - ... p = session.active_window.active_pane - ... return p.pane_current_path is not None - - >>> retry_until(fn) - True - - In pytest: - - >>> assert retry_until(fn, raises=False) - """ - ini = time.time() - - while not fun(): - end = time.time() - if end - ini >= seconds: - if raises: - raise WaitTimeout - return False - time.sleep(interval) - return True diff --git a/src/libtmux/test/retry.py b/src/libtmux/test/retry.py new file mode 100644 index 000000000..1f989e73a --- /dev/null +++ b/src/libtmux/test/retry.py @@ -0,0 +1,71 @@ +"""Retry helpers for libtmux and downstream libtmux libraries.""" + +from __future__ import annotations + +import logging +import time +import typing as t + +from libtmux.exc import WaitTimeout +from libtmux.test.constants import ( + RETRY_INTERVAL_SECONDS, + RETRY_TIMEOUT_SECONDS, +) + +logger = logging.getLogger(__name__) + +if t.TYPE_CHECKING: + import sys + from collections.abc import Callable + + if sys.version_info >= (3, 11): + pass + + +def retry_until( + fun: Callable[[], bool], + seconds: float = RETRY_TIMEOUT_SECONDS, + *, + interval: float = RETRY_INTERVAL_SECONDS, + raises: bool | None = True, +) -> bool: + """ + Retry a function until a condition meets or the specified time passes. + + Parameters + ---------- + fun : callable + A function that will be called repeatedly until it returns ``True`` or + the specified time passes. + seconds : float + Seconds to retry. Defaults to ``8``, which is configurable via + ``RETRY_TIMEOUT_SECONDS`` environment variables. + interval : float + Time in seconds to wait between calls. Defaults to ``0.05`` and is + configurable via ``RETRY_INTERVAL_SECONDS`` environment variable. + raises : bool + Whether or not to raise an exception on timeout. Defaults to ``True``. + + Examples + -------- + >>> def fn(): + ... p = session.active_window.active_pane + ... return p.pane_current_path is not None + + >>> retry_until(fn) + True + + In pytest: + + >>> assert retry_until(fn, raises=False) + """ + ini = time.time() + + while not fun(): + end = time.time() + if end - ini >= seconds: + if raises: + raise WaitTimeout + return False + time.sleep(interval) + return True diff --git a/tests/test_pane.py b/tests/test_pane.py index 21d0cbb87..746467851 100644 --- a/tests/test_pane.py +++ b/tests/test_pane.py @@ -10,7 +10,7 @@ from libtmux.common import has_gte_version, has_lt_version, has_lte_version from libtmux.constants import PaneDirection, ResizeAdjustmentDirection -from libtmux.test import retry_until +from libtmux.test.retry import retry_until if t.TYPE_CHECKING: from libtmux.session import Session diff --git a/tests/test_test.py b/tests/test_test.py index a9471363c..36e35930d 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -7,7 +7,7 @@ import pytest from libtmux.exc import WaitTimeout -from libtmux.test import retry_until +from libtmux.test.retry import retry_until def test_retry_three_times() -> None: From 75502951fcb135fe43bb93a26d19bc77a658cd90 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:54:35 -0600 Subject: [PATCH 20/26] docs(CHANGES) Note move of test helpers --- CHANGES | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CHANGES b/CHANGES index eb74dc809..b1c4851d0 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,41 @@ $ pip install --user --upgrade --pre libtmux - _Future release notes will be placed here_ +### Breaking Changes + +#### Test helpers: Refactor + +Test helper functionality has been split into focused modules (#XXX): + +- `libtmux.test` module split into: + - `libtmux.test.constants`: Test-related constants (`TEST_SESSION_PREFIX`, etc.) + - `libtmux.test.environment`: Environment variable mocking + - `libtmux.test.random`: Random string generation utilities + - `libtmux.test.temporary`: Temporary session/window management + +**Breaking**: Import paths have changed. Update imports: + +```python +# Old (0.45.x and earlier) +from libtmux.test import ( + TEST_SESSION_PREFIX, + get_test_session_name, + get_test_window_name, + namer, + temp_session, + temp_window, + EnvironmentVarGuard, +) +``` + +```python +# New (0.46.0+) +from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.environment import EnvironmentVarGuard +from libtmux.test.random import get_test_session_name, get_test_window_name, namer +from libtmux.test.temporary import temp_session, temp_window +``` + ### Development - CI: Check for runtime dependencies (#574) From 172de1f6fce52e19545ce4e417b48f3b5c83ed61 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 12:54:42 -0600 Subject: [PATCH 21/26] docs(MIGRATION) Note move of test helpers --- MIGRATION | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/MIGRATION b/MIGRATION index fcba337e9..ee58dbce6 100644 --- a/MIGRATION +++ b/MIGRATION @@ -25,6 +25,41 @@ _Detailed migration steps for the next version will be posted here._ +## libtmux 0.45.x (Yet to be released) + +### Test helpers: Module moves + +Test helper functionality has been split into focused modules (#XXX): + +- `libtmux.test` module split into: + - `libtmux.test.constants`: Test-related constants (`TEST_SESSION_PREFIX`, etc.) + - `libtmux.test.environment`: Environment variable mocking + - `libtmux.test.random`: Random string generation utilities + - `libtmux.test.temporary`: Temporary session/window management + +**Breaking**: Import paths have changed. Update imports: + +```python +# Old (0.45.x and earlier) +from libtmux.test import ( + TEST_SESSION_PREFIX, + get_test_session_name, + get_test_window_name, + namer, + temp_session, + temp_window, + EnvironmentVarGuard, +) +``` + +```python +# New (0.46.0+) +from libtmux.test.constants import TEST_SESSION_PREFIX +from libtmux.test.environment import EnvironmentVarGuard +from libtmux.test.random import get_test_session_name, get_test_window_name, namer +from libtmux.test.temporary import temp_session, temp_window +``` + ## 0.35.0: Commands require explicit targets (2024-03-17) ### Commands require explicit targets (#535) From 4d637c5c53b073a6023583b491d629cb43d68e91 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 13:12:53 -0600 Subject: [PATCH 22/26] tests: test -> test/test_retry.py --- tests/{test_test.py => test/test_retry.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_test.py => test/test_retry.py} (100%) diff --git a/tests/test_test.py b/tests/test/test_retry.py similarity index 100% rename from tests/test_test.py rename to tests/test/test_retry.py From 6cbfe31958ac7a835f8f5067070f0e5ac13278d3 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 13:17:54 -0600 Subject: [PATCH 23/26] test: Remove `example_dir`, `fixtures_dir` --- src/libtmux/test/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libtmux/test/__init__.py b/src/libtmux/test/__init__.py index 4bc7627df..8fba9fa82 100644 --- a/src/libtmux/test/__init__.py +++ b/src/libtmux/test/__init__.py @@ -34,8 +34,3 @@ from typing import Self else: from typing_extensions import Self - - -current_dir = pathlib.Path(__file__) -example_dir = current_dir.parent / "examples" -fixtures_dir = current_dir / "fixtures" From 0cb9dfe7198f38122e6d7391e880e276bd13cdba Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 13:29:27 -0600 Subject: [PATCH 24/26] docs: Refactor for `test` helpers and #578 --- docs/index.md | 1 + docs/pytest-plugin/index.md | 8 -------- docs/pytest-plugin/test.md | 6 ------ docs/test-helpers/constants.md | 10 ++++++++++ docs/test-helpers/environment.md | 10 ++++++++++ docs/test-helpers/index.md | 17 +++++++++++++++++ docs/test-helpers/random.md | 10 ++++++++++ docs/test-helpers/temporary.md | 10 ++++++++++ 8 files changed, 58 insertions(+), 14 deletions(-) delete mode 100644 docs/pytest-plugin/test.md create mode 100644 docs/test-helpers/constants.md create mode 100644 docs/test-helpers/environment.md create mode 100644 docs/test-helpers/index.md create mode 100644 docs/test-helpers/random.md create mode 100644 docs/test-helpers/temporary.md diff --git a/docs/index.md b/docs/index.md index 390a2a46d..76c4796b6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,6 +20,7 @@ about topics/index api/index pytest-plugin/index +test-helpers/index ``` ```{toctree} diff --git a/docs/pytest-plugin/index.md b/docs/pytest-plugin/index.md index cdcdf5a82..8f8dca41d 100644 --- a/docs/pytest-plugin/index.md +++ b/docs/pytest-plugin/index.md @@ -147,11 +147,3 @@ def set_home( :show-inheritance: :member-order: bysource ``` - -## Test utilities - -```{toctree} -:maxdepth: 1 - -test -``` diff --git a/docs/pytest-plugin/test.md b/docs/pytest-plugin/test.md deleted file mode 100644 index 8fbff818b..000000000 --- a/docs/pytest-plugin/test.md +++ /dev/null @@ -1,6 +0,0 @@ -# Test helpers - -```{eval-rst} -.. automodule:: libtmux.test - :members: -``` diff --git a/docs/test-helpers/constants.md b/docs/test-helpers/constants.md new file mode 100644 index 000000000..facbfb871 --- /dev/null +++ b/docs/test-helpers/constants.md @@ -0,0 +1,10 @@ +# Constants + +Test-related constants used across libtmux test helpers. + +```{eval-rst} +.. automodule:: libtmux.test.constants + :members: + :undoc-members: + :show-inheritance: +``` \ No newline at end of file diff --git a/docs/test-helpers/environment.md b/docs/test-helpers/environment.md new file mode 100644 index 000000000..e385193a6 --- /dev/null +++ b/docs/test-helpers/environment.md @@ -0,0 +1,10 @@ +# Environment + +Environment variable mocking utilities for tests. + +```{eval-rst} +.. automodule:: libtmux.test.environment + :members: + :undoc-members: + :show-inheritance: +``` \ No newline at end of file diff --git a/docs/test-helpers/index.md b/docs/test-helpers/index.md new file mode 100644 index 000000000..b27fa8d3e --- /dev/null +++ b/docs/test-helpers/index.md @@ -0,0 +1,17 @@ +# Test helpers + +Test helpers for libtmux and downstream libraries. + +```{toctree} +:maxdepth: 2 + +constants +environment +random +temporary +``` + +```{eval-rst} +.. automodule:: libtmux.test + :members: +``` \ No newline at end of file diff --git a/docs/test-helpers/random.md b/docs/test-helpers/random.md new file mode 100644 index 000000000..2222a6cee --- /dev/null +++ b/docs/test-helpers/random.md @@ -0,0 +1,10 @@ +# Random + +Random string generation utilities for test names. + +```{eval-rst} +.. automodule:: libtmux.test.random + :members: + :undoc-members: + :show-inheritance: +``` \ No newline at end of file diff --git a/docs/test-helpers/temporary.md b/docs/test-helpers/temporary.md new file mode 100644 index 000000000..f1ee07b2f --- /dev/null +++ b/docs/test-helpers/temporary.md @@ -0,0 +1,10 @@ +# Temporary Objects + +Context managers for temporary tmux objects (sessions, windows). + +```{eval-rst} +.. automodule:: libtmux.test.temporary + :members: + :undoc-members: + :show-inheritance: +``` \ No newline at end of file From e4b06ba0d7f1b939cc000fdf1f21cfa619c3d93e Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 13:30:37 -0600 Subject: [PATCH 25/26] docs: Add redirect for `test-helpers` --- docs/redirects.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/redirects.txt b/docs/redirects.txt index 1f20db7c0..afff787ad 100644 --- a/docs/redirects.txt +++ b/docs/redirects.txt @@ -15,3 +15,4 @@ "reference/servers.md" "api/servers.md" "reference/sessions.md" "api/sessions.md" "reference/windows.md" "api/windows.md" +"pytest-plugin/test.md" "test-helpers/index.md" From 52bf7c16b8e8b5a9bdfc0a5c7e5be1996e4ac501 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Sun, 23 Feb 2025 13:32:43 -0600 Subject: [PATCH 26/26] Tag v0.45.0 (test helper refactor from #578) --- CHANGES | 10 ++++++---- MIGRATION | 8 ++++---- pyproject.toml | 2 +- src/libtmux/__about__.py | 2 +- uv.lock | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index b1c4851d0..cfb7f923d 100644 --- a/CHANGES +++ b/CHANGES @@ -9,17 +9,19 @@ To install via [pip](https://pip.pypa.io/en/stable/), use: $ pip install --user --upgrade --pre libtmux ``` -## libtmux 0.45.x (Yet to be released) +## libtmux 0.46.x (Yet to be released) - _Future release notes will be placed here_ +## libtmux 0.45.0 (2025-02-23) + ### Breaking Changes #### Test helpers: Refactor -Test helper functionality has been split into focused modules (#XXX): +Test helper functionality has been split into focused modules (#578): - `libtmux.test` module split into: - `libtmux.test.constants`: Test-related constants (`TEST_SESSION_PREFIX`, etc.) @@ -30,7 +32,7 @@ Test helper functionality has been split into focused modules (#XXX): **Breaking**: Import paths have changed. Update imports: ```python -# Old (0.45.x and earlier) +# Old (0.44.x and earlier) from libtmux.test import ( TEST_SESSION_PREFIX, get_test_session_name, @@ -43,7 +45,7 @@ from libtmux.test import ( ``` ```python -# New (0.46.0+) +# New (0.45.0+) from libtmux.test.constants import TEST_SESSION_PREFIX from libtmux.test.environment import EnvironmentVarGuard from libtmux.test.random import get_test_session_name, get_test_window_name, namer diff --git a/MIGRATION b/MIGRATION index ee58dbce6..e3b097e50 100644 --- a/MIGRATION +++ b/MIGRATION @@ -25,11 +25,11 @@ _Detailed migration steps for the next version will be posted here._ -## libtmux 0.45.x (Yet to be released) +## libtmux 0.45.0 (2025-02-23) ### Test helpers: Module moves -Test helper functionality has been split into focused modules (#XXX): +Test helper functionality has been split into focused modules (#578): - `libtmux.test` module split into: - `libtmux.test.constants`: Test-related constants (`TEST_SESSION_PREFIX`, etc.) @@ -40,7 +40,7 @@ Test helper functionality has been split into focused modules (#XXX): **Breaking**: Import paths have changed. Update imports: ```python -# Old (0.45.x and earlier) +# Old (0.44.x and earlier) from libtmux.test import ( TEST_SESSION_PREFIX, get_test_session_name, @@ -53,7 +53,7 @@ from libtmux.test import ( ``` ```python -# New (0.46.0+) +# New (0.45.0+) from libtmux.test.constants import TEST_SESSION_PREFIX from libtmux.test.environment import EnvironmentVarGuard from libtmux.test.random import get_test_session_name, get_test_window_name, namer diff --git a/pyproject.toml b/pyproject.toml index a95f12034..f3dc1aa14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "libtmux" -version = "0.44.2" +version = "0.45.0" description = "Typed library that provides an ORM wrapper for tmux, a terminal multiplexer." requires-python = ">=3.9,<4.0" authors = [ diff --git a/src/libtmux/__about__.py b/src/libtmux/__about__.py index 9f27b720e..4de25b74b 100644 --- a/src/libtmux/__about__.py +++ b/src/libtmux/__about__.py @@ -4,7 +4,7 @@ __title__ = "libtmux" __package_name__ = "libtmux" -__version__ = "0.44.2" +__version__ = "0.45.0" __description__ = "Typed scripting library / ORM / API wrapper for tmux" __email__ = "tony@git-pull.com" __author__ = "Tony Narlock" diff --git a/uv.lock b/uv.lock index 58b73a9ea..eb2387c12 100644 --- a/uv.lock +++ b/uv.lock @@ -381,7 +381,7 @@ wheels = [ [[package]] name = "libtmux" -version = "0.44.2" +version = "0.45.0" source = { editable = "." } [package.dev-dependencies] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy