diff --git a/tests/data/recklessrepo/lightningd/testpluguv/README.md b/tests/data/recklessrepo/lightningd/testpluguv/README.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/data/recklessrepo/lightningd/testpluguv/pyproject.toml b/tests/data/recklessrepo/lightningd/testpluguv/pyproject.toml new file mode 100644 index 000000000000..430f86b8df87 --- /dev/null +++ b/tests/data/recklessrepo/lightningd/testpluguv/pyproject.toml @@ -0,0 +1,9 @@ +[project] +name = "testpluguv" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [ + "pyln-client>=24.4", +] diff --git a/tests/data/recklessrepo/lightningd/testpluguv/testpluguv.py b/tests/data/recklessrepo/lightningd/testpluguv/testpluguv.py new file mode 100755 index 000000000000..50fc52a85753 --- /dev/null +++ b/tests/data/recklessrepo/lightningd/testpluguv/testpluguv.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +from pyln.client import Plugin + +plugin = Plugin() + +__version__ = 'v1' + + +@plugin.init() +def init(options, configuration, plugin, **kwargs): + plugin.log("testpluguv initialized") + + +@plugin.method("uvplugintest") +def uvplugintest(plugin): + return "I live." + + +@plugin.method("getuvpluginversion") +def getuvpluginversion(plugin): + "to test commit/tag checkout" + return __version__ + + +plugin.run() diff --git a/tests/data/recklessrepo/lightningd/testpluguv/uv.lock b/tests/data/recklessrepo/lightningd/testpluguv/uv.lock new file mode 100644 index 000000000000..5775be9323b7 --- /dev/null +++ b/tests/data/recklessrepo/lightningd/testpluguv/uv.lock @@ -0,0 +1,188 @@ +version = 1 +revision = 2 +requires-python = ">=3.13" + +[[package]] +name = "asn1crypto" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" }, +] + +[[package]] +name = "base58" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/45/8ae61209bb9015f516102fa559a2914178da1d5868428bd86a1b4421141d/base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c", size = 6528, upload-time = "2021-10-30T22:12:17.858Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/45/ec96b29162a402fc4c1c5512d114d7b3787b9d1c2ec241d9568b4816ee23/base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2", size = 5621, upload-time = "2021-10-30T22:12:16.658Z" }, +] + +[[package]] +name = "bitarray" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/ee/3b2fcbac3a4192e5d079aaa1850dff2f9ac625861c4c644819c2b34292ec/bitarray-3.6.0.tar.gz", hash = "sha256:20febc849a1f858e6a57a7d47b323fe9e727c579ddd526d317ad8831748a66a8", size = 147946, upload-time = "2025-07-29T18:03:56.681Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/2c/21066c7a97b2c88037b0fc04480fa13b0031c30c6f70452dc9c84fb2b087/bitarray-3.6.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3f96f57cea35ba19fd23a20b38fa0dfa3d87d582507129b8c8e314aa298f59b", size = 144156, upload-time = "2025-07-29T18:01:35.58Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9cc42ea0c440ac1c2a65375688ac5891da12b3820f4a32440791d25ed668/bitarray-3.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81e84054b22babcd6c5cc1eac0de2bfc1054ecdf742720cbfb36efbe89ec6c30", size = 140916, upload-time = "2025-07-29T18:01:36.67Z" }, + { url = "https://files.pythonhosted.org/packages/d7/66/709d259d855528213b1099facddb08d6108cb0074cf88dc357cdd07bacff/bitarray-3.6.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca643295bf5441dd38dadf7571ca4b63961820eedbffbe46ceba0893bf226203", size = 324713, upload-time = "2025-07-29T18:01:37.925Z" }, + { url = "https://files.pythonhosted.org/packages/6c/67/831e366ea4f0d52d622482b8475f87040cbc210d8f5f383935a4cc6363fe/bitarray-3.6.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139963494fc3dd5caee5e38c0a03783ef50be118565e94b1dbb0210770f0b32d", size = 341300, upload-time = "2025-07-29T18:01:39.56Z" }, + { url = "https://files.pythonhosted.org/packages/66/c9/197375b63ca768ac8b1e624f27dc0eccdd451f94c6b9bf8950500d8da134/bitarray-3.6.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:243825f56b58bef28bfc602992a8c6d09bbc625628c195498d6020120d632a09", size = 333724, upload-time = "2025-07-29T18:01:40.861Z" }, + { url = "https://files.pythonhosted.org/packages/e1/23/96c882d798b8bc9d5354ad1fba18ad3ad4f3c0a661a296c8e51ca2941e0f/bitarray-3.6.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:583b46b3ba44121de5e87e95ae379932dc5fd2e37ebdf2c11a6d7975891425c1", size = 327276, upload-time = "2025-07-29T18:01:42.039Z" }, + { url = "https://files.pythonhosted.org/packages/20/8e/51751fe0e6f9fe7980b0467b471ba9ab8d1713a2a6576980d18143511656/bitarray-3.6.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f0be27d06732e2833b672a8fcc32fa195bdb22161eb88f8890de15e30264a01", size = 314903, upload-time = "2025-07-29T18:01:43.302Z" }, + { url = "https://files.pythonhosted.org/packages/49/7a/e4db9876e6e8bb261e64a384d3adb4372f13099b356e559cec85d022b897/bitarray-3.6.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:507e567aee4806576e20752f22533e8b7ec61e7e75062a7ce9222a0675aa0da6", size = 322551, upload-time = "2025-07-29T18:01:44.548Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5a/9460070e6cb671067cc2e115a82da6fc9ef0958542b98b07a5ed4a05a97b/bitarray-3.6.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:22188943a29072b684cd7c99e0b2cfc0af317cea3366c583d820507e6d1f2ed4", size = 316128, upload-time = "2025-07-29T18:01:45.789Z" }, + { url = "https://files.pythonhosted.org/packages/34/6f/f5d78c8e908750b9c3d5839eca2af5f6e99d6c7fe8a0498ef79a1af90bd8/bitarray-3.6.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f92462ea3888c99439f58f7561ecd5dd4cf8b8b1b259ccf5376667b8c46ee747", size = 339337, upload-time = "2025-07-29T18:01:47.684Z" }, + { url = "https://files.pythonhosted.org/packages/0d/d3/f740b601eae4e28e22d8560877fe9881f1b7a96fcb23b186e8580d328929/bitarray-3.6.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3800f3c8c9780f281cf590543fd4b3278fea6988202273a260ecc58136895efb", size = 338607, upload-time = "2025-07-29T18:01:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/4e/81/b9451089eea0ef66996852d2694b0f5afc0a76b1bc45c9a4f8204ae8674d/bitarray-3.6.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a50a66fa34dd7f9dcdbc7602a1b7bf6f9ab030b4f43e892324193423d9ede180", size = 324788, upload-time = "2025-07-29T18:01:51.454Z" }, + { url = "https://files.pythonhosted.org/packages/82/e8/80620fc60ad34bff647881a4f25c15b992c524e0f7af9c7c6c573b03556e/bitarray-3.6.0-cp313-cp313-win32.whl", hash = "sha256:afa24e5750c9b89ad5a7efef037efe49f4e339f20a94bf678c422c0c71e1207a", size = 137841, upload-time = "2025-07-29T18:01:52.95Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ee/303be88b847da29a067babc690e231d7838520dc1af57d14dad5a7ca095c/bitarray-3.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4c5e7edf1e7bcbde3b52058f171a411e2a24a081b3e951d685dfea4c3c383d5", size = 144820, upload-time = "2025-07-29T18:01:54.137Z" }, +] + +[[package]] +name = "bitstring" +version = "4.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bitarray" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/a8/a80c890db75d5bdd5314b5de02c4144c7de94fd0cefcae51acaeb14c6a3f/bitstring-4.3.1.tar.gz", hash = "sha256:a08bc09d3857216d4c0f412a1611056f1cc2b64fd254fb1e8a0afba7cfa1a95a", size = 251426, upload-time = "2025-03-22T09:39:06.978Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/2d/174566b533755ddf8efb32a5503af61c756a983de379f8ad3aed6a982d38/bitstring-4.3.1-py3-none-any.whl", hash = "sha256:69d1587f0ac18dc7d93fc7e80d5f447161a33e57027e726dc18a0a8bacf1711a", size = 71930, upload-time = "2025-03-22T09:39:05.163Z" }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621, upload-time = "2024-09-04T20:45:21.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989, upload-time = "2024-09-04T20:44:28.956Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802, upload-time = "2024-09-04T20:44:30.289Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792, upload-time = "2024-09-04T20:44:32.01Z" }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893, upload-time = "2024-09-04T20:44:33.606Z" }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810, upload-time = "2024-09-04T20:44:35.191Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200, upload-time = "2024-09-04T20:44:36.743Z" }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447, upload-time = "2024-09-04T20:44:38.492Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358, upload-time = "2024-09-04T20:44:40.046Z" }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469, upload-time = "2024-09-04T20:44:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475, upload-time = "2024-09-04T20:44:43.733Z" }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload-time = "2024-09-04T20:44:45.309Z" }, +] + +[[package]] +name = "coincurve" +version = "20.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asn1crypto" }, + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/4c/9e5e51e6c12cec6444c86697992f9c6ccffa19f84d042ff939c8b89206ff/coincurve-20.0.0.tar.gz", hash = "sha256:872419e404300302e938849b6b92a196fabdad651060b559dc310e52f8392829", size = 122865, upload-time = "2024-06-02T18:15:50.787Z" } + +[[package]] +name = "cryptography" +version = "42.0.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/a7/1498799a2ea06148463a9a2c10ab2f6a921a74fb19e231b27dc412a748e2/cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2", size = 671250, upload-time = "2024-06-04T19:55:08.609Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/8b/1b929ba8139430e09e140e6939c2b29c18df1f2fc2149e41bdbdcdaf5d1f/cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e", size = 5899961, upload-time = "2024-06-04T19:53:57.933Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5d/31d833daa800e4fab33209843095df7adb4a78ea536929145534cbc15026/cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d", size = 3114353, upload-time = "2024-06-04T19:54:12.171Z" }, + { url = "https://files.pythonhosted.org/packages/5d/32/f6326c70a9f0f258a201d3b2632bca586ea24d214cec3cf36e374040e273/cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902", size = 3647773, upload-time = "2024-06-04T19:54:07.051Z" }, + { url = "https://files.pythonhosted.org/packages/35/66/2d87e9ca95c82c7ee5f2c09716fc4c4242c1ae6647b9bd27e55e920e9f10/cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801", size = 3839763, upload-time = "2024-06-04T19:54:30.383Z" }, + { url = "https://files.pythonhosted.org/packages/c2/de/8083fa2e68d403553a01a9323f4f8b9d7ffed09928ba25635c29fb28c1e7/cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949", size = 3632661, upload-time = "2024-06-04T19:54:32.955Z" }, + { url = "https://files.pythonhosted.org/packages/07/40/d6f6819c62e808ea74639c3c640f7edd636b86cce62cb14943996a15df92/cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9", size = 3851536, upload-time = "2024-06-04T19:53:53.131Z" }, + { url = "https://files.pythonhosted.org/packages/5c/46/de71d48abf2b6d3c808f4fbb0f4dc44a4e72786be23df0541aa2a3f6fd7e/cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583", size = 3754209, upload-time = "2024-06-04T19:54:55.259Z" }, + { url = "https://files.pythonhosted.org/packages/25/c9/86f04e150c5d5d5e4a731a2c1e0e43da84d901f388e3fea3d5de98d689a7/cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7", size = 3923551, upload-time = "2024-06-04T19:54:16.46Z" }, + { url = "https://files.pythonhosted.org/packages/53/c2/903014dafb7271fb148887d4355b2e90319cad6e810663be622b0c933fc9/cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b", size = 3739265, upload-time = "2024-06-04T19:54:23.194Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/82d704d988a193cbdc69ac3b41c687c36eaed1642cce52530ad810c35645/cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7", size = 3937371, upload-time = "2024-06-04T19:55:04.303Z" }, + { url = "https://files.pythonhosted.org/packages/cf/71/4e0d05c9acd638a225f57fb6162aa3d03613c11b76893c23ea4675bb28c5/cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2", size = 2438849, upload-time = "2024-06-04T19:54:27.39Z" }, + { url = "https://files.pythonhosted.org/packages/06/0f/78da3cad74f2ba6c45321dc90394d70420ea846730dc042ef527f5a224b5/cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba", size = 2889090, upload-time = "2024-06-04T19:54:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/60/12/f064af29190cdb1d38fe07f3db6126091639e1dece7ec77c4ff037d49193/cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28", size = 5901232, upload-time = "2024-06-04T19:54:52.722Z" }, + { url = "https://files.pythonhosted.org/packages/43/c2/4a3eef67e009a522711ebd8ac89424c3a7fe591ece7035d964419ad52a1d/cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e", size = 3648711, upload-time = "2024-06-04T19:54:44.323Z" }, + { url = "https://files.pythonhosted.org/packages/49/1c/9f6d13cc8041c05eebff1154e4e71bedd1db8e174fff999054435994187a/cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70", size = 3841968, upload-time = "2024-06-04T19:54:57.911Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f9/c3d4f19b82bdb25a3d857fe96e7e571c981810e47e3f299cc13ac429066a/cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c", size = 3633032, upload-time = "2024-06-04T19:54:48.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e2/b7e6e8c261536c489d9cf908769880d94bd5d9a187e166b0dc838d2e6a56/cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7", size = 3852478, upload-time = "2024-06-04T19:54:50.599Z" }, + { url = "https://files.pythonhosted.org/packages/a2/68/e16751f6b859bc120f53fddbf3ebada5c34f0e9689d8af32884d8b2e4b4c/cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e", size = 3754102, upload-time = "2024-06-04T19:54:46.231Z" }, + { url = "https://files.pythonhosted.org/packages/0f/38/85c74d0ac4c540780e072b1e6f148ecb718418c1062edcb20d22f3ec5bbb/cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961", size = 3925042, upload-time = "2024-06-04T19:54:34.767Z" }, + { url = "https://files.pythonhosted.org/packages/89/f4/a8b982e88eb5350407ebdbf4717b55043271d878705329e107f4783555f2/cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1", size = 3738833, upload-time = "2024-06-04T19:54:05.231Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2b/be327b580645927bb1a1f32d5a175b897a9b956bc085b095e15c40bac9ed/cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14", size = 3938751, upload-time = "2024-06-04T19:54:37.837Z" }, + { url = "https://files.pythonhosted.org/packages/3c/d5/c6a78ffccdbe4516711ebaa9ed2c7eb6ac5dfa3dc920f2c7e920af2418b0/cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c", size = 2439281, upload-time = "2024-06-04T19:53:55.903Z" }, + { url = "https://files.pythonhosted.org/packages/a2/7b/b0d330852dd5953daee6b15f742f15d9f18e9c0154eb4cfcc8718f0436da/cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a", size = 2886038, upload-time = "2024-06-04T19:54:18.707Z" }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736, upload-time = "2024-03-30T13:22:22.564Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552, upload-time = "2024-03-30T13:22:20.476Z" }, +] + +[[package]] +name = "pyln-bolt7" +version = "1.0.246" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/3c/6d1d6643c6a501e998c17c25d40c3b68ab75be1d16d9055e6a9ffba30fe4/pyln-bolt7-1.0.246.tar.gz", hash = "sha256:2b53744fa21c1b12d2c9c9df153651b122e38fa65d4a5c3f2957317ee148e089", size = 17905, upload-time = "2022-08-31T17:28:20.093Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/f1/30b626e7cec279a2f84084898594c8e84537d6d9af9afbe9858f9a6d8e13/pyln_bolt7-1.0.246-py3-none-any.whl", hash = "sha256:54d48ec27fdc8751762cb068b0a9f2757a58fb57933c6d8f8255d02c27eb63c5", size = 18811, upload-time = "2022-08-31T17:28:22.137Z" }, +] + +[[package]] +name = "pyln-client" +version = "25.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyln-bolt7" }, + { name = "pyln-proto" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/f5/70dae7e6985ba44f53e1c634e204ecc4e9ccde8de6cd90e009672203023f/pyln_client-25.5.tar.gz", hash = "sha256:18656e667c7218f8b40c70f893b936aef4518e87e3ce99081bd52f89e6e5af85", size = 36015, upload-time = "2025-06-16T18:13:48.973Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/45/c504ed8ea59577d8093d377e2f657482f08e87b93aaa039e64421297ff55/pyln_client-25.5-py3-none-any.whl", hash = "sha256:269f9ab346ff679b532af2d24c2a524201884efc5b4781dd40685c8ccaec9507", size = 37362, upload-time = "2025-06-16T18:13:47.394Z" }, +] + +[[package]] +name = "pyln-proto" +version = "25.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "base58" }, + { name = "bitstring" }, + { name = "coincurve" }, + { name = "cryptography" }, + { name = "pysocks" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/44/6f/48b7879c11b698b0004e1b9cf820ce26454fbfa893181a009df7d7184189/pyln_proto-25.5.tar.gz", hash = "sha256:c5e38b726123af723f8c6a4f38ab310cbec46579f52c8e6f666e6beef320b96c", size = 27314, upload-time = "2025-06-16T18:13:46.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/31/4f5ef88ec5abe6a2e2b84f1bb5e1d0818d07f8afd372570916493849bf6c/pyln_proto-25.5-py3-none-any.whl", hash = "sha256:31abcf193744d6253b7f06b7712983c6a4d4c28815d46e1f3803eac17bfe1cf9", size = 31902, upload-time = "2025-06-16T18:13:45.07Z" }, +] + +[[package]] +name = "pysocks" +version = "1.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0", size = 284429, upload-time = "2019-09-20T02:07:35.714Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/59/b4572118e098ac8e46e399a1dd0f2d85403ce8bbaad9ec79373ed6badaf9/PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", size = 16725, upload-time = "2019-09-20T02:06:22.938Z" }, +] + +[[package]] +name = "testpluguv" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pyln-client" }, +] + +[package.metadata] +requires-dist = [{ name = "pyln-client", specifier = ">=24.4" }] diff --git a/tests/data/recklessrepo/rkls_api_lightningd_plugins.json b/tests/data/recklessrepo/rkls_api_lightningd_plugins.json index 3a0484f4f1ee..a91c4844d898 100644 --- a/tests/data/recklessrepo/rkls_api_lightningd_plugins.json +++ b/tests/data/recklessrepo/rkls_api_lightningd_plugins.json @@ -8,6 +8,15 @@ "download_url": null, "type": "dir" }, + { + "name": "testpluguv", + "path": "testpluguv", + "url": "https://api.github.com/repos/lightningd/plugins/contents/webhook?ref=master", + "html_url": "https://github.com/lightningd/plugins/tree/master/testpluguv", + "git_url": "https://api.github.com/repos/lightningd/plugins/git/trees/testpluguv", + "download_url": null, + "type": "dir" + }, { "name": "testplugfail", "path": "testplugfail", diff --git a/tests/test_reckless.py b/tests/test_reckless.py index e0244fa10659..64efa6ab6418 100644 --- a/tests/test_reckless.py +++ b/tests/test_reckless.py @@ -5,6 +5,7 @@ from pyln.testing.utils import VALGRIND import pytest import os +import re import shutil import time import unittest @@ -91,25 +92,66 @@ def canned_github_server(directory): server.terminate() +class RecklessResult: + def __init__(self, process, returncode, stdout, stderr): + self.process = process + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + + def __repr__(self): + return f'self.returncode, self.stdout, self.stderr' + + def search_stdout(self, regex): + """return the matching regex line from reckless output.""" + ex = re.compile(regex) + matching = [] + for line in self.stdout: + if ex.search(line): + matching.append(line) + return matching + + def check_stderr(self): + def output_okay(out): + for warning in ['[notice]', 'WARNING:', 'npm WARN', + 'npm notice', 'DEPRECATION:', 'Creating virtualenv', + 'config file not found:', 'press [Y]']: + if out.startswith(warning): + return True + return False + for e in self.stderr: + if len(e) < 1: + continue + # Don't err on verbosity from pip, npm + if not output_okay(e): + raise Exception(f'reckless stderr contains `{e}`') + + def reckless(cmds: list, dir: PosixPath = None, - autoconfirm=True, timeout: int = 15): + autoconfirm=True, timeout: int = 60): '''Call the reckless executable, optionally with a directory.''' if dir is not None: cmds.insert(0, "-l") cmds.insert(1, str(dir)) cmds.insert(0, "tools/reckless") + if autoconfirm: + process_input = 'Y\n' + else: + process_input = None r = subprocess.run(cmds, capture_output=True, encoding='utf-8', env=my_env, - input='Y\n') + input=process_input, timeout=timeout) + stdout = r.stdout.splitlines() + stderr = r.stderr.splitlines() print(" ".join(r.args), "\n") print("***RECKLESS STDOUT***") - for l in r.stdout.splitlines(): + for l in stdout: print(l) print('\n') print("***RECKLESS STDERR***") - for l in r.stderr.splitlines(): + for l in stderr: print(l) print('\n') - return r + return RecklessResult(r, r.returncode, stdout, stderr) def get_reckless_node(node_factory): @@ -119,28 +161,13 @@ def get_reckless_node(node_factory): return node -def check_stderr(stderr): - def output_okay(out): - for warning in ['[notice]', 'WARNING:', 'npm WARN', - 'npm notice', 'DEPRECATION:', 'Creating virtualenv', - 'config file not found:', 'press [Y]']: - if out.startswith(warning): - return True - return False - for e in stderr.splitlines(): - if len(e) < 1: - continue - # Don't err on verbosity from pip, npm - assert output_okay(e) - - def test_basic_help(): '''Validate that argparse provides basic help info. This requires no config options passed to reckless.''' r = reckless(["-h"]) assert r.returncode == 0 - assert "positional arguments:" in r.stdout.splitlines() - assert "options:" in r.stdout.splitlines() or "optional arguments:" in r.stdout.splitlines() + assert r.search_stdout("positional arguments:") + assert r.search_stdout("options:") or r.search_stdout("optional arguments:") def test_contextual_help(node_factory): @@ -149,7 +176,7 @@ def test_contextual_help(node_factory): 'enable', 'disable', 'source']: r = reckless([subcmd, "-h"], dir=n.lightning_dir) assert r.returncode == 0 - assert "positional arguments:" in r.stdout.splitlines() + assert r.search_stdout("positional arguments:") def test_sources(node_factory): @@ -194,7 +221,7 @@ def test_search(node_factory): n = get_reckless_node(node_factory) r = reckless([f"--network={NETWORK}", "search", "testplugpass"], dir=n.lightning_dir) assert r.returncode == 0 - assert 'found testplugpass in source: https://github.com/lightningd/plugins' in r.stdout + assert r.search_stdout('found testplugpass in source: https://github.com/lightningd/plugins') def test_install(node_factory): @@ -202,10 +229,10 @@ def test_install(node_factory): n = get_reckless_node(node_factory) r = reckless([f"--network={NETWORK}", "-v", "install", "testplugpass"], dir=n.lightning_dir) assert r.returncode == 0 - assert 'dependencies installed successfully' in r.stdout - assert 'plugin installed:' in r.stdout - assert 'testplugpass enabled' in r.stdout - check_stderr(r.stderr) + assert r.search_stdout('dependencies installed successfully') + assert r.search_stdout('plugin installed:') + assert r.search_stdout('testplugpass enabled') + r.check_stderr() plugin_path = Path(n.lightning_dir) / 'reckless/testplugpass' print(plugin_path) assert os.path.exists(plugin_path) @@ -217,10 +244,10 @@ def test_poetry_install(node_factory): n = get_reckless_node(node_factory) r = reckless([f"--network={NETWORK}", "-v", "install", "testplugpyproj"], dir=n.lightning_dir) assert r.returncode == 0 - assert 'dependencies installed successfully' in r.stdout - assert 'plugin installed:' in r.stdout - assert 'testplugpyproj enabled' in r.stdout - check_stderr(r.stderr) + assert r.search_stdout('dependencies installed successfully') + assert r.search_stdout('plugin installed:') + assert r.search_stdout('testplugpyproj enabled') + r.check_stderr() plugin_path = Path(n.lightning_dir) / 'reckless/testplugpyproj' print(plugin_path) assert os.path.exists(plugin_path) @@ -240,7 +267,7 @@ def test_local_dir_install(node_factory): assert r.returncode == 0 r = reckless([f"--network={NETWORK}", "-v", "install", "testplugpass"], dir=n.lightning_dir) assert r.returncode == 0 - assert 'testplugpass enabled' in r.stdout + assert r.search_stdout('testplugpass enabled') plugin_path = Path(n.lightning_dir) / 'reckless/testplugpass' print(plugin_path) assert os.path.exists(plugin_path) @@ -249,9 +276,9 @@ def test_local_dir_install(node_factory): r = reckless(['uninstall', 'testplugpass', '-v'], dir=n.lightning_dir) assert not os.path.exists(plugin_path) r = reckless(['source', 'remove', source_dir], dir=n.lightning_dir) - assert 'plugin source removed' in r.stdout + assert r.search_stdout('plugin source removed') r = reckless(['install', '-v', source_dir], dir=n.lightning_dir) - assert 'testplugpass enabled' in r.stdout + assert r.search_stdout('testplugpass enabled') assert os.path.exists(plugin_path) @@ -263,10 +290,10 @@ def test_disable_enable(node_factory): r = reckless([f"--network={NETWORK}", "-v", "install", "testPlugPass"], dir=n.lightning_dir) assert r.returncode == 0 - assert 'dependencies installed successfully' in r.stdout - assert 'plugin installed:' in r.stdout - assert 'testplugpass enabled' in r.stdout - check_stderr(r.stderr) + assert r.search_stdout('dependencies installed successfully') + assert r.search_stdout('plugin installed:') + assert r.search_stdout('testplugpass enabled') + r.check_stderr() plugin_path = Path(n.lightning_dir) / 'reckless/testplugpass' print(plugin_path) assert os.path.exists(plugin_path) @@ -278,7 +305,7 @@ def test_disable_enable(node_factory): r = reckless([f"--network={NETWORK}", "-v", "enable", "testplugpass.py"], dir=n.lightning_dir) assert r.returncode == 0 - assert 'testplugpass enabled' in r.stdout + assert r.search_stdout('testplugpass enabled') test_plugin = {'name': str(plugin_path / 'testplugpass.py'), 'active': True, 'dynamic': True} time.sleep(1) @@ -322,3 +349,19 @@ def test_tag_install(node_factory): if header == 'requested commit': assert line == 'v1' header = line + + +def test_reckless_uv_install(node_factory): + node = get_reckless_node(node_factory) + node.start() + r = reckless([f"--network={NETWORK}", "-v", "install", "testpluguv"], + dir=node.lightning_dir) + assert r.returncode == 0 + installed_path = Path(node.lightning_dir) / 'reckless/testpluguv' + assert installed_path.is_dir() + assert node.rpc.uvplugintest() == 'I live.' + version = node.rpc.getuvpluginversion() + assert version == 'v1' + + assert r.search_stdout('using installer pythonuv') + r.check_stderr() diff --git a/tools/reckless b/tools/reckless index 891bedb05b95..5dba0daaa6a7 100755 --- a/tools/reckless +++ b/tools/reckless @@ -821,6 +821,9 @@ class InferInstall(): for tier in range(0, 10): # Look for each installers preferred entrypoint format first for inst in INSTALLERS: + # All of this installer's entrypoint options exhausted. + if tier >= len(inst.entries): + continue fmt = inst.entries[tier] if '{name}' in fmt: pre = fmt.split('{name}')[0] @@ -983,6 +986,82 @@ def cargo_installation(cloned_plugin: InstInfo): return cloned_plugin +def install_python_uv(cloned_plugin: InstInfo): + """This uses the rust-based python plugin manager uv to manage the python + installation and create a virtual environment.""" + + source = Path(cloned_plugin.source_loc) / 'source' / cloned_plugin.name + # This virtual env path matches the other python installations and allows + # creating the wrapper in the same manner. Otherwise uv would build it in + # the source/{name} subdirectory. + cloned_plugin.venv = Path('.venv') + + # We want the virtual env at the head of our directory structure and uv + # will need a pyproject.toml there in order to get started. + (Path(cloned_plugin.source_loc) / 'pyproject.toml').\ + symlink_to(source / 'pyproject.toml') + + call = ['uv', '-v', 'sync'] + uv = run(call, cwd=str(cloned_plugin.source_loc), stdout=PIPE, stderr=PIPE, + text=True, check=False) + if uv.returncode != 0: + for line in uv.stderr.splitlines(): + log.debug(line) + log.error('Failed to install virtual environment') + raise InstallationFailure('Failed to create virtual environment!') + + # Delete entrypoint symlink so that a venv wrapper can take it's place + (Path(cloned_plugin.source_loc) / cloned_plugin.entry).unlink() + + create_wrapper(cloned_plugin) + return cloned_plugin + + +def install_python_uv_legacy(cloned_plugin: InstInfo): + """Install a python plugin with uv that was created with a requirements.txt. + This requires creating a bare virtual environment with uv first.""" + source = Path(cloned_plugin.source_loc) / 'source' / cloned_plugin.name + cloned_plugin.venv = Path('.venv') + (Path(cloned_plugin.source_loc) / 'pyproject.toml').\ + symlink_to(source / 'pyproject.toml') + (Path(cloned_plugin.source_loc) / 'requirements.txt').\ + symlink_to(source / 'requirements.txt') + + venv = run(['uv', '-v', 'venv'], cwd=str(cloned_plugin.source_loc), + stdout=PIPE, stderr=PIPE, text=True, check=False) + if venv.returncode != 0: + for line in venv.stderr.splitlines(): + log.debug(line) + log.error('Failed to create virtual environment') + raise InstallationFailure('Failed to create virtual environment!') + for line in venv.stdout.splitlines(): + log.debug(line) + for line in venv.stderr.splitlines(): + log.debug(line) + # Running this as a shell allows overriding any active virtual environment + # which would make uv skip installing packages already present in the + # current env. + call = ['. .venv/bin/activate; uv -v pip install -r requirements.txt'] + uv = run(call, shell=True, cwd=str(cloned_plugin.source_loc), + stdout=PIPE, stderr=PIPE, text=True, check=False) + if uv.returncode != 0: + for line in uv.stderr.splitlines(): + log.debug(line) + log.error('Failed to install virtual environment') + raise InstallationFailure('Failed to create virtual environment!') + for line in uv.stdout.splitlines(): + log.debug(line) + for line in uv.stderr.splitlines(): + log.debug(line) + + # Delete entrypoint symlink so that a venv wrapper can take it's place + (Path(cloned_plugin.source_loc) / cloned_plugin.entry).unlink() + + create_wrapper(cloned_plugin) + log.info('dependencies installed successfully') + return cloned_plugin + + python3venv = Installer('python3venv', exe='python3', manager='pip', entry='{name}.py') python3venv.add_entrypoint('{name}') @@ -994,6 +1073,7 @@ poetryvenv = Installer('poetryvenv', exe='python3', manager='poetry', entry='{name}.py') poetryvenv.add_entrypoint('{name}') poetryvenv.add_entrypoint('__init__.py') +poetryvenv.add_dependency_file('poetry.lock') poetryvenv.add_dependency_file('pyproject.toml') poetryvenv.dependency_call = install_to_python_virtual_environment @@ -1004,6 +1084,13 @@ pyprojectViaPip.add_entrypoint('__init__.py') pyprojectViaPip.add_dependency_file('pyproject.toml') pyprojectViaPip.dependency_call = install_to_python_virtual_environment +pythonuv = Installer('pythonuv', exe='python3', manager='uv', entry="{name}.py") +pythonuv.add_dependency_file('uv.lock') +pythonuv.dependency_call = install_python_uv + +pythonuvlegacy = Installer('pythonuvlegacy', exe='python3', manager='uv', entry='{name}.py') +pythonuvlegacy.add_dependency_file('requirements.txt') +pythonuvlegacy.dependency_call = install_python_uv_legacy # Nodejs plugin installer nodejs = Installer('nodejs', exe='node', @@ -1017,7 +1104,8 @@ rust_cargo = Installer('rust', manager='cargo', entry='Cargo.toml') rust_cargo.add_dependency_file('Cargo.toml') rust_cargo.dependency_call = cargo_installation -INSTALLERS = [python3venv, poetryvenv, pyprojectViaPip, nodejs, rust_cargo] +INSTALLERS = [pythonuv, pythonuvlegacy, python3venv, poetryvenv, + pyprojectViaPip, nodejs, rust_cargo] def help_alias(targets: list): @@ -1395,13 +1483,13 @@ def install(plugin_name: str) -> Union[str, None]: src = None if direct_location: logging.debug(f"install of {name} requested from {direct_location}") - src = InstInfo(name, direct_location, None) - if not src.get_inst_details(): - src = None + src = InstInfo(name, direct_location, name) # Treating a local git repo as a directory allows testing # uncommitted changes. if src and src.srctype == Source.LOCAL_REPO: src.srctype = Source.DIRECTORY + if not src.get_inst_details(): + src = None if not direct_location or not src: log.debug(f"Searching for {name}") if search(name):