From 404873d37020ac3304623ad10e75c5c7347450ec Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 4 Dec 2025 13:11:22 -0500 Subject: [PATCH 01/48] Remove interpreter from base project config --- .idea/bpod-rig.iml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.idea/bpod-rig.iml b/.idea/bpod-rig.iml index af14d19..120c1ed 100644 --- a/.idea/bpod-rig.iml +++ b/.idea/bpod-rig.iml @@ -5,11 +5,10 @@ - - \ No newline at end of file + From 7b31ee605fbe7a5c15088fda820bc85cb65d706a Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 4 Dec 2025 13:14:55 -0500 Subject: [PATCH 02/48] Add test dir to project structure --- .idea/bpod-rig.iml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.idea/bpod-rig.iml b/.idea/bpod-rig.iml index 120c1ed..80eecec 100644 --- a/.idea/bpod-rig.iml +++ b/.idea/bpod-rig.iml @@ -3,6 +3,7 @@ + @@ -11,4 +12,4 @@ - + \ No newline at end of file From ae335f78e28218e4635460516fbd78c04ea2cf98 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 4 Dec 2025 13:25:10 -0500 Subject: [PATCH 03/48] Add George, small reorganization --- pyproject.toml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f240401..cb77165 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,15 +13,20 @@ authors = [ { name = "Austin Pauley", email = "pauley@psy.fsu.edu" }, { name = "Florian Rau", email = "bimac@users.noreply.github.com" }, { name = "Steven Smith", email = "steven@stevenlsmith.com" }, + { name = "George Stuyt", email = "eorge.stuyt@gmail.com"} ] requires-python = ">=3.10" dependencies = [ "bpod-core", #"bpod-gui" - "bpod-core>=0.1.0a2", + "bpod-core>=0.1.0a2", "pydantic==2.11.7", ] +[dependency-groups] +dev = [{ include-group = "test" }] +test = ["pytest>=8.4.2", "pytest-cov>=7.0.0"] + [project.urls] Changelog = "https://github.com/sanworks/bpod-rig/blob/main/CHANGELOG.md" # Documentation = "https://int-brain-lab.github.io/bpod-core/" @@ -152,7 +157,3 @@ docstring-code-format = false # This only has an effect when the `docstring-code-format` setting is # enabled. docstring-code-line-length = "dynamic" - -[dependency-groups] -dev = [{ include-group = "test" }] -test = ["pytest>=8.4.2", "pytest-cov>=7.0.0"] From bbebf1ea51e4a215175938ee67c8f624bd7da4fe Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Sun, 7 Dec 2025 22:48:24 -0500 Subject: [PATCH 04/48] Remove these unused dependencies for the moment --- pyproject.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cb77165..394a2c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,9 +17,6 @@ authors = [ ] requires-python = ">=3.10" dependencies = [ - "bpod-core", - #"bpod-gui" - "bpod-core>=0.1.0a2", "pydantic==2.11.7", ] From 2dad11c125548226d47d1103412ef268cd6ce6ca Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Sun, 7 Dec 2025 23:04:04 -0500 Subject: [PATCH 05/48] update uv.lock to reflect dependency changes --- uv.lock | 258 +------------------------------------------------------- 1 file changed, 1 insertion(+), 257 deletions(-) diff --git a/uv.lock b/uv.lock index 4b20f38..bce5691 100644 --- a/uv.lock +++ b/uv.lock @@ -16,30 +16,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] -[[package]] -name = "bpod-core" -version = "0.1.0a2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-types" }, - { name = "graphviz" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pandas" }, - { name = "pydantic" }, - { name = "pyserial" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/12/64/600b2193e6d7c6d859e337b15b96697650b447a56b22bae81c5fac3d177d/bpod_core-0.1.0a2.tar.gz", hash = "sha256:e17b57d7404adecedc638e5dc449e26170549c22eaa1a3a6ffd622f63bca5657", size = 19352, upload-time = "2025-05-08T00:58:10.087Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/4d/565baec6af516a5caebb8598e023f1f235a3032792ebf0adc1b927b0468e/bpod_core-0.1.0a2-py3-none-any.whl", hash = "sha256:f8ff6d125eceec2d3b70636797250aac055e91b6f3497455a32437473bf9530a", size = 13137, upload-time = "2025-05-08T00:58:09.257Z" }, -] - [[package]] name = "bpod-rig" version = "0.0.2" source = { editable = "." } dependencies = [ - { name = "bpod-core" }, { name = "pydantic" }, ] @@ -54,11 +35,7 @@ test = [ ] [package.metadata] -requires-dist = [ - { name = "bpod-core" }, - { name = "bpod-core", specifier = ">=0.1.0a2" }, - { name = "pydantic", specifier = "==2.11.7" }, -] +requires-dist = [{ name = "pydantic", specifier = "==2.11.7" }] [package.metadata.requires-dev] dev = [ @@ -195,15 +172,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, ] -[[package]] -name = "graphviz" -version = "0.21" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/b3/3ac91e9be6b761a4b30d66ff165e54439dcd48b83f4e20d644867215f6ca/graphviz-0.21.tar.gz", hash = "sha256:20743e7183be82aaaa8ad6c93f8893c923bd6658a04c32ee115edb3c8a835f78", size = 200434, upload-time = "2025-06-15T09:35:05.824Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, -] - [[package]] name = "iniconfig" version = "2.1.0" @@ -213,133 +181,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] -[[package]] -name = "numpy" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, -] - -[[package]] -name = "numpy" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.12'", - "python_full_version == '3.11.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372, upload-time = "2025-06-21T12:28:33.469Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/c7/87c64d7ab426156530676000c94784ef55676df2f13b2796f97722464124/numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", size = 21199346, upload-time = "2025-06-21T11:47:47.57Z" }, - { url = "https://files.pythonhosted.org/packages/58/0e/0966c2f44beeac12af8d836e5b5f826a407cf34c45cb73ddcdfce9f5960b/numpy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", size = 14361143, upload-time = "2025-06-21T11:48:10.766Z" }, - { url = "https://files.pythonhosted.org/packages/7d/31/6e35a247acb1bfc19226791dfc7d4c30002cd4e620e11e58b0ddf836fe52/numpy-2.3.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", size = 5378989, upload-time = "2025-06-21T11:48:19.998Z" }, - { url = "https://files.pythonhosted.org/packages/b0/25/93b621219bb6f5a2d4e713a824522c69ab1f06a57cd571cda70e2e31af44/numpy-2.3.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", size = 6912890, upload-time = "2025-06-21T11:48:31.376Z" }, - { url = "https://files.pythonhosted.org/packages/ef/60/6b06ed98d11fb32e27fb59468b42383f3877146d3ee639f733776b6ac596/numpy-2.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", size = 14569032, upload-time = "2025-06-21T11:48:52.563Z" }, - { url = "https://files.pythonhosted.org/packages/75/c9/9bec03675192077467a9c7c2bdd1f2e922bd01d3a69b15c3a0fdcd8548f6/numpy-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", size = 16930354, upload-time = "2025-06-21T11:49:17.473Z" }, - { url = "https://files.pythonhosted.org/packages/6a/e2/5756a00cabcf50a3f527a0c968b2b4881c62b1379223931853114fa04cda/numpy-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", size = 15879605, upload-time = "2025-06-21T11:49:41.161Z" }, - { url = "https://files.pythonhosted.org/packages/ff/86/a471f65f0a86f1ca62dcc90b9fa46174dd48f50214e5446bc16a775646c5/numpy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", size = 18666994, upload-time = "2025-06-21T11:50:08.516Z" }, - { url = "https://files.pythonhosted.org/packages/43/a6/482a53e469b32be6500aaf61cfafd1de7a0b0d484babf679209c3298852e/numpy-2.3.1-cp311-cp311-win32.whl", hash = "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", size = 6603672, upload-time = "2025-06-21T11:50:19.584Z" }, - { url = "https://files.pythonhosted.org/packages/6b/fb/bb613f4122c310a13ec67585c70e14b03bfc7ebabd24f4d5138b97371d7c/numpy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", size = 13024015, upload-time = "2025-06-21T11:50:39.139Z" }, - { url = "https://files.pythonhosted.org/packages/51/58/2d842825af9a0c041aca246dc92eb725e1bc5e1c9ac89712625db0c4e11c/numpy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", size = 10456989, upload-time = "2025-06-21T11:50:55.616Z" }, - { url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664, upload-time = "2025-06-21T12:15:30.845Z" }, - { url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078, upload-time = "2025-06-21T12:15:52.23Z" }, - { url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554, upload-time = "2025-06-21T12:16:01.434Z" }, - { url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560, upload-time = "2025-06-21T12:16:11.895Z" }, - { url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638, upload-time = "2025-06-21T12:16:32.611Z" }, - { url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729, upload-time = "2025-06-21T12:16:57.439Z" }, - { url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330, upload-time = "2025-06-21T12:17:20.638Z" }, - { url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734, upload-time = "2025-06-21T12:17:47.938Z" }, - { url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411, upload-time = "2025-06-21T12:17:58.475Z" }, - { url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973, upload-time = "2025-06-21T12:18:17.601Z" }, - { url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491, upload-time = "2025-06-21T12:18:33.585Z" }, - { url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381, upload-time = "2025-06-21T12:19:04.103Z" }, - { url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726, upload-time = "2025-06-21T12:19:25.599Z" }, - { url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145, upload-time = "2025-06-21T12:19:34.782Z" }, - { url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409, upload-time = "2025-06-21T12:19:45.228Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630, upload-time = "2025-06-21T12:20:06.544Z" }, - { url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546, upload-time = "2025-06-21T12:20:31.002Z" }, - { url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538, upload-time = "2025-06-21T12:20:54.322Z" }, - { url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327, upload-time = "2025-06-21T12:21:21.053Z" }, - { url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330, upload-time = "2025-06-21T12:25:07.447Z" }, - { url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565, upload-time = "2025-06-21T12:25:26.444Z" }, - { url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262, upload-time = "2025-06-21T12:25:42.196Z" }, - { url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593, upload-time = "2025-06-21T12:21:51.664Z" }, - { url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523, upload-time = "2025-06-21T12:22:13.583Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993, upload-time = "2025-06-21T12:22:22.53Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652, upload-time = "2025-06-21T12:22:33.629Z" }, - { url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561, upload-time = "2025-06-21T12:22:55.056Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349, upload-time = "2025-06-21T12:23:20.53Z" }, - { url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053, upload-time = "2025-06-21T12:23:43.697Z" }, - { url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184, upload-time = "2025-06-21T12:24:10.708Z" }, - { url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678, upload-time = "2025-06-21T12:24:21.596Z" }, - { url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697, upload-time = "2025-06-21T12:24:40.644Z" }, - { url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376, upload-time = "2025-06-21T12:24:56.884Z" }, - { url = "https://files.pythonhosted.org/packages/e8/34/facc13b9b42ddca30498fc51f7f73c3d0f2be179943a4b4da8686e259740/numpy-2.3.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", size = 21070637, upload-time = "2025-06-21T12:26:12.518Z" }, - { url = "https://files.pythonhosted.org/packages/65/b6/41b705d9dbae04649b529fc9bd3387664c3281c7cd78b404a4efe73dcc45/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", size = 5304087, upload-time = "2025-06-21T12:26:22.294Z" }, - { url = "https://files.pythonhosted.org/packages/7a/b4/fe3ac1902bff7a4934a22d49e1c9d71a623204d654d4cc43c6e8fe337fcb/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", size = 6817588, upload-time = "2025-06-21T12:26:32.939Z" }, - { url = "https://files.pythonhosted.org/packages/ae/ee/89bedf69c36ace1ac8f59e97811c1f5031e179a37e4821c3a230bf750142/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", size = 14399010, upload-time = "2025-06-21T12:26:54.086Z" }, - { url = "https://files.pythonhosted.org/packages/15/08/e00e7070ede29b2b176165eba18d6f9784d5349be3c0c1218338e79c27fd/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", size = 16752042, upload-time = "2025-06-21T12:27:19.018Z" }, - { url = "https://files.pythonhosted.org/packages/48/6b/1c6b515a83d5564b1698a61efa245727c8feecf308f4091f565988519d20/numpy-2.3.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", size = 12927246, upload-time = "2025-06-21T12:27:38.618Z" }, -] - [[package]] name = "packaging" version = "25.0" @@ -349,55 +190,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] -[[package]] -name = "pandas" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/6f/75aa71f8a14267117adeeed5d21b204770189c0a0025acbdc03c337b28fc/pandas-2.3.1.tar.gz", hash = "sha256:0a95b9ac964fe83ce317827f80304d37388ea77616b1425f0ae41c9d2d0d7bb2", size = 4487493, upload-time = "2025-07-07T19:20:04.079Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/ca/aa97b47287221fa37a49634532e520300088e290b20d690b21ce3e448143/pandas-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:22c2e866f7209ebc3a8f08d75766566aae02bcc91d196935a1d9e59c7b990ac9", size = 11542731, upload-time = "2025-07-07T19:18:12.619Z" }, - { url = "https://files.pythonhosted.org/packages/80/bf/7938dddc5f01e18e573dcfb0f1b8c9357d9b5fa6ffdee6e605b92efbdff2/pandas-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3583d348546201aff730c8c47e49bc159833f971c2899d6097bce68b9112a4f1", size = 10790031, upload-time = "2025-07-07T19:18:16.611Z" }, - { url = "https://files.pythonhosted.org/packages/ee/2f/9af748366763b2a494fed477f88051dbf06f56053d5c00eba652697e3f94/pandas-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f951fbb702dacd390561e0ea45cdd8ecfa7fb56935eb3dd78e306c19104b9b0", size = 11724083, upload-time = "2025-07-07T19:18:20.512Z" }, - { url = "https://files.pythonhosted.org/packages/2c/95/79ab37aa4c25d1e7df953dde407bb9c3e4ae47d154bc0dd1692f3a6dcf8c/pandas-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd05b72ec02ebfb993569b4931b2e16fbb4d6ad6ce80224a3ee838387d83a191", size = 12342360, upload-time = "2025-07-07T19:18:23.194Z" }, - { url = "https://files.pythonhosted.org/packages/75/a7/d65e5d8665c12c3c6ff5edd9709d5836ec9b6f80071b7f4a718c6106e86e/pandas-2.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1b916a627919a247d865aed068eb65eb91a344b13f5b57ab9f610b7716c92de1", size = 13202098, upload-time = "2025-07-07T19:18:25.558Z" }, - { url = "https://files.pythonhosted.org/packages/65/f3/4c1dbd754dbaa79dbf8b537800cb2fa1a6e534764fef50ab1f7533226c5c/pandas-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fe67dc676818c186d5a3d5425250e40f179c2a89145df477dd82945eaea89e97", size = 13837228, upload-time = "2025-07-07T19:18:28.344Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d6/d7f5777162aa9b48ec3910bca5a58c9b5927cfd9cfde3aa64322f5ba4b9f/pandas-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:2eb789ae0274672acbd3c575b0598d213345660120a257b47b5dafdc618aec83", size = 11336561, upload-time = "2025-07-07T19:18:31.211Z" }, - { url = "https://files.pythonhosted.org/packages/76/1c/ccf70029e927e473a4476c00e0d5b32e623bff27f0402d0a92b7fc29bb9f/pandas-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2b0540963d83431f5ce8870ea02a7430adca100cec8a050f0811f8e31035541b", size = 11566608, upload-time = "2025-07-07T19:18:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/ec/d3/3c37cb724d76a841f14b8f5fe57e5e3645207cc67370e4f84717e8bb7657/pandas-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fe7317f578c6a153912bd2292f02e40c1d8f253e93c599e82620c7f69755c74f", size = 10823181, upload-time = "2025-07-07T19:18:36.151Z" }, - { url = "https://files.pythonhosted.org/packages/8a/4c/367c98854a1251940edf54a4df0826dcacfb987f9068abf3e3064081a382/pandas-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6723a27ad7b244c0c79d8e7007092d7c8f0f11305770e2f4cd778b3ad5f9f85", size = 11793570, upload-time = "2025-07-07T19:18:38.385Z" }, - { url = "https://files.pythonhosted.org/packages/07/5f/63760ff107bcf5146eee41b38b3985f9055e710a72fdd637b791dea3495c/pandas-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3462c3735fe19f2638f2c3a40bd94ec2dc5ba13abbb032dd2fa1f540a075509d", size = 12378887, upload-time = "2025-07-07T19:18:41.284Z" }, - { url = "https://files.pythonhosted.org/packages/15/53/f31a9b4dfe73fe4711c3a609bd8e60238022f48eacedc257cd13ae9327a7/pandas-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:98bcc8b5bf7afed22cc753a28bc4d9e26e078e777066bc53fac7904ddef9a678", size = 13230957, upload-time = "2025-07-07T19:18:44.187Z" }, - { url = "https://files.pythonhosted.org/packages/e0/94/6fce6bf85b5056d065e0a7933cba2616dcb48596f7ba3c6341ec4bcc529d/pandas-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d544806b485ddf29e52d75b1f559142514e60ef58a832f74fb38e48d757b299", size = 13883883, upload-time = "2025-07-07T19:18:46.498Z" }, - { url = "https://files.pythonhosted.org/packages/c8/7b/bdcb1ed8fccb63d04bdb7635161d0ec26596d92c9d7a6cce964e7876b6c1/pandas-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b3cd4273d3cb3707b6fffd217204c52ed92859533e31dc03b7c5008aa933aaab", size = 11340212, upload-time = "2025-07-07T19:18:49.293Z" }, - { url = "https://files.pythonhosted.org/packages/46/de/b8445e0f5d217a99fe0eeb2f4988070908979bec3587c0633e5428ab596c/pandas-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:689968e841136f9e542020698ee1c4fbe9caa2ed2213ae2388dc7b81721510d3", size = 11588172, upload-time = "2025-07-07T19:18:52.054Z" }, - { url = "https://files.pythonhosted.org/packages/1e/e0/801cdb3564e65a5ac041ab99ea6f1d802a6c325bb6e58c79c06a3f1cd010/pandas-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:025e92411c16cbe5bb2a4abc99732a6b132f439b8aab23a59fa593eb00704232", size = 10717365, upload-time = "2025-07-07T19:18:54.785Z" }, - { url = "https://files.pythonhosted.org/packages/51/a5/c76a8311833c24ae61a376dbf360eb1b1c9247a5d9c1e8b356563b31b80c/pandas-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7ff55f31c4fcb3e316e8f7fa194566b286d6ac430afec0d461163312c5841e", size = 11280411, upload-time = "2025-07-07T19:18:57.045Z" }, - { url = "https://files.pythonhosted.org/packages/da/01/e383018feba0a1ead6cf5fe8728e5d767fee02f06a3d800e82c489e5daaf/pandas-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dcb79bf373a47d2a40cf7232928eb7540155abbc460925c2c96d2d30b006eb4", size = 11988013, upload-time = "2025-07-07T19:18:59.771Z" }, - { url = "https://files.pythonhosted.org/packages/5b/14/cec7760d7c9507f11c97d64f29022e12a6cc4fc03ac694535e89f88ad2ec/pandas-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56a342b231e8862c96bdb6ab97170e203ce511f4d0429589c8ede1ee8ece48b8", size = 12767210, upload-time = "2025-07-07T19:19:02.944Z" }, - { url = "https://files.pythonhosted.org/packages/50/b9/6e2d2c6728ed29fb3d4d4d302504fb66f1a543e37eb2e43f352a86365cdf/pandas-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ca7ed14832bce68baef331f4d7f294411bed8efd032f8109d690df45e00c4679", size = 13440571, upload-time = "2025-07-07T19:19:06.82Z" }, - { url = "https://files.pythonhosted.org/packages/80/a5/3a92893e7399a691bad7664d977cb5e7c81cf666c81f89ea76ba2bff483d/pandas-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ac942bfd0aca577bef61f2bc8da8147c4ef6879965ef883d8e8d5d2dc3e744b8", size = 10987601, upload-time = "2025-07-07T19:19:09.589Z" }, - { url = "https://files.pythonhosted.org/packages/32/ed/ff0a67a2c5505e1854e6715586ac6693dd860fbf52ef9f81edee200266e7/pandas-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9026bd4a80108fac2239294a15ef9003c4ee191a0f64b90f170b40cfb7cf2d22", size = 11531393, upload-time = "2025-07-07T19:19:12.245Z" }, - { url = "https://files.pythonhosted.org/packages/c7/db/d8f24a7cc9fb0972adab0cc80b6817e8bef888cfd0024eeb5a21c0bb5c4a/pandas-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6de8547d4fdb12421e2d047a2c446c623ff4c11f47fddb6b9169eb98ffba485a", size = 10668750, upload-time = "2025-07-07T19:19:14.612Z" }, - { url = "https://files.pythonhosted.org/packages/0f/b0/80f6ec783313f1e2356b28b4fd8d2148c378370045da918c73145e6aab50/pandas-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:782647ddc63c83133b2506912cc6b108140a38a37292102aaa19c81c83db2928", size = 11342004, upload-time = "2025-07-07T19:19:16.857Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e2/20a317688435470872885e7fc8f95109ae9683dec7c50be29b56911515a5/pandas-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba6aff74075311fc88504b1db890187a3cd0f887a5b10f5525f8e2ef55bfdb9", size = 12050869, upload-time = "2025-07-07T19:19:19.265Z" }, - { url = "https://files.pythonhosted.org/packages/55/79/20d746b0a96c67203a5bee5fb4e00ac49c3e8009a39e1f78de264ecc5729/pandas-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e5635178b387bd2ba4ac040f82bc2ef6e6b500483975c4ebacd34bec945fda12", size = 12750218, upload-time = "2025-07-07T19:19:21.547Z" }, - { url = "https://files.pythonhosted.org/packages/7c/0f/145c8b41e48dbf03dd18fdd7f24f8ba95b8254a97a3379048378f33e7838/pandas-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f3bf5ec947526106399a9e1d26d40ee2b259c66422efdf4de63c848492d91bb", size = 13416763, upload-time = "2025-07-07T19:19:23.939Z" }, - { url = "https://files.pythonhosted.org/packages/b2/c0/54415af59db5cdd86a3d3bf79863e8cc3fa9ed265f0745254061ac09d5f2/pandas-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:1c78cf43c8fde236342a1cb2c34bcff89564a7bfed7e474ed2fffa6aed03a956", size = 10987482, upload-time = "2025-07-07T19:19:42.699Z" }, - { url = "https://files.pythonhosted.org/packages/48/64/2fd2e400073a1230e13b8cd604c9bc95d9e3b962e5d44088ead2e8f0cfec/pandas-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8dfc17328e8da77be3cf9f47509e5637ba8f137148ed0e9b5241e1baf526e20a", size = 12029159, upload-time = "2025-07-07T19:19:26.362Z" }, - { url = "https://files.pythonhosted.org/packages/d8/0a/d84fd79b0293b7ef88c760d7dca69828d867c89b6d9bc52d6a27e4d87316/pandas-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ec6c851509364c59a5344458ab935e6451b31b818be467eb24b0fe89bd05b6b9", size = 11393287, upload-time = "2025-07-07T19:19:29.157Z" }, - { url = "https://files.pythonhosted.org/packages/50/ae/ff885d2b6e88f3c7520bb74ba319268b42f05d7e583b5dded9837da2723f/pandas-2.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:911580460fc4884d9b05254b38a6bfadddfcc6aaef856fb5859e7ca202e45275", size = 11309381, upload-time = "2025-07-07T19:19:31.436Z" }, - { url = "https://files.pythonhosted.org/packages/85/86/1fa345fc17caf5d7780d2699985c03dbe186c68fee00b526813939062bb0/pandas-2.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f4d6feeba91744872a600e6edbbd5b033005b431d5ae8379abee5bcfa479fab", size = 11883998, upload-time = "2025-07-07T19:19:34.267Z" }, - { url = "https://files.pythonhosted.org/packages/81/aa/e58541a49b5e6310d89474333e994ee57fea97c8aaa8fc7f00b873059bbf/pandas-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fe37e757f462d31a9cd7580236a82f353f5713a80e059a29753cf938c6775d96", size = 12704705, upload-time = "2025-07-07T19:19:36.856Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f9/07086f5b0f2a19872554abeea7658200824f5835c58a106fa8f2ae96a46c/pandas-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5db9637dbc24b631ff3707269ae4559bce4b7fd75c1c4d7e13f40edc42df4444", size = 13189044, upload-time = "2025-07-07T19:19:39.999Z" }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -518,15 +310,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] -[[package]] -name = "pyserial" -version = "3.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" }, -] - [[package]] name = "pytest" version = "8.4.2" @@ -559,36 +342,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "pytz" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - [[package]] name = "tomli" version = "2.2.1" @@ -648,12 +401,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/f8/b1/0c11f5058406b3af7 wheels = [ { url = "https://files.pythonhosted.org/packages/17/69/cd203477f944c353c31bade965f880aa1061fd6bf05ded0726ca845b6ff7/typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", size = 14552, upload-time = "2025-05-21T18:55:22.152Z" }, ] - -[[package]] -name = "tzdata" -version = "2025.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, -] From 2ce64584c488655f03b7fee2ae0f76b8bde8f9b9 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Mon, 8 Dec 2025 14:16:04 -0500 Subject: [PATCH 06/48] Default_Setup: add calibration and logs directories; no longer create a unique folder per Bpod device --- bpod_rig/IO/default_setup.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index b8e2faa..284e169 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -6,13 +6,13 @@ from bpod_rig.examples import calibration, settings -DEFAULT_SUBDIRS = ["Config", "Protocols", "Data"] +DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] DEFAULT_DIR_NAME = "Bpod" logger = logging.getLogger(__name__) -def create_default_directories(machine_id: str, default_path_override: Path = None) -> Path: +def create_default_directories(default_path_override: Path = None) -> Path: """ Create the default Bpod folder structure for a given machine. @@ -21,9 +21,7 @@ def create_default_directories(machine_id: str, default_path_override: Path = No Bpod/ Config/ - Machine-/ - Calibration/ - Settings/ + Calibration/ Protocols/ Data/ @@ -33,9 +31,6 @@ def create_default_directories(machine_id: str, default_path_override: Path = No Parameters ---------- - machine_id : str - Name of the serial# or USB port for the target Bpod machine (e.g., "COM3", "EMU"). - Used to create a machine-specific directory inside Config. default_path_override : pathlib.Path, optional A path to override the default Bpod folder location. If not provided, the default location is `~/Documents/Bpod`. @@ -73,14 +68,6 @@ def create_default_directories(machine_id: str, default_path_override: Path = No ) new_path.mkdir(parents=True, exist_ok=True) - # Create machine-specific config folder - machine_config_dir = bpod_folder_path.joinpath("Config", f"Machine-{machine_id}") - machine_config_dir.mkdir(parents=True, exist_ok=True) - - calibration_dir = machine_config_dir.joinpath("Calibration") - settings_dir = machine_config_dir.joinpath("Settings") - calibration_dir.mkdir(parents=True, exist_ok=True) - settings_dir.mkdir(parents=True, exist_ok=True) if is_new_install: logger.info("Bpod user directory initialized to %s", bpod_folder_path) From 27d8d92e9bc1ee2d92de1ae0d1f3edf9a521999d Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Mon, 8 Dec 2025 14:23:22 -0500 Subject: [PATCH 07/48] Copy default files to top level directory instead of a specific bpod directory; let errors move up the stack --- bpod_rig/IO/default_setup.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 284e169..e926b85 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -76,16 +76,17 @@ def create_default_directories(default_path_override: Path = None) -> Path: return bpod_folder_path -def copy_default_files(bpod_folder_path: Path, machine_id: str, override: bool = False): +def copy_default_files(bpod_folder_path: Path, override: bool = False): """ Function to copy default settings and calibration .json files from the included - examples package into the specified machine_id machine directory. + examples package into the calibration and config folders. Parameters ---------- - bpod_folder_path (pathlib.Path): Path to the Bpod directory - machine_id (str): Name of the serial# or USB serial port (e.g., "COM3") - override (bool) (Optional): Overwrite any existing files + bpod_folder_path : pathlib.Path + Path to the Bpod directory + override : bool, optional + Overwrite any existing files Returns ------- @@ -93,8 +94,8 @@ def copy_default_files(bpod_folder_path: Path, machine_id: str, override: bool = """ # Locate target Calibration and Settings directories for this machine - calibration_dir = bpod_folder_path.joinpath("Config", f"Machine-{machine_id}", "Calibration") - settings_dir = bpod_folder_path.joinpath("Config", f"Machine-{machine_id}", "Settings") + calibration_dir = bpod_folder_path.joinpath("Calibration") + settings_dir = bpod_folder_path.joinpath("Config") calibration_example_dir = Path(calibration.__path__[0]) settings_example_dir = Path(settings.__path__[0]) @@ -110,7 +111,8 @@ def copy_default_files(bpod_folder_path: Path, machine_id: str, override: bool = logger.debug("Copying %s to %s...", cal_file, calibration_dir) shutil.copy2(cal_file, calibration_dir) except Exception as e: # NOQA PERF203 - logger.error("Error copying %s! Original Exception: %s", cal_file, e) + logger.error("Error copying %s!", cal_file) + raise e if len(settings_dir_contents) == 0 or override: for setting_file in default_settings_files: @@ -118,6 +120,5 @@ def copy_default_files(bpod_folder_path: Path, machine_id: str, override: bool = logger.debug("Copying %s to %s...", setting_file, settings_dir) shutil.copy2(setting_file, settings_dir) except Exception as e: # NOQA PERF203 - logger.error( - "Error copying %s! Original Exception: %s", setting_file, e - ) + logger.error("Error copying %s!", setting_file) + raise e From 0c44b5cd5225ccc71ffe408f09f2e79eded1631c Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 08:54:14 -0500 Subject: [PATCH 08/48] Ruff check and format --- bpod_rig/IO/default_setup.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index e926b85..e8ce5d6 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -1,4 +1,4 @@ -"""Module to create the default Bpod user directory and associated subdirs""" +"""Module to create the default Bpod user directory and associated subdirs.""" import logging import shutil @@ -40,7 +40,6 @@ def create_default_directories(default_path_override: Path = None) -> Path: pathlib.Path The path to the created Bpod directory. """ - # Default root is the Documents folder default_root_location = Path.home() / "Documents" @@ -52,12 +51,13 @@ def create_default_directories(default_path_override: Path = None) -> Path: is_new_install = False if not bpod_folder_path.exists(): - logger.debug("Creating default Bpod user directory in %s", default_root_location) + logger.debug( + "Creating default Bpod user directory in %s", default_root_location + ) bpod_folder_path.mkdir(parents=True, exist_ok=True) is_new_install = True - else: logger.debug("Bpod user directory found: %s", bpod_folder_path) - - + else: + logger.debug("Bpod user directory found: %s", bpod_folder_path) # Create top-level subdirectories for subdir in DEFAULT_SUBDIRS: @@ -68,11 +68,9 @@ def create_default_directories(default_path_override: Path = None) -> Path: ) new_path.mkdir(parents=True, exist_ok=True) - if is_new_install: logger.info("Bpod user directory initialized to %s", bpod_folder_path) - return bpod_folder_path @@ -92,7 +90,6 @@ def copy_default_files(bpod_folder_path: Path, override: bool = False): ------- None """ - # Locate target Calibration and Settings directories for this machine calibration_dir = bpod_folder_path.joinpath("Calibration") settings_dir = bpod_folder_path.joinpath("Config") From 555907459f0b706af8ae6b9635142535bac01729 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 09:07:57 -0500 Subject: [PATCH 09/48] Add "bpod" CLI command to launch __main__.main() (for now) eventually plan to have a dedicated entrypoint --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 394a2c8..1d0f15d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,12 +31,14 @@ Homepage = "https://github.com/sanworks/bpod-rig" Issues = "https://github.com/sanworks/bpod-rig/issues" Repository = "https://github.com/sanworks/bpod-rig.git" - [tool.setuptools.packages] find = { where = [""], include = ["*", "examples"] } # We want to include the examples folder in the wheel incase its needed # We need to explicitly include examples as it is a reserved name +[project.scripts] +bpod = "bpod_rig.__main__:main" + [tool.ruff] # Exclude a variety of commonly ignored directories. From 6783b11eae15386c8cbf32c4cb584bef4821d8a0 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 09:09:15 -0500 Subject: [PATCH 10/48] __main__: remove unused arguments for create default folder structure --- bpod_rig/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index d4c28e5..df54dcf 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -15,10 +15,10 @@ def main(): logger.info("Initializing bpod-rig!") # Setup folders. "EMU" is the USB serial port argument for the emulator. # create_default_directories() is called later to add folders for each FSM on the PC - bpod_directory = default_setup.create_default_directories("EMU") + bpod_directory = default_setup.create_default_directories() # Populate this machine's folders with the default config and calibration files. - default_setup.copy_default_files(bpod_directory, "EMU") + default_setup.copy_default_files(bpod_directory) inital_system_config = utils.init_system_configuration( bpod_directory From 6edf70f980efd5908769257ae67ad0186b5988f2 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 09:42:08 -0500 Subject: [PATCH 11/48] deafult_setup: Create two functions to save the path to the Bpod directory if the user chooses to override it - create_default_path_file - get_default_path --- bpod_rig/IO/default_setup.py | 70 ++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index e8ce5d6..e112e65 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -119,3 +119,73 @@ def copy_default_files(bpod_folder_path: Path, override: bool = False): except Exception as e: # NOQA PERF203 logger.error("Error copying %s!", setting_file) raise e + + +def create_default_path_file(user_defined_path: Path, file_save_location: Path = None): + """ + Writes the user-defined default path into a file in the default Bpod folder so it + can be retrieved later. + + By default, the Bpod directory is located in the user's 'Documents/Bpod/' directory. + If the user chooses to override that directory, a persistent link to that directory + is written to a .txt file in the default directory to reference later. + + Parameters + ---------- + user_defined_path : pathlib.Path + User-defined default Bpod Path + file_save_location : pathlib.Path, optional + Path to save the bpod_path.txt pointer file to. If not provided defaults to + [USER_HOME_DIR]/Documents/Bpod/ + + Used for testing purposes. Should not be used in normal operation. + + Raises: + FileNotFoundError + If the file_save_location parameter does not exist, this error is raised + + """ + + if not file_save_location: + file_save_location = Path.home() / "Documents/Bpod/" + else: + if not file_save_location.exists(): + logger.error( + "File save directory %s does not exist!", + file_save_location + ) + raise FileNotFoundError( + "File save directory %s does not exist!", + file_save_location + ) + + pointer_file_path = file_save_location / "bpod_path.txt" + + try: + pointer_file_path.write_text(str(user_defined_path)) + except Exception as e: + logger.error("Error writing default path pointer file: %s") + raise e + +def get_default_path_file() -> Path: + """ + Returns the path to the default Bpod directory if the user chose to store it in a + directory other than the default 'Documents/Bpod' directory. + + Returns + ------- + Pathlib.Path: + Path to the user-defined default Bpod directory + + """ + pointer_file_path = Path.home() / "Documents/Bpod/bpod_path.txt" + + try: + if pointer_file_path.exists(): + default_path_pointer = pointer_file_path.read_text() + return Path(default_path_pointer) + else: + raise FileNotFoundError("Default path pointer file %s not found!", pointer_file_path) + except Exception as e: + logger.error("Error reading default path pointer file: %s", pointer_file_path) + raise e From 20f85596d0e911b35e0b69d40ce4502afa307871 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 10:43:31 -0500 Subject: [PATCH 12/48] default_setup: remove unneeded else --- bpod_rig/IO/default_setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index e112e65..11eb25a 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -184,8 +184,10 @@ def get_default_path_file() -> Path: if pointer_file_path.exists(): default_path_pointer = pointer_file_path.read_text() return Path(default_path_pointer) - else: - raise FileNotFoundError("Default path pointer file %s not found!", pointer_file_path) + + raise FileNotFoundError( + "Default path pointer file %s not found!", pointer_file_path + ) except Exception as e: logger.error("Error reading default path pointer file: %s", pointer_file_path) raise e From 3b580f181f594fdbccca95ff26112aa7a7ab55c3 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 10:44:44 -0500 Subject: [PATCH 13/48] default_setup: ruff format; update docstrings --- bpod_rig/IO/default_setup.py | 37 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 11eb25a..589d57f 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -13,8 +13,7 @@ def create_default_directories(default_path_override: Path = None) -> Path: - """ - Create the default Bpod folder structure for a given machine. + """Create the default Bpod folder structure for a given machine. By default, the Bpod directory will be created inside the user's Documents folder with the following top-level subdirectories: @@ -75,9 +74,10 @@ def create_default_directories(default_path_override: Path = None) -> Path: def copy_default_files(bpod_folder_path: Path, override: bool = False): - """ - Function to copy default settings and calibration .json files from the included - examples package into the calibration and config folders. + """Function to copy default files into their respective folders. + + Copies the default calibration and configuration files from the examples module + to their respective directories. Parameters ---------- @@ -89,6 +89,7 @@ def copy_default_files(bpod_folder_path: Path, override: bool = False): Returns ------- None + """ # Locate target Calibration and Settings directories for this machine calibration_dir = bpod_folder_path.joinpath("Calibration") @@ -122,9 +123,7 @@ def copy_default_files(bpod_folder_path: Path, override: bool = False): def create_default_path_file(user_defined_path: Path, file_save_location: Path = None): - """ - Writes the user-defined default path into a file in the default Bpod folder so it - can be retrieved later. + """Writes the user-defined default path into a file. By default, the Bpod directory is located in the user's 'Documents/Bpod/' directory. If the user chooses to override that directory, a persistent link to that directory @@ -140,23 +139,14 @@ def create_default_path_file(user_defined_path: Path, file_save_location: Path = Used for testing purposes. Should not be used in normal operation. - Raises: - FileNotFoundError - If the file_save_location parameter does not exist, this error is raised - """ - if not file_save_location: file_save_location = Path.home() / "Documents/Bpod/" else: if not file_save_location.exists(): - logger.error( - "File save directory %s does not exist!", - file_save_location - ) + logger.error("File save directory %s does not exist!", file_save_location) raise FileNotFoundError( - "File save directory %s does not exist!", - file_save_location + "File save directory %s does not exist!", file_save_location ) pointer_file_path = file_save_location / "bpod_path.txt" @@ -167,10 +157,13 @@ def create_default_path_file(user_defined_path: Path, file_save_location: Path = logger.error("Error writing default path pointer file: %s") raise e + def get_default_path_file() -> Path: - """ - Returns the path to the default Bpod directory if the user chose to store it in a - directory other than the default 'Documents/Bpod' directory. + """Returns the path to the default Bpod directory from the default directory. + + If the user chose to save the Bpod directory in a directory other than the default + 'Documents/Bpod', the user-defined path is saved to 'Documents/Bpod/bpod_path.txt'. + This function reads and returns the user-defined path to the Bpod directory. Returns ------- From 99646c9c15fefb42e6200f94a010aee2dce0989f Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 10:46:46 -0500 Subject: [PATCH 14/48] default_setup: return None if the bpod_path.txt file does not exist --- bpod_rig/IO/default_setup.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 589d57f..eb35a9e 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -158,7 +158,7 @@ def create_default_path_file(user_defined_path: Path, file_save_location: Path = raise e -def get_default_path_file() -> Path: +def get_default_path_file() -> Path | None: """Returns the path to the default Bpod directory from the default directory. If the user chose to save the Bpod directory in a directory other than the default @@ -170,6 +170,11 @@ def get_default_path_file() -> Path: Pathlib.Path: Path to the user-defined default Bpod directory + or + + None + If bpod_path.txt does not exist, return None + """ pointer_file_path = Path.home() / "Documents/Bpod/bpod_path.txt" @@ -177,10 +182,9 @@ def get_default_path_file() -> Path: if pointer_file_path.exists(): default_path_pointer = pointer_file_path.read_text() return Path(default_path_pointer) + else: + return None - raise FileNotFoundError( - "Default path pointer file %s not found!", pointer_file_path - ) except Exception as e: logger.error("Error reading default path pointer file: %s", pointer_file_path) raise e From bc8afe1aed6f69a6cdadd39410984302b8970d75 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 11:56:31 -0500 Subject: [PATCH 15/48] default_setup: wrapper to return the default Bpod directory or the user-defined directory saved to disk --- bpod_rig/IO/cli_io.py | 0 bpod_rig/IO/default_setup.py | 22 ++++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 bpod_rig/IO/cli_io.py diff --git a/bpod_rig/IO/cli_io.py b/bpod_rig/IO/cli_io.py new file mode 100644 index 0000000..e69de29 diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index eb35a9e..920a2a3 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -182,9 +182,27 @@ def get_default_path_file() -> Path | None: if pointer_file_path.exists(): default_path_pointer = pointer_file_path.read_text() return Path(default_path_pointer) - else: - return None + return None + # If the file does not exist, return None except Exception as e: logger.error("Error reading default path pointer file: %s", pointer_file_path) raise e + + +def get_bpod_directory_path() -> Path: + """Returns the path to the Bpod directory. + + Returns + ------- + Pathlib.Path: + Path to the Bpod directory + + """ + default_path_file = get_default_path_file() + + if default_path_file: + return default_path_file + + return Path.home() / "Documents/Bpod" + # If the default path isn't saved to disk, return the default From df11c74921ad3e8cc1b5bae26ad7bd67332b3720 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 11:56:48 -0500 Subject: [PATCH 16/48] default_setup: Start implementing logic for system initialization --- bpod_rig/__main__.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index df54dcf..8fc251f 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -1,21 +1,38 @@ """main entry point for bpod-rig""" import logging +from pydantic import ValidationError from bpod_rig.config import utils +from bpod_rig.config.system_settings import SystemPaths from bpod_rig.IO import default_setup logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) - def main(): ### Everything below is subject to change and is for testing purposes only - logger.info("Initializing bpod-rig!") - # Setup folders. "EMU" is the USB serial port argument for the emulator. - # create_default_directories() is called later to add folders for each FSM on the PC - bpod_directory = default_setup.create_default_directories() + + bpod_dir_verified = False # Let's put this in a BpodSystem class later + + # Initialize Setup + bpod_directory = default_setup.get_bpod_directory_path() + + try: + system_paths = SystemPaths(base_dir=bpod_directory) + bpod_dir_verified = True + except ValidationError as ve: + logger.error("Error initializing system paths: %s", exc_info=ve) + + if not bpod_dir_verified: + logger.info("The Bpod directory at %s failed to verify!" + " This could be due to a partially initialized folder structure or " + "improper file path!", bpod_directory) + response = input(f"Would you like to re-create the default folder structure at {bpod_directory}? (y/n)") + + if response == "y": + bpod_directory = default_setup.create_default_directories() # Populate this machine's folders with the default config and calibration files. default_setup.copy_default_files(bpod_directory) From 00f61afb95fc0e13b6d95a898c46db960c5200b3 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 21:23:33 -0500 Subject: [PATCH 17/48] cli_io: Module to handle IO via CLI --- bpod_rig/IO/cli_io.py | 12 ++++++++++++ pyproject.toml | 1 + uv.lock | 18 +++++++++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bpod_rig/IO/cli_io.py b/bpod_rig/IO/cli_io.py index e69de29..f09e477 100644 --- a/bpod_rig/IO/cli_io.py +++ b/bpod_rig/IO/cli_io.py @@ -0,0 +1,12 @@ +import click + +def yes_no_prompt(prompt: str) -> bool: + response = '' + + while response not in ['y', 'n', 'Y', 'N']: + response = click.input(prompt, prompt_suffix=' [y/N] ') + + if response in ['y', 'Y']: + return True + else: + return False diff --git a/pyproject.toml b/pyproject.toml index 1d0f15d..09d7810 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ authors = [ ] requires-python = ">=3.10" dependencies = [ + "click>=8.3.1", "pydantic==2.11.7", ] diff --git a/uv.lock b/uv.lock index bce5691..6d35840 100644 --- a/uv.lock +++ b/uv.lock @@ -21,6 +21,7 @@ name = "bpod-rig" version = "0.0.2" source = { editable = "." } dependencies = [ + { name = "click" }, { name = "pydantic" }, ] @@ -35,7 +36,10 @@ test = [ ] [package.metadata] -requires-dist = [{ name = "pydantic", specifier = "==2.11.7" }] +requires-dist = [ + { name = "click", specifier = ">=8.3.1" }, + { name = "pydantic", specifier = "==2.11.7" }, +] [package.metadata.requires-dev] dev = [ @@ -47,6 +51,18 @@ test = [ { name = "pytest-cov", specifier = ">=7.0.0" }, ] +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + [[package]] name = "colorama" version = "0.4.6" From 381099f47075bf81c37ce4b2274afb612cf52c22 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 10 Dec 2025 21:40:11 -0500 Subject: [PATCH 18/48] __main__: Recreate default folder structure based on user input --- bpod_rig/__main__.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 8fc251f..86ceab3 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -5,7 +5,7 @@ from bpod_rig.config import utils from bpod_rig.config.system_settings import SystemPaths -from bpod_rig.IO import default_setup +from bpod_rig.IO import default_setup, cli_io logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -26,13 +26,28 @@ def main(): logger.error("Error initializing system paths: %s", exc_info=ve) if not bpod_dir_verified: - logger.info("The Bpod directory at %s failed to verify!" - " This could be due to a partially initialized folder structure or " - "improper file path!", bpod_directory) - response = input(f"Would you like to re-create the default folder structure at {bpod_directory}? (y/n)") - - if response == "y": + logger.info( + "The Bpod directory at %s failed to verify!" + " This could be due to a partially initialized folder structure or " + "improper file path!", + bpod_directory, + ) + response = cli_io.yes_no_prompt( + f"Would you like to (re)initialize {bpod_directory} as the base" + f" Bpod directory?" + ) + + if response: bpod_directory = default_setup.create_default_directories() + try: + system_paths = SystemPaths(base_dir=bpod_directory) + bpod_dir_verified = True + logging.debug("System paths at %s verified", bpod_directory) + except ValidationError as ve: + logger.error("Error initializing system paths: %s", exc_info=ve) + else: + logger.info("No valid Bpod directory! Shutting down.") + return # Populate this machine's folders with the default config and calibration files. default_setup.copy_default_files(bpod_directory) From 66ae924221ec434438e410c24c8c9c60bbf93351 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 11 Dec 2025 15:35:02 -0500 Subject: [PATCH 19/48] config.utils: function to verify standard directories exist in a Bpod directory; based on model's definition of required fields --- bpod_rig/config/utils.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bpod_rig/config/utils.py b/bpod_rig/config/utils.py index 924463e..188fdc2 100644 --- a/bpod_rig/config/utils.py +++ b/bpod_rig/config/utils.py @@ -18,6 +18,24 @@ def init_system_configuration(bpod_dir: Path) -> SystemSettings: system_settings = SystemSettings(paths=system_paths) return system_settings +def verify_bpod_directory(system_paths: SystemPaths) -> bool: + + dir_verified = True + sp_fields = SystemPaths.model_fields + sp_fields = [field for field in sp_fields if field != "metadata"] + sp_as_dict = system_paths.model_dump() + + for field in sp_fields: + field_path = sp_as_dict[field] + if field_path is None: + continue + # Skip None values that are not implemented yet + if not field_path.exists(): + logger.error("Bpod subdirectory %s does not exist", field_path) + dir_verified = False + + return dir_verified + def save_system_configuration( system_settings: SystemSettings, save_dir_override: Path | None = None From 15d3a0c7a9f738ec83431670366f4757715eddf3 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 11 Dec 2025 16:14:34 -0500 Subject: [PATCH 20/48] cli_io: somehow used a function that doesn't exist --- bpod_rig/IO/cli_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpod_rig/IO/cli_io.py b/bpod_rig/IO/cli_io.py index f09e477..1019004 100644 --- a/bpod_rig/IO/cli_io.py +++ b/bpod_rig/IO/cli_io.py @@ -4,7 +4,7 @@ def yes_no_prompt(prompt: str) -> bool: response = '' while response not in ['y', 'n', 'Y', 'N']: - response = click.input(prompt, prompt_suffix=' [y/N] ') + response = click.prompt(prompt, prompt_suffix=' [y/N] ') if response in ['y', 'Y']: return True From 64c0bd9c08d8bcab9599f610ce354c2410fddb2e Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 11 Dec 2025 16:15:16 -0500 Subject: [PATCH 21/48] __main__: verify the Bpod directory and adjust logic to use it --- bpod_rig/__main__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 86ceab3..6aef7ad 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -21,7 +21,7 @@ def main(): try: system_paths = SystemPaths(base_dir=bpod_directory) - bpod_dir_verified = True + bpod_dir_verified = utils.verify_bpod_directory(system_paths) except ValidationError as ve: logger.error("Error initializing system paths: %s", exc_info=ve) @@ -49,8 +49,10 @@ def main(): logger.info("No valid Bpod directory! Shutting down.") return - # Populate this machine's folders with the default config and calibration files. - default_setup.copy_default_files(bpod_directory) + # Populate this machine's folders with the default config and calibration files. + default_setup.copy_default_files(bpod_directory) + else: + logging.debug("System paths at %s verified", bpod_directory) inital_system_config = utils.init_system_configuration( bpod_directory From 6fd182d760652d9b2d812a7259971a59581a1928 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 11 Dec 2025 16:39:42 -0500 Subject: [PATCH 22/48] __main__: rename bpod_directory -> bpod_directory_path for clarity few formatting fixes; user input to copy the example files (for now); pass the path to create_default_directories; some logging --- bpod_rig/__main__.py | 52 ++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 6aef7ad..1f9c134 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -1,4 +1,4 @@ -"""main entry point for bpod-rig""" +"""main entry point for bpod-rig.""" import logging from pydantic import ValidationError @@ -10,17 +10,22 @@ logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) + def main(): ### Everything below is subject to change and is for testing purposes only logger.info("Initializing bpod-rig!") - bpod_dir_verified = False # Let's put this in a BpodSystem class later - # Initialize Setup - bpod_directory = default_setup.get_bpod_directory_path() + ### Find and verify the Bpod directory ### + bpod_directory_path = default_setup.get_bpod_directory_path() + + # TODO: There needs to be an extra check to determine if there has been no + # initialization performed. Will implement once I add in the path library + # suggested by Steven as it will be a much more robust location to store files + # for now we assume verification failure means broken OR never implemented -ACP try: - system_paths = SystemPaths(base_dir=bpod_directory) + system_paths = SystemPaths(base_dir=bpod_directory_path) bpod_dir_verified = utils.verify_bpod_directory(system_paths) except ValidationError as ve: logger.error("Error initializing system paths: %s", exc_info=ve) @@ -30,35 +35,44 @@ def main(): "The Bpod directory at %s failed to verify!" " This could be due to a partially initialized folder structure or " "improper file path!", - bpod_directory, + bpod_directory_path, ) response = cli_io.yes_no_prompt( - f"Would you like to (re)initialize {bpod_directory} as the base" + f"Would you like to (re)initialize {bpod_directory_path} as the base" f" Bpod directory?" ) if response: - bpod_directory = default_setup.create_default_directories() + logging.info("Initializing Bpod directory at %s", bpod_directory_path) + bpod_directory_path = default_setup.create_default_directories( + default_path_override=bpod_directory_path + ) + # TODO: refactor this function to always require a path be given -ACP try: - system_paths = SystemPaths(base_dir=bpod_directory) + system_paths = SystemPaths(base_dir=bpod_directory_path) bpod_dir_verified = True - logging.debug("System paths at %s verified", bpod_directory) except ValidationError as ve: logger.error("Error initializing system paths: %s", exc_info=ve) - else: - logger.info("No valid Bpod directory! Shutting down.") - return - # Populate this machine's folders with the default config and calibration files. - default_setup.copy_default_files(bpod_directory) + response = cli_io.yes_no_prompt( + f"Would you like to copy the default protocols and calibration files to {bpod_directory_path}" + ) + + if response: + default_setup.copy_default_files(bpod_directory_path) + + if bpod_dir_verified: + logging.debug("System paths at %s verified", bpod_directory_path) else: - logging.debug("System paths at %s verified", bpod_directory) + logger.info("No valid Bpod directory! Shutting down.") + return + - inital_system_config = utils.init_system_configuration( - bpod_directory - ) + + inital_system_config = utils.init_system_configuration(bpod_directory_path) result = utils.save_system_configuration(inital_system_config) + if __name__ == "__main__": main() From a747748a28ab41bd96443fd7dd3ab8008d55ff48 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 17 Dec 2025 10:33:02 -0500 Subject: [PATCH 23/48] cli_io: prompt user for path with some error checking --- bpod_rig/IO/cli_io.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/bpod_rig/IO/cli_io.py b/bpod_rig/IO/cli_io.py index 1019004..b938cf9 100644 --- a/bpod_rig/IO/cli_io.py +++ b/bpod_rig/IO/cli_io.py @@ -1,5 +1,10 @@ +import logging +from pathlib import Path + import click +logger = logging.getLogger(__name__) + def yes_no_prompt(prompt: str) -> bool: response = '' @@ -10,3 +15,21 @@ def yes_no_prompt(prompt: str) -> bool: return True else: return False + +def prompt_for_path(prompt: str) -> Path: + validated = False + path = [] + + while not validated: + unverified_path = click.prompt(prompt) + response = yes_no_prompt(f"Is {unverified_path} the correct path?") + if response: + try: + path = Path(unverified_path) + validated = True + except Exception as e: + logger.error("Invalid path: %s", exc_info=e) + click.echo("Invalid path entered, please try again.", err=True) + + return path + From 2d137f048baf3dd5857a01af22470b233fd4ce37 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 17 Dec 2025 10:33:56 -0500 Subject: [PATCH 24/48] Add platformdirs as a dependency --- pyproject.toml | 1 + uv.lock | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 09d7810..cae345c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ authors = [ requires-python = ">=3.10" dependencies = [ "click>=8.3.1", + "platformdirs>=4.5.1", "pydantic==2.11.7", ] diff --git a/uv.lock b/uv.lock index 6d35840..806e115 100644 --- a/uv.lock +++ b/uv.lock @@ -22,6 +22,7 @@ version = "0.0.2" source = { editable = "." } dependencies = [ { name = "click" }, + { name = "platformdirs" }, { name = "pydantic" }, ] @@ -38,6 +39,7 @@ test = [ [package.metadata] requires-dist = [ { name = "click", specifier = ">=8.3.1" }, + { name = "platformdirs", specifier = ">=4.5.1" }, { name = "pydantic", specifier = "==2.11.7" }, ] @@ -206,6 +208,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" From 3a292bdea94b9b8e84ef92e55d68c5668d7a8ad5 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 17 Dec 2025 10:35:21 -0500 Subject: [PATCH 25/48] Default setup: create_default_directories now takes an explicit path --- bpod_rig/IO/default_setup.py | 42 ++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 920a2a3..ff3bb2f 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -9,20 +9,23 @@ DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] DEFAULT_DIR_NAME = "Bpod" -logger = logging.getLogger(__name__) +SYSTEM_CONFIG_DIR = platformdirs.user_config_path(DEFAULT_DIR_NAME) +DEFAULT_BPOD_PATH = platformdirs.user_documents_path() / DEFAULT_DIR_NAME +logger = logging.getLogger(__name__) -def create_default_directories(default_path_override: Path = None) -> Path: - """Create the default Bpod folder structure for a given machine. +def create_default_directories(bpod_directory_path: Path = None) -> Path: + """Create the default Bpod folder structure. - By default, the Bpod directory will be created inside the user's Documents - folder with the following top-level subdirectories: + The Bpod directory will be created inside the given path with the following + top-level subdirectories: Bpod/ Config/ Calibration/ Protocols/ Data/ + Logs/ This function only creates the directory structure — it does NOT populate default calibration or settings files. To copy default files, call @@ -30,47 +33,38 @@ def create_default_directories(default_path_override: Path = None) -> Path: Parameters ---------- - default_path_override : pathlib.Path, optional - A path to override the default Bpod folder location. If not provided, - the default location is `~/Documents/Bpod`. + bpod_directory_path : pathlib.Path + The path to initialize the Bpod folder location Returns ------- pathlib.Path The path to the created Bpod directory. """ - # Default root is the Documents folder - default_root_location = Path.home() / "Documents" - - if default_path_override: - default_root_location = default_path_override - - bpod_folder_path = default_root_location / DEFAULT_DIR_NAME - is_new_install = False - if not bpod_folder_path.exists(): + if not bpod_directory_path.exists(): logger.debug( - "Creating default Bpod user directory in %s", default_root_location + "Creating default Bpod user directory in %s", bpod_directory_path ) - bpod_folder_path.mkdir(parents=True, exist_ok=True) + bpod_directory_path.mkdir(parents=True, exist_ok=True) is_new_install = True else: - logger.debug("Bpod user directory found: %s", bpod_folder_path) + logger.debug("Bpod user directory found: %s", bpod_directory_path) # Create top-level subdirectories for subdir in DEFAULT_SUBDIRS: - new_path = bpod_folder_path.joinpath(subdir) + new_path = bpod_directory_path.joinpath(subdir) if not new_path.exists(): logger.debug( - "Creating default subdirectory [%s] in %s", subdir, bpod_folder_path + "Creating default subdirectory [%s] in %s", subdir, bpod_directory_path ) new_path.mkdir(parents=True, exist_ok=True) if is_new_install: - logger.info("Bpod user directory initialized to %s", bpod_folder_path) + logger.info("Bpod user directory initialized to %s", bpod_directory_path) - return bpod_folder_path + return bpod_directory_path def copy_default_files(bpod_folder_path: Path, override: bool = False): From 07a4f70c7e2296d5594b2f8f67795a1b84742f94 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 17 Dec 2025 10:36:55 -0500 Subject: [PATCH 26/48] default_setup: remove pointer file functions; new function to check whether the system dir exsits and return a path if it has one --- bpod_rig/IO/default_setup.py | 71 ++++-------------------------------- 1 file changed, 8 insertions(+), 63 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index ff3bb2f..d312e70 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -116,72 +116,17 @@ def copy_default_files(bpod_folder_path: Path, override: bool = False): raise e -def create_default_path_file(user_defined_path: Path, file_save_location: Path = None): - """Writes the user-defined default path into a file. - - By default, the Bpod directory is located in the user's 'Documents/Bpod/' directory. - If the user chooses to override that directory, a persistent link to that directory - is written to a .txt file in the default directory to reference later. - - Parameters - ---------- - user_defined_path : pathlib.Path - User-defined default Bpod Path - file_save_location : pathlib.Path, optional - Path to save the bpod_path.txt pointer file to. If not provided defaults to - [USER_HOME_DIR]/Documents/Bpod/ - - Used for testing purposes. Should not be used in normal operation. - - """ - if not file_save_location: - file_save_location = Path.home() / "Documents/Bpod/" +def check_system_config_dir() -> Path: + if SYSTEM_CONFIG_DIR.exists(): + logger.debug("Found system config directory: %s", SYSTEM_CONFIG_DIR) + # Is there a path/config file? else: - if not file_save_location.exists(): - logger.error("File save directory %s does not exist!", file_save_location) - raise FileNotFoundError( - "File save directory %s does not exist!", file_save_location - ) - - pointer_file_path = file_save_location / "bpod_path.txt" - - try: - pointer_file_path.write_text(str(user_defined_path)) - except Exception as e: - logger.error("Error writing default path pointer file: %s") - raise e - - -def get_default_path_file() -> Path | None: - """Returns the path to the default Bpod directory from the default directory. - - If the user chose to save the Bpod directory in a directory other than the default - 'Documents/Bpod', the user-defined path is saved to 'Documents/Bpod/bpod_path.txt'. - This function reads and returns the user-defined path to the Bpod directory. - - Returns - ------- - Pathlib.Path: - Path to the user-defined default Bpod directory - - or - - None - If bpod_path.txt does not exist, return None - - """ - pointer_file_path = Path.home() / "Documents/Bpod/bpod_path.txt" - - try: - if pointer_file_path.exists(): - default_path_pointer = pointer_file_path.read_text() - return Path(default_path_pointer) + logger.debug("No system config directory found!") + logger.info("Bpod has not been initialized on this system!") + logger.info(f"Creating system config directory at {SYSTEM_CONFIG_DIR}") + SYSTEM_CONFIG_DIR.mkdir(parents=True, exist_ok=True) return None - # If the file does not exist, return None - except Exception as e: - logger.error("Error reading default path pointer file: %s", pointer_file_path) - raise e def get_bpod_directory_path() -> Path: From dc91038fd994d2e8733c569e4f3d60121b1814a4 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 17 Dec 2025 10:37:37 -0500 Subject: [PATCH 27/48] default_setup: update get_bpod_directory --- bpod_rig/IO/default_setup.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index d312e70..3ae3f3c 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -4,7 +4,9 @@ import shutil from pathlib import Path +import platformdirs from bpod_rig.examples import calibration, settings +from bpod_rig.IO import cli_io DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] DEFAULT_DIR_NAME = "Bpod" @@ -128,20 +130,22 @@ def check_system_config_dir() -> Path: return None +def get_bpod_directory() -> Path: + path_from_system = check_system_config_dir() -def get_bpod_directory_path() -> Path: - """Returns the path to the Bpod directory. + if path_from_system is None: + logger.info("Bpod has not been initialized on this system!") + response = cli_io.yes_no_prompt( + f"Bpod has not been initialized on this system! Would you like to override the default path {DEFAULT_BPOD_PATH}?" + ) + if response: + logger.debug("User is going to override the path!") + bpod_path = cli_io.prompt_for_path( + "Please enter the path to create the Bpod directory" + ) + else: + bpod_path=DEFAULT_BPOD_PATH - Returns - ------- - Pathlib.Path: - Path to the Bpod directory - """ - default_path_file = get_default_path_file() - if default_path_file: - return default_path_file - return Path.home() / "Documents/Bpod" - # If the default path isn't saved to disk, return the default From 579b70e8d00a24eb12595c33962b9cf387e45921 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 17 Dec 2025 10:38:12 -0500 Subject: [PATCH 28/48] __main__: Remove old todo;call get_bpod_directory() --- bpod_rig/__main__.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 1f9c134..ac8fc30 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -17,12 +17,7 @@ def main(): bpod_dir_verified = False # Let's put this in a BpodSystem class later ### Find and verify the Bpod directory ### - bpod_directory_path = default_setup.get_bpod_directory_path() - - # TODO: There needs to be an extra check to determine if there has been no - # initialization performed. Will implement once I add in the path library - # suggested by Steven as it will be a much more robust location to store files - # for now we assume verification failure means broken OR never implemented -ACP + bpod_directory_path = default_setup.get_bpod_directory() try: system_paths = SystemPaths(base_dir=bpod_directory_path) @@ -44,10 +39,7 @@ def main(): if response: logging.info("Initializing Bpod directory at %s", bpod_directory_path) - bpod_directory_path = default_setup.create_default_directories( - default_path_override=bpod_directory_path - ) - # TODO: refactor this function to always require a path be given -ACP + bpod_directory_path = default_setup.create_default_directories(bpod_directory_path) try: system_paths = SystemPaths(base_dir=bpod_directory_path) bpod_dir_verified = True @@ -57,7 +49,6 @@ def main(): response = cli_io.yes_no_prompt( f"Would you like to copy the default protocols and calibration files to {bpod_directory_path}" ) - if response: default_setup.copy_default_files(bpod_directory_path) @@ -68,9 +59,7 @@ def main(): return - inital_system_config = utils.init_system_configuration(bpod_directory_path) - result = utils.save_system_configuration(inital_system_config) From 13576d0b9529c3eb70ddc4d445f6330826e5e3a0 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:18:21 -0500 Subject: [PATCH 29/48] default_setup: Function to return the Bpod dir from the system config System configuration file constant path --- bpod_rig/IO/default_setup.py | 55 +++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 3ae3f3c..2f0254a 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -5,13 +5,17 @@ from pathlib import Path import platformdirs +from pydantic import ValidationError + from bpod_rig.examples import calibration, settings +from bpod_rig.config import utils from bpod_rig.IO import cli_io DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] DEFAULT_DIR_NAME = "Bpod" SYSTEM_CONFIG_DIR = platformdirs.user_config_path(DEFAULT_DIR_NAME) +SYSTEM_CONFIG_FILE = SYSTEM_CONFIG_DIR / "system_config.json" DEFAULT_BPOD_PATH = platformdirs.user_documents_path() / DEFAULT_DIR_NAME logger = logging.getLogger(__name__) @@ -118,16 +122,53 @@ def copy_default_files(bpod_folder_path: Path, override: bool = False): raise e -def check_system_config_dir() -> Path: +def get_bpod_dir_from_system() -> Path | None: + """Attempts to get the Bpod directory path from the system configuration file + + Checks to see if the system configuration directory exists, if so, check to see + if there is a system configuration file. If there is attempt to read the Bpod + directory path from the configuration file. + + Returns + ------- + pathlib.Path + Path to the Bpod directory read from the system configuration file + + None + If the system configuration directory does not exist, the configuration file + does not exist, or there is an error reading from the file, return None + + """ + if SYSTEM_CONFIG_DIR.exists(): + # Check if the system configuration directory exists logger.debug("Found system config directory: %s", SYSTEM_CONFIG_DIR) - # Is there a path/config file? + if SYSTEM_CONFIG_FILE.exists(): + # Check if the system configuration file exists + logger.debug( + "system_config.json found! Attempting to read Bpod directory path!" + ) + try: + # Attempt to load and read path from system configuration file + system_settings = utils.load_system_configuration(SYSTEM_CONFIG_FILE) + return system_settings.paths.base_dir + except ValidationError as e: + logger.error( + "Configuration file at %s failed to validate!", + SYSTEM_CONFIG_FILE, + exc_info=e, + ) + logger.error( + "Unable to read Bpod directory path from system configuration file!" + ) + else: + logger.warning( + "No system_config.json found! Has system been fully initialized?" + ) else: - logger.debug("No system config directory found!") - logger.info("Bpod has not been initialized on this system!") - logger.info(f"Creating system config directory at {SYSTEM_CONFIG_DIR}") - SYSTEM_CONFIG_DIR.mkdir(parents=True, exist_ok=True) - return None + logger.warning("No system config directory found!") + + return None def get_bpod_directory() -> Path: From 985dbbcca8147521d78522387e1b60ef59d2e9a2 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:28:30 -0500 Subject: [PATCH 30/48] default_setup: function to check if the system is initialized based on the presence of certain directories and files --- bpod_rig/IO/default_setup.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 2f0254a..71482a8 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -171,6 +171,27 @@ def get_bpod_dir_from_system() -> Path | None: return None +def check_system_is_initialized() -> bool: + """Checks whether Bpod has been initialized on this system before + + If the system configuration directory does not exist, the system has not been initialized + If the system configuration directory exists, but the system_config.json file + does not exist, the system likely has only been partially initialized and needs to + be reinitialized. + + Returns + ------ + bool + True if system is initialized, False otherwise + """ + + if SYSTEM_CONFIG_DIR.exists(): + logger.debug("System configuration directory found: %s", SYSTEM_CONFIG_DIR) + if SYSTEM_CONFIG_FILE.exists(): + logger.debug("System configuration file found: %s", SYSTEM_CONFIG_FILE) + return True + return False + def get_bpod_directory() -> Path: path_from_system = check_system_config_dir() From 7343ad814c4f81d31cc5e4fa122c07bc5c7a7fdb Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:30:11 -0500 Subject: [PATCH 31/48] default_setup: Update get_bpod_directory function --- bpod_rig/IO/default_setup.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 71482a8..9a150be 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -192,22 +192,12 @@ def check_system_is_initialized() -> bool: return True return False -def get_bpod_directory() -> Path: - path_from_system = check_system_config_dir() - - if path_from_system is None: - logger.info("Bpod has not been initialized on this system!") - response = cli_io.yes_no_prompt( - f"Bpod has not been initialized on this system! Would you like to override the default path {DEFAULT_BPOD_PATH}?" - ) - if response: - logger.debug("User is going to override the path!") - bpod_path = cli_io.prompt_for_path( - "Please enter the path to create the Bpod directory" - ) - else: - bpod_path=DEFAULT_BPOD_PATH - +def get_bpod_directory() -> Path: + bpod_path = get_bpod_dir_from_system() + if bpod_path is None: + logger.info("Unable to get Bpod path from system configuration!") + bpod_path = DEFAULT_BPOD_PATH + return bpod_path From 0a1539538f331f95d26627bbe62aaec1b20a5778 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:31:09 -0500 Subject: [PATCH 32/48] __main__: update logging messages; check for initialization prompt user for path if not initialized --- bpod_rig/__main__.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index ac8fc30..9fad334 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -13,17 +13,30 @@ def main(): ### Everything below is subject to change and is for testing purposes only - logger.info("Initializing bpod-rig!") + logger.info("Starting bpod-rig!") bpod_dir_verified = False # Let's put this in a BpodSystem class later - ### Find and verify the Bpod directory ### - bpod_directory_path = default_setup.get_bpod_directory() + ### Has Bpod been initialized on this system before? ### + system_initialized = default_setup.check_system_is_initialized() + if not system_initialized: + # Time to do some initialization + bpod_directory_path = [] + response = cli_io.yes_no_prompt( + f"Bpod has not been initialized on this system! Would you like to override the default path {DEFAULT_BPOD_PATH}?" + ) + if response: + logger.debug("User is going to override the path!") + bpod_path = cli_io.prompt_for_path( + "Please enter the path to create the Bpod directory" + ) + else: + bpod_directory_path = default_setup.get_bpod_directory() try: system_paths = SystemPaths(base_dir=bpod_directory_path) bpod_dir_verified = utils.verify_bpod_directory(system_paths) except ValidationError as ve: - logger.error("Error initializing system paths: %s", exc_info=ve) + logger.error("Error validating system paths: %s", exc_info=ve) if not bpod_dir_verified: logger.info( From 7dc50ed4ed9ebcadc348caddedba66c4b8d1bf34 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:50:01 -0500 Subject: [PATCH 33/48] defaults: Finally made a defaults module update default_setup and system_settings to reference the defaults module --- bpod_rig/IO/default_setup.py | 12 ++++-------- bpod_rig/config/system_settings.py | 9 ++++----- bpod_rig/defaults.py | 13 +++++++++++++ 3 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 bpod_rig/defaults.py diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 9a150be..cfe575e 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -4,19 +4,15 @@ import shutil from pathlib import Path -import platformdirs from pydantic import ValidationError from bpod_rig.examples import calibration, settings from bpod_rig.config import utils from bpod_rig.IO import cli_io - -DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] -DEFAULT_DIR_NAME = "Bpod" - -SYSTEM_CONFIG_DIR = platformdirs.user_config_path(DEFAULT_DIR_NAME) -SYSTEM_CONFIG_FILE = SYSTEM_CONFIG_DIR / "system_config.json" -DEFAULT_BPOD_PATH = platformdirs.user_documents_path() / DEFAULT_DIR_NAME +from defaults import ( + DEFAULT_SUBDIRS, SYSTEM_CONFIG_DIR, SYSTEM_CONFIG_FILE, + DEFAULT_BPOD_PATH +) logger = logging.getLogger(__name__) diff --git a/bpod_rig/config/system_settings.py b/bpod_rig/config/system_settings.py index dfe0406..1f9f7b4 100644 --- a/bpod_rig/config/system_settings.py +++ b/bpod_rig/config/system_settings.py @@ -9,11 +9,10 @@ from bpod_rig.config.base import ModelWithMetadata from bpod_rig.config.bpod_settings import BpodPaths - -DEFAULT_PROTOCOL_DIR_NAME = "Protocols" -DEFAULT_DATA_DIR_NAME = "Data" -DEFAULT_CONFIG_DIR_NAME = "Config" -DEFAULT_LOG_DIR_NAME = "Logs" +from defaults import ( + DEFAULT_PROTOCOL_DIR_NAME, DEFAULT_DATA_DIR_NAME, + DEFAULT_CONFIG_DIR_NAME +) logger = logging.getLogger(__name__) diff --git a/bpod_rig/defaults.py b/bpod_rig/defaults.py new file mode 100644 index 0000000..3145b38 --- /dev/null +++ b/bpod_rig/defaults.py @@ -0,0 +1,13 @@ +import platformdirs + +DEFAULT_DIR_NAME = "Bpod" +DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] +DEFAULT_PROTOCOL_DIR_NAME = "Protocols" +DEFAULT_DATA_DIR_NAME = "Data" +DEFAULT_CONFIG_DIR_NAME = "Config" +DEFAULT_LOG_DIR_NAME = "Logs" + +SYSTEM_CONFIG_DIR = platformdirs.user_config_path(DEFAULT_DIR_NAME) +SYSTEM_CONFIG_FILE = SYSTEM_CONFIG_DIR / "system_config.json" +DEFAULT_BPOD_PATH = platformdirs.user_documents_path() / DEFAULT_DIR_NAME + From 1b75ebd3a51e8ced4fffe4cc675e20cb09394483 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:59:16 -0500 Subject: [PATCH 34/48] defaults: add comments; update system path to be site_path instead of user path --- bpod_rig/defaults.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bpod_rig/defaults.py b/bpod_rig/defaults.py index 3145b38..6bf0c2d 100644 --- a/bpod_rig/defaults.py +++ b/bpod_rig/defaults.py @@ -1,5 +1,7 @@ +"""Module to hold a bunch of default values that are references in multiple places""" import platformdirs +# Default Names DEFAULT_DIR_NAME = "Bpod" DEFAULT_SUBDIRS = ["Config", "Calibration", "Protocols", "Data", "Logs"] DEFAULT_PROTOCOL_DIR_NAME = "Protocols" @@ -7,7 +9,8 @@ DEFAULT_CONFIG_DIR_NAME = "Config" DEFAULT_LOG_DIR_NAME = "Logs" -SYSTEM_CONFIG_DIR = platformdirs.user_config_path(DEFAULT_DIR_NAME) +# Defaults or unchanging paths +SYSTEM_CONFIG_DIR = platformdirs.site_config_path(DEFAULT_DIR_NAME) SYSTEM_CONFIG_FILE = SYSTEM_CONFIG_DIR / "system_config.json" DEFAULT_BPOD_PATH = platformdirs.user_documents_path() / DEFAULT_DIR_NAME From f8bca59f855b3d615d08f41bef02bf746e7a1b81 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 25 Dec 2025 12:59:53 -0500 Subject: [PATCH 35/48] __main__: reference defaults module for Bpod path; make Bpod directory if it does not exist --- bpod_rig/__main__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 9fad334..34f6ee1 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -6,6 +6,7 @@ from bpod_rig.config import utils from bpod_rig.config.system_settings import SystemPaths from bpod_rig.IO import default_setup, cli_io +from bpod_rig.defaults import DEFAULT_BPOD_PATH logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -20,7 +21,7 @@ def main(): system_initialized = default_setup.check_system_is_initialized() if not system_initialized: # Time to do some initialization - bpod_directory_path = [] + bpod_path = DEFAULT_BPOD_PATH response = cli_io.yes_no_prompt( f"Bpod has not been initialized on this system! Would you like to override the default path {DEFAULT_BPOD_PATH}?" ) @@ -29,6 +30,16 @@ def main(): bpod_path = cli_io.prompt_for_path( "Please enter the path to create the Bpod directory" ) + + try: + bpod_path.mkdir(parents=True, exist_ok=True) + except (IOError, OSError) as e: + logger.error( + "Unable to create the Bpod directory: %s", + bpod_path, + exc_info=e + ) + else: bpod_directory_path = default_setup.get_bpod_directory() From e3806dcab84cc6923463bd0f7953dbfec1d702c0 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Mon, 29 Dec 2025 15:10:37 -0500 Subject: [PATCH 36/48] default_setup: remove some extraneous code and checks that are duplicated --- bpod_rig/IO/default_setup.py | 59 +++++++++++------------------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index cfe575e..8d869e1 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -8,7 +8,6 @@ from bpod_rig.examples import calibration, settings from bpod_rig.config import utils -from bpod_rig.IO import cli_io from defaults import ( DEFAULT_SUBDIRS, SYSTEM_CONFIG_DIR, SYSTEM_CONFIG_FILE, DEFAULT_BPOD_PATH @@ -16,6 +15,7 @@ logger = logging.getLogger(__name__) + def create_default_directories(bpod_directory_path: Path = None) -> Path: """Create the default Bpod folder structure. @@ -46,9 +46,7 @@ def create_default_directories(bpod_directory_path: Path = None) -> Path: is_new_install = False if not bpod_directory_path.exists(): - logger.debug( - "Creating default Bpod user directory in %s", bpod_directory_path - ) + logger.debug("Creating default Bpod user directory in %s", bpod_directory_path) bpod_directory_path.mkdir(parents=True, exist_ok=True) is_new_install = True else: @@ -131,38 +129,24 @@ def get_bpod_dir_from_system() -> Path | None: Path to the Bpod directory read from the system configuration file None - If the system configuration directory does not exist, the configuration file - does not exist, or there is an error reading from the file, return None + If there is an error reading from the file, return None """ - if SYSTEM_CONFIG_DIR.exists(): - # Check if the system configuration directory exists - logger.debug("Found system config directory: %s", SYSTEM_CONFIG_DIR) - if SYSTEM_CONFIG_FILE.exists(): - # Check if the system configuration file exists - logger.debug( - "system_config.json found! Attempting to read Bpod directory path!" - ) - try: - # Attempt to load and read path from system configuration file - system_settings = utils.load_system_configuration(SYSTEM_CONFIG_FILE) - return system_settings.paths.base_dir - except ValidationError as e: - logger.error( - "Configuration file at %s failed to validate!", - SYSTEM_CONFIG_FILE, - exc_info=e, - ) - logger.error( - "Unable to read Bpod directory path from system configuration file!" - ) - else: - logger.warning( - "No system_config.json found! Has system been fully initialized?" - ) - else: - logger.warning("No system config directory found!") + try: + # Attempt to load and read path from system configuration file + system_settings = utils.load_system_configuration(SYSTEM_CONFIG_FILE) + return system_settings.paths.base_dir + except ValidationError as e: + logger.error( + "Configuration file at %s failed to validate!", + SYSTEM_CONFIG_FILE, + exc_info=e, + ) + + logger.error( + "Unable to read Bpod directory path from system configuration file!" + ) return None @@ -188,12 +172,3 @@ def check_system_is_initialized() -> bool: return True return False - -def get_bpod_directory() -> Path: - bpod_path = get_bpod_dir_from_system() - - if bpod_path is None: - logger.info("Unable to get Bpod path from system configuration!") - bpod_path = DEFAULT_BPOD_PATH - - return bpod_path From d064a9e8bf1ac9c525789ae2218d24cd793ea358 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Fri, 2 Jan 2026 03:23:17 -0500 Subject: [PATCH 37/48] defaults: use user_config_path and rename file to config.json --- bpod_rig/defaults.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bpod_rig/defaults.py b/bpod_rig/defaults.py index 6bf0c2d..2603074 100644 --- a/bpod_rig/defaults.py +++ b/bpod_rig/defaults.py @@ -10,7 +10,7 @@ DEFAULT_LOG_DIR_NAME = "Logs" # Defaults or unchanging paths -SYSTEM_CONFIG_DIR = platformdirs.site_config_path(DEFAULT_DIR_NAME) -SYSTEM_CONFIG_FILE = SYSTEM_CONFIG_DIR / "system_config.json" +SYSTEM_CONFIG_DIR = platformdirs.user_config_path(DEFAULT_DIR_NAME) +SYSTEM_CONFIG_FILE = SYSTEM_CONFIG_DIR / "config.json" DEFAULT_BPOD_PATH = platformdirs.user_documents_path() / DEFAULT_DIR_NAME From bf70d5350a63b6b7ebd1774dc0b2d57304fb77d5 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Fri, 2 Jan 2026 03:25:46 -0500 Subject: [PATCH 38/48] __main__: import defaults; some refactoring --- bpod_rig/__main__.py | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 34f6ee1..cdd1de7 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -6,7 +6,7 @@ from bpod_rig.config import utils from bpod_rig.config.system_settings import SystemPaths from bpod_rig.IO import default_setup, cli_io -from bpod_rig.defaults import DEFAULT_BPOD_PATH +from bpod_rig.defaults import DEFAULT_BPOD_PATH, SYSTEM_CONFIG_DIR, SYSTEM_CONFIG_FILE logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -22,10 +22,10 @@ def main(): if not system_initialized: # Time to do some initialization bpod_path = DEFAULT_BPOD_PATH - response = cli_io.yes_no_prompt( + override_directory = cli_io.yes_no_prompt( f"Bpod has not been initialized on this system! Would you like to override the default path {DEFAULT_BPOD_PATH}?" ) - if response: + if override_directory: logger.debug("User is going to override the path!") bpod_path = cli_io.prompt_for_path( "Please enter the path to create the Bpod directory" @@ -41,7 +41,8 @@ def main(): ) else: - bpod_directory_path = default_setup.get_bpod_directory() + # System has already been initialized + bpod_path = default_setup.get_bpod_dir_from_system() try: system_paths = SystemPaths(base_dir=bpod_directory_path) @@ -54,30 +55,25 @@ def main(): "The Bpod directory at %s failed to verify!" " This could be due to a partially initialized folder structure or " "improper file path!", - bpod_directory_path, + bpod_path, ) - response = cli_io.yes_no_prompt( - f"Would you like to (re)initialize {bpod_directory_path} as the base" + reinitialize = cli_io.yes_no_prompt( + f"Would you like to (re)initialize {bpod_path} as the base" f" Bpod directory?" ) - if response: - logging.info("Initializing Bpod directory at %s", bpod_directory_path) - bpod_directory_path = default_setup.create_default_directories(bpod_directory_path) - try: - system_paths = SystemPaths(base_dir=bpod_directory_path) - bpod_dir_verified = True - except ValidationError as ve: - logger.error("Error initializing system paths: %s", exc_info=ve) - - response = cli_io.yes_no_prompt( - f"Would you like to copy the default protocols and calibration files to {bpod_directory_path}" + if reinitialize: + logging.info("Initializing Bpod directory at %s", bpod_path) + bpod_path = default_setup.create_default_directories(bpod_path) + bpod_dir_verified = True + copy_default = cli_io.yes_no_prompt( + f"Would you like to copy the default protocols and calibration files to {bpod_path}" ) - if response: - default_setup.copy_default_files(bpod_directory_path) + if copy_default: + default_setup.copy_default_files(bpod_path) if bpod_dir_verified: - logging.debug("System paths at %s verified", bpod_directory_path) + logging.debug("System paths at %s verified", bpod_path) else: logger.info("No valid Bpod directory! Shutting down.") return From 402c73d0f611a6e5c07fc710f239594b185a81ab Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Fri, 2 Jan 2026 03:27:05 -0500 Subject: [PATCH 39/48] __main__: comments to organize each step in the loading sequence Create SYSTEM_CONFIG_DIR if it doesn't exist Create the Bpod directory if it doesn't exist Verify the structure of the Bpod Path save user and system config --- bpod_rig/__main__.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index cdd1de7..2d92597 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -31,25 +31,38 @@ def main(): "Please enter the path to create the Bpod directory" ) + # Create Bpod directories if the system was not initialized yet try: - bpod_path.mkdir(parents=True, exist_ok=True) + bpod_path = default_setup.create_default_directories(bpod_path) except (IOError, OSError) as e: logger.error( "Unable to create the Bpod directory: %s", bpod_path, exc_info=e ) + return + # Create the system configuration directory + try: + SYSTEM_CONFIG_DIR.mkdir(exist_ok=True) + except (IOError, OSError) as e: + logger.error( + "Unable to create the system configuration directory: %s", + SYSTEM_CONFIG_DIR, + exc_info=e + ) + return else: # System has already been initialized bpod_path = default_setup.get_bpod_dir_from_system() - try: - system_paths = SystemPaths(base_dir=bpod_directory_path) - bpod_dir_verified = utils.verify_bpod_directory(system_paths) - except ValidationError as ve: - logger.error("Error validating system paths: %s", exc_info=ve) + ## Verify Bpod Folder Structure ## + + # Instantiate SystemPaths object to generate subdirectories + system_paths = SystemPaths(base_dir=bpod_path) + bpod_dir_verified = utils.verify_bpod_directory(system_paths) + # Verification Failed if not bpod_dir_verified: logger.info( "The Bpod directory at %s failed to verify!" @@ -66,6 +79,7 @@ def main(): logging.info("Initializing Bpod directory at %s", bpod_path) bpod_path = default_setup.create_default_directories(bpod_path) bpod_dir_verified = True + copy_default = cli_io.yes_no_prompt( f"Would you like to copy the default protocols and calibration files to {bpod_path}" ) @@ -79,9 +93,12 @@ def main(): return - inital_system_config = utils.init_system_configuration(bpod_directory_path) - result = utils.save_system_configuration(inital_system_config) - + inital_system_config = utils.init_system_configuration(bpod_path) + user_config_path = utils.save_system_configuration(inital_system_config) + system_config_path = utils.save_system_configuration( + inital_system_config, + save_dir_override=SYSTEM_CONFIG_DIR + ) if __name__ == "__main__": main() From 2d2ad44ca01bc8eab1473cf795c8d15e5bf49045 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 22 Jan 2026 15:00:18 -0500 Subject: [PATCH 40/48] default_setup: allow errors to bubble up --- bpod_rig/IO/default_setup.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index 8d869e1..ebbce91 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -127,28 +127,11 @@ def get_bpod_dir_from_system() -> Path | None: ------- pathlib.Path Path to the Bpod directory read from the system configuration file - - None - If there is an error reading from the file, return None - """ - try: - # Attempt to load and read path from system configuration file - system_settings = utils.load_system_configuration(SYSTEM_CONFIG_FILE) - return system_settings.paths.base_dir - except ValidationError as e: - logger.error( - "Configuration file at %s failed to validate!", - SYSTEM_CONFIG_FILE, - exc_info=e, - ) - - logger.error( - "Unable to read Bpod directory path from system configuration file!" - ) - - return None + # Attempt to load and read path from system configuration file + system_settings = utils.load_system_configuration(SYSTEM_CONFIG_FILE) + return system_settings.paths.base_dir def check_system_is_initialized() -> bool: From 48223e423ee496e20e7f14c662d3835fc4cd16bb Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 22 Jan 2026 15:00:38 -0500 Subject: [PATCH 41/48] __main__: cleanup startup process --- bpod_rig/__main__.py | 86 ++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 34 deletions(-) diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 2d92597..3f7f196 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -15,46 +15,64 @@ def main(): ### Everything below is subject to change and is for testing purposes only logger.info("Starting bpod-rig!") - bpod_dir_verified = False # Let's put this in a BpodSystem class later + # Let's put this in a BpodSystem class later + bpod_dir_verified = False + reinitialize = False + system_initialized = False + bpod_path = None ### Has Bpod been initialized on this system before? ### system_initialized = default_setup.check_system_is_initialized() if not system_initialized: - # Time to do some initialization - bpod_path = DEFAULT_BPOD_PATH - override_directory = cli_io.yes_no_prompt( - f"Bpod has not been initialized on this system! Would you like to override the default path {DEFAULT_BPOD_PATH}?" + logging.debug( + "System is not initialized. Creating system config dir [%s]", + SYSTEM_CONFIG_DIR ) - if override_directory: - logger.debug("User is going to override the path!") - bpod_path = cli_io.prompt_for_path( - "Please enter the path to create the Bpod directory" - ) - - # Create Bpod directories if the system was not initialized yet - try: - bpod_path = default_setup.create_default_directories(bpod_path) - except (IOError, OSError) as e: - logger.error( - "Unable to create the Bpod directory: %s", - bpod_path, - exc_info=e - ) - return - # Create the system configuration directory try: SYSTEM_CONFIG_DIR.mkdir(exist_ok=True) except (IOError, OSError) as e: logger.error( - "Unable to create the system configuration directory: %s", + "Unable to create the system configuration directory: %s Exiting...", SYSTEM_CONFIG_DIR, exc_info=e ) return else: + logger.debug( + "System configuration directory is already initialized." + " Attempting to read bpod_path from configuration file" + ) # System has already been initialized - bpod_path = default_setup.get_bpod_dir_from_system() + try: + bpod_path = default_setup.get_bpod_dir_from_system() + except ValidationError as e: + logger.error( + "Configuration file at %s failed to validate!", + SYSTEM_CONFIG_FILE, + exc_info=e, + ) + logger.error( + "Unable to read Bpod directory path from existing system configuration " + "file! Exiting..." + ) + return + + if bpod_path is None: + # This is the first time initializing the system; override default path? + override_directory = cli_io.yes_no_prompt( + f"Bpod has not been initialized on this system! " + f"Would you like to override the default path {DEFAULT_BPOD_PATH}?" + ) + if override_directory: + logger.debug("User is going to override the path!") + bpod_path = cli_io.prompt_for_path( + "Please enter the path to create the Bpod directory" + ) + else: + bpod_path = DEFAULT_BPOD_PATH + + logger.info("Bpod path set to: %s", bpod_path) ## Verify Bpod Folder Structure ## @@ -75,21 +93,21 @@ def main(): f" Bpod directory?" ) - if reinitialize: - logging.info("Initializing Bpod directory at %s", bpod_path) - bpod_path = default_setup.create_default_directories(bpod_path) - bpod_dir_verified = True + if reinitialize or not system_initialized: + logging.info("Initializing Bpod directory at %s", bpod_path) + bpod_path = default_setup.create_default_directories(bpod_path) + bpod_dir_verified = True - copy_default = cli_io.yes_no_prompt( - f"Would you like to copy the default protocols and calibration files to {bpod_path}" - ) - if copy_default: - default_setup.copy_default_files(bpod_path) + copy_default = cli_io.yes_no_prompt( + f"Would you like to copy the default protocols and calibration files to {bpod_path}" + ) + if copy_default: + default_setup.copy_default_files(bpod_path) if bpod_dir_verified: logging.debug("System paths at %s verified", bpod_path) else: - logger.info("No valid Bpod directory! Shutting down.") + logger.error("No valid Bpod directory! Shutting down.") return From 486181d3856085b117bd00147939c06e9af239b1 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 22 Jan 2026 15:04:47 -0500 Subject: [PATCH 42/48] default_setup: convert to explicit import; remove unused imports --- bpod_rig/IO/default_setup.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/default_setup.py index ebbce91..69eacf6 100644 --- a/bpod_rig/IO/default_setup.py +++ b/bpod_rig/IO/default_setup.py @@ -4,13 +4,10 @@ import shutil from pathlib import Path -from pydantic import ValidationError - from bpod_rig.examples import calibration, settings from bpod_rig.config import utils -from defaults import ( +from bpod_rig.defaults import ( DEFAULT_SUBDIRS, SYSTEM_CONFIG_DIR, SYSTEM_CONFIG_FILE, - DEFAULT_BPOD_PATH ) logger = logging.getLogger(__name__) From ceaa0af5bf0b8cf2101da808709c67ccad7da78a Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 22 Jan 2026 15:05:07 -0500 Subject: [PATCH 43/48] test_default_setup: fix default setup test --- tests/bpod_rig/IO/test_default_setup.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/bpod_rig/IO/test_default_setup.py b/tests/bpod_rig/IO/test_default_setup.py index 43666a7..4b46ada 100644 --- a/tests/bpod_rig/IO/test_default_setup.py +++ b/tests/bpod_rig/IO/test_default_setup.py @@ -7,10 +7,6 @@ from bpod_rig.IO import default_setup -class TestCreateDefaultDirectories: - pass - - class TestCreateDefaultDirectories: @pytest.fixture(autouse=True) def setup_and_teardown(self): @@ -18,12 +14,11 @@ def setup_and_teardown(self): self.temp_dir = tempfile.TemporaryDirectory() self.temp_path = Path(self.temp_dir.name) self.bpod_path = self.temp_path / "Bpod" - self.machine_id = "COM3" yield # test runs here self.temp_dir.cleanup() def test_folder_creation(self): - default_setup.create_default_directories("EMU", self.bpod_path) + default_setup.create_default_directories(self.bpod_path) assert self.bpod_path.exists() From 2f62d43da9579559268043260cbaa527eb21a7c6 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Thu, 22 Jan 2026 15:10:03 -0500 Subject: [PATCH 44/48] system_settings: another explicit import --- bpod_rig/config/system_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpod_rig/config/system_settings.py b/bpod_rig/config/system_settings.py index 1f9f7b4..ce08c10 100644 --- a/bpod_rig/config/system_settings.py +++ b/bpod_rig/config/system_settings.py @@ -9,7 +9,7 @@ from bpod_rig.config.base import ModelWithMetadata from bpod_rig.config.bpod_settings import BpodPaths -from defaults import ( +from bpod_rig.defaults import ( DEFAULT_PROTOCOL_DIR_NAME, DEFAULT_DATA_DIR_NAME, DEFAULT_CONFIG_DIR_NAME ) From 4c34b1213982ce60f793292e4a66c28d01470652 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 4 Feb 2026 11:53:46 -0500 Subject: [PATCH 45/48] Config: refactor update_modification_time to be a member of the superclass; override update_modification_time in SystemSettings to ensure the function gets called for the subfields that require it --- bpod_rig/config/base.py | 25 ++++++++++++++++++- bpod_rig/config/system_settings.py | 40 +++++++++++++++++------------- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/bpod_rig/config/base.py b/bpod_rig/config/base.py index 2c54180..abce830 100644 --- a/bpod_rig/config/base.py +++ b/bpod_rig/config/base.py @@ -1,6 +1,7 @@ """Base classes for configuration classes.""" import datetime +import logging from typing import Annotated, Any, Optional from pydantic import ( @@ -12,6 +13,7 @@ model_validator, ) +logger = logging.getLogger(__name__) class SettingsMetadata(BaseModel): model_config = ConfigDict() @@ -37,7 +39,7 @@ class SettingsMetadata(BaseModel): str, Field( min_length=1, - max_length=128, + max_length=128, title="Username of settings creator", description="Username of the creator of this settings model.", ), @@ -100,6 +102,27 @@ def forward_username(cls, data: Any) -> Any: return data + def update_modification_time(self) -> bool: + """ + Update metadata modified_datetime field. + + Parameters + ---------- + None + + Returns + ------- + bool + If successful, return True, else return False. + """ + try: + self.metadata.modified_datetime = datetime.datetime.now() + except Exception as e: + logger.error("Error setting save_time field for %s!", self, exc_info=e) + return False + return True + + class ModuleBase(ModelWithMetadata): name: Annotated[ str, diff --git a/bpod_rig/config/system_settings.py b/bpod_rig/config/system_settings.py index ce08c10..977c2e8 100644 --- a/bpod_rig/config/system_settings.py +++ b/bpod_rig/config/system_settings.py @@ -169,28 +169,34 @@ class SystemSettings(ModelWithMetadata): ), ] = None - def set_modification_time(self, date_time: datetime.datetime) -> bool: - """ - Update all metadata save_time fields. - Parameters - ---------- - date_time : datetime.datetime - Date and time to update metadata save_time fields to. + def update_modification_time(self) -> bool: + """ + Override update_modification_time to also update the modified_datetime metadata + field of SystemSettings subfields Returns ------- - bool - If successful, return True, else return False. + bool: + True if all times were updated, False otherwise """ - try: - self.metadata.modified_datetime = date_time - self.paths.metadata.modified_datetime = date_time - if self.bpod_dirs: - for bpod_dir in self.bpod_dirs: - bpod_dir.metadata.modified_datetime = date_time - except Exception as e: - logger.error("Error setting save_time fields!", exc_info=e) + + all_success = True + + all_success &= super().update_modification_time() + # Update the SystemSettings save time + + if self.bpod_dirs: + for bpod_dir in self.bpod_dirs: + all_success &= bpod_dir.update_modification_time() + # Update the save time for each bpod_dir + + if self.paths: + all_success &= self.paths.update_modification_time() + # Update the save time for the system paths + + if not all_success: return False return True + From a153c690f5af74e0fab0bd2ce04a066f7f309072 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 4 Feb 2026 11:55:02 -0500 Subject: [PATCH 46/48] Config.utils: function to save the system paths; save_system_configuration call to update_modification_time modified to match refactor --- bpod_rig/config/utils.py | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/bpod_rig/config/utils.py b/bpod_rig/config/utils.py index 188fdc2..76c99f3 100644 --- a/bpod_rig/config/utils.py +++ b/bpod_rig/config/utils.py @@ -5,6 +5,7 @@ from pydantic_core import from_json from bpod_rig.config.system_settings import SystemPaths, SystemSettings +from bpod_rig.defaults import SYSTEM_CONFIG_DIR from bpod_rig.IO import json_handler logger = logging.getLogger(__name__) @@ -37,13 +38,46 @@ def verify_bpod_directory(system_paths: SystemPaths) -> bool: return dir_verified +def save_system_paths( + system_paths: SystemPaths, save_dir_override: Path | None +) -> None: + """ + Save the system paths to the system configuration directory as json-formatted text + + Simultaneously will update the save_time field + + Parameters + ---------- + system_paths : SystemPaths + SystemPaths instance to serialize and write to disk + save_dir_override : Path | None + Optional Path to override the default save directory. Default SYSTEM_CONFIG_DIR + used if not provided + + Returns + ------- + None + """ + + logger.debug("Saving system paths as JSON!") + system_paths.update_modification_time() + + if save_dir_override is not None: + save_dir = save_dir_override + else: + save_dir = SYSTEM_CONFIG_DIR + + system_paths_json = system_paths.model_dump_json(indent=2) + json_handler.write_json(system_paths_json, save_dir, "paths") + + def save_system_configuration( system_settings: SystemSettings, save_dir_override: Path | None = None ) -> Path: """ Save system_configuration instance to disk as json-formatted text. - Simultaneously will update the modified_datetime field of the models to datetime.now() + Simultaneously will update the save_time field Parameters ---------- @@ -59,8 +93,7 @@ def save_system_configuration( Path to the saved file is returned """ logger.debug("Saving system configuration as JSON!") - modified_datetime = datetime.datetime.now() - system_settings.set_modification_time(modified_datetime) + system_settings.update_modification_time() if save_dir_override is None: if system_settings.paths.base_config_dir is None: From 1b2e22e56ab74e14316997358d95052a9ff5d9a4 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Wed, 4 Feb 2026 16:37:24 -0500 Subject: [PATCH 47/48] Add testcase for passing metadata object and ignoring the username field --- tests/bpod_rig/config/test_models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/bpod_rig/config/test_models.py b/tests/bpod_rig/config/test_models.py index abcc277..bf16c9e 100644 --- a/tests/bpod_rig/config/test_models.py +++ b/tests/bpod_rig/config/test_models.py @@ -4,7 +4,7 @@ import pytest from pydantic import ValidationError -from bpod_rig.config.base import SettingsMetadata +from bpod_rig.config.base import SettingsMetadata, ModelWithMetadata from bpod_rig.config.system_settings import SystemPaths from bpod_rig.config.bpod_settings import BpodPaths @@ -81,8 +81,14 @@ def test_pass_username(self, paths): sp = SystemPaths(base_dir=paths["bpod_dir"], username="valid_username") + # Does username get forwarded properly to the SettingsMatadata object assert sp.metadata.username == "valid_username" + # Does the SettingsMetadata object get properly forwarded + md = SettingsMetadata() + sp2 = SystemPaths(base_dir=paths["bpod_dir"], metadata=md, username="beepbop") + assert sp2.metadata.username != "beepbop" + def test_not_paths(self): """Tests that non-path inputs for directories raise validation errors.""" with pytest.raises(ValidationError): From edcbab7d1fe73b50f159315be371cf2dce02c673 Mon Sep 17 00:00:00 2001 From: Austin Pauley Date: Tue, 10 Feb 2026 08:50:17 -0500 Subject: [PATCH 48/48] Refactor: rename default_setup -> startup --- bpod_rig/IO/{default_setup.py => startup.py} | 0 bpod_rig/__main__.py | 10 +++++----- .../IO/{test_default_setup.py => test_startup.py} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename bpod_rig/IO/{default_setup.py => startup.py} (100%) rename tests/bpod_rig/IO/{test_default_setup.py => test_startup.py} (82%) diff --git a/bpod_rig/IO/default_setup.py b/bpod_rig/IO/startup.py similarity index 100% rename from bpod_rig/IO/default_setup.py rename to bpod_rig/IO/startup.py diff --git a/bpod_rig/__main__.py b/bpod_rig/__main__.py index 3f7f196..1d46f1f 100644 --- a/bpod_rig/__main__.py +++ b/bpod_rig/__main__.py @@ -5,7 +5,7 @@ from bpod_rig.config import utils from bpod_rig.config.system_settings import SystemPaths -from bpod_rig.IO import default_setup, cli_io +from bpod_rig.IO import startup, cli_io from bpod_rig.defaults import DEFAULT_BPOD_PATH, SYSTEM_CONFIG_DIR, SYSTEM_CONFIG_FILE logging.basicConfig(level=logging.DEBUG) @@ -22,7 +22,7 @@ def main(): bpod_path = None ### Has Bpod been initialized on this system before? ### - system_initialized = default_setup.check_system_is_initialized() + system_initialized = startup.check_system_is_initialized() if not system_initialized: logging.debug( "System is not initialized. Creating system config dir [%s]", @@ -45,7 +45,7 @@ def main(): ) # System has already been initialized try: - bpod_path = default_setup.get_bpod_dir_from_system() + bpod_path = startup.get_bpod_dir_from_system() except ValidationError as e: logger.error( "Configuration file at %s failed to validate!", @@ -95,14 +95,14 @@ def main(): if reinitialize or not system_initialized: logging.info("Initializing Bpod directory at %s", bpod_path) - bpod_path = default_setup.create_default_directories(bpod_path) + bpod_path = startup.create_default_directories(bpod_path) bpod_dir_verified = True copy_default = cli_io.yes_no_prompt( f"Would you like to copy the default protocols and calibration files to {bpod_path}" ) if copy_default: - default_setup.copy_default_files(bpod_path) + startup.copy_default_files(bpod_path) if bpod_dir_verified: logging.debug("System paths at %s verified", bpod_path) diff --git a/tests/bpod_rig/IO/test_default_setup.py b/tests/bpod_rig/IO/test_startup.py similarity index 82% rename from tests/bpod_rig/IO/test_default_setup.py rename to tests/bpod_rig/IO/test_startup.py index 4b46ada..0450705 100644 --- a/tests/bpod_rig/IO/test_default_setup.py +++ b/tests/bpod_rig/IO/test_startup.py @@ -4,7 +4,7 @@ import pytest -from bpod_rig.IO import default_setup +from bpod_rig.IO import startup class TestCreateDefaultDirectories: @@ -20,5 +20,5 @@ def setup_and_teardown(self): self.temp_dir.cleanup() def test_folder_creation(self): - default_setup.create_default_directories(self.bpod_path) + startup.create_default_directories(self.bpod_path) assert self.bpod_path.exists()