diff --git a/.gitignore b/.gitignore index b4564e5a..9b3c1ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,5 @@ _local # OS stuff .DS_Store + +.envrc diff --git a/examples/iterative_design_nand.py b/examples/iterative_design_nand.py index ca69b0cd..23ad81cd 100644 --- a/examples/iterative_design_nand.py +++ b/examples/iterative_design_nand.py @@ -17,6 +17,7 @@ import typer import faebryk.library._F as F +from faebryk.core.graph import GraphFunctions from faebryk.core.module import Module from faebryk.libs.brightness import TypicalLuminousIntensity from faebryk.libs.examples.buildutil import apply_design_to_pcb @@ -119,7 +120,9 @@ def App(): app.add(c) # parametrizing - for _, t in app.get_graph().nodes_with_trait(F.ElectricLogic.has_pulls): + for _, t in GraphFunctions(app.get_graph()).nodes_with_trait( + F.ElectricLogic.has_pulls + ): for pull_resistor in (r for r in t.get_pulls() if r): pull_resistor.resistance.merge(F.Range.from_center_rel(100 * P.kohm, 0.05)) power_source.power.voltage.merge(3 * P.V) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index e21497d4..00000000 --- a/poetry.lock +++ /dev/null @@ -1,2025 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. - -[[package]] -name = "aiosqlite" -version = "0.17.0" -description = "asyncio bridge to the standard sqlite3 module" -optional = false -python-versions = ">=3.6" -files = [ - {file = "aiosqlite-0.17.0-py3-none-any.whl", hash = "sha256:6c49dc6d3405929b1d08eeccc72306d3677503cc5e5e43771efc1e00232e8231"}, - {file = "aiosqlite-0.17.0.tar.gz", hash = "sha256:f0e6acc24bc4864149267ac82fb46dfb3be4455f99fe21df82609cc6e6baee51"}, -] - -[package.dependencies] -typing_extensions = ">=3.7.2" - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = "*" -files = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] - -[[package]] -name = "black" -version = "24.8.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "certifi" -version = "2024.8.30" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "contourpy" -version = "1.3.0" -description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false -python-versions = ">=3.9" -files = [ - {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, - {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"}, - {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"}, - {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, - {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, - {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, - {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, - {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, - {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, - {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"}, - {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"}, - {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"}, - {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, -] - -[package.dependencies] -numpy = ">=1.23" - -[package.extras] -bokeh = ["bokeh", "selenium"] -docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] -test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] - -[[package]] -name = "cycler" -version = "0.12.1" -description = "Composable style cycles" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, - {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, -] - -[package.extras] -docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] -tests = ["pytest", "pytest-cov", "pytest-xdist"] - -[[package]] -name = "dataclasses-json" -version = "0.6.7" -description = "Easily serialize dataclasses to and from JSON." -optional = false -python-versions = "<4.0,>=3.7" -files = [ - {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, - {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, -] - -[package.dependencies] -marshmallow = ">=3.18.0,<4.0.0" -typing-inspect = ">=0.4.0,<1" - -[[package]] -name = "deprecated" -version = "1.2.14" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, - {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] - -[[package]] -name = "distlib" -version = "0.3.8" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - -[[package]] -name = "easyeda2kicad" -version = "0.8.0" -description = "A Python script that convert any electronic components from LCSC or EasyEDA to a Kicad library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "easyeda2kicad-0.8.0-py3-none-any.whl", hash = "sha256:9e13757f9bd93752064cb28b85d844113711c31b664cb1df1a60a9ae59599aa8"}, - {file = "easyeda2kicad-0.8.0.tar.gz", hash = "sha256:a781be6d1076f6e06886a4292373eb930c9921de4c709d6dd91bb6ea104f4a4b"}, -] - -[package.dependencies] -pydantic = ">=2.0.0" -requests = ">2.0.0" - -[package.extras] -dev = ["pre-commit (>=2.17.0)"] - -[[package]] -name = "execnet" -version = "2.1.1" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "filelock" -version = "3.16.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, - {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] -typing = ["typing-extensions (>=4.12.2)"] - -[[package]] -name = "flexcache" -version = "0.3" -description = "Saves and loads to the cache a transformed versions of a source object." -optional = false -python-versions = ">=3.9" -files = [ - {file = "flexcache-0.3-py3-none-any.whl", hash = "sha256:d43c9fea82336af6e0115e308d9d33a185390b8346a017564611f1466dcd2e32"}, - {file = "flexcache-0.3.tar.gz", hash = "sha256:18743bd5a0621bfe2cf8d519e4c3bfdf57a269c15d1ced3fb4b64e0ff4600656"}, -] - -[package.dependencies] -typing-extensions = "*" - -[package.extras] -test = ["pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"] - -[[package]] -name = "flexparser" -version = "0.3.1" -description = "Parsing made fun ... using typing." -optional = false -python-versions = ">=3.9" -files = [ - {file = "flexparser-0.3.1-py3-none-any.whl", hash = "sha256:2e3e2936bec1f9277f777ef77297522087d96adb09624d4fe4240fd56885c013"}, - {file = "flexparser-0.3.1.tar.gz", hash = "sha256:36f795d82e50f5c9ae2fde1c33f21f88922fdd67b7629550a3cc4d0b40a66856"}, -] - -[package.dependencies] -typing-extensions = "*" - -[package.extras] -test = ["pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"] - -[[package]] -name = "fonttools" -version = "4.53.1" -description = "Tools to manipulate font files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, - {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, - {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, - {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, - {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, - {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, - {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, - {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, - {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, - {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, - {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, - {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, - {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, - {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, -] - -[package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] -graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "pycairo", "scipy"] -lxml = ["lxml (>=4.0)"] -pathops = ["skia-pathops (>=0.5.0)"] -plot = ["matplotlib"] -repacker = ["uharfbuzz (>=0.23.0)"] -symfont = ["sympy"] -type1 = ["xattr"] -ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0)"] -woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] - -[[package]] -name = "freetype-py" -version = "2.5.1" -description = "Freetype python bindings" -optional = false -python-versions = ">=3.7" -files = [ - {file = "freetype-py-2.5.1.zip", hash = "sha256:cfe2686a174d0dd3d71a9d8ee9bf6a2c23f5872385cf8ce9f24af83d076e2fbd"}, - {file = "freetype_py-2.5.1-py3-none-macosx_10_9_universal2.whl", hash = "sha256:d01ded2557694f06aa0413f3400c0c0b2b5ebcaabeef7aaf3d756be44f51e90b"}, - {file = "freetype_py-2.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2f6b3d68496797da23204b3b9c4e77e67559c80390fc0dc8b3f454ae1cd819"}, - {file = "freetype_py-2.5.1-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:289b443547e03a4f85302e3ac91376838e0d11636050166662a4f75e3087ed0b"}, - {file = "freetype_py-2.5.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:cd3bfdbb7e1a84818cfbc8025fca3096f4f2afcd5d4641184bf0a3a2e6f97bbf"}, - {file = "freetype_py-2.5.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3c1aefc4f0d5b7425f014daccc5fdc7c6f914fb7d6a695cc684f1c09cd8c1660"}, - {file = "freetype_py-2.5.1-py3-none-win_amd64.whl", hash = "sha256:0b7f8e0342779f65ca13ef8bc103938366fecade23e6bb37cb671c2b8ad7f124"}, -] - -[[package]] -name = "gprof2dot" -version = "2024.6.6" -description = "Generate a dot graph from the output of several profilers." -optional = false -python-versions = ">=3.8" -files = [ - {file = "gprof2dot-2024.6.6-py2.py3-none-any.whl", hash = "sha256:45b14ad7ce64e299c8f526881007b9eb2c6b75505d5613e96e66ee4d5ab33696"}, - {file = "gprof2dot-2024.6.6.tar.gz", hash = "sha256:fa1420c60025a9eb7734f65225b4da02a10fc6dd741b37fa129bc6b41951e5ab"}, -] - -[[package]] -name = "identify" -version = "2.6.1" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "iso8601" -version = "1.1.0" -description = "Simple module to parse ISO 8601 dates" -optional = false -python-versions = ">=3.6.2,<4.0" -files = [ - {file = "iso8601-1.1.0-py3-none-any.whl", hash = "sha256:8400e90141bf792bce2634df533dc57e3bee19ea120a87bebcd3da89a58ad73f"}, - {file = "iso8601-1.1.0.tar.gz", hash = "sha256:32811e7b81deee2063ea6d2e94f8819a86d1f3811e49d23623a41fa832bef03f"}, -] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "kicadcliwrapper" -version = "1.0.1" -description = "Kicad-CLI python bindings" -optional = false -python-versions = "<4.0,>=3.12" -files = [ - {file = "kicadcliwrapper-1.0.1-py3-none-any.whl", hash = "sha256:330c584a5229ab7f72a0f90f2c071a80b6cfa2cdff34c99fcae56fa588ca99f7"}, - {file = "kicadcliwrapper-1.0.1.tar.gz", hash = "sha256:ef9816afe0119c0c8f7d72f79a137b4330423e483512df5eb229f892dd1d5e2a"}, -] - -[package.dependencies] -black = ">=24.4.2,<25.0.0" -typing-extensions = ">=4.6.3,<5.0.0" - -[[package]] -name = "kiwisolver" -version = "1.4.7" -description = "A fast implementation of the Cassowary constraint solver" -optional = false -python-versions = ">=3.8" -files = [ - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, - {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, - {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, - {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, - {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, - {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, - {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, - {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, - {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, - {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, - {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, - {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, - {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, - {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, - {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, - {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, - {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, - {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, - {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, - {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, - {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, - {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, - {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, - {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, - {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, - {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, - {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, - {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, - {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, - {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "marshmallow" -version = "3.22.0" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -optional = false -python-versions = ">=3.8" -files = [ - {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, - {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, -] - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] -tests = ["pytest", "pytz", "simplejson"] - -[[package]] -name = "matplotlib" -version = "3.9.2" -description = "Python plotting package" -optional = false -python-versions = ">=3.9" -files = [ - {file = "matplotlib-3.9.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb"}, - {file = "matplotlib-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66"}, - {file = "matplotlib-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a"}, - {file = "matplotlib-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, - {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, - {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, - {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, - {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, - {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, - {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, - {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca"}, - {file = "matplotlib-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea"}, - {file = "matplotlib-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697"}, - {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -kiwisolver = ">=1.3.1" -numpy = ">=1.23" -packaging = ">=20.0" -pillow = ">=8" -pyparsing = ">=2.3.1" -python-dateutil = ">=2.7" - -[package.extras] -dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "more-itertools" -version = "10.5.0" -description = "More routines for operating on iterables, beyond itertools" -optional = false -python-versions = ">=3.8" -files = [ - {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, - {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, -] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "networkx" -version = "3.3" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.10" -files = [ - {file = "networkx-3.3-py3-none-any.whl", hash = "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2"}, - {file = "networkx-3.3.tar.gz", hash = "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9"}, -] - -[package.extras] -default = ["matplotlib (>=3.6)", "numpy (>=1.23)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"] -developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] -doc = ["myst-nb (>=1.0)", "numpydoc (>=1.7)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=2.0)", "pygraphviz (>=1.12)", "sympy (>=1.10)"] -test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "numpy" -version = "2.1.1" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.10" -files = [ - {file = "numpy-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f"}, - {file = "numpy-2.1.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab"}, - {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7"}, - {file = "numpy-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6"}, - {file = "numpy-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0"}, - {file = "numpy-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647"}, - {file = "numpy-2.1.1-cp310-cp310-win32.whl", hash = "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728"}, - {file = "numpy-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0"}, - {file = "numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95"}, - {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca"}, - {file = "numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf"}, - {file = "numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e"}, - {file = "numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2"}, - {file = "numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d"}, - {file = "numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f"}, - {file = "numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521"}, - {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b"}, - {file = "numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201"}, - {file = "numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a"}, - {file = "numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313"}, - {file = "numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed"}, - {file = "numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136"}, - {file = "numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0"}, - {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb"}, - {file = "numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df"}, - {file = "numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78"}, - {file = "numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556"}, - {file = "numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b"}, - {file = "numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f"}, - {file = "numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468"}, - {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef"}, - {file = "numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f"}, - {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c"}, - {file = "numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd"}, - {file = "numpy-2.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39"}, - {file = "numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd"}, -] - -[[package]] -name = "objprint" -version = "0.2.3" -description = "A library that can print Python objects in human readable format" -optional = false -python-versions = ">=3.6" -files = [ - {file = "objprint-0.2.3-py3-none-any.whl", hash = "sha256:1721e6f97bae5c5b86c2716a0d45a9dd2c9a4cd9f52cfc8a0dfbe801805554cb"}, - {file = "objprint-0.2.3.tar.gz", hash = "sha256:73d0ad5a7c3151fce634c8892e5c2a050ccae3b1a353bf1316f08b7854da863b"}, -] - -[[package]] -name = "packaging" -version = "24.1" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "patool" -version = "2.4.0" -description = "portable archive file manager" -optional = false -python-versions = ">=3.10" -files = [ - {file = "patool-2.4.0-py2.py3-none-any.whl", hash = "sha256:0ab5ab376b0f7838c495a8583fab2d73b43889820c7ef2e9300a7e5c952054f2"}, - {file = "patool-2.4.0.tar.gz", hash = "sha256:2c54bcd2b904bf37253fad4f5d04cec808056b15fa974fd36b2a84fc931f3c0f"}, -] - -[[package]] -name = "pillow" -version = "10.4.0" -description = "Python Imaging Library (Fork)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, - {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, - {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, - {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, - {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, - {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, - {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, - {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, - {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, - {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] -fpx = ["olefile"] -mic = ["olefile"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] -xmp = ["defusedxml"] - -[[package]] -name = "pint" -version = "0.24.3" -description = "Physical quantities module" -optional = false -python-versions = ">=3.9" -files = [ - {file = "Pint-0.24.3-py3-none-any.whl", hash = "sha256:d98667e46fd03a1b94694fbfa104ec30858684d8ab26952e2a348b48059089bb"}, - {file = "pint-0.24.3.tar.gz", hash = "sha256:d54771093e8b94c4e0a35ac638c2444ddf3ef685652bab7675ffecfa0c5c5cdf"}, -] - -[package.dependencies] -appdirs = ">=1.4.4" -flexcache = ">=0.3" -flexparser = ">=0.3" -typing-extensions = "*" - -[package.extras] -babel = ["babel (<=2.8)"] -bench = ["pytest", "pytest-codspeed"] -dask = ["dask"] -mip = ["mip (>=1.13)"] -numpy = ["numpy (>=1.23)"] -pandas = ["pint-pandas (>=0.3)"] -test = ["pytest", "pytest-benchmark", "pytest-cov", "pytest-mpl", "pytest-subtests"] -testbase = ["pytest", "pytest-benchmark", "pytest-cov", "pytest-subtests"] -uncertainties = ["uncertainties (>=3.1.6)"] -xarray = ["xarray"] - -[[package]] -name = "platformdirs" -version = "4.3.6" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, -] - -[package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pre-commit" -version = "3.8.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, - {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "psutil" -version = "6.0.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "pydantic" -version = "2.9.2" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.23.4" -typing-extensions = {version = ">=4.6.1", markers = "python_version < \"3.13\""} - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] - -[[package]] -name = "pydantic-core" -version = "2.23.4" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pygments" -version = "2.18.0" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pyinstrument" -version = "4.7.3" -description = "Call stack profiler for Python. Shows you why your code is slow!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyinstrument-4.7.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6a79912f8a096ccad1b88a527719563f6b2b5dc94057873c2ca840dc6378cfee"}, - {file = "pyinstrument-4.7.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:089f7afb326ee937656ee1767813dc793ad20b3d353d081e16255b63830a4787"}, - {file = "pyinstrument-4.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f65107079f68dcaeb58ee032d98075ab7ac49be419c60673406043e0675393b4"}, - {file = "pyinstrument-4.7.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9402e339d802a7f5b1ad716b8411ab98f45e51c4b261e662b8a470c251af0acc"}, - {file = "pyinstrument-4.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d1f4e0155f563f66e821210c225af8b64a2283c0feff776c49feba623e7bafd"}, - {file = "pyinstrument-4.7.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c619f3064dae5284b904c4862b35639c35ecd439bb5b4152924f7ccb69edc5e3"}, - {file = "pyinstrument-4.7.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b4d80deaf76cc171b3b707e2babc9a7046610c4e11022167949e60fc2dc62be"}, - {file = "pyinstrument-4.7.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c5fbe9d24154a118a4b86bed5ae228c3d8698216fad65257aca97e790527197a"}, - {file = "pyinstrument-4.7.3-cp310-cp310-win32.whl", hash = "sha256:7405aec2227ed87dc3bc3a8eb82b5dcdec68861d564ee0d429f9a51ca30ccd58"}, - {file = "pyinstrument-4.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:8043b9c1fb0c19a2957098930c3bad43ecdc1cf8e1d3f32a3b9ef74fdd3df028"}, - {file = "pyinstrument-4.7.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77594adf4713bc3e430e300561a2d837213cf9015414c0e0de6aef0cb9cebd80"}, - {file = "pyinstrument-4.7.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:70afa765c06e4f7605033b85ef82ed946ec8e6ae1835e25f6cbb01205a624197"}, - {file = "pyinstrument-4.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1321514863be18138a6d761696b3f6e8645390dd2f6c8a6d66a453f0d5187c"}, - {file = "pyinstrument-4.7.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de40b44ff2fe78493b944b679cc084e72b2648c37a96fcfbccb9171a4449e509"}, - {file = "pyinstrument-4.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a7c481daec4bd77a3dbfbe01a0155e03352dd700f3c3efe4bdbc30821b20e19"}, - {file = "pyinstrument-4.7.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ae2c966c91da630a23dbff5f7e61ad2eee133cfaf1e4acf7e09fcf506cbb6251"}, - {file = "pyinstrument-4.7.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fa2715e3ac3ce2f4b9c4e468a9a4faf43ca645beea002cb47533902576f4f64d"}, - {file = "pyinstrument-4.7.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61db15f8b59a3a1964041a8df260667fb5dabddd928301e3580cf93d7a05e352"}, - {file = "pyinstrument-4.7.3-cp311-cp311-win32.whl", hash = "sha256:4766bbb2b451460432c97baf00bbda56653429671e8daec344d343f21fb05b8f"}, - {file = "pyinstrument-4.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:b2d2a0e401db6800f63de0539415cdff46b138914d771a46db0b3f673f9827e7"}, - {file = "pyinstrument-4.7.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7c29f7a23e0f704f5f21aeeb47193460601e7359d09156ea043395870494b39a"}, - {file = "pyinstrument-4.7.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:84ceb25f24ceb03dc770b6c142ec4419506d3a04d66d778810cb8da76df25651"}, - {file = "pyinstrument-4.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d564d6f6151d3cab28430092cdcbd4aefe0834551af4b4f97e6e57025a348557"}, - {file = "pyinstrument-4.7.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e23ce5fcc30346e576b98ca24bd2a9a68cbc42b90cdb0d8f376fa82cee2fe23"}, - {file = "pyinstrument-4.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23d5ad174d2a488c164abee4407f3f3a6e6d5721ab1fab9e0ad9570631704c2"}, - {file = "pyinstrument-4.7.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d87749f68b9cc221628aab989a4a73b16030c27c714ecd83892d716f863d9739"}, - {file = "pyinstrument-4.7.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:897d09c876f18b713498be21430b39428a9254ffec0c6c06796fce0e6a8fe437"}, - {file = "pyinstrument-4.7.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2092910e745cfd0a62dadf041afb38239195244871ee127b1028e7e790602e6b"}, - {file = "pyinstrument-4.7.3-cp312-cp312-win32.whl", hash = "sha256:e9824e11290f6f2772c257cc0bd07f59405759287db6ebcbb06f962a3eba68fb"}, - {file = "pyinstrument-4.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:cf1e67b37e936f647ce731fff5d2f54e102813274d350671dc5961ec8b46b3ff"}, - {file = "pyinstrument-4.7.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6de792dc65dcc75e73b721f4e89aa60a4d2f8617e5a5da060244058018ad0399"}, - {file = "pyinstrument-4.7.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:73da379506a09cdff2fdd23a0b3eb8f020f473d019f604538e0e5045613e33d4"}, - {file = "pyinstrument-4.7.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21e05f53810a6ff5fa261da838935fd1b2ab2bf30a7c053f6c72bcaaa6de0933"}, - {file = "pyinstrument-4.7.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d648596ea04409ca3ca260029041ed7fa046b776205bf9a0b75cda0a4f4d2515"}, - {file = "pyinstrument-4.7.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d98997347047a217ef6b844273d3753e543e0984f2220e9dd284cbef6054c2a"}, - {file = "pyinstrument-4.7.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7f09ebad95af94f5427c20005fc7ba84a0a3deae6324434d7ec3be99d369bf37"}, - {file = "pyinstrument-4.7.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8a66aee3d2cf0cc6b8e57cb189fd9fb16d13b8d538419999596ce4f58b5d4a9a"}, - {file = "pyinstrument-4.7.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eaa45270af0b9d86f1cef705520e9b43f4a1cd18397083f8a594a28f898d078b"}, - {file = "pyinstrument-4.7.3-cp313-cp313-win32.whl", hash = "sha256:6e85b34a9b8ed4df4deaa0afe63bc765ea29003eb5b9b3bc0323f7ad7f7cd0fd"}, - {file = "pyinstrument-4.7.3-cp313-cp313-win_amd64.whl", hash = "sha256:6002ea1018d6d6f9b6f1c66b3e14805213573bd69f79b2e7ad2c507441b3e73e"}, - {file = "pyinstrument-4.7.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b68c5b97690604741bb1f028ec75d2a6298500f415590ae92a766f71b82fc72a"}, - {file = "pyinstrument-4.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df9ba133f5a771dd30df1d3b868af75bdb7f12c9ebd5ddd463d09aa6334d96ef"}, - {file = "pyinstrument-4.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bfad987207c89b51f80be71f5362cead4ccd62b9f407248b87e91863bba70e4d"}, - {file = "pyinstrument-4.7.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd559498902d1560d728238eea53d8dd54cb8f697b816cacce5524f09d8757"}, - {file = "pyinstrument-4.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:470a4f6de1a1edf7debe87917b5d12f94fe59975a8a0e91c22ad789b55720073"}, - {file = "pyinstrument-4.7.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f29ed5778b83bf40bd808f120cd2ea11ef94acd2aa5b64398e6d56958b88ab26"}, - {file = "pyinstrument-4.7.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:6d642d8c69091fd49286136b7d958f8dbac969a3f6259c7c6d78e8ff207d235e"}, - {file = "pyinstrument-4.7.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:346bc584c542c4c77ca46e8f55eb2d3265ee992839e06d535a22ca65c5b9e767"}, - {file = "pyinstrument-4.7.3-cp38-cp38-win32.whl", hash = "sha256:66af331f9da06df36afbdbd2b7128ae725bb444f24584d2ed1f4c67d1b2759b8"}, - {file = "pyinstrument-4.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:57992c5f73fad7b560e27f864ff9824c6ccc834d48bbeaf4cecf66193cfe28c6"}, - {file = "pyinstrument-4.7.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8b944c939c49af88cec1e20e9c28eec80c478fc2fd53b23ed58702bcb5bcbcf9"}, - {file = "pyinstrument-4.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:edd85ee9c6aa5be0bf78d48ad2eb5e02fdab1a646875d90fa09cbc61f4c91a01"}, - {file = "pyinstrument-4.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e381fc56ba4a77cb45d82eb69689d900a5ee7205a5eb90131234b21ae7a1991"}, - {file = "pyinstrument-4.7.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98e1b7695c234786e82500394ef50f205713f8702a31aec84fdd0687e0ab8405"}, - {file = "pyinstrument-4.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03dd0c51f6ca706be5c27715e9b4527aa82003c2705d3173943c5b4a2b7a47e8"}, - {file = "pyinstrument-4.7.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2b312442f01fbf2582cd7c929703608cb82874b73a0f3250cbeffc4abddae4f5"}, - {file = "pyinstrument-4.7.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e660d9a7f57909574010056dbc80869866623669455516ffc7421988286ddaf3"}, - {file = "pyinstrument-4.7.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:886ccb349aefcbd5be1f33247b3a1af4ad5d34939338d99e94bae064886bf0d8"}, - {file = "pyinstrument-4.7.3-cp39-cp39-win32.whl", hash = "sha256:1ce2828cc29b17720f3c66345ea6f9ff54a3860d0488b59c985377ce2e6a710b"}, - {file = "pyinstrument-4.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:e562e608f878540d19a514774e0f24fccaeac035674cf2b2afacdae9e0e19b29"}, - {file = "pyinstrument-4.7.3.tar.gz", hash = "sha256:3ad61041ff1880d4c99d3384cd267e38a0a6472b5a4dd765992db376bd4394c8"}, -] - -[package.extras] -bin = ["click", "nox"] -docs = ["furo (==2024.7.18)", "myst-parser (==3.0.1)", "sphinx (==7.4.7)", "sphinx-autobuild (==2024.4.16)", "sphinxcontrib-programoutput (==0.17)"] -examples = ["django", "litestar", "numpy"] -test = ["cffi (>=v1.17.0rc1)", "flaky", "greenlet (>=3.0.0a1)", "ipython", "pytest", "pytest-asyncio (==0.23.8)", "trio"] -types = ["typing-extensions"] - -[[package]] -name = "pyparsing" -version = "3.1.4" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.6.8" -files = [ - {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, - {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, -] - -[package.extras] -diagrams = ["jinja2", "railroad-diagrams"] - -[[package]] -name = "pypika-tortoise" -version = "0.1.6" -description = "Forked from pypika and streamline just for tortoise-orm" -optional = false -python-versions = ">=3.7,<4.0" -files = [ - {file = "pypika-tortoise-0.1.6.tar.gz", hash = "sha256:d802868f479a708e3263724c7b5719a26ad79399b2a70cea065f4a4cadbebf36"}, - {file = "pypika_tortoise-0.1.6-py3-none-any.whl", hash = "sha256:2d68bbb7e377673743cff42aa1059f3a80228d411fbcae591e4465e173109fd8"}, -] - -[[package]] -name = "pytest" -version = "8.3.3" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2" - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-xdist" -version = "3.6.1" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, -] - -[package.dependencies] -execnet = ">=2.1" -pytest = ">=7.0.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2024.2" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "requests" -version = "2.32.3" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rich" -version = "13.8.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, - {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "ruff" -version = "0.6.7" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.6.7-py3-none-linux_armv6l.whl", hash = "sha256:08277b217534bfdcc2e1377f7f933e1c7957453e8a79764d004e44c40db923f2"}, - {file = "ruff-0.6.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6707a32e03b791f4448dc0dce24b636cbcdee4dd5607adc24e5ee73fd86c00a"}, - {file = "ruff-0.6.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:533d66b7774ef224e7cf91506a7dafcc9e8ec7c059263ec46629e54e7b1f90ab"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a86aac6f915932d259f7bec79173e356165518859f94649d8c50b81ff087e9"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b3f8822defd260ae2460ea3832b24d37d203c3577f48b055590a426a722d50ef"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ba4efe5c6dbbb58be58dd83feedb83b5e95c00091bf09987b4baf510fee5c99"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:525201b77f94d2b54868f0cbe5edc018e64c22563da6c5c2e5c107a4e85c1c0d"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8854450839f339e1049fdbe15d875384242b8e85d5c6947bb2faad33c651020b"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f0b62056246234d59cbf2ea66e84812dc9ec4540518e37553513392c171cb18"}, - {file = "ruff-0.6.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b1462fa56c832dc0cea5b4041cfc9c97813505d11cce74ebc6d1aae068de36b"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:02b083770e4cdb1495ed313f5694c62808e71764ec6ee5db84eedd82fd32d8f5"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:0c05fd37013de36dfa883a3854fae57b3113aaa8abf5dea79202675991d48624"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f49c9caa28d9bbfac4a637ae10327b3db00f47d038f3fbb2195c4d682e925b14"}, - {file = "ruff-0.6.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a0e1655868164e114ba43a908fd2d64a271a23660195017c17691fb6355d59bb"}, - {file = "ruff-0.6.7-py3-none-win32.whl", hash = "sha256:a939ca435b49f6966a7dd64b765c9df16f1faed0ca3b6f16acdf7731969deb35"}, - {file = "ruff-0.6.7-py3-none-win_amd64.whl", hash = "sha256:590445eec5653f36248584579c06252ad2e110a5d1f32db5420de35fb0e1c977"}, - {file = "ruff-0.6.7-py3-none-win_arm64.whl", hash = "sha256:b28f0d5e2f771c1fe3c7a45d3f53916fc74a480698c4b5731f0bea61e52137c8"}, - {file = "ruff-0.6.7.tar.gz", hash = "sha256:44e52129d82266fa59b587e2cd74def5637b730a69c4542525dfdecfaae38bd5"}, -] - -[[package]] -name = "scipy" -version = "1.14.0" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.10" -files = [ - {file = "scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484"}, - {file = "scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6"}, - {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7"}, - {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1"}, - {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0"}, - {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0"}, - {file = "scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d"}, - {file = "scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"}, - {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"}, - {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"}, - {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"}, - {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"}, - {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"}, - {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"}, - {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"}, - {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"}, - {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"}, -] - -[package.dependencies] -numpy = ">=1.23.5,<2.3" - -[package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - -[[package]] -name = "sexpdata" -version = "1.0.2" -description = "S-expression parser for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sexpdata-1.0.2-py3-none-any.whl", hash = "sha256:b39c918f055a85c5c35c1d4f7930aabb176bd29016e5ba5692e7e849914b2a1a"}, - {file = "sexpdata-1.0.2.tar.gz", hash = "sha256:92b67b0361f6766f8f9e44b9519cf3fbcfafa755db85bbf893c3e1cf4ddac109"}, -] - -[[package]] -name = "shapely" -version = "2.0.6" -description = "Manipulation and analysis of geometric objects" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shapely-2.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29a34e068da2d321e926b5073539fd2a1d4429a2c656bd63f0bd4c8f5b236d0b"}, - {file = "shapely-2.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c84c3f53144febf6af909d6b581bc05e8785d57e27f35ebaa5c1ab9baba13b"}, - {file = "shapely-2.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad2fae12dca8d2b727fa12b007e46fbc522148a584f5d6546c539f3464dccde"}, - {file = "shapely-2.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3304883bd82d44be1b27a9d17f1167fda8c7f5a02a897958d86c59ec69b705e"}, - {file = "shapely-2.0.6-cp310-cp310-win32.whl", hash = "sha256:3ec3a0eab496b5e04633a39fa3d5eb5454628228201fb24903d38174ee34565e"}, - {file = "shapely-2.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:28f87cdf5308a514763a5c38de295544cb27429cfa655d50ed8431a4796090c4"}, - {file = "shapely-2.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5aeb0f51a9db176da9a30cb2f4329b6fbd1e26d359012bb0ac3d3c7781667a9e"}, - {file = "shapely-2.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a7a78b0d51257a367ee115f4d41ca4d46edbd0dd280f697a8092dd3989867b2"}, - {file = "shapely-2.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32c23d2f43d54029f986479f7c1f6e09c6b3a19353a3833c2ffb226fb63a855"}, - {file = "shapely-2.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3dc9fb0eb56498912025f5eb352b5126f04801ed0e8bdbd867d21bdbfd7cbd0"}, - {file = "shapely-2.0.6-cp311-cp311-win32.whl", hash = "sha256:d93b7e0e71c9f095e09454bf18dad5ea716fb6ced5df3cb044564a00723f339d"}, - {file = "shapely-2.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:c02eb6bf4cfb9fe6568502e85bb2647921ee49171bcd2d4116c7b3109724ef9b"}, - {file = "shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0"}, - {file = "shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3"}, - {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8"}, - {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fea108334be345c283ce74bf064fa00cfdd718048a8af7343c59eb40f59726"}, - {file = "shapely-2.0.6-cp312-cp312-win32.whl", hash = "sha256:42fd4cd4834747e4990227e4cbafb02242c0cffe9ce7ef9971f53ac52d80d55f"}, - {file = "shapely-2.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:665990c84aece05efb68a21b3523a6b2057e84a1afbef426ad287f0796ef8a48"}, - {file = "shapely-2.0.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:42805ef90783ce689a4dde2b6b2f261e2c52609226a0438d882e3ced40bb3013"}, - {file = "shapely-2.0.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d2cb146191a47bd0cee8ff5f90b47547b82b6345c0d02dd8b25b88b68af62d7"}, - {file = "shapely-2.0.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3fdef0a1794a8fe70dc1f514440aa34426cc0ae98d9a1027fb299d45741c381"}, - {file = "shapely-2.0.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c665a0301c645615a107ff7f52adafa2153beab51daf34587170d85e8ba6805"}, - {file = "shapely-2.0.6-cp313-cp313-win32.whl", hash = "sha256:0334bd51828f68cd54b87d80b3e7cee93f249d82ae55a0faf3ea21c9be7b323a"}, - {file = "shapely-2.0.6-cp313-cp313-win_amd64.whl", hash = "sha256:d37d070da9e0e0f0a530a621e17c0b8c3c9d04105655132a87cfff8bd77cc4c2"}, - {file = "shapely-2.0.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fa7468e4f5b92049c0f36d63c3e309f85f2775752e076378e36c6387245c5462"}, - {file = "shapely-2.0.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed5867e598a9e8ac3291da6cc9baa62ca25706eea186117034e8ec0ea4355653"}, - {file = "shapely-2.0.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81d9dfe155f371f78c8d895a7b7f323bb241fb148d848a2bf2244f79213123fe"}, - {file = "shapely-2.0.6-cp37-cp37m-win32.whl", hash = "sha256:fbb7bf02a7542dba55129062570211cfb0defa05386409b3e306c39612e7fbcc"}, - {file = "shapely-2.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:837d395fac58aa01aa544495b97940995211e3e25f9aaf87bc3ba5b3a8cd1ac7"}, - {file = "shapely-2.0.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c6d88ade96bf02f6bfd667ddd3626913098e243e419a0325ebef2bbd481d1eb6"}, - {file = "shapely-2.0.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8b3b818c4407eaa0b4cb376fd2305e20ff6df757bf1356651589eadc14aab41b"}, - {file = "shapely-2.0.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbc783529a21f2bd50c79cef90761f72d41c45622b3e57acf78d984c50a5d13"}, - {file = "shapely-2.0.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2423f6c0903ebe5df6d32e0066b3d94029aab18425ad4b07bf98c3972a6e25a1"}, - {file = "shapely-2.0.6-cp38-cp38-win32.whl", hash = "sha256:2de00c3bfa80d6750832bde1d9487e302a6dd21d90cb2f210515cefdb616e5f5"}, - {file = "shapely-2.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:3a82d58a1134d5e975f19268710e53bddd9c473743356c90d97ce04b73e101ee"}, - {file = "shapely-2.0.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:392f66f458a0a2c706254f473290418236e52aa4c9b476a072539d63a2460595"}, - {file = "shapely-2.0.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eba5bae271d523c938274c61658ebc34de6c4b33fdf43ef7e938b5776388c1be"}, - {file = "shapely-2.0.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060566bc4888b0c8ed14b5d57df8a0ead5c28f9b69fb6bed4476df31c51b0af"}, - {file = "shapely-2.0.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b02154b3e9d076a29a8513dffcb80f047a5ea63c897c0cd3d3679f29363cf7e5"}, - {file = "shapely-2.0.6-cp39-cp39-win32.whl", hash = "sha256:44246d30124a4f1a638a7d5419149959532b99dfa25b54393512e6acc9c211ac"}, - {file = "shapely-2.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:2b542d7f1dbb89192d3512c52b679c822ba916f93479fa5d4fc2fe4fa0b3c9e8"}, - {file = "shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6"}, -] - -[package.dependencies] -numpy = ">=1.14,<3" - -[package.extras] -docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "tortoise-orm" -version = "0.21.6" -description = "Easy async ORM for python, built with relations in mind" -optional = false -python-versions = "<4.0,>=3.8" -files = [ - {file = "tortoise_orm-0.21.6-py3-none-any.whl", hash = "sha256:98fcf07dce3396075eac36b0d2b14d2267ff875d32455e03ee15e38de2f138df"}, - {file = "tortoise_orm-0.21.6.tar.gz", hash = "sha256:0fbc718001647bf282c01eaaa360f94f1432c9281701244180703d48d58a88ec"}, -] - -[package.dependencies] -aiosqlite = ">=0.16.0,<0.18.0" -iso8601 = ">=1.0.2,<2.0.0" -pydantic = ">=2.0,<2.7.0 || >2.7.0,<3.0" -pypika-tortoise = ">=0.1.6,<0.2.0" -pytz = "*" - -[package.extras] -accel = ["ciso8601", "orjson", "uvloop"] -aiomysql = ["aiomysql"] -asyncmy = ["asyncmy (>=0.2.8,<0.3.0)"] -asyncodbc = ["asyncodbc (>=0.1.1,<0.2.0)"] -asyncpg = ["asyncpg"] -psycopg = ["psycopg[binary,pool] (>=3.0.12,<4.0.0)"] - -[[package]] -name = "typer" -version = "0.12.5" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, -] - -[package.dependencies] -click = ">=8.0.0" -rich = ">=10.11.0" -shellingham = ">=1.3.0" -typing-extensions = ">=3.7.4.3" - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "typing-inspect" -version = "0.9.0" -description = "Runtime inspection utilities for typing module." -optional = false -python-versions = "*" -files = [ - {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, - {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, -] - -[package.dependencies] -mypy-extensions = ">=0.3.0" -typing-extensions = ">=3.7.4" - -[[package]] -name = "urllib3" -version = "2.2.3" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "virtualenv" -version = "20.26.5" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, - {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "viztracer" -version = "0.16.3" -description = "A debugging and profiling tool that can trace and visualize python code execution" -optional = false -python-versions = ">=3.8" -files = [ - {file = "viztracer-0.16.3-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:3a39fcee0ffd35639b75565afce9a48ea92a4386f4c2f4a2752b4ec068f273cb"}, - {file = "viztracer-0.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c223312ecf4adb2271ca4ea84b546b278ec2dbe15b5ace5aa630cb07c2b53d4f"}, - {file = "viztracer-0.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1320a6ebb89d691a1eced48f5d842d5d459d79bf85ad4fbf004e46bbf417c0d5"}, - {file = "viztracer-0.16.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a96df402baacdca90c23559c4632e517db2e91628d191a7670d95809960ca8e"}, - {file = "viztracer-0.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff04b25518eac932fec618c8297794c9894ac75db543b1c872cea945ae986feb"}, - {file = "viztracer-0.16.3-cp310-cp310-win32.whl", hash = "sha256:8f56dad6111e1f3a0bd1f6de541d87c0c145fd88d4fd2b613b0c0d1f538b1a29"}, - {file = "viztracer-0.16.3-cp310-cp310-win_amd64.whl", hash = "sha256:59c32b87b4c5ba25b78dcb993b255512106b669042ce2b31bb7b6a92edc43d67"}, - {file = "viztracer-0.16.3-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:cca0a017635f6ade7cd73474bcdc19b2730cdc468f338912afe749234c6ebe30"}, - {file = "viztracer-0.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d6a938c197f4f723ad2f8d7b8e1aa0449e249dd4702e67445aaf42392786378"}, - {file = "viztracer-0.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8721e1fd177b4abff0cbee141a31d6b819867833c92866f238815b321178c6df"}, - {file = "viztracer-0.16.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73c0e88ecc68572599405cca54f52d7d9b7dc811992bf8edbee1860486627542"}, - {file = "viztracer-0.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73697d43ed42f6e5110166387c3b4d76b4e181bf70822bac3f7f3e39b12be21e"}, - {file = "viztracer-0.16.3-cp311-cp311-win32.whl", hash = "sha256:dac628f38a23154a184bd5f37e215d8ada83e33892593036112584dec83b7369"}, - {file = "viztracer-0.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:13df50118ff0e3266c32b66f0ff26df202f7c4d9583a550dc537098303141cec"}, - {file = "viztracer-0.16.3-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:4f07b263abe9ade8390253eebdd4658eb44288e98d1b7b9afae42a54d15197f1"}, - {file = "viztracer-0.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f4b0b59cd97f7d6fb37766105cb062d042df87648670f560a3d730050026087"}, - {file = "viztracer-0.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a2f9b2bf4f8e051cf9ee70bd7e6ff91c33239bcd01fa37005423915bfccaa64"}, - {file = "viztracer-0.16.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69971a9b24801259511545ec2a3b5a4f90733f64fca799268fb6d64d72c9e93e"}, - {file = "viztracer-0.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2f208a89c79a87ba563ee39f035df924a0109c4a2300a8098181e25f1f8afea"}, - {file = "viztracer-0.16.3-cp312-cp312-win32.whl", hash = "sha256:bf23e06d8f9a870711a3441123c364ac6a5d0caa288c6acdd116212f81e46b2a"}, - {file = "viztracer-0.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:594e9dabf5cca584654d6813844be0377ae292e8dd507a013e9e427fd10d6fbd"}, - {file = "viztracer-0.16.3-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:5dc505dd2a607a8d9d2c0c488dc297593a3cca2227a4c31630d20fa0e4b27087"}, - {file = "viztracer-0.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a50b7876bcb1762767878422fe4ba0ff46854ddf2d494b8b236b595da889156b"}, - {file = "viztracer-0.16.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f17cf5d67112b236bc3501f563beaa2a542121c0d8d2087b4e9a9d24763394d"}, - {file = "viztracer-0.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f4d5732e1a7f3f1fa4bee0f53f17155002fabef97d620bec774158bac7bd701"}, - {file = "viztracer-0.16.3-cp38-cp38-win32.whl", hash = "sha256:e79d7e55fffd32cbb49466f96233f292602c732832f9a9cc075773762be6f487"}, - {file = "viztracer-0.16.3-cp38-cp38-win_amd64.whl", hash = "sha256:8663707e5527f614cc038b6fa189697f0809e5593fa9522c0152261b79b493f0"}, - {file = "viztracer-0.16.3-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:38d330348f6be0f17628657a2e454b760de40383f3065ad48fe6e2ae57478d7a"}, - {file = "viztracer-0.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bbb88d7cbfe3d12021f97d7f58b7a21eae20d799ea7092f55d2a60eff1dbe2"}, - {file = "viztracer-0.16.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1378cb93f62e58976ee31d394ad9b667eca5d0edfe8371609e2e68c6427e03"}, - {file = "viztracer-0.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c5bad11791c952b107f88b3a6a2a39848b0d0d6b8499222575854789722936"}, - {file = "viztracer-0.16.3-cp39-cp39-win32.whl", hash = "sha256:65d072348f96e402dd28d21e36e6d3f513f26dc79bf89cbde0efe594f239f8f5"}, - {file = "viztracer-0.16.3-cp39-cp39-win_amd64.whl", hash = "sha256:b8e35a109e9b1d5515ffeb0806e834274c0133a73d0a2a2b2fd24f031a1f43b3"}, - {file = "viztracer-0.16.3.tar.gz", hash = "sha256:943cb874cf92cdc786cd87938ac64ea081e3ae06ef73f577deac5b4a2a9621d5"}, -] - -[package.dependencies] -objprint = ">0.1.3" - -[package.extras] -full = ["orjson"] - -[[package]] -name = "wrapt" -version = "1.16.0" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.6" -files = [ - {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, - {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, - {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, - {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, - {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, - {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, - {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, - {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, - {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, - {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, - {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, - {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, - {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, - {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, - {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, - {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, - {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, - {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, - {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, - {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, - {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, - {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, - {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, - {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, - {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, - {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, - {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, - {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, - {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, - {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, - {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, - {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, - {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, - {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, - {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, - {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, - {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, -] - -[metadata] -lock-version = "2.0" -python-versions = "^3.12,<3.13" -content-hash = "86c6b9a700cf392c5c6fbafcc24f293dc8c5a913a8aaab1c01d3957e197784eb" diff --git a/pyproject.toml b/pyproject.toml index e1f471fe..67306643 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,11 @@ [build-system] # using scikit-build-core>0.9.3 disables editable mode -requires = ["hatchling", "scikit-build-core==0.9.2", "nanobind~=2.2.0", "hatch-vcs"] +requires = [ + "hatchling", + "scikit-build-core==0.9.2", + "nanobind~=2.2.0", + "hatch-vcs", +] build-backend = "hatchling.build" [project] @@ -17,9 +22,7 @@ classifiers = [ ] requires-python = ">=3.12,<3.13" dependencies = [ - "networkx==3.3", "numpy>=1.24.3,<3.0.0", - "scipy>=1.11.1,<=1.14.0", "matplotlib~=3.7.1", "sexpdata==1.0.2", "black~=24.4.2", @@ -36,7 +39,8 @@ dependencies = [ "typer>=0.12,<0.13", "isort~=5.6.4", "ruff>=0.6.4,<0.7.0", - "pint~=0.24.3", + # Waiting for a new release that works on python 3.13 + "pint @ git+https://github.com/hgrecco/pint.git@a8bcb6ee1d0d61278bf17e332bc1aa473672e273", "deprecated~=1.2.14", "more-itertools~=10.4.0", "psutil~=6.0.0", @@ -66,6 +70,9 @@ faebryk = "faebryk.tools.main:__main__" [tool.hatch] +[tool.hatch.metadata] +allow-direct-references = true + [tool.hatch.env] #requires = ["hatch-pip-compile"] @@ -93,7 +100,7 @@ cmake.source-dir = "src/faebryk/core/cpp" [tool.pytest] [tool.pytest.ini_options] # loadscope to run tests for each file in same worker -addopts = ["--import-mode=importlib", "--numprocesses=auto", "--dist=loadscope"] +addopts = ["--import-mode=importlib", "--numprocesses=auto"] filterwarnings = ["ignore:.*:DeprecationWarning"] testpaths = ["test"] diff --git a/src/faebryk/core/core.py b/src/faebryk/core/core.py index 4249d283..27c98f2a 100644 --- a/src/faebryk/core/core.py +++ b/src/faebryk/core/core.py @@ -10,11 +10,6 @@ logger = logging.getLogger(__name__) -LINK_TB = ConfigFlag( - "LINK_TB", - False, - "Save stack trace for each link. Warning: Very slow! Just use for debug", -) ID_REPR = ConfigFlag("ID_REPR", False, "Add object id to repr") diff --git a/src/faebryk/core/cpp/.clang-format b/src/faebryk/core/cpp/.clang-format index d9a358cb..71b5bd16 100644 --- a/src/faebryk/core/cpp/.clang-format +++ b/src/faebryk/core/cpp/.clang-format @@ -5,4 +5,5 @@ BreakConstructorInitializers: BeforeComma ConstructorInitializerIndentWidth: 2 AllowShortFunctionsOnASingleLine: None AllowShortLambdasOnASingleLine: None -ColumnLimit: 89 \ No newline at end of file +ColumnLimit: 89 +SeparateDefinitionBlocks: Always diff --git a/src/faebryk/core/cpp/CMakeLists.txt b/src/faebryk/core/cpp/CMakeLists.txt index 60e24f9a..253fd8da 100644 --- a/src/faebryk/core/cpp/CMakeLists.txt +++ b/src/faebryk/core/cpp/CMakeLists.txt @@ -38,6 +38,8 @@ message(STATUS "Python_EXECUTABLE: ${Python_EXECUTABLE}") message(STATUS "Python_INCLUDE_DIR: ${Python_INCLUDE_DIR}") message(STATUS "Python_LIBRARY: ${Python_LIBRARY}") +# To make a debug build, run cmake with -DCMAKE_BUILD_TYPE=Debug +# or in environment: FBRK_CPP_DEBUG_BUILD=y if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") @@ -56,7 +58,10 @@ if(${EDITABLE} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") endif() # source files --------------------------------------------------------- include_directories(${CMAKE_SOURCE_DIR}/include) -set(SOURCE_FILES src/main.cpp) +file(GLOB_RECURSE SOURCE_FILES + "${CMAKE_SOURCE_DIR}/src/*.cpp" +) + # build ---------------------------------------------------------------- nanobind_add_module(${PROJECT_NAME} ${SOURCE_FILES}) diff --git a/src/faebryk/core/cpp/__init__.py b/src/faebryk/core/cpp/__init__.py index 7a331763..085cdb7d 100644 --- a/src/faebryk/core/cpp/__init__.py +++ b/src/faebryk/core/cpp/__init__.py @@ -5,9 +5,15 @@ import logging from importlib.metadata import Distribution +from faebryk.libs.util import ConfigFlag, at_exit, global_lock + logger = logging.getLogger(__name__) +LEAK_WARNINGS = ConfigFlag("CPP_LEAK_WARNINGS", default=False) +DEBUG_BUILD = ConfigFlag("CPP_DEBUG_BUILD", default=False) + + # Check if installed as editable def is_editable_install(): distro = Distribution.from_name("faebryk") @@ -58,27 +64,33 @@ def compile_and_load(): if arch in ["arm64", "x86_64"]: other_flags += [f"-DCMAKE_OSX_ARCHITECTURES={arch}"] - run_live( - [ - "cmake", - "-S", - str(_cmake_dir), - "-B", - str(_build_dir), - "-DEDITABLE=1", - "-DPython_EXECUTABLE=" + sys.executable, - *other_flags, - ], - logger=logger, - ) - run_live( - [ - "cmake", - "--build", - str(_build_dir), - ], - logger=logger, - ) + if DEBUG_BUILD: + other_flags += ["-DCMAKE_BUILD_TYPE=Debug"] + + with global_lock(_build_dir / "lock", timeout_s=60): + run_live( + [ + "cmake", + "-S", + str(_cmake_dir), + "-B", + str(_build_dir), + "-DEDITABLE=1", + "-DPython_EXECUTABLE=" + sys.executable, + *other_flags, + ], + logger=logger, + ) + run_live( + [ + "cmake", + "--build", + str(_build_dir), + "--", + "-j", + ], + logger=logger, + ) if not _build_dir.exists(): raise RuntimeError("build directory not found") @@ -97,7 +109,8 @@ def compile_and_load(): + "\n" + "# This file is auto-generated by nanobind.\n" + "# Do not edit this file directly; edit the corresponding\n" - + "# C++ file instead." + + "# C++ file instead.\n" + # + "from typing import overload\n" + pyi_source.read_text(), is_pyi=True, ) @@ -111,3 +124,15 @@ def compile_and_load(): from faebryk_core_cpp_editable import * # type: ignore # noqa: E402, F403 else: from faebryk_core_cpp import * # type: ignore # noqa: E402, F403 + + +def cleanup(): + if LEAK_WARNINGS: + print("\n--- Nanobind leakage analysis ".ljust(80, "-")) + # nanobind automatically prints leaked objects at exit + from faebryk.core.cpp import set_leak_warnings + + set_leak_warnings(bool(LEAK_WARNINGS)) + + +at_exit(cleanup, on_exception=False) diff --git a/src/faebryk/core/cpp/__init__.pyi b/src/faebryk/core/cpp/__init__.pyi index e0a05567..a4ec03f4 100644 --- a/src/faebryk/core/cpp/__init__.pyi +++ b/src/faebryk/core/cpp/__init__.pyi @@ -4,6 +4,150 @@ # This file is auto-generated by nanobind. # Do not edit this file directly; edit the corresponding # C++ file instead. +import enum +from collections.abc import Callable, Sequence, Set +from typing import overload -def add(arg0: int, arg1: int, /) -> int: +class Graph: + def __init__(self) -> None: ... + def get_edges(self, arg: GraphInterface, /) -> dict[GraphInterface, Link]: ... + def invalidate(self) -> None: ... + @property + def node_count(self) -> int: ... + @property + def edge_count(self) -> int: ... + def node_projection(self) -> set[Node]: ... + def nodes_by_names(self, arg: Set[str], /) -> list[tuple[Node, str]]: ... + def bfs_visit( + self, + filter: Callable[[Sequence[GraphInterface], Link], bool], + start: Sequence[GraphInterface], + ) -> set[GraphInterface]: ... + def __repr__(self) -> str: ... + +class GraphInterface: + def __init__(self) -> None: ... + def __repr__(self) -> str: ... + def get_graph(self) -> Graph: ... + @property + def G(self) -> Graph: ... + def get_gif_edges(self) -> set[GraphInterface]: ... + @property + def edges(self) -> dict[GraphInterface, Link]: ... + @property + def node(self) -> Node: ... + @node.setter + def node(self, arg: Node, /) -> None: ... + def is_connected_to(self, arg: GraphInterface, /) -> Link | None: ... + @property + def name(self) -> str: ... + @name.setter + def name(self, arg: str, /) -> None: ... + def get_connected_nodes(self, types: Sequence[type]) -> set[Node]: ... + @overload + def connect(self, other: GraphInterface) -> None: ... + @overload + def connect(self, others: Sequence[GraphInterface]) -> None: ... + @overload + def connect(self, other: GraphInterface, link: Link) -> None: ... + +class GraphInterfaceHierarchical(GraphInterface): + def __init__(self, is_parent: bool) -> None: ... + def get_parent(self) -> tuple[Node, str] | None: ... + def get_children(self) -> list[Node]: ... + @property + def is_parent(self) -> bool: ... + def disconnect_parent(self) -> None: ... + +class GraphInterfaceModuleConnection(GraphInterface): + def __init__(self) -> None: ... + +class GraphInterfaceModuleSibling(GraphInterfaceHierarchical): + def __init__(self, is_parent: bool) -> None: ... + +class GraphInterfaceReference(GraphInterface): + def __init__(self) -> None: ... + def get_referenced_gif(self) -> GraphInterfaceSelf: ... + def get_reference(self) -> Node: ... + +class GraphInterfaceReferenceUnboundError(Exception): + pass + +class GraphInterfaceSelf(GraphInterface): + def __init__(self) -> None: ... + +class Link: + pass + +class LinkDirect(Link): + def __init__(self) -> None: ... + +class LinkDirectConditional(LinkDirect): + def __init__( + self, + arg: Callable[ + [GraphInterface, GraphInterface], LinkDirectConditionalFilterResult + ], + /, + ) -> None: ... + +class LinkDirectConditionalFilterResult(enum.Enum): + FILTER_PASS = 0 + + FILTER_FAIL_RECOVERABLE = 1 + + FILTER_FAIL_UNRECOVERABLE = 2 + +class LinkFilteredException(Exception): + pass + +class LinkNamedParent(LinkParent): + def __init__(self, arg: str, /) -> None: ... + +class LinkParent(Link): + def __init__(self) -> None: ... + +class LinkPointer(Link): + def __init__(self) -> None: ... + +class LinkSibling(LinkPointer): + def __init__(self) -> None: ... + +class Node: + def __init__(self) -> None: ... + @staticmethod + def transfer_ownership(arg: Node, /) -> Node: ... + def get_graph(self) -> Graph: ... + @property + def self_gif(self) -> GraphInterfaceSelf: ... + @property + def children(self) -> GraphInterfaceHierarchical: ... + @property + def parent(self) -> GraphInterfaceHierarchical: ... + def get_children( + self, + direct_only: bool, + types: Sequence[type] | None = None, + include_root: bool = False, + f_filter: Callable[[Node], bool] | None = None, + sort: bool = True, + ) -> list[Node]: ... + def get_parent(self) -> tuple[Node, str] | None: ... + def get_parent_force(self) -> tuple[Node, str]: ... + def get_name(self, accept_no_parent: bool = False) -> str: ... + def get_hierarchy(self) -> list[tuple[Node, str]]: ... + def get_full_name(self, types: bool = False) -> str: ... + def __repr__(self) -> str: ... + +class NodeException(Exception): + pass + +class NodeNoParent(Exception): + pass + +def add(i: int, j: int = 1) -> int: """A function that adds two numbers""" + +def call_python_function(func: Callable[[], int]) -> int: ... +def print_obj(obj: object) -> None: ... +def set_leak_warnings(value: bool) -> None: ... diff --git a/src/faebryk/core/cpp/cpp_helpers.py b/src/faebryk/core/cpp/cpp_helpers.py new file mode 100644 index 00000000..1674db0a --- /dev/null +++ b/src/faebryk/core/cpp/cpp_helpers.py @@ -0,0 +1,7 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + + +import logging + +logger = logging.getLogger(__name__) diff --git a/src/faebryk/core/cpp/include/graph/graph.hpp b/src/faebryk/core/cpp/include/graph/graph.hpp new file mode 100644 index 00000000..328727dd --- /dev/null +++ b/src/faebryk/core/cpp/include/graph/graph.hpp @@ -0,0 +1,194 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include "util.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nb = nanobind; + +template using Set = std::unordered_set; +template using Map = std::unordered_map; + +class Graph; +class GraphInterface; +class GraphInterfaceHierarchical; +class GraphInterfaceSelf; +class Link; +class Node; + +using GI_ref = std::shared_ptr; +using GI_ref_weak = GraphInterface *; +using Link_ref = std::shared_ptr; +using Node_ref = std::shared_ptr; +using Graph_ref = std::shared_ptr; +using GI_refs_weak = std::vector; +using HierarchicalNodeRef = std::pair; + +class Node { + private: + std::optional py_handle{}; + + public: + struct NodeException : public std::runtime_error { + NodeException(Node &node, const std::string &msg) + : std::runtime_error(msg) { + } + }; + + struct NodeNoParent : public NodeException { + NodeNoParent(Node &node, const std::string &msg) + : NodeException(node, msg) { + } + }; + + private: + std::shared_ptr self; + std::shared_ptr children; + std::shared_ptr parent; + + public: + /** + * Don't call this constructor directly from C++ if you don't know what you're + * doing. Requires ownership transfer & py_handle setting. + */ + Node(); + // TODO add checks for whether this was called + static Node_ref transfer_ownership(Node_ref node); + + std::shared_ptr get_graph(); + std::shared_ptr get_self_gif(); + std::shared_ptr get_children_gif(); + std::shared_ptr get_parent_gif(); + + std::optional get_parent(); + HierarchicalNodeRef get_parent_force(); + std::string get_root_id(); + std::string get_name(bool accept_no_parent = false); + std::vector get_hierarchy(); + std::string get_full_name(bool types = false); + std::string repr(); + + std::string get_type_name(); + // TODO replace with constructor + void set_py_handle(nb::object handle); + std::optional get_py_handle(); + + private: + std::unordered_set get_children_all(bool include_root); + std::unordered_set get_children_direct(); + + public: + std::vector + get_children(bool direct_only, + std::optional> types = {}, + bool include_root = true, + std::function f_filter = nullptr, bool sort = true); +}; + +class GraphInterface { + Node_ref node{}; + std::string name{}; + + protected: + void register_graph(std::shared_ptr gi); + + public: + GraphInterface(); + ~GraphInterface(); + + Graph_ref G; + + template static std::shared_ptr factory(); + std::unordered_set get_gif_edges(); + std::unordered_map &get_edges(); + std::optional is_connected(GI_ref_weak to); + Graph_ref get_graph(); + std::unordered_set get_connected_nodes(std::vector types); + void connect(GI_ref_weak other); + void connect(GI_refs_weak others); + void connect(GI_ref_weak other, Link_ref link); + // TODO replace with set_node(Node_ref node, std::string name) + void set_node(Node_ref node); + Node_ref get_node(); + void set_name(std::string name); + std::string get_name(); + std::string get_full_name(bool types = false); + std::string repr(); + // force vtable, for typename + virtual void do_stuff() {}; +}; + +class Link { + GI_ref_weak from; + GI_ref_weak to; + bool setup = false; + + protected: + Link(); + Link(GI_ref_weak from, GI_ref_weak to); + + public: + std::pair get_connections(); + virtual void set_connections(GI_ref_weak from, GI_ref_weak to); + bool is_setup(); +}; + +using Path = std::vector; + +class Graph { + Set v; + std::vector> e; + + Map> e_cache = {}; + Map> e_cache_simple = {}; + bool invalidated = false; + + public: + void hold(GI_ref gi); + void merge(Graph &other); + static void add_edge(Link_ref link); + static void remove_edge(Link_ref link); + static Graph_ref merge_graphs(Graph_ref g1, Graph_ref g2); + + std::unordered_set get_gif_edges(GI_ref_weak from); + std::unordered_map &get_edges(GI_ref_weak from); + + Graph(); + ~Graph(); + + void remove_node(GI_ref node); + + void invalidate(); + int node_count(); + int edge_count(); + + std::string repr(); + + // Algorithms + std::unordered_set node_projection(); + std::vector> + nodes_by_names(std::unordered_set names); + std::unordered_set + bfs_visit(std::function &, Link_ref)> filter, + std::vector start); +}; + +template inline std::shared_ptr GraphInterface::factory() { + static_assert(std::is_base_of::value, + "T must be a subclass of GraphInterface"); + auto gi = std::make_shared(); + gi->register_graph(gi); + return gi; +} diff --git a/src/faebryk/core/cpp/include/graph/graphinterfaces.hpp b/src/faebryk/core/cpp/include/graph/graphinterfaces.hpp new file mode 100644 index 00000000..b0eeeaa5 --- /dev/null +++ b/src/faebryk/core/cpp/include/graph/graphinterfaces.hpp @@ -0,0 +1,73 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include "graph.hpp" + +class LinkParent; + +class GraphInterfaceSelf : public GraphInterface { + public: + using GraphInterface::GraphInterface; +}; + +class GraphInterfaceHierarchical : public GraphInterface { + bool is_parent; + + public: + GraphInterfaceHierarchical(bool is_parent); + + template static std::shared_ptr factory(bool is_parent); + bool get_is_parent(); + std::vector get_children(); + std::vector get_children_with_names(); + std::optional get_parent(); + void disconnect_parent(); + + static bool is_uplink(GI_ref_weak from, GI_ref_weak to); + static bool is_downlink(GI_ref_weak from, GI_ref_weak to); + + private: + std::optional> get_parent_link(); +}; + +/** Represents a reference to a node object */ +class GraphInterfaceReference : public GraphInterface { + public: + /** Cannot resolve unbound reference */ + struct UnboundError : public std::runtime_error { + UnboundError(const std::string &msg) + : std::runtime_error(msg) { + } + }; + + public: + using GraphInterface::GraphInterface; + + GraphInterfaceSelf *get_referenced_gif(); + Node_ref get_reference(); +}; + +// TODO move those back to python when inherited GIFs work again + +class GraphInterfaceModuleSibling : public GraphInterfaceHierarchical { + public: + using GraphInterfaceHierarchical::GraphInterfaceHierarchical; +}; + +class GraphInterfaceModuleConnection : public GraphInterface { + public: + using GraphInterface::GraphInterface; +}; + +template +inline std::shared_ptr GraphInterfaceHierarchical::factory(bool is_parent) { + static_assert(std::is_base_of::value, + "T must be a subclass of GraphInterfaceHierarchical"); + + auto gi = std::make_shared(is_parent); + gi->register_graph(gi); + return gi; +} \ No newline at end of file diff --git a/src/faebryk/core/cpp/include/graph/links.hpp b/src/faebryk/core/cpp/include/graph/links.hpp new file mode 100644 index 00000000..d1074198 --- /dev/null +++ b/src/faebryk/core/cpp/include/graph/links.hpp @@ -0,0 +1,95 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include "graph/graph.hpp" +#include "graphinterfaces.hpp" + +class LinkDirect : public Link { + public: + LinkDirect(); + LinkDirect(GI_ref_weak from, GI_ref_weak to); +}; + +class LinkParent : public Link { + GraphInterfaceHierarchical *parent; + GraphInterfaceHierarchical *child; + + public: + LinkParent(); + LinkParent(GraphInterfaceHierarchical *from, GraphInterfaceHierarchical *to); + + void set_connections(GI_ref_weak from, GI_ref_weak to) override; + GraphInterfaceHierarchical *get_parent(); + GraphInterfaceHierarchical *get_child(); +}; + +class LinkNamedParent : public LinkParent { + std::string name; + + public: + LinkNamedParent(std::string name); + LinkNamedParent(std::string name, GraphInterfaceHierarchical *from, + GraphInterfaceHierarchical *to); + + std::string get_name(); +}; + +class LinkDirectShallow : public LinkDirect { + // TODO +}; + +class LinkPointer : public Link { + GraphInterfaceSelf *pointee; + GraphInterface *pointer; + + public: + LinkPointer(); + LinkPointer(GI_ref_weak from, GraphInterfaceSelf *to); + void set_connections(GI_ref_weak from, GI_ref_weak to) override; + GraphInterface *get_pointer(); + GraphInterfaceSelf *get_pointee(); +}; + +class LinkSibling : public LinkPointer { + public: + LinkSibling(); + LinkSibling(GI_ref_weak from, GraphInterfaceSelf *to); +}; + +class LinkDirectConditional : public LinkDirect { + + public: + enum FilterResult { + FILTER_PASS, + FILTER_FAIL_RECOVERABLE, + FILTER_FAIL_UNRECOVERABLE + }; + + using FilterF = std::function; + + struct LinkFilteredException : public std::runtime_error { + LinkFilteredException(std::string msg) + : std::runtime_error(msg) { + } + }; + + private: + FilterF filter; + + public: + LinkDirectConditional(FilterF filter); + LinkDirectConditional(FilterF filter, GI_ref_weak from, GI_ref_weak to); + void set_connections(GI_ref_weak from, GI_ref_weak to) override; +}; + +class LinkDirectDerived : public LinkDirectConditional { + private: + static LinkDirectConditional::FilterF make_filter_from_path(Path path); + + public: + LinkDirectDerived(Path path); + LinkDirectDerived(Path path, GI_ref_weak from, GI_ref_weak to); +}; diff --git a/src/faebryk/core/cpp/include/nano.hpp b/src/faebryk/core/cpp/include/nano.hpp new file mode 100644 index 00000000..46546ad9 --- /dev/null +++ b/src/faebryk/core/cpp/include/nano.hpp @@ -0,0 +1,114 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +namespace nb = nanobind; + +// Fix for nanobind new override +template > +struct new__; + +// 0-arg function +template struct new__ { + std::remove_reference_t func; + + new__(Func &&f) + : func((nb::detail::forward_t)f) { + } + + template + NB_INLINE void execute(Class &cl, const Extra &...extra) { + nb::detail::wrap_base_new(cl, false); + + auto wrapper_cls = [func = + (nb::detail::forward_t)func](nb::type_object h) { + // printf("Called wrapper_cls with %s\n", nb::type_name(h).c_str()); + return func(); + }; + auto wrapper = [func = (nb::detail::forward_t)func]() { + throw std::runtime_error("Called wrapper in 0-arg function"); + return func(); + }; + + cl.def_static("__new__", std::move(wrapper), extra...); + cl.def_static("__new__", std::move(wrapper_cls), nb::arg("cls"), extra...); + cl.def( + "__init__", + [](nb::handle) { + // throw std::runtime_error("Called init in 0-arg function"); + }, + extra...); + } +}; + +// function with arguments +template +struct new__ { + std::remove_reference_t func; + + new__(Func &&f) + : func((nb::detail::forward_t)f) { + } + + template + NB_INLINE void execute(Class &cl, const Extra &...extra) { + nb::detail::wrap_base_new(cl, sizeof...(Args) != 0); + + constexpr bool is_first_arg_type_object = + std::is_same_v; + + // function that needs cls as first argument + if constexpr (is_first_arg_type_object) { + auto wrapper = [func = (nb::detail::forward_t)func](nb::type_object h, + Args... args) { + // printf("Called wrapper with %s\n", nb::type_name(h).c_str()); + return func(h, (nb::detail::forward_t)args...); + }; + auto wrapper_nocls = [func = + (nb::detail::forward_t)func](Args... args) { + throw std::runtime_error("Called wrapper_nocls"); + }; + + cl.def_static("__new__", std::move(wrapper_nocls), extra...); + cl.def_static("__new__", std::move(wrapper), nb::arg("cls"), extra...); + cl.def( + "__init__", + [](nb::handle, Args...) { + // throw std::runtime_error("Called init in handle function"); + }, + extra...); + } else { + // function that does not need cls as first argument + auto wrapper_cls = [func = (nb::detail::forward_t)func]( + nb::type_object h, FirstArg arg1, Args... args) { + // printf("Called wrapper_cls with %s\n", nb::type_name(h).c_str()); + return func(arg1, (nb::detail::forward_t)args...); + }; + auto wrapper = [func = (nb::detail::forward_t)func](FirstArg arg1, + Args... args) { + throw std::runtime_error("Called wrapper for non-handle function"); + }; + + cl.def_static("__new__", std::move(wrapper), extra...); + cl.def_static("__new__", std::move(wrapper_cls), nb::arg("cls"), extra...); + cl.def( + "__init__", + [](nb::handle, FirstArg, Args...) { + // throw std::runtime_error("Called init in non-handle function"); + }, + extra...); + } + } +}; + +template new__(Func &&f) -> new__; + +#define FACTORY(pyclass, newc, ...) \ + { \ + auto _ = pyclass; \ + new__(newc).execute(_, ##__VA_ARGS__); \ + } diff --git a/src/faebryk/core/cpp/include/pyutil.hpp b/src/faebryk/core/cpp/include/pyutil.hpp new file mode 100644 index 00000000..ac9c39a0 --- /dev/null +++ b/src/faebryk/core/cpp/include/pyutil.hpp @@ -0,0 +1,30 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +namespace nb = nanobind; + +namespace pyutil { + +// TODO avoid calling these functions +// instance checks can be done without a python call by recreating the type tree in C++ +// (for graph objects ofc) + +inline bool isinstance(nb::object obj, nb::type_object type) { + // Call the Python isinstance function + nb::object isinstance_func = nb::module_::import_("builtins").attr("isinstance"); + return nb::cast(isinstance_func(obj, type)); +} + +inline bool isinstance(nb::object obj, std::vector types) { + return std::any_of(types.begin(), types.end(), [obj](auto type) { + return isinstance(obj, type); + }); +} + +} // namespace pyutil diff --git a/src/faebryk/core/cpp/include/util.hpp b/src/faebryk/core/cpp/include/util.hpp new file mode 100644 index 00000000..25af413c --- /dev/null +++ b/src/faebryk/core/cpp/include/util.hpp @@ -0,0 +1,24 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include + +namespace util { + +template std::string get_type_name(const T *obj) { + int status; + std::unique_ptr demangled_name( + abi::__cxa_demangle(typeid(*obj).name(), nullptr, nullptr, &status), std::free); + return demangled_name ? demangled_name.get() : "unknown type"; +} + +template std::string get_type_name(const std::shared_ptr &obj) { + return get_type_name(obj.get()); +} + +} // namespace util diff --git a/src/faebryk/core/cpp/src/graph/graph.cpp b/src/faebryk/core/cpp/src/graph/graph.cpp new file mode 100644 index 00000000..76bcd349 --- /dev/null +++ b/src/faebryk/core/cpp/src/graph/graph.cpp @@ -0,0 +1,200 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#include "graph/graph.hpp" +#include "graph/links.hpp" +#include + +Graph::Graph() { +} + +Graph::~Graph() { + if (!this->invalidated) { + printf("WARNING: graph not invalidated\n"); + } +} + +void Graph::hold(GI_ref gi) { + this->v.insert(gi); +} + +Graph_ref Graph::merge_graphs(Graph_ref g1, Graph_ref g2) { + if (g1 == g2) { + return g1; + } + + auto G_target = (g1->node_count() >= g2->node_count()) ? g1 : g2; + auto G_source = (g1 == G_target) ? g2 : g1; + + assert(G_source->node_count() > 0); + + for (auto &v : G_source->v) { + v->G = G_target; + } + G_target->merge(*G_source); + G_source->invalidate(); + + return G_target; +} + +void Graph::add_edge(Link_ref link) { + auto [from, to] = link->get_connections(); + + auto G = Graph::merge_graphs(from->G, to->G); + + // remove existing link + if (G->e_cache_simple[from].contains(to)) { + // this->remove_edge(this->e_cache[from][to]); + // TODO: reconsider this + throw std::runtime_error("link already exists"); + } + + G->e_cache_simple[from].insert(to); + G->e_cache_simple[to].insert(from); + G->e_cache[from][to] = link; + G->e_cache[to][from] = link; + G->e.push_back(std::make_tuple(from, to, link)); +} + +void Graph::remove_edge(Link_ref link) { + auto [from, to] = link->get_connections(); + auto G = from->G; + if (G != to->G) { + throw std::runtime_error("link not in graph"); + } + + if (!G->e_cache_simple[from].contains(to)) { + return; + } + if (G->e_cache[from][to] != link) { + throw std::runtime_error("link not in graph"); + } + G->e_cache_simple[from].erase(to); + G->e_cache[from].erase(to); + G->e_cache[to].erase(from); + std::erase_if(G->e, [link](const auto &edge) { + return std::get<2>(edge) == link; + }); + + // TODO + if (G->e_cache_simple[from].empty()) { + // G->remove_node(from); + } + if (G->e_cache_simple[to].empty()) { + // G->remove_node(to); + } +} + +void Graph::merge(Graph &other) { + this->v.merge(other.v); + this->e.insert(this->e.end(), other.e.begin(), other.e.end()); + this->e_cache.merge(other.e_cache); + this->e_cache_simple.merge(other.e_cache_simple); +} + +std::unordered_set Graph::get_gif_edges(GI_ref_weak from) { + return this->e_cache_simple[from]; +} + +std::unordered_map &Graph::get_edges(GI_ref_weak from) { + return this->e_cache[from]; +} + +void Graph::remove_node(GI_ref node) { + auto node_ptr = node.get(); + this->v.erase(node); + + // TODO remove G ref from Gif + + for (auto &[from, tos] : this->e_cache_simple) { + tos.erase(node_ptr); + } + this->e_cache_simple.erase(node_ptr); + + for (auto &[to, link] : this->e_cache[node_ptr]) { + this->e_cache[to].erase(node_ptr); + } + this->e_cache.erase(node_ptr); + + std::erase_if(this->e, [node_ptr](const auto &edge) { + return std::get<0>(edge) == node_ptr || std::get<1>(edge) == node_ptr; + }); +} + +void Graph::invalidate() { + this->invalidated = true; + this->v.clear(); +} + +int Graph::node_count() { + return this->v.size(); +} + +int Graph::edge_count() { + return this->e.size(); +} + +std::string Graph::repr() { + std::stringstream ss; + ss << "node_count() << ", E:" << this->edge_count() << "] at " + << this << ">"; + return ss.str(); +} + +// Algorithms -------------------------------------------------------------------------- + +std::unordered_set Graph::node_projection() { + std::unordered_set nodes; + for (auto &gif : this->v) { + if (auto self_gif = dynamic_cast(gif.get())) { + auto node = self_gif->get_node(); + assert(node); + nodes.insert(node); + } + } + return nodes; +} + +std::vector> +Graph::nodes_by_names(std::unordered_set names) { + std::vector> nodes; + for (auto &node : this->node_projection()) { + auto full_name = node->get_full_name(); + if (names.contains(full_name)) { + nodes.push_back({node, full_name}); + } + } + return nodes; +} + +std::unordered_set +Graph::bfs_visit(std::function &, Link_ref)> filter, + std::vector start) { + std::unordered_set visited; + std::queue> queue; + queue.push(start); + + while (!queue.empty()) { + auto path = queue.front(); + queue.pop(); + + auto current = path.back(); + + for (auto &[next, link] : this->e_cache[current]) { + if (visited.contains(next)) { + continue; + } + + std::vector next_path(path); + next_path.push_back(next); + + if (filter(next_path, link)) { + queue.push(next_path); + visited.insert(next); + } + } + } + + return visited; +} diff --git a/src/faebryk/core/cpp/src/graph/graphinterface.cpp b/src/faebryk/core/cpp/src/graph/graphinterface.cpp new file mode 100644 index 00000000..256b25fc --- /dev/null +++ b/src/faebryk/core/cpp/src/graph/graphinterface.cpp @@ -0,0 +1,116 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#include "graph/graphinterfaces.hpp" +#include "graph/links.hpp" +#include "pyutil.hpp" +#include + +std::shared_ptr GraphInterface::get_graph() { + return this->G; +} + +void GraphInterface::set_node(Node_ref node) { + assert(!this->node); + this->node = node; +} + +Node_ref GraphInterface::get_node() { + assert(this->node); + return this->node; +} + +void GraphInterface::set_name(std::string name) { + assert(this->name.empty()); + this->name = name; +} + +std::string GraphInterface::get_name() { + return this->name; +} + +std::string GraphInterface::get_full_name(bool types) { + assert(this->node); + + std::stringstream ss; + ss << this->get_node()->get_full_name(types) << "." << this->name; + if (types) { + ss << "|" << util::get_type_name(this) << "|"; + } + return ss.str(); +} + +std::string GraphInterface::repr() { + std::stringstream ss; + ss << "<" << util::get_type_name(this) << " at " << this << ">"; + return ss.str(); +} + +GraphInterface::~GraphInterface() { +} + +GraphInterface::GraphInterface() + : G(std::make_shared()) { +} + +void GraphInterface::connect(GI_ref_weak other) { + auto link = std::make_shared(this, other); + Graph::add_edge(link); +} + +void GraphInterface::connect(GI_refs_weak others) { + for (auto other : others) { + this->connect(other); + } +} + +void GraphInterface::connect(GI_ref_weak other, Link_ref link) { + if (link->is_setup()) { + throw std::runtime_error("link already setup"); + } + link->set_connections(this, other); + Graph::add_edge(link); +} + +void GraphInterface::register_graph(std::shared_ptr gi) { + this->G->hold(gi); +} + +std::optional GraphInterface::is_connected(GI_ref_weak to) { + auto &edges = this->get_edges(); + auto edge = edges.find(to); + if (edge == edges.end()) { + return {}; + } + return edge->second; +} + +Set GraphInterface::get_gif_edges() { + return this->G->get_gif_edges(this); +} + +std::unordered_map &GraphInterface::get_edges() { + return this->G->get_edges(this); +} + +std::unordered_set +GraphInterface::get_connected_nodes(std::vector types) { + auto edges = this->get_edges(); + std::unordered_set nodes; + for (auto [to, link] : edges) { + if (auto direct_link = std::dynamic_pointer_cast(link)) { + auto node = to->get_node(); + for (auto type : types) { + auto py_handle = node->get_py_handle(); + if (!py_handle) { + continue; + } + if (pyutil::isinstance(py_handle.value(), type)) { + nodes.insert(node); + } + } + } + } + return nodes; +} diff --git a/src/faebryk/core/cpp/src/graph/graphinterfaces.cpp b/src/faebryk/core/cpp/src/graph/graphinterfaces.cpp new file mode 100644 index 00000000..4817903b --- /dev/null +++ b/src/faebryk/core/cpp/src/graph/graphinterfaces.cpp @@ -0,0 +1,117 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#include "graph/graphinterfaces.hpp" +#include "graph/links.hpp" + +// GraphInterfaceSelf ------------------------------------------------------------------ +// GraphInterfaceHierarchical ---------------------------------------------------------- + +bool GraphInterfaceHierarchical::get_is_parent() { + return this->is_parent; +} + +std::vector GraphInterfaceHierarchical::get_children() { + assert(this->is_parent); + + auto edges = this->get_edges(); + std::vector children; + for (auto [to, link] : edges) { + if (auto named_link = std::dynamic_pointer_cast(link)) { + children.push_back(to->get_node()); + } + } + return children; +} + +std::vector GraphInterfaceHierarchical::get_children_with_names() { + assert(this->is_parent); + + auto edges = this->get_edges(); + std::vector children; + for (auto [to, link] : edges) { + if (auto named_link = dynamic_cast(link.get())) { + children.push_back(std::make_pair(to->get_node(), named_link->get_name())); + } + } + return children; +} + +std::optional> +GraphInterfaceHierarchical::get_parent_link() { + assert(!this->is_parent); + + auto edges = this->get_edges(); + for (auto [to, link] : edges) { + if (auto parent_link = std::dynamic_pointer_cast(link)) { + return parent_link; + } + } + return std::nullopt; +} + +std::optional GraphInterfaceHierarchical::get_parent() { + auto link = this->get_parent_link(); + if (!link) { + return std::nullopt; + } + auto p = (*link)->get_parent(); + // if unnamed, name is empty string + std::string name; + if (auto named_link = std::dynamic_pointer_cast(*link)) { + name = named_link->get_name(); + } + return std::make_pair(p->get_node(), name); +} + +GraphInterfaceHierarchical::GraphInterfaceHierarchical(bool is_parent) + : GraphInterface() + , is_parent(is_parent) { +} + +void GraphInterfaceHierarchical::disconnect_parent() { + auto link = this->get_parent_link(); + if (!link) { + return; + } + Graph::remove_edge(*link); +} + +bool GraphInterfaceHierarchical::is_uplink(GI_ref_weak from, GI_ref_weak to) { + auto from_gif = dynamic_cast(from); + auto to_gif = dynamic_cast(to); + if (!from_gif || !to_gif) { + return false; + } + return !from_gif->is_parent && to_gif->is_parent; +} + +bool GraphInterfaceHierarchical::is_downlink(GI_ref_weak from, GI_ref_weak to) { + auto from_gif = dynamic_cast(from); + auto to_gif = dynamic_cast(to); + if (!from_gif || !to_gif) { + return false; + } + return from_gif->is_parent && !to_gif->is_parent; +} + +// GraphInterfaceReference ------------------------------------------------------------- +GraphInterfaceSelf *GraphInterfaceReference::get_referenced_gif() { + auto edges = this->get_edges(); + for (auto [to, link] : edges) { + if (auto pointer_link = std::dynamic_pointer_cast(link)) { + if (!std::dynamic_pointer_cast(link)) { + return pointer_link->get_pointee(); + } + } + } + throw UnboundError("Reference is not bound"); +} + +Node_ref GraphInterfaceReference::get_reference() { + return this->get_referenced_gif()->get_node(); +} + +// GraphInterfaceModuleSibling ------------------------------------------------------- +// GraphInterfaceModuleConnection ----------------------------------------------------- diff --git a/src/faebryk/core/cpp/src/graph/link.cpp b/src/faebryk/core/cpp/src/graph/link.cpp new file mode 100644 index 00000000..db84246a --- /dev/null +++ b/src/faebryk/core/cpp/src/graph/link.cpp @@ -0,0 +1,35 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#include "graph/graph.hpp" +#include "graph/links.hpp" + +Link::Link() + : from(nullptr) + , to(nullptr) + , setup(false) { +} + +Link::Link(GI_ref_weak from, GI_ref_weak to) + : from(from) + , to(to) + , setup(true) { +} + +std::pair Link::get_connections() { + if (!this->setup) { + throw std::runtime_error("link not setup"); + } + return {this->from, this->to}; +} + +void Link::set_connections(GI_ref_weak from, GI_ref_weak to) { + this->from = from; + this->to = to; + this->setup = true; +} + +bool Link::is_setup() { + return this->setup; +} diff --git a/src/faebryk/core/cpp/src/graph/links.cpp b/src/faebryk/core/cpp/src/graph/links.cpp new file mode 100644 index 00000000..82ddb4ed --- /dev/null +++ b/src/faebryk/core/cpp/src/graph/links.cpp @@ -0,0 +1,168 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#include "graph/links.hpp" + +// LinkDirect -------------------------------------------------------------------------- +LinkDirect::LinkDirect() + : Link() { +} + +LinkDirect::LinkDirect(GI_ref_weak from, GI_ref_weak to) + : Link(from, to) { +} + +// LinkParent -------------------------------------------------------------------------- +LinkParent::LinkParent() + : Link() + , parent(nullptr) + , child(nullptr) { +} + +LinkParent::LinkParent(GraphInterfaceHierarchical *from, GraphInterfaceHierarchical *to) + : Link(from, to) + , parent(nullptr) + , child(nullptr) { + this->set_connections(from, to); +} + +void LinkParent::set_connections(GI_ref_weak from, GI_ref_weak to) { + auto from_h = dynamic_cast(from); + auto to_h = dynamic_cast(to); + + if (!from_h || !to_h) { + throw std::runtime_error("invalid gifs for LinkParent"); + } + + Link::set_connections(from, to); + if (from_h->get_is_parent() && !to_h->get_is_parent()) { + this->parent = from_h; + this->child = to_h; + } else if (!from_h->get_is_parent() && to_h->get_is_parent()) { + this->parent = to_h; + this->child = from_h; + } else { + throw std::runtime_error("invalid parent-child relationship"); + } +} + +GraphInterfaceHierarchical *LinkParent::get_parent() { + if (!this->is_setup()) { + throw std::runtime_error("link not setup"); + } + return this->parent; +} + +GraphInterfaceHierarchical *LinkParent::get_child() { + if (!this->is_setup()) { + throw std::runtime_error("link not setup"); + } + return this->child; +} + +// LinkNamedParent --------------------------------------------------------------------- +LinkNamedParent::LinkNamedParent(std::string name) + : LinkParent() + , name(name) { +} + +LinkNamedParent::LinkNamedParent(std::string name, GraphInterfaceHierarchical *from, + GraphInterfaceHierarchical *to) + : LinkParent(from, to) + , name(name) { +} + +std::string LinkNamedParent::get_name() { + return this->name; +} + +// LinkPointer ------------------------------------------------------------------------- +LinkPointer::LinkPointer() + : Link() + , pointee(nullptr) + , pointer(nullptr) { +} + +LinkPointer::LinkPointer(GI_ref_weak from, GraphInterfaceSelf *to) + : Link(from, to) + , pointee(nullptr) + , pointer(nullptr) { + this->set_connections(from, to); +} + +void LinkPointer::set_connections(GI_ref_weak from, GI_ref_weak to) { + auto from_s = dynamic_cast(from); + auto to_s = dynamic_cast(to); + + if (!from_s && !to_s) { + throw std::runtime_error("LinkPointer needs to point to a self-gif"); + } + if (!to_s) { + to_s = from_s; + from = to; + } + + Link::set_connections(from, to_s); + this->pointer = from; + this->pointee = to_s; +} + +GraphInterfaceSelf *LinkPointer::get_pointee() { + if (!this->is_setup()) { + throw std::runtime_error("link not setup"); + } + return this->pointee; +} + +GraphInterface *LinkPointer::get_pointer() { + if (!this->is_setup()) { + throw std::runtime_error("link not setup"); + } + return this->pointer; +} + +// LinkSibling ------------------------------------------------------------------------ +LinkSibling::LinkSibling() + : LinkPointer() { +} + +LinkSibling::LinkSibling(GI_ref_weak from, GraphInterfaceSelf *to) + : LinkPointer(from, to) { +} + +// LinkDirectConditional ---------------------------------------------------------------- +LinkDirectConditional::LinkDirectConditional(FilterF filter) + : LinkDirect() + , filter(filter) { +} + +LinkDirectConditional::LinkDirectConditional(FilterF filter, GI_ref_weak from, + GI_ref_weak to) + : LinkDirect(from, to) + , filter(filter) { + this->set_connections(from, to); +} + +void LinkDirectConditional::set_connections(GI_ref_weak from, GI_ref_weak to) { + if (this->filter(from, to) != FilterResult::FILTER_PASS) { + throw LinkFilteredException("LinkDirectConditional filtered"); + } + LinkDirect::set_connections(from, to); +} + +// LinkDirectDerived ------------------------------------------------------------------- +LinkDirectDerived::LinkDirectDerived(Path path) + : LinkDirectConditional(make_filter_from_path(path)) { +} + +LinkDirectDerived::LinkDirectDerived(Path path, GI_ref_weak from, GI_ref_weak to) + : LinkDirectConditional(make_filter_from_path(path), from, to) { +} + +LinkDirectConditional::FilterF LinkDirectDerived::make_filter_from_path(Path path) { + // TODO + return [path](GI_ref_weak, GI_ref_weak) { + return LinkDirectConditional::FilterResult::FILTER_PASS; + }; +} diff --git a/src/faebryk/core/cpp/src/graph/node.cpp b/src/faebryk/core/cpp/src/graph/node.cpp new file mode 100644 index 00000000..61bb9628 --- /dev/null +++ b/src/faebryk/core/cpp/src/graph/node.cpp @@ -0,0 +1,226 @@ +/* This file is part of the faebryk project + * SPDX-License-Identifier: MIT + */ + +#include "graph/graph.hpp" +#include "graph/links.hpp" +#include "pyutil.hpp" + +Node::Node() + : self(GraphInterfaceSelf::factory()) + , children(GraphInterfaceHierarchical::factory(true)) + , parent(GraphInterfaceHierarchical::factory(false)) { + + this->self->set_name("self"); + + this->children->set_name("children"); + this->children->connect(this->self.get(), std::make_shared()); + + this->parent->set_name("parent"); + this->parent->connect(this->self.get(), std::make_shared()); +} + +Node_ref Node::transfer_ownership(Node_ref node) { + // TODO consider some hacking with nb::cast during new to avoid this + + node->self->set_node(node); + node->children->set_node(node); + node->parent->set_node(node); + + auto other = nb::find(node); + node->set_py_handle(other); + + return node; +} + +void Node::set_py_handle(nb::object handle) { + if (this->py_handle.has_value()) { + throw std::runtime_error("py_handle already set"); + } + assert(handle.is_valid()); + this->py_handle = handle; +} + +std::shared_ptr Node::get_graph() { + return this->self->get_graph(); +} + +std::shared_ptr Node::get_self_gif() { + return this->self; +} + +std::shared_ptr Node::get_children_gif() { + return this->children; +} + +std::shared_ptr Node::get_parent_gif() { + return this->parent; +} + +std::optional Node::get_parent() { + return this->parent->get_parent(); +} + +HierarchicalNodeRef Node::get_parent_force() { + auto p = this->get_parent(); + if (!p) { + throw NodeNoParent(*this, __func__); + } + return *p; +} + +std::string Node::get_root_id() { + std::stringstream ss; + ss << std::hex << std::uppercase << reinterpret_cast(this); + auto out = ss.str(); + return "*" + out.substr(out.size() - 4); +} + +std::string Node::get_name(bool accept_no_parent) { + if (!accept_no_parent) { + return this->get_parent_force().second; + } + auto p = this->get_parent(); + if (!p) { + return this->get_root_id(); + } + return p->second; +} + +std::vector Node::get_hierarchy() { + auto p = this->get_parent(); + if (!p) { + return std::vector{std::make_pair(this->self->get_node(), this->get_root_id())}; + } + auto [parent, name] = *p; + auto parent_hierarchy = parent->get_hierarchy(); + parent_hierarchy.push_back(std::make_pair(this->self->get_node(), name)); + return parent_hierarchy; +} + +std::string Node::get_full_name(bool types) { + std::stringstream ss; + auto p = this->get_parent(); + if (p) { + auto [parent, name] = *p; + auto parent_hierarchy = parent->get_full_name(types); + ss << parent_hierarchy << "." << name; + } else { + ss << this->get_root_id(); + } + if (types) { + ss << "|" << this->get_type_name(); + } + return ss.str(); +} + +std::string Node::repr() { + std::stringstream ss; + ss << "<" << this->get_full_name(true) << ">"; + return ss.str(); +} + +std::string Node::get_type_name() { + if (this->py_handle.has_value()) { + auto out = std::string( + nb::repr(this->py_handle.value().type().attr("__name__")).c_str()); + // format : 'ClassName' + // extract ClassName + // remove quotes + auto pos = out.find_first_of('\''); + if (pos != std::string::npos) { + out = out.substr(pos + 1, out.size() - 2); + } + return out; + } + return util::get_type_name(this); +} + +std::optional Node::get_py_handle() { + return this->py_handle; +} + +std::unordered_set Node::get_children_all(bool include_root) { + std::unordered_set out; + + auto direct_children = this->get_children_direct(); + if (include_root) { + out.insert(this->self->get_node()); + } + for (auto child : direct_children) { + out.merge(child->get_children_all(false)); + } + out.merge(direct_children); + + return out; +} + +std::unordered_set Node::get_children_direct() { + auto children = this->children->get_children(); + return std::unordered_set(children.begin(), children.end()); +} + +std::vector +Node::get_children(bool direct_only, std::optional> types, + bool include_root, std::function f_filter, + bool sort) { + std::unordered_set children; + if (direct_only) { + children = this->get_children_direct(); + if (include_root) { + children.insert(this->self->get_node()); + } + } else { + children = this->get_children_all(include_root); + } + + // always true if Node in types + if (types) { + auto type_h = nb::type(); + for (auto type : *types) { + if (type.ptr() == type_h.ptr()) { + types = {}; + break; + } + } + } + + std::vector children_filtered; + + // If no filtering is needed, copy all children directly + if (!types && !f_filter) { + children_filtered.assign(children.begin(), children.end()); + } else { + for (auto node : children) { + // filter by type + if (types) { + auto handle = node->get_py_handle(); + if (!handle) { + continue; + } + if (!pyutil::isinstance(*handle, *types)) { + continue; + } + } + + // filter by function + if (f_filter && !f_filter(node)) { + continue; + } + + children_filtered.push_back(node); + } + } + + if (sort) { + // Custom comparator for sorting by node name + auto comp = [](const Node_ref &a, const Node_ref &b) { + return a->get_name(true) < b->get_name(true); + }; + + // Sort the children_filtered vector using the custom comparator + std::sort(children_filtered.begin(), children_filtered.end(), comp); + } + + return children_filtered; +} diff --git a/src/faebryk/core/cpp/src/main.cpp b/src/faebryk/core/cpp/src/main.cpp index 808c2138..4ed85c84 100644 --- a/src/faebryk/core/cpp/src/main.cpp +++ b/src/faebryk/core/cpp/src/main.cpp @@ -2,6 +2,10 @@ * SPDX-License-Identifier: MIT */ +#include "graph/graph.hpp" +#include "graph/graphinterfaces.hpp" +#include "graph/links.hpp" +#include "nano.hpp" #include // check if c++20 is used @@ -9,8 +13,6 @@ #error "C++20 is required" #endif -namespace nb = nanobind; - #if EDITABLE #define PYMOD(m) NB_MODULE(faebryk_core_cpp_editable, m) #warning "EDITABLE" @@ -18,14 +20,141 @@ namespace nb = nanobind; #define PYMOD(m) NB_MODULE(faebryk_core_cpp, m) #endif +// #include + +namespace nb = nanobind; +using namespace nb::literals; + // ------------------------------------------------------------------------------------- int add(int i, int j) { return i + j; } +int call_python_function(std::function func) { + auto out = func(); + printf("%d\n", out); + return out; +} + +void print_obj(nb::handle obj) { + printf("%s\n", nb::repr(obj).c_str()); +} + +void print_obj_pyptr(PyObject *pyobj) { + auto obj = nb::handle(pyobj); + print_obj(obj); +} + PYMOD(m) { m.doc() = "faebryk core c++ module"; - m.def("add", &add, "A function that adds two numbers"); + m.def("add", &add, "i"_a, "j"_a = 1, "A function that adds two numbers"); + m.def("call_python_function", &call_python_function, "func"_a); + m.def("set_leak_warnings", &nb::set_leak_warnings, "value"_a); + m.def("print_obj", &print_obj, "obj"_a); + + // Graph + using GI = GraphInterface; + + FACTORY( + nb::class_(m, "GraphInterface") + .def("__repr__", &GI::repr) + .def("get_graph", &GI::get_graph) + .def_prop_ro("G", &GI::get_graph) + .def("get_gif_edges", &GI::get_gif_edges, nb::rv_policy::reference) + .def_prop_ro("edges", &GI::get_edges, nb::rv_policy::reference) + .def_prop_rw("node", &GI::get_node, &GI::set_node) + .def("is_connected_to", &GI::is_connected) + .def_prop_rw("name", &GI::get_name, &GI::set_name) + .def("get_connected_nodes", &GI::get_connected_nodes, "types"_a) + .def("connect", nb::overload_cast(&GI::connect), "other"_a) + .def("connect", nb::overload_cast(&GI::connect), "others"_a) + .def("connect", nb::overload_cast(&GI::connect), + "other"_a, "link"_a), + &GraphInterface::factory); + + nb::class_(m, "Graph") + .def(nb::init<>()) + .def("get_edges", &Graph::get_edges, nb::rv_policy::reference) + .def("invalidate", &Graph::invalidate) + .def_prop_ro("node_count", &Graph::node_count) + .def_prop_ro("edge_count", &Graph::edge_count) + .def("node_projection", &Graph::node_projection) + .def("nodes_by_names", &Graph::nodes_by_names) + .def("bfs_visit", &Graph::bfs_visit, "filter"_a, "start"_a, + nb::rv_policy::reference) + .def("__repr__", &Graph::repr); + + // Graph interfaces + FACTORY((nb::class_(m, "GraphInterfaceSelf")), + &GraphInterfaceSelf::factory); + + FACTORY((nb::class_(m, "GraphInterfaceReference") + .def("get_referenced_gif", &GraphInterfaceReference::get_referenced_gif, + nb::rv_policy::reference) + .def("get_reference", &GraphInterfaceReference::get_reference)), + &GraphInterfaceReference::factory); + + nb::exception( + m, "GraphInterfaceReferenceUnboundError"); + + FACTORY( + (nb::class_(m, "GraphInterfaceHierarchical") + .def("get_parent", &GraphInterfaceHierarchical::get_parent) + .def("get_children", &GraphInterfaceHierarchical::get_children) + .def_prop_ro("is_parent", &GraphInterfaceHierarchical::get_is_parent) + .def("disconnect_parent", &GraphInterfaceHierarchical::disconnect_parent)), + &GraphInterfaceHierarchical::factory, "is_parent"_a); + + FACTORY((nb::class_( + m, "GraphInterfaceModuleSibling")), + &GraphInterfaceModuleSibling::factory, + "is_parent"_a); + + FACTORY((nb::class_( + m, "GraphInterfaceModuleConnection")), + &GraphInterfaceModuleConnection::factory); + + // Links + nb::class_(m, "Link"); + nb::class_(m, "LinkParent").def(nb::init<>()); + nb::class_(m, "LinkNamedParent") + .def(nb::init()); + nb::class_(m, "LinkDirect").def(nb::init<>()); + nb::class_(m, "LinkPointer").def(nb::init<>()); + nb::class_(m, "LinkSibling").def(nb::init<>()); + nb::class_(m, "LinkDirectConditional") + .def(nb::init()); + + nb::exception(m, + "LinkFilteredException"); + nb::enum_(m, + "LinkDirectConditionalFilterResult") + .value("FILTER_PASS", LinkDirectConditional::FilterResult::FILTER_PASS) + .value("FILTER_FAIL_RECOVERABLE", + LinkDirectConditional::FilterResult::FILTER_FAIL_RECOVERABLE) + .value("FILTER_FAIL_UNRECOVERABLE", + LinkDirectConditional::FilterResult::FILTER_FAIL_UNRECOVERABLE); + + // Node + nb::class_(m, "Node") + .def(nb::init<>()) + .def_static("transfer_ownership", &Node::transfer_ownership) + .def("get_graph", &Node::get_graph) + .def_prop_ro("self_gif", &Node::get_self_gif) + .def_prop_ro("children", &Node::get_children_gif) + .def_prop_ro("parent", &Node::get_parent_gif) + .def("get_children", &Node::get_children, "direct_only"_a, + "types"_a = nb::none(), "include_root"_a = false, "f_filter"_a = nb::none(), + "sort"_a = true) + .def("get_parent", &Node::get_parent) + .def("get_parent_force", &Node::get_parent_force) + .def("get_name", &Node::get_name, "accept_no_parent"_a = false) + .def("get_hierarchy", &Node::get_hierarchy) + .def("get_full_name", &Node::get_full_name, "types"_a = false) + .def("__repr__", &Node::repr); + + nb::exception(m, "NodeException"); + nb::exception(m, "NodeNoParent"); } diff --git a/src/faebryk/core/graph.py b/src/faebryk/core/graph.py index 16d66a1c..3de67b47 100644 --- a/src/faebryk/core/graph.py +++ b/src/faebryk/core/graph.py @@ -2,142 +2,48 @@ # SPDX-License-Identifier: MIT import logging -from abc import abstractmethod -from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Mapping, Self +from typing import TYPE_CHECKING -from typing_extensions import deprecated - -from faebryk.libs.util import ( - ConfigFlag, - LazyMixin, - SharedReference, - bfs_visit, - lazy_construct, -) - -logger = logging.getLogger(__name__) - -# only for typechecker +from faebryk.core.cpp import Graph +from faebryk.core.node import Node if TYPE_CHECKING: - from faebryk.core.link import Link - -# TODO create GraphView base class - -LAZY = ConfigFlag("LAZY", False, "Use lazy construction for graphs") - - -class Graph[T, GT](LazyMixin, SharedReference[GT]): - # perf counter - counter = 0 - - def __init__(self, G: GT): - super().__init__(G) - type(self).counter += 1 - - @property - @deprecated("Use call") - def G(self): - return self() - - def merge(self, other: Self) -> tuple[Self, bool]: - lhs, rhs = self, other - - # if node not init, graph is empty / not existing - # thus we dont have to merge the graph, just need to setup the ref - if LAZY: - if not lhs.is_init or not rhs.is_init: - if not lhs.is_init: - lhs, rhs = rhs, lhs - rhs.links = lhs.links - rhs.object = lhs.object - lhs.links.add(rhs) - rhs._init = True - return lhs, True - - if lhs() == rhs(): - return self, False - - unioned = self._union(self(), other()) - if unioned is rhs(): - lhs, rhs = rhs, lhs - if unioned is not lhs(): - lhs.set(unioned) - - res = lhs.link(rhs) - if not res: - return self, False - - # TODO remove, should not be needed - assert isinstance(res.representative, type(self)) + from faebryk.core.trait import Trait - return res.representative, True - - def __repr__(self) -> str: - G = self() - node_cnt = self.node_cnt - edge_cnt = self.edge_cnt - g_repr = f"{type(G).__name__}({node_cnt=},{edge_cnt=})({hex(id(G))})" - return f"{type(self).__name__}({g_repr})" - - @property - @abstractmethod - def node_cnt(self) -> int: ... - - @property - @abstractmethod - def edge_cnt(self) -> int: ... - - @abstractmethod - def v(self, obj: T): ... - - @abstractmethod - def add_edge(self, from_obj: T, to_obj: T, link: "Link"): ... - - # TODO implement everywhere - @abstractmethod - def remove_edge(self, from_obj: T, to_obj: T | None = None): ... - - @abstractmethod - def is_connected(self, from_obj: T, to_obj: T) -> "Link | None": ... - - @abstractmethod - def get_edges(self, obj: T) -> Mapping[T, "Link"]: ... - - @staticmethod - @abstractmethod - def _union(rep: GT, old: GT) -> GT: ... - - def bfs_visit( - self, - filter: Callable[[list[T], "Link"], bool], - start: Iterable[T], - G: GT | None = None, - ): - G = G or self() - - return bfs_visit( - lambda n: [ - o for o, link in self.get_edges(n[-1]).items() if filter(n + [o], link) - ], - start, - ) - - def __str__(self) -> str: - return f"{type(self).__name__}(V={self.node_cnt}, E={self.edge_cnt})" - - @abstractmethod - def __iter__(self) -> Iterator[T]: ... - - # TODO subgraph should return a new GraphView - @abstractmethod - def subgraph(self, node_filter: Callable[[T], bool]) -> Iterable[T]: ... +logger = logging.getLogger(__name__) - def subgraph_type(self, *types: type[T]): - return self.subgraph(lambda n: isinstance(n, types)) - # TODO remove boilerplate for lazy, if it becomes a bit more usable - def __init_subclass__(cls) -> None: - super().__init_subclass__() - if LAZY: - lazy_construct(cls) +# TODO move these to C++ +# just here for making refactoring easier for the moment +# a bit weird typecheck +class GraphFunctions: + # Make all kinds of graph filtering functions so we can optimize them in the future + # Avoid letting user query all graph nodes always because quickly very slow + def __init__(self, graph: Graph): + self.graph = graph + + def node_projection(self) -> list["Node"]: + return list(self.nodes_of_type(Node)) + + def nodes_with_trait[T: "Trait"](self, trait: type[T]) -> list[tuple["Node", T]]: + return [ + (n, n.get_trait(trait)) + for n in self.node_projection() + if n.has_trait(trait) + ] + + # TODO: Waiting for python to add support for type mapping + def nodes_with_traits[*Ts]( + self, traits: tuple[*Ts] + ): # -> list[tuple[Node, tuple[*Ts]]]: + return [ + (n, tuple(n.get_trait(trait) for trait in traits)) # type: ignore + for n in self.node_projection() + if all(n.has_trait(trait) for trait in traits) # type: ignore + ] + + def nodes_of_type[T: "Node"](self, t: type[T]) -> set[T]: + return {n for n in self.graph.node_projection() if isinstance(n, t)} + + def nodes_of_types(self, t: tuple[type["Node"], ...]) -> set["Node"]: + return {n for n in self.graph.node_projection() if isinstance(n, t)} diff --git a/src/faebryk/core/graphinterface.py b/src/faebryk/core/graphinterface.py index 6b1f4dc5..43a8b9ba 100644 --- a/src/faebryk/core/graphinterface.py +++ b/src/faebryk/core/graphinterface.py @@ -1,220 +1,14 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT import logging -from typing import TYPE_CHECKING, Iterable, Mapping, Optional - -from typing_extensions import Self, deprecated - -from faebryk.core.core import ID_REPR, FaebrykLibObject -from faebryk.core.graph_backends.default import GraphImpl -from faebryk.core.link import ( - Link, - LinkDirect, - LinkNamedParent, - LinkPointer, - LinkSibling, -) -from faebryk.libs.util import ( - KeyErrorNotFound, - exceptions_to_log, - find, - not_none, - try_avoid_endless_recursion, -) - -if TYPE_CHECKING: - from faebryk.core.node import Node - from faebryk.core.trait import Trait logger = logging.getLogger(__name__) -class Graph(GraphImpl["GraphInterface"]): - # Make all kinds of graph filtering functions so we can optimize them in the future - # Avoid letting user query all graph nodes always because quickly very slow - - def node_projection(self) -> set["Node"]: - from faebryk.core.node import Node - - return Node.get_nodes_from_gifs(self.subgraph_type(GraphInterfaceSelf)) - - def nodes_with_trait[T: "Trait"](self, trait: type[T]) -> list[tuple["Node", T]]: - return [ - (n, n.get_trait(trait)) - for n in self.node_projection() - if n.has_trait(trait) - ] - - # Waiting for python to add support for type mapping - def nodes_with_traits[*Ts]( - self, traits: tuple[*Ts] - ): # -> list[tuple[Node, tuple[*Ts]]]: - return [ - (n, tuple(n.get_trait(trait) for trait in traits)) - for n in self.node_projection() - if all(n.has_trait(trait) for trait in traits) - ] - - def nodes_by_names(self, names: Iterable[str]) -> list[tuple["Node", str]]: - return [ - (n, node_name) - for n in self.node_projection() - if (node_name := n.get_full_name()) in names - ] - - def nodes_of_type[T: "Node"](self, t: type[T]) -> set[T]: - return {n for n in self.node_projection() if isinstance(n, t)} - - def nodes_of_types(self, t: tuple[type["Node"], ...]) -> set["Node"]: - return {n for n in self.node_projection() if isinstance(n, t)} - - -class GraphInterface(FaebrykLibObject): - GT = Graph - - def __init__(self) -> None: - super().__init__() - self.G = self.GT() - - # can't put it into constructor - # else it needs a reference when defining IFs - self._node: Optional["Node"] = None - self.name: str = type(self).__name__ - - @property - def node(self): - return not_none(self._node) - - @node.setter - def node(self, value: "Node"): - self._node = value - - # Graph stuff - @property - def edges(self) -> Mapping["GraphInterface", Link]: - return self.G.get_edges(self) - - def get_links(self) -> list[Link]: - return list(self.edges.values()) - - def get_links_by_type[T: Link](self, link_type: type[T]) -> list[T]: - return [link for link in self.get_links() if isinstance(link, link_type)] - - @property - @deprecated("Use get_links") - def connections(self): - return self.get_links() - - def get_direct_connections(self) -> set["GraphInterface"]: - return set(self.edges.keys()) - - def is_connected(self, other: "GraphInterface"): - return self is other or self.G.is_connected(self, other) - - # Less graph-specific stuff - - # TODO make link trait to initialize from list - def connect(self, other: "GraphInterface", linkcls=None) -> Self: - assert other is not self - - if linkcls is None: - linkcls = LinkDirect - link = linkcls([other, self]) - - _, no_path = self.G.merge(other.G) - - if not no_path: - dup = self.is_connected(other) - assert ( - not dup or type(dup) is linkcls - ), f"Already connected with different link type: {dup}" - - self.G.add_edge(self, other, link=link) - - if logger.isEnabledFor(logging.DEBUG): - with exceptions_to_log(): - logger.debug(f"GIF connection: {link}") - - return self - - def get_full_name(self, types: bool = False): - typestr = f"|{type(self).__name__}|" if types else "" - return f"{self.node.get_full_name(types=types)}.{self.name}{typestr}" - - def __str__(self) -> str: - return f"{str(self.node)}.{self.name}" - - @try_avoid_endless_recursion - def __repr__(self) -> str: - id_str = f"(@{hex(id(self))})" if ID_REPR else "" - - return ( - f"{self.get_full_name(types=True)}{id_str}" - if self._node is not None - else "| " - ) - - def get_connected_nodes[T: "Node"](self, types: type[T]) -> set[T]: - from faebryk.core.link import LinkDirect - from faebryk.core.node import Node - - return { - n - for n in Node.get_nodes_from_gifs( - g for g, t in self.edges.items() if isinstance(t, LinkDirect) - ) - if isinstance(n, types) - } - - -class GraphInterfaceHierarchical(GraphInterface): - def __init__(self, is_parent: bool) -> None: - super().__init__() - self.is_parent = is_parent - - # TODO make consistent api with get_parent - def get_children(self) -> list[tuple[str, "Node"]]: - assert self.is_parent - - hier_conns = self.get_links_by_type(LinkNamedParent) - if len(hier_conns) == 0: - return [] - - return [(c.name, c.get_child().node) for c in hier_conns] - - def get_parent(self) -> tuple["Node", str] | None: - assert not self.is_parent - - conns = self.get_links_by_type(LinkNamedParent) - if not conns: - return None - assert len(conns) == 1 - conn = conns[0] - parent = conn.get_parent() - - return parent.node, conn.name - - def disconnect_parent(self): - self.G.remove_edge(self) - - -class GraphInterfaceSelf(GraphInterface): ... - - -class GraphInterfaceReference[T: "Node"](GraphInterface): - """Represents a reference to a node object""" - - class UnboundError(Exception): - """Cannot resolve unbound reference""" - - def get_referenced_gif(self) -> GraphInterfaceSelf: - try: - return find( - self.get_links_by_type(LinkPointer), - lambda link: not isinstance(link, LinkSibling), - ).pointee - except KeyErrorNotFound as ex: - raise GraphInterfaceReference.UnboundError from ex - - def get_reference(self) -> T: - return self.get_referenced_gif().node +from faebryk.core.cpp import ( # noqa: E402, F401 + GraphInterface, + GraphInterfaceHierarchical, + GraphInterfaceReference, + GraphInterfaceReferenceUnboundError, + GraphInterfaceSelf, +) diff --git a/src/faebryk/core/link.py b/src/faebryk/core/link.py index 7ba9dfd4..b9d2e510 100644 --- a/src/faebryk/core/link.py +++ b/src/faebryk/core/link.py @@ -1,143 +1,18 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -import inspect import logging -from typing import TYPE_CHECKING, Callable - -from faebryk.core.core import LINK_TB, FaebrykLibObject -from faebryk.libs.util import is_type_pair logger = logging.getLogger(__name__) -if TYPE_CHECKING: - from faebryk.core.graphinterface import ( - GraphInterface, - GraphInterfaceHierarchical, - GraphInterfaceSelf, - ) - - -class Link(FaebrykLibObject): - def __init__(self) -> None: - super().__init__() - - if LINK_TB: - self.tb = inspect.stack() - - def get_connections(self) -> list["GraphInterface"]: - raise NotImplementedError - - def __eq__(self, __value: "Link") -> bool: - return set(self.get_connections()) == set(__value.get_connections()) - - def __hash__(self) -> int: - return super().__hash__() - - def __str__(self) -> str: - return ( - f"{type(self).__name__}" - f"([{', '.join(str(i) for i in self.get_connections())}])" - ) - - def __repr__(self) -> str: - return f"{type(self).__name__}()" - - -class LinkPointer(Link): - """A Link that points towards a self-gif""" - - def __init__( - self, - interfaces: list["GraphInterfaceSelf | GraphInterface"], - ) -> None: - from faebryk.core.graphinterface import GraphInterface, GraphInterfaceSelf - - super().__init__() - assert len(interfaces) == 2 - - pair = is_type_pair( - interfaces[0], interfaces[1], GraphInterfaceSelf, GraphInterface - ) - if not pair: - raise TypeError("Interfaces must be one self-gif and one other-gif") - self.pointee, self.pointer = pair - - def get_connections(self) -> list["GraphInterface"]: - return [self.pointee, self.pointer] - - -class LinkSibling(LinkPointer): - """A link represents a connection between a self-gif and a gif in the same node""" - - -class LinkParent(Link): - def __init__(self, interfaces: list["GraphInterface"]) -> None: - super().__init__() - from faebryk.core.graphinterface import GraphInterfaceHierarchical - - assert all([isinstance(i, GraphInterfaceHierarchical) for i in interfaces]) - # TODO rethink invariant - assert len(interfaces) == 2 - assert len([i for i in interfaces if i.is_parent]) == 1 # type: ignore - - self.interfaces: list["GraphInterfaceHierarchical"] = interfaces # type: ignore - - def get_connections(self): - return self.interfaces - - def get_parent(self): - return [i for i in self.interfaces if i.is_parent][0] - - def get_child(self): - return [i for i in self.interfaces if not i.is_parent][0] - - -class LinkNamedParent(LinkParent): - def __init__(self, name: str, interfaces: list["GraphInterface"]) -> None: - super().__init__(interfaces) - self.name = name - - @classmethod - def curry(cls, name: str): - def curried(interfaces: list["GraphInterface"]): - return cls(name, interfaces) - - return curried - - -class LinkDirect(Link): - """Represents a symmetrical link between two interfaces of the same type""" - - def __init__(self, interfaces: list["GraphInterface"]) -> None: - super().__init__() - assert ( - len(set(map(type, interfaces))) == 1 - ), "Interfaces must be of the same type" - self.interfaces = interfaces - - def get_connections(self) -> list["GraphInterface"]: - return self.interfaces - - -class LinkFilteredException(Exception): ... - - -class _TLinkDirectShallow(LinkDirect): - def __new__(cls, *args, **kwargs): - if cls is _TLinkDirectShallow: - raise TypeError( - "Can't instantiate abstract class _TLinkDirectShallow directly" - ) - return super().__new__(cls) - - -def LinkDirectShallow(if_filter: Callable[[LinkDirect, "GraphInterface"], bool]): - class _LinkDirectShallow(_TLinkDirectShallow): - i_filter = if_filter - - def __init__(self, interfaces: list["GraphInterface"]) -> None: - if not all(map(self.i_filter, interfaces)): - raise LinkFilteredException() - super().__init__(interfaces) - return _LinkDirectShallow +from faebryk.core.cpp import ( # noqa: E402, F401 + Link, + LinkDirect, + LinkDirectConditional, + LinkDirectConditionalFilterResult, + LinkFilteredException, + LinkNamedParent, + LinkParent, + LinkPointer, + LinkSibling, +) diff --git a/src/faebryk/core/module.py b/src/faebryk/core/module.py index a23bf857..9218f8e6 100644 --- a/src/faebryk/core/module.py +++ b/src/faebryk/core/module.py @@ -6,7 +6,7 @@ from faebryk.core.moduleinterface import GraphInterfaceModuleSibling from faebryk.core.node import Node, NodeException, f_field from faebryk.core.trait import Trait -from faebryk.libs.util import unique_ref +from faebryk.libs.util import cast_assert, unique_ref if TYPE_CHECKING: from faebryk.core.moduleinterface import ModuleInterface @@ -29,7 +29,7 @@ class TraitT(Trait): ... def get_most_special(self) -> "Module": specialers = { specialer - for specialer_gif in self.specialized.get_direct_connections() + for specialer_gif in self.specialized.get_gif_edges() if (specialer := specialer_gif.node) is not self and isinstance(specialer, Module) } @@ -118,11 +118,13 @@ def get_node_prop_matrix[N: Node](sub_type: type[N]): assert not has_parent or attach_to is None if not has_parent: if attach_to: - attach_to.add(special, container=attach_to.specialized) + attach_to.add(special, container=attach_to.specialized_) else: gen_parent = self.get_parent() if gen_parent: - gen_parent[0].add(special, name=f"{gen_parent[1]}_specialized") + cast_assert(Node, gen_parent[0]).add( + special, name=f"{gen_parent[1]}_specialized" + ) return special diff --git a/src/faebryk/core/moduleinterface.py b/src/faebryk/core/moduleinterface.py index 74eb405b..12ea622d 100644 --- a/src/faebryk/core/moduleinterface.py +++ b/src/faebryk/core/moduleinterface.py @@ -4,25 +4,27 @@ from typing import ( Iterable, Sequence, + cast, ) from typing_extensions import Self -from faebryk.core.core import LINK_TB -from faebryk.core.graphinterface import ( - GraphInterface, - GraphInterfaceHierarchical, +from faebryk.core.cpp import ( # noqa: F401 + GraphInterfaceModuleConnection, + GraphInterfaceModuleSibling, ) +from faebryk.core.graphinterface import GraphInterface from faebryk.core.link import ( Link, LinkDirect, - LinkDirectShallow, + LinkDirectConditional, + LinkDirectConditionalFilterResult, LinkFilteredException, - _TLinkDirectShallow, ) -from faebryk.core.node import Node +from faebryk.core.node import CNode, Node from faebryk.core.trait import Trait -from faebryk.libs.util import cast_assert, once, print_stack +from faebryk.library.can_specialize import can_specialize +from faebryk.libs.util import cast_assert, is_type_set_subclasses, once logger = logging.getLogger(__name__) @@ -32,37 +34,30 @@ # Chain resolve is for deciding what to do in a case like this # if1 -> link1 -> if2 -> link2 -> if3 # This will then decide with which link if1 and if3 are connected -def _resolve_link_transitive(links: Iterable[type[Link]]) -> type[Link]: - from faebryk.libs.util import is_type_set_subclasses +def _resolve_link_transitive(links: set[type[Link]]) -> type[Link]: + if len(links) == 1: + return next(iter(links)) - uniq = set(links) - assert uniq - - if len(uniq) == 1: - return next(iter(uniq)) - - if is_type_set_subclasses(uniq, {_TLinkDirectShallow}): + if is_type_set_subclasses(links, {LinkDirectConditional}): # TODO this only works if the filter is identical raise NotImplementedError() - if is_type_set_subclasses(uniq, {LinkDirect, _TLinkDirectShallow}): - return [u for u in uniq if issubclass(u, _TLinkDirectShallow)][0] + if is_type_set_subclasses(links, {LinkDirect, LinkDirectConditional}): + return [u for u in links if issubclass(u, LinkDirectConditional)][0] raise NotImplementedError() # This one resolves the case if1 -> link1 -> if2; if1 -> link2 -> if2 def _resolve_link_duplicate(links: Iterable[type[Link]]) -> type[Link]: - from faebryk.libs.util import is_type_set_subclasses - uniq = set(links) assert uniq if len(uniq) == 1: return next(iter(uniq)) - if is_type_set_subclasses(uniq, {LinkDirect, _TLinkDirectShallow}): - return [u for u in uniq if not issubclass(u, _TLinkDirectShallow)][0] + if is_type_set_subclasses(uniq, {LinkDirect, LinkDirectConditional}): + return [u for u in uniq if not issubclass(u, LinkDirectConditional)][0] raise NotImplementedError() @@ -84,12 +79,6 @@ def dec(self): _CONNECT_DEPTH = _LEVEL() -class GraphInterfaceModuleSibling(GraphInterfaceHierarchical): ... - - -class GraphInterfaceModuleConnection(GraphInterface): ... - - # CONNECT PROCEDURE # connect # connect_siblings @@ -116,27 +105,37 @@ class TraitT(Trait): ... specialized: GraphInterface connected: GraphInterfaceModuleConnection - # TODO rename - @classmethod - @once - def LinkDirectShallow(cls): + class _LinkDirectShallow(LinkDirectConditional): """ Make link that only connects up but not down """ - def test(node: Node): - return not any(isinstance(p[0], cls) for p in node.get_hierarchy()[:-1]) + def has_no_parent_with_type(self, node: CNode): + parents = (p[0] for p in node.get_hierarchy()[:-1]) + return not any(isinstance(p, self.test_type) for p in parents) - class _LinkDirectShallowMif( - LinkDirectShallow(lambda link, gif: test(gif.node)) - ): ... + def __init__(self, test_type: type["ModuleInterface"]): + self.test_type = test_type + super().__init__( + lambda src, dst: LinkDirectConditionalFilterResult.FILTER_PASS + if self.has_no_parent_with_type(dst.node) + else LinkDirectConditionalFilterResult.FILTER_FAIL_UNRECOVERABLE + ) + + # TODO rename + @classmethod + @once + def LinkDirectShallow(cls): + class _LinkDirectShallowMif(ModuleInterface._LinkDirectShallow): + def __init__(self): + super().__init__(test_type=cls) return _LinkDirectShallowMif def __preinit__(self) -> None: ... @staticmethod - def _get_connected(gif: GraphInterface): + def _get_connected(gif: GraphInterface, clss: bool): assert isinstance(gif.node, ModuleInterface) connections = gif.edges.items() @@ -144,19 +143,43 @@ def _get_connected(gif: GraphInterface): assert len(connections) == len({c[0] for c in connections}) return { - cast_assert(ModuleInterface, s.node): link + cast_assert(ModuleInterface, s.node): (link if not clss else type(link)) for s, link in connections if s.node is not gif.node } - def get_connected(self): - return self._get_connected(self.connected) + def get_connected(self, clss: bool = False): + return self._get_connected(self.connected, clss) - def get_specialized(self): - return self._get_connected(self.specialized) + def get_specialized(self, clss: bool = False): + return self._get_connected(self.specialized, clss) - def get_specializes(self): - return self._get_connected(self.specializes) + def get_specializes(self, clss: bool = False): + return self._get_connected(self.specializes, clss) + + @staticmethod + def _cross_connect( + s_group: dict["ModuleInterface", type[Link]], + d_group: dict["ModuleInterface", type[Link]], + linkcls: type[Link], + hint=None, + ): + if logger.isEnabledFor(logging.DEBUG) and hint is not None: + logger.debug(f"Connect {hint} {s_group} -> {d_group}") + + for s, slink in s_group.items(): + linkclss = {slink, linkcls} + linkclss_ambiguous = len(linkclss) > 1 + for d, dlink in d_group.items(): + # can happen while connection trees are resolving + if s is d: + continue + if not linkclss_ambiguous and dlink in linkclss: + link = linkcls + else: + link = _resolve_link_transitive(linkclss | {dlink}) + + s._connect_across_hierarchies(d, linkcls=link) def _connect_siblings_and_connections( self, other: "ModuleInterface", linkcls: type[Link] @@ -176,36 +199,23 @@ def _connect_siblings_and_connections( if logger.isEnabledFor(logging.DEBUG): logger.debug(f"MIF connection: {self} to {other}") - def cross_connect( - s_group: dict[ModuleInterface, type[Link] | Link], - d_group: dict[ModuleInterface, type[Link] | Link], - hint=None, - ): - if logger.isEnabledFor(logging.DEBUG) and hint is not None: - logger.debug(f"Connect {hint} {s_group} -> {d_group}") - - for s, slink in s_group.items(): - if isinstance(slink, Link): - slink = type(slink) - for d, dlink in d_group.items(): - if isinstance(dlink, Link): - dlink = type(dlink) - # can happen while connection trees are resolving - if s is d: - continue - link = _resolve_link_transitive([slink, dlink, linkcls]) - - s._connect_across_hierarchies(d, linkcls=link) - # Connect to all connections - s_con = self.get_connected() | {self: linkcls} - d_con = other.get_connected() | {other: linkcls} - cross_connect(s_con, d_con, "connections") + s_con = self.get_connected(clss=True) | {self: linkcls} + d_con = other.get_connected(clss=True) | {other: linkcls} + ModuleInterface._cross_connect(s_con, d_con, linkcls, "connections") # Connect to all siblings - s_sib = self.get_specialized() | self.get_specializes() | {self: linkcls} - d_sib = other.get_specialized() | other.get_specializes() | {other: linkcls} - cross_connect(s_sib, d_sib, "siblings") + s_sib = ( + self.get_specialized(clss=True) + | self.get_specializes(clss=True) + | {self: linkcls} + ) + d_sib = ( + other.get_specialized(clss=True) + | other.get_specializes(clss=True) + | {other: linkcls} + ) + ModuleInterface._cross_connect(s_sib, d_sib, linkcls, "siblings") return self @@ -261,7 +271,7 @@ def _is_connected(a, b): # depends on connections between src_i & dst_i # e.g. if any Shallow, we need to choose shallow link = _resolve_link_transitive( - [type(sublink) for _, _, sublink in connection_map if sublink] + {type(sublink) for _, _, sublink in connection_map if sublink} ) if logger.isEnabledFor(logging.DEBUG): @@ -278,8 +288,6 @@ def _connect_across_hierarchies( resolved = _resolve_link_duplicate([type(existing_link), linkcls]) if resolved is type(existing_link): return - if LINK_TB: - print(print_stack(existing_link.tb)) raise NotImplementedError( "Overriding existing links not implemented, tried to override " + f"{existing_link} with {resolved}" @@ -287,7 +295,7 @@ def _connect_across_hierarchies( # level 0 connect try: - self.connected.connect(other.connected, linkcls=linkcls) + self.connected.connect(other.connected, linkcls()) except LinkFilteredException: return @@ -314,13 +322,6 @@ def _connect_across_hierarchies( _CONNECT_DEPTH.dec() - def get_direct_connections(self) -> set["ModuleInterface"]: - return { - gif.node - for gif in self.connected.get_direct_connections() - if isinstance(gif.node, ModuleInterface) and gif.node is not self - } - def connect(self: Self, *other: Self, linkcls=None) -> Self: # TODO consider some type of check at the end within the graph instead # assert type(other) is type(self) @@ -347,12 +348,19 @@ def connect_shallow(self, other: Self) -> Self: return self.connect(other, linkcls=type(self).LinkDirectShallow()) def is_connected_to(self, other: "ModuleInterface"): - return self.connected.is_connected(other.connected) + return self.connected.is_connected_to(other.connected) def specialize[T: ModuleInterface](self, special: T) -> T: logger.debug(f"Specializing MIF {self} with {special}") - assert isinstance(special, type(self)) + extra = set() + # allow non-base specialization if explicitly allowed + if special.has_trait(can_specialize): + extra = set(special.get_trait(can_specialize).get_specializable_types()) + + assert isinstance(special, type(self)) or any( + issubclass(t, type(self)) for t in extra + ) # This is doing the heavy lifting self.connect(special) @@ -360,4 +368,4 @@ def specialize[T: ModuleInterface](self, special: T) -> T: # Establish sibling relationship self.specialized.connect(special.specializes) - return special + return cast(T, special) diff --git a/src/faebryk/core/node.py b/src/faebryk/core/node.py index 415d03b3..a48882a8 100644 --- a/src/faebryk/core/node.py +++ b/src/faebryk/core/node.py @@ -9,6 +9,7 @@ Any, Callable, Iterable, + Self, Type, cast, get_args, @@ -18,25 +19,22 @@ from deprecated import deprecated from more_itertools import partition -from faebryk.core.core import ID_REPR, FaebrykLibObject +from faebryk.core.cpp import Node as CNode from faebryk.core.graphinterface import ( GraphInterface, - GraphInterfaceHierarchical, - GraphInterfaceSelf, ) -from faebryk.core.link import Link, LinkNamedParent, LinkSibling +from faebryk.core.link import LinkNamedParent, LinkSibling from faebryk.libs.exceptions import FaebrykException from faebryk.libs.util import ( KeyErrorNotFound, - PostInitCaller, Tree, cast_assert, debugging, find, not_none, + post_init_decorator, times, try_avoid_endless_recursion, - try_or, zip_dicts_by_key, ) @@ -126,9 +124,9 @@ def _(*args: P.args, **kwargs: P.kwargs) -> Callable[[], T]: def __() -> T: return con(*args, **kwargs) - return _d_field(__) + return _d_field(__) # type: ignore - return _ + return _ # type: ignore def list_f_field[T, **P](n: int, con: Callable[P, T]) -> Callable[P, list[T]]: @@ -138,9 +136,9 @@ def _(*args: P.args, **kwargs: P.kwargs) -> Callable[[], list[T]]: def __() -> list[T]: return [con(*args, **kwargs) for _ in range(n)] - return _d_field(__) + return _d_field(__) # type: ignore - return _ + return _ # type: ignore class NodeException(FaebrykException): @@ -180,21 +178,17 @@ class InitVar(dataclass_InitVar): # ----------------------------------------------------------------------------- -class Node(FaebrykLibObject, metaclass=PostInitCaller): +@post_init_decorator +class Node(CNode): runtime_anon: list["Node"] runtime: dict[str, "Node"] - specialized: list["Node"] - - self_gif: GraphInterfaceSelf - children: GraphInterfaceHierarchical = f_field(GraphInterfaceHierarchical)( - is_parent=True - ) - parent: GraphInterfaceHierarchical = f_field(GraphInterfaceHierarchical)( - is_parent=False - ) + specialized_: list["Node"] _init: bool = False + class _Skipped(Exception): + pass + def __hash__(self) -> int: # TODO proper hash return hash(id(self)) @@ -223,22 +217,33 @@ def add[T: Node | GraphInterface]( raise FieldContainerError(f"Expected dict got {type(container)}") if name in container: raise FieldExistsError(name) - container[name] = obj + # TODO consider setting name for non runtime container + # if container is not self.runtime: + # name = f"{container_name}[{name}]" + pass else: if not isinstance(container, list): raise FieldContainerError(f"Expected list got {type(container)}") - container.append(obj) - name = f"{container_name}[{len(container) - 1}]" + name = f"{container_name}[{len(container)}]" - if isinstance(obj, GraphInterface): - self._handle_add_gif(name, obj) + try: + if isinstance(obj, GraphInterface): + self._handle_add_gif(name, obj) + else: + self._handle_add_node(name, obj) + except Node._Skipped: + return obj + + # add to container + if isinstance(container, dict): + container[name] = obj else: - self._handle_add_node(name, obj) + container.append(obj) return obj def add_to_container[T: Node]( - self, n: int, factory: Callable[[], T], container: list[T] | None = None + self, n: int, factory: Callable[[], T], container: list["Node"] | None = None ): if container is None: container = self.runtime_anon @@ -248,10 +253,6 @@ def add_to_container[T: Node]( self.add(obj, container=container) return constr - def __init_subclass__(cls, *, init: bool = True) -> None: - super().__init_subclass__() - cls._init = init - @classmethod def __faebryk_fields__(cls) -> tuple[dict[str, Any], dict[str, Any]]: def all_vars(cls): @@ -383,15 +384,18 @@ def _setup_fields(self, cls): def handle_add(name, obj): del objects[name] + try: + if isinstance(obj, GraphInterface): + self._handle_add_gif(name, obj) + elif isinstance(obj, Node): + self._handle_add_node(name, obj) + else: + raise TypeError( + f"Cannot handle adding field {name=} of type {type(obj)}" + ) + except Node._Skipped: + return added_objects[name] = obj - if isinstance(obj, GraphInterface): - self._handle_add_gif(name, obj) - elif isinstance(obj, Node): - self._handle_add_node(name, obj) - else: - raise TypeError( - f"Cannot handle adding field {name=} of type {type(obj)}" - ) def append(name, inst): if isinstance(inst, LL_Types): @@ -467,7 +471,7 @@ def __new__(cls, *args, **kwargs): out = super().__new__(cls) return out - def _setup(self) -> None: + def _setup(self, *args, **kwargs) -> None: cls = type(self) # print(f"Called Node init {cls.__qualname__:<20} {'-' * 80}") @@ -484,30 +488,41 @@ def _setup(self) -> None: _, _ = self._setup_fields(cls) # Call 2-stage constructors + if self._init: - for base in reversed(type(self).mro()): - if hasattr(base, "__preinit__"): - base.__preinit__(self) - for base in reversed(type(self).mro()): - if hasattr(base, "__postinit__"): - base.__postinit__(self) + for f_name in ("__preinit__", "__postinit__"): + for base in reversed(type(self).mro()): + if hasattr(base, f_name): + f = getattr(base, f_name) + f(self) def __init__(self): + super().__init__() + CNode.transfer_ownership(self) assert not hasattr(self, "_is_setup") self._is_setup = True - def __preinit__(self): ... + def __preinit__(self, *args, **kwargs) -> None: ... - def __postinit__(self): ... + def __postinit__(self, *args, **kwargs) -> None: ... def __post_init__(self, *args, **kwargs): - self._setup() + if not getattr(self, "_is_setup", False): + raise Exception( + "Node constructor hasn't been called." + "Did you forget to call super().__init__()?" + f"{type(self).__qualname__}" + ) + self._setup(*args, **kwargs) + + def __init_subclass__(cls, *, init: bool = True) -> None: + cls._init = init + post_init_decorator(cls) def _handle_add_gif(self, name: str, gif: GraphInterface): gif.node = self gif.name = name - if not isinstance(gif, GraphInterfaceSelf): - gif.connect(self.self_gif, linkcls=LinkSibling) + gif.connect(self.self_gif, LinkSibling()) def _handle_add_node(self, name: str, node: "Node"): if node.get_parent(): @@ -515,14 +530,14 @@ def _handle_add_node(self, name: str, node: "Node"): from faebryk.core.trait import TraitImpl - if isinstance(node, TraitImpl): + if TraitImpl.is_traitimpl(node): if self.has_trait(node.__trait__): if not node.handle_duplicate( - cast_assert(TraitImpl, self.get_trait(node.__trait__)), self + cast(TraitImpl, self.get_trait(node.__trait__)), self ): - return + raise Node._Skipped() - node.parent.connect(self.children, LinkNamedParent.curry(name)) + node.parent.connect(self.children, LinkNamedParent(name)) node._handle_added_to_parent() def _remove_child(self, node: "Node"): @@ -530,32 +545,9 @@ def _remove_child(self, node: "Node"): def _handle_added_to_parent(self): ... - def get_graph(self): - return self.self_gif.G - - def get_parent(self): - return self.parent.get_parent() - - def get_name(self): - p = self.get_parent() - if not p: - raise NodeNoParent(self, "Parent required for name") - return p[1] - - def get_hierarchy(self) -> list[tuple["Node", str]]: - parent = self.get_parent() - if not parent: - return [(self, "*")] - parent_obj, name = parent - - return parent_obj.get_hierarchy() + [(self, name)] - - def get_full_name(self, types: bool = False): - hierarchy = self.get_hierarchy() - if types: - return ".".join([f"{name}|{type(obj).__name__}" for obj, name in hierarchy]) - else: - return ".".join([f"{name}" for _, name in hierarchy]) + def builder(self, op: Callable[[Self], Any]) -> Self: + op(self) + return self # printing ------------------------------------------------------------------------- @@ -563,11 +555,6 @@ def get_full_name(self, types: bool = False): def __str__(self) -> str: return f"<{self.get_full_name(types=True)}>" - @try_avoid_endless_recursion - def __repr__(self) -> str: - id_str = f"(@{hex(id(self))})" if ID_REPR else "" - return f"<{self.get_full_name(types=True)}>{id_str}" - def pretty_params(self) -> str: from faebryk.core.parameter import Parameter @@ -588,18 +575,24 @@ def add_trait[_TImpl: "TraitImpl"](self, trait: _TImpl) -> _TImpl: def _find_trait_impl[V: "Trait | TraitImpl"]( self, trait: type[V], only_implemented: bool ) -> V | None: - from faebryk.core.trait import TraitImpl, TraitImplementationConfusedWithTrait + from faebryk.core.trait import ( + Trait, + TraitImpl, + TraitImplementationConfusedWithTrait, + ) - if issubclass(trait, TraitImpl): + if TraitImpl.is_traitimpl_type(trait): if not trait.__trait__.__decless_trait__: - raise TraitImplementationConfusedWithTrait(self, trait) + raise TraitImplementationConfusedWithTrait( + self, cast(type[Trait], trait) + ) trait = trait.__trait__ out = self.get_children( direct_only=True, - types=TraitImpl, - f_filter=lambda impl: impl.implements(trait) - and (impl.is_implemented() or not only_implemented), + types=Trait, + f_filter=lambda impl: trait.is_traitimpl(impl) + and (cast(TraitImpl, impl).is_implemented() or not only_implemented), ) assert len(out) <= 1 @@ -618,54 +611,15 @@ def has_trait(self, trait: type["Trait | TraitImpl"]) -> bool: return self.try_get_trait(trait) is not None def get_trait[V: "Trait | TraitImpl"](self, trait: Type[V]) -> V: - from faebryk.core.trait import TraitNotFound + from faebryk.core.trait import Trait, TraitNotFound impl = self.try_get_trait(trait) if not impl: - raise TraitNotFound(self, trait) + raise TraitNotFound(self, cast(type[Trait], trait)) return cast_assert(trait, impl) # Graph stuff ---------------------------------------------------------------------- - - def _get_children_direct(self): - return { - gif.node - for gif, link in self.get_graph().get_edges(self.children).items() - if isinstance(link, LinkNamedParent) - } - - def _get_children_all(self, include_root: bool): - # TODO looks like get_node_tree is 2x faster - - def _filter(path, link): - next_node = path[-1] - prev_node = path[-2] if len(path) >= 2 else None - - # Only look at hierarchy - if not isinstance( - next_node, (GraphInterfaceSelf, GraphInterfaceHierarchical) - ): - return False - - # Only children - if ( - isinstance(prev_node, GraphInterfaceHierarchical) - and isinstance(next_node, GraphInterfaceHierarchical) - and not prev_node.is_parent - and next_node.is_parent - ): - return False - - return True - - out = self.bfs_node(_filter) - - if not include_root: - out.remove(self) - - return set(out) - def get_children[T: Node]( self, direct_only: bool, @@ -674,29 +628,18 @@ def get_children[T: Node]( f_filter: Callable[[T], bool] | None = None, sort: bool = True, ) -> set[T]: - if direct_only: - out = self._get_children_direct() - if include_root: - out.add(self) - else: - out = self._get_children_all(include_root=include_root) - - if types is not Node or f_filter: - out = { - n for n in out if isinstance(n, types) and (not f_filter or f_filter(n)) - } - - out = cast(set[T], out) - - if sort: - out = set( - sorted( - out, - key=lambda n: try_or(n.get_name, default="", catch=NodeNoParent), + return cast( + set[T], + set( + super().get_children( + direct_only=direct_only, + types=types if isinstance(types, tuple) else (types,), + include_root=include_root, + f_filter=f_filter, # type: ignore + sort=sort, ) - ) - - return out + ), + ) def get_tree[T: Node]( self, @@ -731,11 +674,6 @@ def get_tree[T: Node]( return tree - def bfs_node(self, filter: Callable[[list[GraphInterface], Link], bool]): - return Node.get_nodes_from_gifs( - self.get_graph().bfs_visit(filter, [self.self_gif]) - ) - @staticmethod def get_nodes_from_gifs(gifs: Iterable[GraphInterface]): # TODO move this to gif? @@ -745,6 +683,9 @@ def get_nodes_from_gifs(gifs: Iterable[GraphInterface]): # Hierarchy queries ---------------------------------------------------------------- + def get_hierarchy(self) -> list[tuple["Node", str]]: + return [(cast_assert(Node, n), name) for n, name in super().get_hierarchy()] + def get_parent_f( self, filter_expr: Callable[["Node"], bool], diff --git a/src/faebryk/core/parameter.py b/src/faebryk/core/parameter.py index 607075de..7f610202 100644 --- a/src/faebryk/core/parameter.py +++ b/src/faebryk/core/parameter.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: MIT import logging from typing import ( + Any, Callable, Concatenate, Optional, @@ -14,17 +15,23 @@ from faebryk.core.node import Node from faebryk.core.trait import Trait from faebryk.libs.units import Quantity, UnitsContainer -from faebryk.libs.util import Tree, TwistArgs, is_type_pair, try_avoid_endless_recursion +from faebryk.libs.util import ( + Tree, + TwistArgs, + cast_assert, + is_type_pair, + try_avoid_endless_recursion, +) logger = logging.getLogger(__name__) def _resolved[PV, O]( - func: Callable[["Parameter[PV]", "Parameter[PV]"], O], + func: Callable[["Parameter", "Parameter"], O], ) -> Callable[ [ - "PV | set[PV] | tuple[PV, PV] | Parameter[PV]", - "PV | set[PV] | tuple[PV, PV] | Parameter[PV]", + "PV | set | tuple[PV, PV] | Parameter", + "PV | set | tuple[PV, PV] | Parameter", ], O, ]: @@ -36,10 +43,10 @@ def wrap(*args): def _resolved_self[PV, O, **P]( - func: Callable[Concatenate["Parameter[PV]", P], O], -) -> Callable[Concatenate["PV | set[PV] | tuple[PV, PV] | Parameter[PV]", P], O]: + func: Callable[Concatenate["Parameter", P], O], +) -> Callable[Concatenate["PV | set | tuple[PV, PV] | Parameter", P], O]: def wrap( - p: "PV | set[PV] | tuple[PV, PV] | Parameter[PV]", + p: "PV | set | tuple[PV, PV] | Parameter", *args: P.args, **kwargs: P.kwargs, ): @@ -48,9 +55,10 @@ def wrap( return wrap -class Parameter[PV](Node): - type LIT = PV | set[PV] | tuple[PV, PV] - type LIT_OR_PARAM = LIT | "Parameter[PV]" +class Parameter(Node): + type PV = Any + type LIT = PV | set | tuple[PV, PV] + type LIT_OR_PARAM = LIT | "Parameter" class TraitT(Trait): ... @@ -60,13 +68,20 @@ class TraitT(Trait): ... class MergeException(Exception): ... class SupportsSetOps: - def __contains__(self, other: "Parameter[PV].LIT_OR_PARAM") -> bool: ... + def __contains__(self, other: "Parameter.LIT_OR_PARAM") -> bool: ... + + @staticmethod + def check(other: "Parameter.LIT_OR_PARAM") -> bool: + return hasattr(other, "__contains__") + + class is_dynamic(TraitT): + def execute(self) -> None: ... - def try_compress(self) -> "Parameter[PV]": + def try_compress(self) -> "Parameter": return self @classmethod - def from_literal(cls, value: LIT_OR_PARAM) -> '"Parameter[PV]"': + def from_literal(cls, value: LIT_OR_PARAM) -> '"Parameter"': from faebryk.library.Constant import Constant from faebryk.library.Range import Range from faebryk.library.Set import Set @@ -80,7 +95,7 @@ def from_literal(cls, value: LIT_OR_PARAM) -> '"Parameter[PV]"': else: return Constant(value) - def _merge(self, other: "Parameter[PV]") -> "Parameter[PV]": + def _merge(self, other: "Parameter") -> "Parameter": from faebryk.library.ANY import ANY from faebryk.library.Operation import Operation from faebryk.library.Set import Set @@ -98,20 +113,22 @@ def _is_pair[T, U](type1: type[T], type2: type[U]) -> Optional[tuple[T, U]]: except ValueError: ... - if pair := _is_pair(Parameter[PV], TBD): + if pair := _is_pair(Parameter, TBD): return pair[0] - if pair := _is_pair(Parameter[PV], ANY): + if pair := _is_pair(Parameter, ANY): return pair[0] # TODO remove as soon as possible - if pair := _is_pair(Parameter[PV], Operation): + if pair := _is_pair(Parameter, Operation): # TODO make MergeOperation that inherits from Operation # and return that instead, application can check if result is MergeOperation # if it was checking mergeability raise self.MergeException("cant merge range with operation") - if pair := _is_pair(Parameter[PV], Parameter[PV].SupportsSetOps): + if any(Parameter.SupportsSetOps.check(x) for x in (self, other)): + pair = (self, other) + # if pair := _is_pair(Parameter, Parameter.SupportsSetOps): out = self.intersect(*pair) if isinstance(out, Operation): raise self.MergeException("not resolvable") @@ -123,16 +140,16 @@ def _is_pair[T, U](type1: type[T], type2: type[U]) -> Optional[tuple[T, U]]: raise NotImplementedError - def _narrowed(self, other: "Parameter[PV]"): + def _narrowed(self, other: "Parameter"): if self is other: return - if self.narrowed_by.is_connected(other.narrows): + if self.narrowed_by.is_connected_to(other.narrows): return self.narrowed_by.connect(other.narrows) @_resolved - def is_mergeable_with(self: "Parameter[PV]", other: "Parameter[PV]") -> bool: + def is_mergeable_with(self: "Parameter", other: "Parameter") -> bool: try: self._merge(other) return True @@ -142,7 +159,7 @@ def is_mergeable_with(self: "Parameter[PV]", other: "Parameter[PV]") -> bool: return False @_resolved - def is_subset_of(self: "Parameter[PV]", other: "Parameter[PV]") -> bool: + def is_subset_of(self: "Parameter", other: "Parameter") -> bool: from faebryk.library.ANY import ANY from faebryk.library.Operation import Operation from faebryk.library.TBD import TBD @@ -150,7 +167,7 @@ def is_subset_of(self: "Parameter[PV]", other: "Parameter[PV]") -> bool: lhs = self rhs = other - def is_either_instance(t: type["Parameter[PV]"]): + def is_either_instance(t: type["Parameter"]): return isinstance(lhs, t) or isinstance(rhs, t) # Not resolveable @@ -167,7 +184,9 @@ def is_either_instance(t: type["Parameter[PV]"]): return lhs & rhs == lhs @_resolved - def merge(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + def merge(self: "Parameter", other: "Parameter") -> "Parameter": + if self is other: + return self out = self._merge(other) self._narrowed(out) @@ -176,7 +195,7 @@ def merge(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": return out @_resolved - def override(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + def override(self: "Parameter", other: "Parameter") -> "Parameter": if not other.is_subset_of(self): raise self.MergeException("override not possible") @@ -185,9 +204,7 @@ def override(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": # TODO: replace with graph-based @staticmethod - def arithmetic_op( - op1: "Parameter[PV]", op2: "Parameter[PV]", op: Callable - ) -> "Parameter[PV]": + def arithmetic_op(op1: "Parameter", op2: "Parameter", op: Callable) -> "Parameter": from faebryk.library.ANY import ANY from faebryk.library.Constant import Constant from faebryk.library.Operation import Operation @@ -255,7 +272,7 @@ def _is_pair[T, U]( raise NotImplementedError @staticmethod - def intersect(op1: "Parameter[PV]", op2: "Parameter[PV]") -> "Parameter[PV]": + def intersect(op1: "Parameter", op2: "Parameter") -> "Parameter": from faebryk.library.Constant import Constant from faebryk.library.Operation import Operation from faebryk.library.Range import Range @@ -317,56 +334,56 @@ def op(a, b): return Operation((op1, op2), op) @_resolved - def __add__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __add__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: a + b) @_resolved - def __radd__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __radd__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: b + a) @_resolved - def __sub__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __sub__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: a - b) @_resolved - def __rsub__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __rsub__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: b - a) # TODO PV | float @_resolved - def __mul__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __mul__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: a * b) @_resolved - def __rmul__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __rmul__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: b * a) # TODO PV | float @_resolved - def __truediv__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __truediv__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: a / b) @_resolved - def __rtruediv__(self: "Parameter[PV]", other: "Parameter[PV]"): + def __rtruediv__(self: "Parameter", other: "Parameter"): return self.arithmetic_op(self, other, lambda a, b: b / a) @_resolved - def __pow__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + def __pow__(self: "Parameter", other: "Parameter") -> "Parameter": return self.arithmetic_op(self, other, lambda a, b: a**b) @_resolved - def __rpow__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + def __rpow__(self: "Parameter", other: "Parameter") -> "Parameter": return self.arithmetic_op(self, other, lambda a, b: b**a) @_resolved - def __and__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + def __and__(self: "Parameter", other: "Parameter") -> "Parameter": return self.intersect(self, other) @_resolved - def __rand__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + def __rand__(self: "Parameter", other: "Parameter") -> "Parameter": return self.intersect(other, self) - def get_most_narrow(self) -> "Parameter[PV]": + def get_most_narrow(self) -> "Parameter": out = self.get_narrowing_chain()[-1] com = out.try_compress() @@ -378,12 +395,12 @@ def get_most_narrow(self) -> "Parameter[PV]": return out @staticmethod - def resolve_all(params: "Sequence[Parameter[PV]]") -> "Parameter[PV]": + def resolve_all(params: "Sequence[Parameter]") -> "Parameter": from faebryk.library.TBD import TBD params_set = list(params) if not params_set: - return TBD[PV]() + return TBD() it = iter(params_set) most_specific = next(it) for param in it: @@ -408,15 +425,15 @@ def __str__(self) -> str: def get_narrowing_chain(self) -> list["Parameter"]: out: list[Parameter] = [self] - narrowers = self.narrowed_by.get_connected_nodes(Parameter) + narrowers = self.narrowed_by.get_connected_nodes([Parameter]) if narrowers: assert len(narrowers) == 1, "Narrowing tree diverged" - out += next(iter(narrowers)).get_narrowing_chain() + out += cast_assert(Parameter, next(iter(narrowers))).get_narrowing_chain() assert id(self) not in map(id, out[1:]), "Narrowing tree cycle" return out def get_narrowed_siblings(self) -> set["Parameter"]: - return self.narrows.get_connected_nodes(Parameter) + return self.narrows.get_connected_nodes([Parameter]) # type: ignore def __copy__(self) -> Self: return type(self)() @@ -434,9 +451,7 @@ def get_tree_param(self, include_root: bool = True) -> Tree["Parameter"]: # util functions ------------------------------------------------------------------- @_resolved_self - def enum_parameter_representation( - self: "Parameter[PV]", required: bool = False - ) -> str: + def enum_parameter_representation(self: "Parameter", required: bool = False) -> str: return self._enum_parameter_representation(required=required) def _enum_parameter_representation(self, required: bool = False) -> str: @@ -444,7 +459,7 @@ def _enum_parameter_representation(self, required: bool = False) -> str: @_resolved_self def as_unit( - self: "Parameter[PV]", + self: "Parameter", unit: UnitsContainer, base: int = 1000, required: bool = False, @@ -459,7 +474,7 @@ def _as_unit(self, unit: UnitsContainer, base: int, required: bool) -> str: @_resolved_self def as_unit_with_tolerance( - self: "Parameter[PV]", + self: "Parameter", unit: UnitsContainer, base: int = 1000, required: bool = False, @@ -472,7 +487,7 @@ def _as_unit_with_tolerance( return self._as_unit(unit, base=base, required=required) @_resolved_self - def get_max(self: "Parameter[PV]") -> PV: + def get_max(self: "Parameter") -> PV: return self._max() def _max(self): diff --git a/src/faebryk/core/reference.py b/src/faebryk/core/reference.py index b05d7bd2..2d4749fb 100644 --- a/src/faebryk/core/reference.py +++ b/src/faebryk/core/reference.py @@ -1,6 +1,10 @@ from collections import defaultdict +from typing import cast -from faebryk.core.graphinterface import GraphInterfaceReference +from faebryk.core.graphinterface import ( + GraphInterfaceReference, + GraphInterfaceReferenceUnboundError, +) from faebryk.core.link import LinkPointer from faebryk.core.node import Node, constructed_field @@ -21,8 +25,8 @@ def __init__(self, out_type: type[O] | None = None): def get(instance: Node) -> O: try: - return self.gifs[instance].get_reference() - except GraphInterfaceReference.UnboundError as ex: + return cast(O, self.gifs[instance].get_reference()) + except GraphInterfaceReferenceUnboundError as ex: raise Reference.UnboundError from ex def set_(instance: Node, value: O): @@ -38,7 +42,7 @@ def set_(instance: Node, value: O): raise TypeError(f"Expected {out_type} got {type(value)}") # attach our gif to what we're referring to - self.gifs[instance].connect(value.self_gif, LinkPointer) + self.gifs[instance].connect(value.self_gif, LinkPointer()) property.__init__(self, get, set_) diff --git a/src/faebryk/core/trait.py b/src/faebryk/core/trait.py index 4d958d14..24e6310f 100644 --- a/src/faebryk/core/trait.py +++ b/src/faebryk/core/trait.py @@ -1,6 +1,7 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT import logging +from typing import TypeGuard, cast from faebryk.core.node import Node, NodeException from faebryk.libs.util import cast_assert @@ -46,21 +47,75 @@ def __init__(self, node: Node, *args: object) -> None: class Trait(Node): __decless_trait__: bool = False + # TODO once @classmethod def impl[T: "Trait"](cls: type[T]): - class _Impl(TraitImpl, cls): ... + # Implements TraitImpl + class _Impl(cls): + __trait__: type[Trait] = cls + + @property + def obj(self) -> Node: + p = self.get_parent() + if not p: + raise TraitUnbound(self) + return cast_assert(Node, p[0]) + + def get_obj[TN: Node](self, type: type[TN]) -> TN: + return cast_assert(type, self.obj) + + def cmp(self, new_t: "TraitImpl") -> tuple[bool, "TraitImpl"]: + assert TraitImpl.is_traitimpl(new_t) + + # If new same or more specific + if new_t.implements(self.__trait__): + return True, new_t + + # hack type (ghetto protocol) + traitimpl = cast(TraitImpl, self) + + # If we are more specific + if self.implements(new_t.__trait__): + return True, traitimpl + + return False, traitimpl + + def implements(self, trait: type[Trait]): + return trait.is_traitimpl(self) + + # Overwriteable ------------------------------------------------------------ + + def _handle_added_to_parent(self): + self.on_obj_set() + + def on_obj_set(self): ... + + def handle_duplicate(self, old: "TraitImpl", node: Node) -> bool: + assert old is not self + _, candidate = old.cmp(cast(TraitImpl, self)) + if candidate is not self: + # raise TraitAlreadyExists(node, self) + return False + + node.del_trait(old.__trait__) + return True + + # override this to implement a dynamic trait + def is_implemented(self): + return True # this should be outside the class def to prevent # __init_subclass__ from overwriting it _Impl.__trait__ = cls + _Impl.__name__ = f"{cls.__name__}Impl" return _Impl def __new__(cls, *args, **kwargs): - if not issubclass(cls, TraitImpl): - raise TypeError("Don't instantiate Trait use Trait.impl instead") + if not TraitImpl.is_traitimpl_type(cls): + raise TypeError(f"Don't instantiate Trait [{cls}] use Trait.impl instead") - return super().__new__(cls) + return super().__new__(cls) # type: ignore @classmethod def decless(cls): @@ -70,56 +125,50 @@ class _Trait(cls): ... return _Trait.impl() + @classmethod + def is_traitimpl(cls, obj: "Trait") -> TypeGuard["TraitImpl"]: + assert issubclass(cls, Trait) + if not TraitImpl.is_traitimpl(obj): + return False + return issubclass(obj.__trait__, cls) -class TraitImpl(Node): - __trait__: type[Trait] = None - - @property - def obj(self) -> Node: - p = self.get_parent() - if not p: - raise TraitUnbound(self) - return p[0] - - def get_obj[T: Node](self, type: type[T]) -> T: - return cast_assert(type, self.obj) - - def cmp(self, other: "TraitImpl") -> tuple[bool, "TraitImpl"]: - assert type(other), TraitImpl - - # If other same or more specific - if other.implements(self.__trait__): - return True, other + # TODO check subclasses implementing abstractmethods (use subclass_init) - # If we are more specific - if self.implements(other.__trait__): - return True, self - return False, self +# Hack, using this as protocol +# Can't use actual protocol because CNode doesn't allow multiple inheritance +class TraitImpl(Node): + """ + Warning: Do not instancecheck against this type! + """ - def implements(self, trait: type[Trait]): - assert issubclass(trait, Trait) + __trait__: type[Trait] - return issubclass(self.__trait__, trait) + @property + def obj(self) -> Node: ... + def get_obj[T: Node](self, type: type[T]) -> T: ... + def cmp(self, other: "TraitImpl") -> tuple[bool, "TraitImpl"]: ... + def implements(self, trait: type[Trait]): ... # Overwriteable -------------------------------------------------------------------- - - def _handle_added_to_parent(self): - self.on_obj_set() - + def _handle_added_to_parent(self): ... def on_obj_set(self): ... + def handle_duplicate(self, old: "TraitImpl", node: Node) -> bool: + """ + Returns True if the duplicate was handled, False if the trait should be skipped + """ + ... - def handle_duplicate(self, other: "TraitImpl", node: Node) -> bool: - assert other is not self - _, candidate = other.cmp(self) - if candidate is not self: - return False + def is_implemented(self): ... - node.del_trait(other.__trait__) - return True - - # raise TraitAlreadyExists(node, self) + @staticmethod + def is_traitimpl(obj) -> TypeGuard["TraitImpl"]: + if not isinstance(obj, Trait): + return False + return getattr(obj, "__trait__", None) is not None - # override this to implement a dynamic trait - def is_implemented(self): - return True + @staticmethod + def is_traitimpl_type(obj) -> TypeGuard[type["TraitImpl"]]: + if not issubclass(obj, Trait): + return False + return getattr(obj, "__trait__", None) is not None diff --git a/src/faebryk/exporters/esphome/esphome.py b/src/faebryk/exporters/esphome/esphome.py index e33cacfe..b638a86d 100644 --- a/src/faebryk/exporters/esphome/esphome.py +++ b/src/faebryk/exporters/esphome/esphome.py @@ -7,7 +7,7 @@ import yaml import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions from faebryk.core.parameter import Parameter logger = logging.getLogger(__name__) @@ -53,7 +53,7 @@ def merge_dicts(*dicts: dict) -> dict: def make_esphome_config(G: Graph) -> dict: - esphome_components = G.nodes_with_trait(F.has_esphome_config) + esphome_components = GraphFunctions(G).nodes_with_trait(F.has_esphome_config) esphome_config = merge_dicts(*[t.get_config() for _, t in esphome_components]) diff --git a/src/faebryk/exporters/netlist/graph.py b/src/faebryk/exporters/netlist/graph.py index a67c8e18..41c49ad9 100644 --- a/src/faebryk/exporters/netlist/graph.py +++ b/src/faebryk/exporters/netlist/graph.py @@ -4,12 +4,11 @@ import logging from abc import abstractmethod -import networkx as nx - import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions from faebryk.core.module import Module from faebryk.exporters.netlist.netlist import T2Netlist +from faebryk.libs.util import KeyErrorAmbiguous logger = logging.getLogger(__name__) @@ -53,7 +52,7 @@ def get_or_set_name_and_value_of_node(c: Module): class can_represent_kicad_footprint_via_attached_component( can_represent_kicad_footprint.impl() ): - def __init__(self, component: Module, graph: nx.Graph) -> None: + def __init__(self, component: Module, graph: Graph) -> None: """ graph has to be electrically closed """ @@ -101,21 +100,17 @@ def add_or_get_net(interface: F.Electrical): net.part_of.connect(interface) return net if len(nets) > 1: - raise Exception(f"Multiple nets interconnected: {nets}") + raise KeyErrorAmbiguous(list(nets), "Multiple nets interconnected") return next(iter(nets)) -def attach_nets_and_kicad_info(g: Graph): - # g has to be closed - - Gclosed = g - +def attach_nets_and_kicad_info(G: Graph): # group comps & fps node_fps = { n: t.get_footprint() # TODO maybe nicer to just look for footprints # and get their respective components instead - for n, t in Gclosed.nodes_with_trait(F.has_footprint) + for n, t in GraphFunctions(G).nodes_with_trait(F.has_footprint) if isinstance(n, Module) } @@ -127,9 +122,8 @@ def attach_nets_and_kicad_info(g: Graph): for n, fp in node_fps.items(): if fp.has_trait(can_represent_kicad_footprint): continue - fp.add(can_represent_kicad_footprint_via_attached_component(n, Gclosed)) + fp.add(can_represent_kicad_footprint_via_attached_component(n, G)) for fp in node_fps.values(): - # TODO use graph for mif in fp.get_children(direct_only=True, types=F.Pad): add_or_get_net(mif.net) diff --git a/src/faebryk/exporters/netlist/netlist.py b/src/faebryk/exporters/netlist/netlist.py index 149b2215..41f6d292 100644 --- a/src/faebryk/exporters/netlist/netlist.py +++ b/src/faebryk/exporters/netlist/netlist.py @@ -5,7 +5,7 @@ from dataclasses import dataclass import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions logger = logging.getLogger(__name__) @@ -38,7 +38,7 @@ class Vertex: def make_t2_netlist_from_graph(G: Graph) -> T2Netlist: from faebryk.exporters.netlist.graph import can_represent_kicad_footprint - nets = G.nodes_of_type(F.Net) + nets = GraphFunctions(G).nodes_of_type(F.Net) t2_nets = [ T2Netlist.Net( @@ -60,7 +60,7 @@ def make_t2_netlist_from_graph(G: Graph) -> T2Netlist: comps = { t.get_footprint().get_trait(can_represent_kicad_footprint).get_kicad_obj() - for _, t in G.nodes_with_trait(F.has_footprint) + for _, t in GraphFunctions(G).nodes_with_trait(F.has_footprint) } not_found = [ diff --git a/src/faebryk/exporters/pcb/kicad/transformer.py b/src/faebryk/exporters/pcb/kicad/transformer.py index 7c40c11c..de0baa87 100644 --- a/src/faebryk/exporters/pcb/kicad/transformer.py +++ b/src/faebryk/exporters/pcb/kicad/transformer.py @@ -16,7 +16,7 @@ from typing_extensions import deprecated import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions from faebryk.core.module import Module from faebryk.core.moduleinterface import ModuleInterface from faebryk.core.node import Node @@ -224,7 +224,7 @@ def attach(self): footprints = { (f.propertys["Reference"].value, f.name): f for f in self.pcb.footprints } - for node, fpt in self.graph.nodes_with_trait(F.has_footprint): + for node, fpt in GraphFunctions(self.graph).nodes_with_trait(F.has_footprint): if not node.has_trait(F.has_overriden_name): continue g_fp = fpt.get_footprint() @@ -259,7 +259,7 @@ def attach(self): attached = { n: t.get_fp() - for n, t in self.graph.nodes_with_trait( + for n, t in GraphFunctions(self.graph).nodes_with_trait( PCB_Transformer.has_linked_kicad_footprint ) } @@ -303,7 +303,7 @@ def get_fp(cmp: Node) -> Footprint: def get_all_footprints(self) -> List[tuple[Module, Footprint]]: return [ (cast_assert(Module, cmp), t.get_fp()) - for cmp, t in self.graph.nodes_with_trait( + for cmp, t in GraphFunctions(self.graph).nodes_with_trait( PCB_Transformer.has_linked_kicad_footprint ) ] @@ -503,7 +503,7 @@ def _get_pad(ffp: F.Footprint, intf: F.Electrical): pin_map = ffp.get_trait(F.has_kicad_footprint).get_pin_names() pin_name = find( pin_map.items(), - lambda pad_and_name: intf.is_connected_to(pad_and_name[0].net) is not None, + lambda pad_and_name: intf.is_connected_to(pad_and_name[0].net), )[1] fp = PCB_Transformer.get_fp(ffp) @@ -878,7 +878,7 @@ def insert_jlcpcb_serial( # Positioning ---------------------------------------------------------------------- def move_footprints(self): # position modules with defined positions - pos_mods = self.graph.nodes_with_traits( + pos_mods = GraphFunctions(self.graph).nodes_with_traits( (F.has_pcb_position, self.has_linked_kicad_footprint) ) diff --git a/src/faebryk/exporters/pcb/layout/heuristic_decoupling.py b/src/faebryk/exporters/pcb/layout/heuristic_decoupling.py index 661bf143..d1ee36e0 100644 --- a/src/faebryk/exporters/pcb/layout/heuristic_decoupling.py +++ b/src/faebryk/exporters/pcb/layout/heuristic_decoupling.py @@ -199,7 +199,7 @@ def place_next_to_pad( nfp = module.get_trait(F.has_footprint).get_footprint() npad = find( nfp.get_children(direct_only=True, types=F.Pad), - lambda p: p.net.is_connected_to(pad.net) is not None, + lambda p: p.net.is_connected_to(pad.net), ) nkfp, nkpad = npad.get_trait(PCB_Transformer.has_linked_kicad_pad).get_pad() if len(nkpad) != 1: @@ -249,7 +249,7 @@ def place_next_to( for parent_pad, child in zip(pads_intf, children): intf = find( child.get_children(direct_only=True, types=F.Electrical), - lambda x: x.is_connected_to(parent_intf) is not None, + lambda x: x.is_connected_to(parent_intf), ) logger.debug(f"Placing {intf} next to {parent_pad}") diff --git a/src/faebryk/exporters/pcb/routing/routing.py b/src/faebryk/exporters/pcb/routing/routing.py index 57bdaf34..de8ea334 100644 --- a/src/faebryk/exporters/pcb/routing/routing.py +++ b/src/faebryk/exporters/pcb/routing/routing.py @@ -11,6 +11,7 @@ import networkx as nx import faebryk.library._F as F +from faebryk.core.graph import GraphFunctions from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer from faebryk.exporters.pcb.routing.grid import ( Coord, @@ -124,7 +125,7 @@ def draw_circle(self, coord: OutCoord, size=0.5, layer="User.9"): ) def route_all(self): - nets = self.transformer.graph.nodes_of_type(F.Net) + nets = GraphFunctions(self.transformer.graph).nodes_of_type(F.Net) # TODO add net picking heuristic for net in nets: diff --git a/src/faebryk/exporters/pcb/routing/util.py b/src/faebryk/exporters/pcb/routing/util.py index 809cc333..03356bdc 100644 --- a/src/faebryk/exporters/pcb/routing/util.py +++ b/src/faebryk/exporters/pcb/routing/util.py @@ -219,6 +219,6 @@ def group_pads_that_are_connected_already( def get_routes_of_pad(pad: F.Pad): return { route - for mif in pad.pcb.get_direct_connections() + for mif in pad.pcb.get_connected() if (route := mif.get_parent_of_type(Route)) } diff --git a/src/faebryk/exporters/schematic/kicad/transformer.py b/src/faebryk/exporters/schematic/kicad/transformer.py index 0aa9ea6b..d5a61501 100644 --- a/src/faebryk/exporters/schematic/kicad/transformer.py +++ b/src/faebryk/exporters/schematic/kicad/transformer.py @@ -13,7 +13,7 @@ # import numpy as np # from shapely import Polygon import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions from faebryk.core.module import Module from faebryk.core.node import Node from faebryk.libs.exceptions import FaebrykException @@ -125,7 +125,9 @@ def attach(self): symbols = { (f.propertys["Reference"].value, f.lib_id): f for f in self.sch.symbols } - for node, sym_trait in self.graph.nodes_with_trait(F.Symbol.has_symbol): + for node, sym_trait in GraphFunctions(self.graph).nodes_with_trait( + F.Symbol.has_symbol + ): # FIXME: I believe this trait is used as a proxy for being a component # since, names are replaced with designators during typical pipelines if not node.has_trait(F.has_overriden_name): @@ -151,7 +153,7 @@ def attach(self): # Log what we were able to attach attached = { n: t.symbol - for n, t in self.graph.nodes_with_trait( + for n, t in GraphFunctions(self.graph).nodes_with_trait( SchTransformer.has_linked_sch_symbol ) } @@ -238,7 +240,7 @@ def get_symbol(cmp: Node) -> F.Symbol: def get_all_symbols(self) -> List[tuple[Module, F.Symbol]]: return [ (cast_assert(Module, cmp), t.symbol) - for cmp, t in self.graph.nodes_with_trait( + for cmp, t in GraphFunctions(self.graph).nodes_with_trait( SchTransformer.has_linked_sch_symbol ) ] diff --git a/src/faebryk/library/ANY.py b/src/faebryk/library/ANY.py index fd81098c..fede5fb4 100644 --- a/src/faebryk/library/ANY.py +++ b/src/faebryk/library/ANY.py @@ -5,7 +5,7 @@ from faebryk.libs.units import UnitsContainer -class ANY[PV](Parameter[PV]): +class ANY(Parameter): """ Allow parameter to take any value. Operations with this parameter automatically resolve to ANY too. diff --git a/src/faebryk/library/Analog_Devices_ADM2587EBRWZ.py b/src/faebryk/library/Analog_Devices_ADM2587EBRWZ.py index 86801728..ef855918 100644 --- a/src/faebryk/library/Analog_Devices_ADM2587EBRWZ.py +++ b/src/faebryk/library/Analog_Devices_ADM2587EBRWZ.py @@ -97,6 +97,7 @@ def pin_association_heuristic(self): ) def __init__(self, full_duplex: bool = False): + super().__init__() self._full_duplex = full_duplex if full_duplex: raise NotImplementedError("Full duplex RS485 not implemented") diff --git a/src/faebryk/library/BH1750FVI_TR.py b/src/faebryk/library/BH1750FVI_TR.py index e08eb0cc..ee13ff0e 100644 --- a/src/faebryk/library/BH1750FVI_TR.py +++ b/src/faebryk/library/BH1750FVI_TR.py @@ -6,14 +6,14 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P logger = logging.getLogger(__name__) class BH1750FVI_TR(Module): class _bh1750_esphome_config(F.has_esphome_config.impl()): - update_interval: F.TBD[Quantity] + update_interval: F.TBD def get_config(self) -> dict: val = self.update_interval.get_most_narrow() diff --git a/src/faebryk/library/BJT.py b/src/faebryk/library/BJT.py index 03c7dde3..f1935aef 100644 --- a/src/faebryk/library/BJT.py +++ b/src/faebryk/library/BJT.py @@ -22,8 +22,8 @@ class OperationRegion(Enum): SATURATION = auto() CUT_OFF = auto() - doping_type: Parameter[DopingType] - operation_region: Parameter[OperationRegion] + doping_type: Parameter + operation_region: Parameter emitter: F.Electrical base: F.Electrical diff --git a/src/faebryk/library/Battery.py b/src/faebryk/library/Battery.py index 60ec3372..c7157166 100644 --- a/src/faebryk/library/Battery.py +++ b/src/faebryk/library/Battery.py @@ -5,12 +5,11 @@ import faebryk.library._F as F import faebryk.libs.library.L as L from faebryk.core.module import Module -from faebryk.libs.units import Quantity class Battery(Module): - voltage: F.TBD[Quantity] - capacity: F.TBD[Quantity] + voltage: F.TBD + capacity: F.TBD power: F.ElectricPower diff --git a/src/faebryk/library/Button.py b/src/faebryk/library/Button.py index a74cf6d4..bd4a48fb 100644 --- a/src/faebryk/library/Button.py +++ b/src/faebryk/library/Button.py @@ -6,14 +6,13 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) class Button(Module): unnamed = L.list_field(2, F.Electrical) - height: F.TBD[Quantity] + height: F.TBD designator_prefix = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.S diff --git a/src/faebryk/library/ButtonCell.py b/src/faebryk/library/ButtonCell.py index 30810149..9700fb3b 100644 --- a/src/faebryk/library/ButtonCell.py +++ b/src/faebryk/library/ButtonCell.py @@ -53,9 +53,9 @@ class Size(IntEnum): N_2430 = 2430 N_2450 = 2450 - material: F.TBD[Material] - shape: F.TBD[Shape] - size: F.TBD[Size] + material: F.TBD + shape: F.TBD + size: F.TBD designator_prefix = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.B diff --git a/src/faebryk/library/Capacitor.py b/src/faebryk/library/Capacitor.py index 14107527..c5847b2b 100644 --- a/src/faebryk/library/Capacitor.py +++ b/src/faebryk/library/Capacitor.py @@ -7,7 +7,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity from faebryk.libs.util import join_if_non_empty logger = logging.getLogger(__name__) @@ -26,9 +25,9 @@ class TemperatureCoefficient(IntEnum): unnamed = L.list_field(2, F.Electrical) - capacitance: F.TBD[Quantity] - rated_voltage: F.TBD[Quantity] - temperature_coefficient: F.TBD[TemperatureCoefficient] + capacitance: F.TBD + rated_voltage: F.TBD + temperature_coefficient: F.TBD attach_to_footprint: F.can_attach_to_footprint_symmetrically designator_prefix = L.f_field(F.has_designator_prefix_defined)( diff --git a/src/faebryk/library/Common_Mode_Filter.py b/src/faebryk/library/Common_Mode_Filter.py index b36ba92b..151e0b22 100644 --- a/src/faebryk/library/Common_Mode_Filter.py +++ b/src/faebryk/library/Common_Mode_Filter.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) @@ -15,10 +14,10 @@ class Common_Mode_Filter(Module): coil_a: F.Inductor coil_b: F.Inductor - inductance: F.TBD[Quantity] - self_resonant_frequency: F.TBD[Quantity] - rated_current: F.TBD[Quantity] - dc_resistance: F.TBD[Quantity] + inductance: F.TBD + self_resonant_frequency: F.TBD + rated_current: F.TBD + dc_resistance: F.TBD designator_prefix = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.FL diff --git a/src/faebryk/library/Comparator.py b/src/faebryk/library/Comparator.py index b8f25f9d..03d6dbef 100644 --- a/src/faebryk/library/Comparator.py +++ b/src/faebryk/library/Comparator.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class Comparator(Module): @@ -15,12 +14,12 @@ class OutputType(Enum): PushPull = auto() OpenDrain = auto() - common_mode_rejection_ratio: F.TBD[Quantity] - input_bias_current: F.TBD[Quantity] - input_hysteresis_voltage: F.TBD[Quantity] - input_offset_voltage: F.TBD[Quantity] - propagation_delay: F.TBD[Quantity] - output_type: F.TBD[OutputType] + common_mode_rejection_ratio: F.TBD + input_bias_current: F.TBD + input_hysteresis_voltage: F.TBD + input_offset_voltage: F.TBD + propagation_delay: F.TBD + output_type: F.TBD power: F.ElectricPower inverting_input: F.Electrical diff --git a/src/faebryk/library/Constant.py b/src/faebryk/library/Constant.py index 71aaf244..14059b90 100644 --- a/src/faebryk/library/Constant.py +++ b/src/faebryk/library/Constant.py @@ -8,10 +8,11 @@ from faebryk.core.parameter import Parameter, _resolved from faebryk.libs.units import Quantity, UnitsContainer, to_si_str +from faebryk.libs.util import once -class Constant[PV](Parameter[PV], Parameter[PV].SupportsSetOps): - type LIT_OR_PARAM = Parameter[PV].LIT_OR_PARAM +class Constant(Parameter): + type LIT_OR_PARAM = Parameter.LIT_OR_PARAM def __init__(self, value: LIT_OR_PARAM) -> None: super().__init__() @@ -32,6 +33,9 @@ def __repr__(self): @_resolved def __eq__(self, other) -> bool: + if self is other: + return True + if not isinstance(other, Constant): return False @@ -42,9 +46,16 @@ def __eq__(self, other) -> bool: return self.value == other.value - def __hash__(self) -> int: + @once + def _hash_val(self): + # assert not isinstance(self.value, Parameter) return hash(self.value) + def __hash__(self) -> int: + if isinstance(self.value, Parameter): + return hash(self.value) + return self._hash_val() + # comparison operators @_resolved def __le__(self, other) -> bool: @@ -98,12 +109,12 @@ def __int__(self): return int(self.value) @_resolved - def __contains__(self, other: Parameter[PV]) -> bool: + def __contains__(self, other: Parameter) -> bool: if not isinstance(other, Constant): return False return other.value == self.value - def try_compress(self) -> Parameter[PV]: + def try_compress(self) -> Parameter: if isinstance(self.value, Parameter): return self.value return super().try_compress() diff --git a/src/faebryk/library/Crystal.py b/src/faebryk/library/Crystal.py index 96f56058..378fc234 100644 --- a/src/faebryk/library/Crystal.py +++ b/src/faebryk/library/Crystal.py @@ -4,7 +4,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class Crystal(Module): @@ -17,13 +16,13 @@ class Crystal(Module): # ---------------------------------------- # parameters # ---------------------------------------- - frequency: F.TBD[Quantity] - frequency_tolerance: F.TBD[F.Range] - frequency_temperature_tolerance: F.TBD[F.Range] - frequency_ageing: F.TBD[F.Range] - equivalent_series_resistance: F.TBD[Quantity] - shunt_capacitance: F.TBD[Quantity] - load_capacitance: F.TBD[Quantity] + frequency: F.TBD + frequency_tolerance: F.TBD + frequency_temperature_tolerance: F.TBD + frequency_ageing: F.TBD + equivalent_series_resistance: F.TBD + shunt_capacitance: F.TBD + load_capacitance: F.TBD # ---------------------------------------- # traits diff --git a/src/faebryk/library/DifferentialPair.py b/src/faebryk/library/DifferentialPair.py index 668cbe1b..c885c27f 100644 --- a/src/faebryk/library/DifferentialPair.py +++ b/src/faebryk/library/DifferentialPair.py @@ -5,14 +5,13 @@ import faebryk.library._F as F from faebryk.core.moduleinterface import ModuleInterface -from faebryk.libs.units import Quantity class DifferentialPair(ModuleInterface): p: F.SignalElectrical n: F.SignalElectrical - impedance: F.TBD[Quantity] + impedance: F.TBD def terminated(self) -> Self: terminated_bus = type(self)() diff --git a/src/faebryk/library/Diode.py b/src/faebryk/library/Diode.py index 9342d5cd..89c9db3e 100644 --- a/src/faebryk/library/Diode.py +++ b/src/faebryk/library/Diode.py @@ -5,15 +5,14 @@ from faebryk.core.module import Module from faebryk.core.parameter import Parameter from faebryk.libs.library import L -from faebryk.libs.units import Quantity class Diode(Module): - forward_voltage: F.TBD[Quantity] - max_current: F.TBD[Quantity] - current: F.TBD[Quantity] - reverse_working_voltage: F.TBD[Quantity] - reverse_leakage_current: F.TBD[Quantity] + forward_voltage: F.TBD + max_current: F.TBD + current: F.TBD + reverse_working_voltage: F.TBD + reverse_leakage_current: F.TBD anode: F.Electrical cathode: F.Electrical @@ -45,6 +44,6 @@ def pin_association_heuristic(self): ) def get_needed_series_resistance_for_current_limit( - self, input_voltage_V: Parameter[Quantity] - ) -> Parameter[Quantity]: + self, input_voltage_V: Parameter + ) -> Parameter: return (input_voltage_V - self.forward_voltage) / self.current diff --git a/src/faebryk/library/Diodes_Incorporated_AP2552W6_7.py b/src/faebryk/library/Diodes_Incorporated_AP2552W6_7.py index a76610b7..37ccdf1d 100644 --- a/src/faebryk/library/Diodes_Incorporated_AP2552W6_7.py +++ b/src/faebryk/library/Diodes_Incorporated_AP2552W6_7.py @@ -13,7 +13,7 @@ from faebryk.library.has_pcb_position import has_pcb_position from faebryk.libs.library import L # noqa: F401 from faebryk.libs.picker.picker import DescriptiveProperties -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P from faebryk.libs.util import assert_once logger = logging.getLogger(__name__) @@ -26,7 +26,7 @@ class Diodes_Incorporated_AP2552W6_7(Module): """ @assert_once - def set_current_limit(self, current: Parameter[Quantity]) -> F.Resistor: + def set_current_limit(self, current: Parameter) -> F.Resistor: self.current_limit.merge(current) current_limit_setting_resistor = self.ilim.add(F.Resistor()) @@ -64,7 +64,7 @@ def set_current_limit(self, current: Parameter[Quantity]) -> F.Resistor: fault: F.ElectricLogic ilim: F.SignalElectrical - current_limit: F.TBD[Quantity] + current_limit: F.TBD # ---------------------------------------- # traits # ---------------------------------------- diff --git a/src/faebryk/library/EEPROM.py b/src/faebryk/library/EEPROM.py index e89b050f..f7ecbf51 100644 --- a/src/faebryk/library/EEPROM.py +++ b/src/faebryk/library/EEPROM.py @@ -4,7 +4,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class EEPROM(Module): @@ -25,7 +24,7 @@ def set_address(self, addr: int): # modules, interfaces, parameters # ---------------------------------------- - memory_size: F.TBD[Quantity] + memory_size: F.TBD power: F.ElectricPower i2c: F.I2C diff --git a/src/faebryk/library/ElectricLogic.py b/src/faebryk/library/ElectricLogic.py index a0733cf2..2bab0db7 100644 --- a/src/faebryk/library/ElectricLogic.py +++ b/src/faebryk/library/ElectricLogic.py @@ -10,7 +10,7 @@ from faebryk.libs.library import L -class ElectricLogic(F.SignalElectrical, F.Logic): +class ElectricLogic(F.SignalElectrical): class has_pulls(F.Logic.TraitT): @abstractmethod def get_pulls(self) -> tuple[F.Resistor | None, F.Resistor | None]: ... @@ -90,7 +90,7 @@ class PushPull(Enum): # ---------------------------------------- # modules, interfaces, parameters # ---------------------------------------- - push_pull: F.TBD[PushPull] + push_pull: F.TBD # ---------------------------------------- # traits @@ -99,6 +99,8 @@ class PushPull(Enum): def pulled(self): return ElectricLogic.can_be_pulled_defined(self.signal, self.reference) + specializable_types = L.f_field(F.can_specialize_defined)([F.Logic]) + # ---------------------------------------- # functions # ---------------------------------------- @@ -106,7 +108,6 @@ def set(self, on: bool): """ Set the logic signal by directly connecting to the reference. """ - super().set(on) r = self.reference self.signal.connect(r.hv if on else r.lv) diff --git a/src/faebryk/library/ElectricLogicGate.py b/src/faebryk/library/ElectricLogicGate.py index 7c4b80bf..d44971ac 100644 --- a/src/faebryk/library/ElectricLogicGate.py +++ b/src/faebryk/library/ElectricLogicGate.py @@ -2,37 +2,18 @@ # SPDX-License-Identifier: MIT import faebryk.library._F as F -from faebryk.core.trait import TraitImpl from faebryk.libs.library import L from faebryk.libs.util import times class ElectricLogicGate(F.LogicGate): - def __init__( - self, - input_cnt: F.Constant[int], - output_cnt: F.Constant[int], - *functions: TraitImpl, - ) -> None: - self.input_cnt = input_cnt - self.output_cnt = output_cnt - super().__init__(input_cnt, output_cnt, *functions) - - def __preinit__(self): - self_logic = self - - for in_if_l, in_if_el in zip(self_logic.inputs, self.inputs): - in_if_l.specialize(in_if_el) - for out_if_l, out_if_el in zip(self_logic.outputs, self.outputs): - out_if_l.specialize(out_if_el) - @L.rt_field def inputs(self): - return times(self.input_cnt, F.ElectricLogic) + return times(self._input_cnt, F.ElectricLogic) @L.rt_field def outputs(self): - return times(self.output_cnt, F.ElectricLogic) + return times(self._output_cnt, F.ElectricLogic) @L.rt_field def single_electric_reference(self): diff --git a/src/faebryk/library/ElectricLogicGates.py b/src/faebryk/library/ElectricLogicGates.py index dc3d78fe..79a866ae 100644 --- a/src/faebryk/library/ElectricLogicGates.py +++ b/src/faebryk/library/ElectricLogicGates.py @@ -7,19 +7,19 @@ class ElectricLogicGates(Namespace): class OR(F.ElectricLogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_or_gate()) class NOR(F.ElectricLogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_nor_gate()) class NAND(F.ElectricLogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__( input_cnt, F.Constant(1), F.LogicGate.can_logic_nand_gate() ) class XOR(F.ElectricLogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_xor_gate()) diff --git a/src/faebryk/library/ElectricPower.py b/src/faebryk/library/ElectricPower.py index f8f03927..2d103eaf 100644 --- a/src/faebryk/library/ElectricPower.py +++ b/src/faebryk/library/ElectricPower.py @@ -9,49 +9,43 @@ from faebryk.core.moduleinterface import ModuleInterface from faebryk.core.node import Node from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P from faebryk.libs.util import RecursionGuard class ElectricPower(F.Power): - class can_be_decoupled_power(F.can_be_decoupled_defined): - def __init__(self) -> None: ... - + class can_be_decoupled_power(F.can_be_decoupled.impl()): def on_obj_set(self): obj = self.get_obj(ElectricPower) - super().__init__(hv=obj.hv, lv=obj.lv) + self.hv = obj.hv + self.lv = obj.lv def decouple(self): obj = self.get_obj(ElectricPower) - return ( - super() - .decouple() - .builder( - lambda c: c.rated_voltage.merge( - F.Range(obj.voltage * 2.0, math.inf * P.V) - ) + return F.can_be_decoupled_defined.decouple(self).builder( + lambda c: c.rated_voltage.merge( + F.Range(obj.voltage * 2.0, math.inf * P.V) ) ) - class can_be_surge_protected_power(F.can_be_surge_protected_defined): - def __init__(self) -> None: ... - + class can_be_surge_protected_power(F.can_be_surge_protected.impl()): def on_obj_set(self): obj = self.get_obj(ElectricPower) - super().__init__(obj.lv, obj.hv) + self.lv = obj.lv + self.hv = obj.hv def protect(self): obj = self.get_obj(ElectricPower) return [ tvs.builder(lambda t: t.reverse_working_voltage.merge(obj.voltage)) - for tvs in super().protect() + for tvs in F.can_be_surge_protected_defined.protect(self) ] hv: F.Electrical lv: F.Electrical - voltage: F.TBD[Quantity] - max_current: F.TBD[Quantity] + voltage: F.TBD + max_current: F.TBD """ Only for this particular power interface Does not propagate to connections diff --git a/src/faebryk/library/Electrical.py b/src/faebryk/library/Electrical.py index 6fce6013..10d96819 100644 --- a/src/faebryk/library/Electrical.py +++ b/src/faebryk/library/Electrical.py @@ -3,11 +3,10 @@ import faebryk.library._F as F from faebryk.core.moduleinterface import ModuleInterface -from faebryk.libs.units import Quantity class Electrical(ModuleInterface): - potential: F.TBD[Quantity] + potential: F.TBD def get_net(self): from faebryk.library.Net import Net diff --git a/src/faebryk/library/Filter.py b/src/faebryk/library/Filter.py index b3ddec65..6fc33304 100644 --- a/src/faebryk/library/Filter.py +++ b/src/faebryk/library/Filter.py @@ -15,9 +15,9 @@ class Response(Enum): BANDSTOP = auto() OTHER = auto() - cutoff_frequency: F.TBD[float] - order: F.TBD[int] - response: F.TBD[Response] + cutoff_frequency: F.TBD + order: F.TBD + response: F.TBD in_: F.Signal out: F.Signal diff --git a/src/faebryk/library/Fuse.py b/src/faebryk/library/Fuse.py index 8a957956..2a5876e7 100644 --- a/src/faebryk/library/Fuse.py +++ b/src/faebryk/library/Fuse.py @@ -7,7 +7,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) @@ -22,9 +21,9 @@ class ResponseType(Enum): FAST = auto() unnamed = L.list_field(2, F.Electrical) - fuse_type: F.TBD[FuseType] - response_type: F.TBD[ResponseType] - trip_current: F.TBD[Quantity] + fuse_type: F.TBD + response_type: F.TBD + trip_current: F.TBD attach_to_footprint: F.can_attach_to_footprint_symmetrically diff --git a/src/faebryk/library/GDT.py b/src/faebryk/library/GDT.py index 46149b66..41b1766c 100644 --- a/src/faebryk/library/GDT.py +++ b/src/faebryk/library/GDT.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) @@ -16,8 +15,8 @@ class GDT(Module): tube_1: F.Electrical tube_2: F.Electrical - dc_breakdown_voltage: F.TBD[Quantity] - impulse_discharge_current: F.TBD[Quantity] + dc_breakdown_voltage: F.TBD + impulse_discharge_current: F.TBD @L.rt_field def can_bridge(self): diff --git a/src/faebryk/library/GenericBusProtection.py b/src/faebryk/library/GenericBusProtection.py index ba7865bb..00165803 100644 --- a/src/faebryk/library/GenericBusProtection.py +++ b/src/faebryk/library/GenericBusProtection.py @@ -9,7 +9,9 @@ from faebryk.libs.library import L -class GenericBusProtection[T: ModuleInterface](Module): +class GenericBusProtection(Module): + type T = ModuleInterface + @L.rt_field def bus_unprotected(self): return self.bus_factory() @@ -23,7 +25,9 @@ def __init__(self, bus_factory: Callable[[], T]) -> None: self.bus_factory = bus_factory def __preinit__(self): - def get_mifs[U: ModuleInterface](bus: T, mif_type: type[U]) -> set[U]: + def get_mifs[U: ModuleInterface]( + bus: "GenericBusProtection.T", mif_type: type[U] + ) -> set[U]: return bus.get_children(direct_only=True, types=mif_type) raw = list( diff --git a/src/faebryk/library/HLK_LD2410B_P.py b/src/faebryk/library/HLK_LD2410B_P.py index 88e7c620..7ddea11e 100644 --- a/src/faebryk/library/HLK_LD2410B_P.py +++ b/src/faebryk/library/HLK_LD2410B_P.py @@ -4,12 +4,12 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P class HLK_LD2410B_P(Module): class _ld2410b_esphome_config(F.has_esphome_config.impl()): - throttle: F.TBD[Quantity] + throttle: F.TBD def get_config(self) -> dict: val = self.throttle.get_most_narrow() @@ -20,7 +20,7 @@ def get_config(self) -> dict: uart_candidates = { mif - for mif in obj.uart.get_direct_connections() + for mif in obj.uart.get_connected() if mif.has_trait(F.is_esphome_bus) and mif.has_trait(F.has_esphome_config) } diff --git a/src/faebryk/library/Header.py b/src/faebryk/library/Header.py index ee384c98..642c26dc 100644 --- a/src/faebryk/library/Header.py +++ b/src/faebryk/library/Header.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity from faebryk.libs.util import times @@ -36,16 +35,16 @@ def __preinit__(self): self.pin_count_horizonal.merge(self._horizontal_pin_count) self.pin_count_vertical.merge(self._vertical_pin_count) - pin_pitch: F.TBD[Quantity] - mating_pin_lenght: F.TBD[Quantity] - conection_pin_lenght: F.TBD[Quantity] - spacer_height: F.TBD[Quantity] - pin_type: F.TBD[PinType] - pad_type: F.TBD[PadType] - angle: F.TBD[Angle] + pin_pitch: F.TBD + mating_pin_lenght: F.TBD + conection_pin_lenght: F.TBD + spacer_height: F.TBD + pin_type: F.TBD + pad_type: F.TBD + angle: F.TBD - pin_count_horizonal: F.TBD[int] - pin_count_vertical: F.TBD[int] + pin_count_horizonal: F.TBD + pin_count_vertical: F.TBD @L.rt_field def contact(self): diff --git a/src/faebryk/library/I2C.py b/src/faebryk/library/I2C.py index 7db966d9..e96c1ef2 100644 --- a/src/faebryk/library/I2C.py +++ b/src/faebryk/library/I2C.py @@ -6,7 +6,7 @@ import faebryk.library._F as F from faebryk.core.moduleinterface import ModuleInterface from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P logger = logging.getLogger(__name__) @@ -15,7 +15,7 @@ class I2C(ModuleInterface): scl: F.ElectricLogic sda: F.ElectricLogic - frequency: F.TBD[Quantity] + frequency: F.TBD @L.rt_field def single_electric_reference(self): diff --git a/src/faebryk/library/Inductor.py b/src/faebryk/library/Inductor.py index 9120dc78..6b0b28cf 100644 --- a/src/faebryk/library/Inductor.py +++ b/src/faebryk/library/Inductor.py @@ -5,17 +5,16 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity from faebryk.libs.util import join_if_non_empty class Inductor(Module): unnamed = L.list_field(2, F.Electrical) - inductance: F.TBD[Quantity] - self_resonant_frequency: F.TBD[Quantity] - rated_current: F.TBD[Quantity] - dc_resistance: F.TBD[Quantity] + inductance: F.TBD + self_resonant_frequency: F.TBD + rated_current: F.TBD + dc_resistance: F.TBD @L.rt_field def can_bridge(self): diff --git a/src/faebryk/library/LDO.py b/src/faebryk/library/LDO.py index aa38a06a..e397c857 100644 --- a/src/faebryk/library/LDO.py +++ b/src/faebryk/library/LDO.py @@ -7,7 +7,7 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P from faebryk.libs.util import assert_once, join_if_non_empty @@ -24,14 +24,14 @@ class OutputPolarity(Enum): POSITIVE = auto() NEGATIVE = auto() - max_input_voltage: F.TBD[Quantity] - output_voltage: F.TBD[Quantity] - output_polarity: F.TBD[OutputPolarity] - output_type: F.TBD[OutputType] - output_current: F.TBD[Quantity] - psrr: F.TBD[Quantity] - dropout_voltage: F.TBD[Quantity] - quiescent_current: F.TBD[Quantity] + max_input_voltage: F.TBD + output_voltage: F.TBD + output_polarity: F.TBD + output_type: F.TBD + output_current: F.TBD + psrr: F.TBD + dropout_voltage: F.TBD + quiescent_current: F.TBD enable: F.ElectricLogic power_in: F.ElectricPower diff --git a/src/faebryk/library/LED.py b/src/faebryk/library/LED.py index 12b9f60b..ab13dcc2 100644 --- a/src/faebryk/library/LED.py +++ b/src/faebryk/library/LED.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.parameter import Parameter -from faebryk.libs.units import Quantity class LED(F.Diode): @@ -40,9 +39,9 @@ class Color(Enum): ULTRA_VIOLET = auto() INFRA_RED = auto() - brightness: F.TBD[Quantity] - max_brightness: F.TBD[Quantity] - color: F.TBD[Color] + brightness: F.TBD + max_brightness: F.TBD + color: F.TBD def __preinit__(self): self.current.merge(self.brightness / self.max_brightness * self.max_current) @@ -51,12 +50,12 @@ def __preinit__(self): # F.Range(0 * P.millicandela, self.max_brightness) # ) - def set_intensity(self, intensity: Parameter[Quantity]) -> None: + def set_intensity(self, intensity: Parameter) -> None: self.brightness.merge(intensity * self.max_brightness) def connect_via_current_limiting_resistor( self, - input_voltage: Parameter[Quantity], + input_voltage: Parameter, resistor: F.Resistor, target: F.Electrical, low_side: bool, diff --git a/src/faebryk/library/LEDIndicator.py b/src/faebryk/library/LEDIndicator.py index f9710910..01448a3c 100644 --- a/src/faebryk/library/LEDIndicator.py +++ b/src/faebryk/library/LEDIndicator.py @@ -19,6 +19,7 @@ class LEDIndicator(Module): power_switch = L.f_field(F.PowerSwitch)(normally_closed=False) def __init__(self, use_mosfet: bool = True): + super().__init__() self._use_mosfet = use_mosfet def __preinit__(self): diff --git a/src/faebryk/library/Logic.py b/src/faebryk/library/Logic.py index 8edc33ba..f15595b1 100644 --- a/src/faebryk/library/Logic.py +++ b/src/faebryk/library/Logic.py @@ -2,11 +2,12 @@ # SPDX-License-Identifier: MIT import faebryk.library._F as F -from faebryk.libs.library import L +from faebryk.libs.library import L # noqa: F401 class Logic(F.Signal): - state = L.f_field(F.Range)(False, True) + """ + Acts as protocol, because multi inheritance is not supported + """ - def set(self, on: bool): - self.state.merge(on) + def set(self, on: bool): ... diff --git a/src/faebryk/library/Logic74xx.py b/src/faebryk/library/Logic74xx.py index 6cf3250f..da09e054 100644 --- a/src/faebryk/library/Logic74xx.py +++ b/src/faebryk/library/Logic74xx.py @@ -50,7 +50,7 @@ class Family(Enum): CD4000 = auto() power: F.ElectricPower - logic_family: F.TBD[Family] + logic_family: F.TBD designator = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.U diff --git a/src/faebryk/library/LogicGate.py b/src/faebryk/library/LogicGate.py index d9917341..11f01157 100644 --- a/src/faebryk/library/LogicGate.py +++ b/src/faebryk/library/LogicGate.py @@ -49,8 +49,8 @@ def xor(self, *ins: F.Logic): def __init__( self, - input_cnt: F.Constant[int], - output_cnt: F.Constant[int], + input_cnt: F.Constant, + output_cnt: F.Constant, *functions: TraitImpl, ) -> None: super().__init__() diff --git a/src/faebryk/library/LogicGates.py b/src/faebryk/library/LogicGates.py index 16c0ef08..53e18797 100644 --- a/src/faebryk/library/LogicGates.py +++ b/src/faebryk/library/LogicGates.py @@ -7,19 +7,19 @@ class LogicGates(Namespace): class OR(F.LogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_or_gate()) class NOR(F.LogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_nor_gate()) class NAND(F.LogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__( input_cnt, F.Constant(1), F.LogicGate.can_logic_nand_gate() ) class XOR(F.LogicGate): - def __init__(self, input_cnt: F.Constant[int]): + def __init__(self, input_cnt: F.Constant): super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_xor_gate()) diff --git a/src/faebryk/library/MOSFET.py b/src/faebryk/library/MOSFET.py index 250c54b4..d430b7e3 100644 --- a/src/faebryk/library/MOSFET.py +++ b/src/faebryk/library/MOSFET.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class MOSFET(Module): @@ -18,12 +17,12 @@ class SaturationType(Enum): ENHANCEMENT = auto() DEPLETION = auto() - channel_type: F.TBD[ChannelType] - saturation_type: F.TBD[SaturationType] - gate_source_threshold_voltage: F.TBD[Quantity] - max_drain_source_voltage: F.TBD[Quantity] - max_continuous_drain_current: F.TBD[Quantity] - on_resistance: F.TBD[Quantity] + channel_type: F.TBD + saturation_type: F.TBD + gate_source_threshold_voltage: F.TBD + max_drain_source_voltage: F.TBD + max_continuous_drain_current: F.TBD + on_resistance: F.TBD source: F.Electrical gate: F.Electrical diff --git a/src/faebryk/library/MultiCapacitor.py b/src/faebryk/library/MultiCapacitor.py index 5c5cdbf8..e2aaba17 100644 --- a/src/faebryk/library/MultiCapacitor.py +++ b/src/faebryk/library/MultiCapacitor.py @@ -6,7 +6,6 @@ import faebryk.library._F as F # noqa: F401 from faebryk.core.parameter import Parameter from faebryk.libs.library import L # noqa: F401 -from faebryk.libs.units import Quantity from faebryk.libs.util import times # noqa: F401 logger = logging.getLogger(__name__) @@ -26,6 +25,7 @@ class MultiCapacitor(F.Capacitor): # ---------------------------------------- def __init__(self, count: int): + super().__init__() self._count = count @L.rt_field @@ -45,11 +45,11 @@ def __preinit__(self): # ------------------------------------ self.capacitance.merge(sum(c.capacitance for c in self.capacitors)) - def set_equal_capacitance(self, capacitance: Parameter[Quantity]): + def set_equal_capacitance(self, capacitance: Parameter): op = capacitance / self._count self.set_equal_capacitance_each(op) - def set_equal_capacitance_each(self, capacitance: Parameter[Quantity]): + def set_equal_capacitance_each(self, capacitance: Parameter): for c in self.capacitors: c.capacitance.merge(capacitance) diff --git a/src/faebryk/library/OLED_Module.py b/src/faebryk/library/OLED_Module.py index b7590a4a..f460b6b9 100644 --- a/src/faebryk/library/OLED_Module.py +++ b/src/faebryk/library/OLED_Module.py @@ -38,9 +38,9 @@ class DisplayController(Enum): power: F.ElectricPower i2c: F.I2C - display_resolution: F.TBD[DisplayResolution] - display_controller: F.TBD[DisplayController] - display_size: F.TBD[DisplaySize] + display_resolution: F.TBD + display_controller: F.TBD + display_size: F.TBD def __preinit__(self): self.power.voltage.merge(F.Range(3.0 * P.V, 5 * P.V)) diff --git a/src/faebryk/library/OpAmp.py b/src/faebryk/library/OpAmp.py index 8b9bb054..e05e1530 100644 --- a/src/faebryk/library/OpAmp.py +++ b/src/faebryk/library/OpAmp.py @@ -4,17 +4,16 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class OpAmp(Module): - bandwidth: F.TBD[Quantity] - common_mode_rejection_ratio: F.TBD[Quantity] - input_bias_current: F.TBD[Quantity] - input_offset_voltage: F.TBD[Quantity] - gain_bandwidth_product: F.TBD[Quantity] - output_current: F.TBD[Quantity] - slew_rate: F.TBD[Quantity] + bandwidth: F.TBD + common_mode_rejection_ratio: F.TBD + input_bias_current: F.TBD + input_offset_voltage: F.TBD + gain_bandwidth_product: F.TBD + output_current: F.TBD + slew_rate: F.TBD power: F.ElectricPower inverting_input: F.Electrical diff --git a/src/faebryk/library/Operation.py b/src/faebryk/library/Operation.py index 8bb9829e..5cbc8194 100644 --- a/src/faebryk/library/Operation.py +++ b/src/faebryk/library/Operation.py @@ -11,15 +11,15 @@ logger = logging.getLogger(__name__) -class Operation[PV](Parameter[PV]): +class Operation(Parameter): class OperationNotExecutable(Exception): ... - type LIT_OR_PARAM = Parameter[PV].LIT_OR_PARAM + type LIT_OR_PARAM = Parameter.LIT_OR_PARAM def __init__( self, operands: typing.Iterable[LIT_OR_PARAM], - operation: typing.Callable[..., Parameter[PV]], + operation: typing.Callable[..., Parameter], ) -> None: super().__init__() self.operands = tuple(self.from_literal(o) for o in operands) @@ -65,7 +65,7 @@ def _execute(self): logger.debug(f"{operands=} resolved to {out}") return out - def try_compress(self) -> Parameter[PV]: + def try_compress(self) -> Parameter: try: return self._execute() except Operation.OperationNotExecutable: diff --git a/src/faebryk/library/Pad.py b/src/faebryk/library/Pad.py index 63636e60..a9c7d14d 100644 --- a/src/faebryk/library/Pad.py +++ b/src/faebryk/library/Pad.py @@ -38,7 +38,7 @@ def find_pad_for_intf_with_parent_that_has_footprint( pads = [ pad for pad in footprint.get_children(direct_only=True, types=Pad) - if pad.net.is_connected_to(intf) is not None + if pad.net.is_connected_to(intf) ] return pads diff --git a/src/faebryk/library/Potentiometer.py b/src/faebryk/library/Potentiometer.py index 587859d6..606ae66b 100644 --- a/src/faebryk/library/Potentiometer.py +++ b/src/faebryk/library/Potentiometer.py @@ -4,13 +4,12 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class Potentiometer(Module): resistors_ifs = L.list_field(2, F.Electrical) wiper: F.Electrical - total_resistance: F.TBD[Quantity] + total_resistance: F.TBD resistors = L.list_field(2, F.Resistor) def __preinit__(self): diff --git a/src/faebryk/library/PoweredLED.py b/src/faebryk/library/PoweredLED.py index d1dffb11..b0eaf5ab 100644 --- a/src/faebryk/library/PoweredLED.py +++ b/src/faebryk/library/PoweredLED.py @@ -12,6 +12,7 @@ class PoweredLED(Module): led: F.LED def __init__(self, low_side_resistor: bool = True): + super().__init__() self._low_side_resistor = low_side_resistor def __preinit__(self): diff --git a/src/faebryk/library/RJ45_Receptacle.py b/src/faebryk/library/RJ45_Receptacle.py index 84c3f74d..6d4ea630 100644 --- a/src/faebryk/library/RJ45_Receptacle.py +++ b/src/faebryk/library/RJ45_Receptacle.py @@ -21,4 +21,4 @@ class Mounting(Enum): designator_prefix = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.J ) - mounting: F.TBD[Mounting] + mounting: F.TBD diff --git a/src/faebryk/library/RP2040Pinmux.py b/src/faebryk/library/RP2040Pinmux.py index 0675343d..b9d0167c 100644 --- a/src/faebryk/library/RP2040Pinmux.py +++ b/src/faebryk/library/RP2040Pinmux.py @@ -27,6 +27,7 @@ class RP2040Pinmux(F.Pinmux): # traits # ---------------------------------------- def __init__(self, mcu: "RP2040"): + super().__init__() self._mcu = mcu def __preinit__(self): diff --git a/src/faebryk/library/Range.py b/src/faebryk/library/Range.py index c217873b..a7939911 100644 --- a/src/faebryk/library/Range.py +++ b/src/faebryk/library/Range.py @@ -18,46 +18,45 @@ def __lt__(self, __value) -> bool: ... def __ge__(self, __value) -> bool: ... -class Range[PV: _SupportsRangeOps](Parameter[PV], Parameter[PV].SupportsSetOps): - type LIT_OR_PARAM = Parameter[PV].LIT_OR_PARAM +class Range(Parameter): + type PV = Parameter.PV + type LIT_OR_PARAM = Parameter.LIT_OR_PARAM class MinMaxError(Exception): ... - def __init__(self, *bounds: PV | Parameter[PV]) -> None: + def __init__(self, *bounds: PV | Parameter) -> None: super().__init__() - self._bounds: list[Parameter[PV]] = [ - Parameter[PV].from_literal(b) for b in bounds - ] + self._bounds: list[Parameter] = [Parameter.from_literal(b) for b in bounds] - def _get_narrowed_bounds(self) -> list[Parameter[PV]]: + def _get_narrowed_bounds(self) -> list[Parameter]: return list({b.get_most_narrow() for b in self._bounds}) @property - def min(self) -> Parameter[PV]: + def min(self) -> Parameter: try: return min(self._get_narrowed_bounds()) except (TypeError, ValueError): raise self.MinMaxError() @property - def max(self) -> Parameter[PV]: + def max(self) -> Parameter: try: return max(self._get_narrowed_bounds()) except (TypeError, ValueError): raise self.MinMaxError() @property - def bounds(self) -> list[Parameter[PV]]: + def bounds(self) -> list[Parameter]: try: return [self.min, self.max] except self.MinMaxError: return self._get_narrowed_bounds() - def as_tuple(self) -> tuple[Parameter[PV], Parameter[PV]]: + def as_tuple(self) -> tuple[Parameter, Parameter]: return (self.min, self.max) - def as_center_tuple(self, relative=False) -> tuple[Parameter[PV], Parameter[PV]]: + def as_center_tuple(self, relative=False) -> tuple[Parameter, Parameter]: center = (self.min + self.max) / 2 delta = (self.max - self.min) / 2 if relative: @@ -65,15 +64,15 @@ def as_center_tuple(self, relative=False) -> tuple[Parameter[PV], Parameter[PV]] return center, delta @classmethod - def from_center(cls, center: LIT_OR_PARAM, delta: LIT_OR_PARAM) -> "Range[PV]": + def from_center(cls, center: LIT_OR_PARAM, delta: LIT_OR_PARAM) -> "Range": return cls(center - delta, center + delta) @classmethod - def from_center_rel(cls, center: PV, factor: PV) -> "Range[PV]": + def from_center_rel(cls, center: PV, factor: PV) -> "Range": return cls.from_center(center, center * factor) @classmethod - def _with_bound(cls, bound: LIT_OR_PARAM, other: float) -> "Range[PV]": + def _with_bound(cls, bound: LIT_OR_PARAM, other: float) -> "Range": try: other_with_unit = Parameter.with_same_unit(bound, other) except NotImplementedError: @@ -82,11 +81,11 @@ def _with_bound(cls, bound: LIT_OR_PARAM, other: float) -> "Range[PV]": return cls(bound, other_with_unit) @classmethod - def lower_bound(cls, lower: LIT_OR_PARAM) -> "Range[PV]": + def lower_bound(cls, lower: LIT_OR_PARAM) -> "Range": return cls._with_bound(lower, inf) @classmethod - def upper_bound(cls, upper: LIT_OR_PARAM) -> "Range[PV]": + def upper_bound(cls, upper: LIT_OR_PARAM) -> "Range": return cls._with_bound(upper, 0) def __str__(self) -> str: @@ -125,9 +124,9 @@ def __format__(self, format_spec): def __copy__(self) -> Self: return type(self)(*self._bounds) - def try_compress(self) -> Parameter[PV]: + def try_compress(self) -> Parameter: # compress into constant if possible - if len(set(map(id, self.bounds))) == 1: + if len(set(map(id, self._get_narrowed_bounds()))) == 1: return Parameter.from_literal(self.bounds[0]) return super().try_compress() diff --git a/src/faebryk/library/Relay.py b/src/faebryk/library/Relay.py index 4b9a20ac..6a02c9f7 100644 --- a/src/faebryk/library/Relay.py +++ b/src/faebryk/library/Relay.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) @@ -21,12 +20,12 @@ class Relay(Module): switch_b_nc: F.Electrical coil_power: F.ElectricPower - coil_rated_voltage: F.TBD[Quantity] - coil_rated_current: F.TBD[Quantity] - coil_resistance: F.TBD[Quantity] - contact_max_switching_voltage: F.TBD[Quantity] - contact_rated_switching_current: F.TBD[Quantity] - contact_max_switchng_current: F.TBD[Quantity] + coil_rated_voltage: F.TBD + coil_rated_current: F.TBD + coil_resistance: F.TBD + contact_max_switching_voltage: F.TBD + contact_rated_switching_current: F.TBD + contact_max_switchng_current: F.TBD designator_prefix = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.K diff --git a/src/faebryk/library/Resistor.py b/src/faebryk/library/Resistor.py index 31f00cf3..1a8515fb 100644 --- a/src/faebryk/library/Resistor.py +++ b/src/faebryk/library/Resistor.py @@ -8,16 +8,16 @@ from faebryk.core.parameter import Parameter from faebryk.libs.library import L from faebryk.libs.picker.picker import PickError, has_part_picked_remove -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P from faebryk.libs.util import join_if_non_empty class Resistor(Module): unnamed = L.list_field(2, F.Electrical) - resistance: F.TBD[Quantity] - rated_power: F.TBD[Quantity] - rated_voltage: F.TBD[Quantity] + resistance: F.TBD + rated_power: F.TBD + rated_voltage: F.TBD attach_to_footprint: F.can_attach_to_footprint_symmetrically designator_prefix = L.f_field(F.has_designator_prefix_defined)( diff --git a/src/faebryk/library/ResistorVoltageDivider.py b/src/faebryk/library/ResistorVoltageDivider.py index 8ba17374..01f2febc 100644 --- a/src/faebryk/library/ResistorVoltageDivider.py +++ b/src/faebryk/library/ResistorVoltageDivider.py @@ -15,8 +15,8 @@ class ResistorVoltageDivider(Module): node = L.list_field(3, F.Electrical) - ratio: F.TBD[float] - max_current: F.TBD[float] + ratio: F.TBD + max_current: F.TBD @L.rt_field def can_bridge(self): diff --git a/src/faebryk/library/Resistor_Voltage_Divider.py b/src/faebryk/library/Resistor_Voltage_Divider.py index cc7dd64a..5b286465 100644 --- a/src/faebryk/library/Resistor_Voltage_Divider.py +++ b/src/faebryk/library/Resistor_Voltage_Divider.py @@ -6,7 +6,6 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) @@ -15,8 +14,8 @@ class Resistor_Voltage_Divider(Module): resistor = L.list_field(2, F.Resistor) node = L.list_field(3, F.Electrical) - ratio: F.TBD[Quantity] - max_current: F.TBD[Quantity] + ratio: F.TBD + max_current: F.TBD def __preinit__(self): self.node[0].connect_via(self.resistor[0], self.node[1]) diff --git a/src/faebryk/library/SCD40.py b/src/faebryk/library/SCD40.py index 17f2c5c7..7a8f6944 100644 --- a/src/faebryk/library/SCD40.py +++ b/src/faebryk/library/SCD40.py @@ -5,7 +5,7 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P class SCD40(Module): @@ -14,7 +14,7 @@ class SCD40(Module): """ class _scd4x_esphome_config(F.has_esphome_config.impl()): - update_interval: F.TBD[Quantity] + update_interval: F.TBD def get_config(self) -> dict: val = self.update_interval.get_most_narrow() diff --git a/src/faebryk/library/SPIFlash.py b/src/faebryk/library/SPIFlash.py index 9e9880c8..bd04106c 100644 --- a/src/faebryk/library/SPIFlash.py +++ b/src/faebryk/library/SPIFlash.py @@ -4,14 +4,13 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import Quantity class SPIFlash(Module): power: F.ElectricPower qspi = L.f_field(F.MultiSPI)(4) - memory_size: F.TBD[Quantity] + memory_size: F.TBD designator_prefix = L.f_field(F.has_designator_prefix_defined)( F.has_designator_prefix.Prefix.U ) diff --git a/src/faebryk/library/Set.py b/src/faebryk/library/Set.py index 112b5ece..97e6d698 100644 --- a/src/faebryk/library/Set.py +++ b/src/faebryk/library/Set.py @@ -8,10 +8,10 @@ from faebryk.libs.units import UnitsContainer -class Set[PV](Parameter[PV], Parameter[PV].SupportsSetOps): - type LIT_OR_PARAM = Parameter[PV].LIT_OR_PARAM +class Set(Parameter): + type LIT_OR_PARAM = Parameter.LIT_OR_PARAM - def __init__(self, params: Iterable[Parameter[LIT_OR_PARAM]]) -> None: + def __init__(self, params: Iterable[Parameter]) -> None: super().__init__() # make primitves to constants @@ -20,7 +20,7 @@ def __init__(self, params: Iterable[Parameter[LIT_OR_PARAM]]) -> None: ) @staticmethod - def _flatten(params: set[Parameter[PV]]) -> set[Parameter[PV]]: + def _flatten(params: set[Parameter]) -> set[Parameter]: param_set = set( p for p in params if not isinstance(p, Set) and isinstance(p, Parameter) ) @@ -28,11 +28,11 @@ def _flatten(params: set[Parameter[PV]]) -> set[Parameter[PV]]: return param_set | set_set - def flat(self) -> set[Parameter[PV]]: + def flat(self) -> set[Parameter]: return Set._flatten(self._params) @property - def params(self) -> set[Parameter[PV]]: + def params(self) -> set[Parameter]: return self.flat() def __str__(self) -> str: @@ -67,7 +67,7 @@ def copy(self) -> Self: return type(self)(self.params) @_resolved - def __contains__(self, other: Parameter[PV]) -> bool: + def __contains__(self, other: Parameter) -> bool: def nested_in(p): if other == p: return True @@ -77,7 +77,7 @@ def nested_in(p): return any(nested_in(p) for p in self.params) - def try_compress(self) -> Parameter[PV]: + def try_compress(self) -> Parameter: # compress into constant if possible if len(set(map(id, self.params))) == 1: return Parameter.from_literal(next(iter(self.params))) diff --git a/src/faebryk/library/SignalElectrical.py b/src/faebryk/library/SignalElectrical.py index 780fa2bd..fa28488b 100644 --- a/src/faebryk/library/SignalElectrical.py +++ b/src/faebryk/library/SignalElectrical.py @@ -4,20 +4,24 @@ from typing import Iterable import faebryk.library._F as F -from faebryk.core.graphinterface import GraphInterface -from faebryk.core.link import LinkFilteredException, _TLinkDirectShallow +from faebryk.core.link import LinkDirectConditional, LinkDirectConditionalFilterResult from faebryk.core.module import Module from faebryk.core.moduleinterface import ModuleInterface -from faebryk.core.node import Node +from faebryk.core.node import CNode, Node from faebryk.libs.library import L class SignalElectrical(F.Signal): - class LinkIsolatedReference(_TLinkDirectShallow): - def __init__(self, interfaces: list[GraphInterface]) -> None: - if any(isinstance(gif.node, F.ElectricPower) for gif in interfaces): - raise LinkFilteredException("All nodes are ElectricPower") - super().__init__(interfaces) + class LinkIsolatedReference(LinkDirectConditional): + def test(self, node: CNode): + return not isinstance(node, F.ElectricPower) + + def __init__(self) -> None: + super().__init__( + lambda src, dst: LinkDirectConditionalFilterResult.FILTER_PASS + if self.test(dst.node) + else LinkDirectConditionalFilterResult.FILTER_FAIL_UNRECOVERABLE + ) # ---------------------------------------- # modules, interfaces, parameters diff --git a/src/faebryk/library/Switch.py b/src/faebryk/library/Switch.py index 129477b1..98a6b4ad 100644 --- a/src/faebryk/library/Switch.py +++ b/src/faebryk/library/Switch.py @@ -10,7 +10,9 @@ from faebryk.libs.util import once -class _TSwitch[T: ModuleInterface](Module): +class _TSwitch(Module): + T = ModuleInterface + def __init__(self, t: type[T]): super().__init__() self.t = t @@ -22,7 +24,7 @@ def is_instance(obj: Module, t: type[T]) -> bool: @once # This means we can use a normal "isinstance" to test for them def Switch[T: ModuleInterface](interface_type: type[T]): - class _Switch(_TSwitch[interface_type]): + class _Switch(_TSwitch): def __init__(self) -> None: super().__init__(interface_type) diff --git a/src/faebryk/library/TBD.py b/src/faebryk/library/TBD.py index 0f52ec82..33fbdff4 100644 --- a/src/faebryk/library/TBD.py +++ b/src/faebryk/library/TBD.py @@ -3,11 +3,12 @@ from textwrap import indent -from faebryk.core.parameter import Parameter +from faebryk.core.parameter import Parameter, _resolved from faebryk.libs.units import UnitsContainer -class TBD[PV](Parameter[PV]): +class TBD(Parameter): + @_resolved def __eq__(self, __value: object) -> bool: if isinstance(__value, TBD): return True diff --git a/src/faebryk/library/TVS.py b/src/faebryk/library/TVS.py index 205643a5..987e4548 100644 --- a/src/faebryk/library/TVS.py +++ b/src/faebryk/library/TVS.py @@ -4,10 +4,9 @@ import logging import faebryk.library._F as F -from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) class TVS(F.Diode): - reverse_breakdown_voltage: F.TBD[Quantity] + reverse_breakdown_voltage: F.TBD diff --git a/src/faebryk/library/UART_Base.py b/src/faebryk/library/UART_Base.py index 025fe017..e6477593 100644 --- a/src/faebryk/library/UART_Base.py +++ b/src/faebryk/library/UART_Base.py @@ -4,14 +4,13 @@ import faebryk.library._F as F from faebryk.core.moduleinterface import ModuleInterface from faebryk.libs.library import L -from faebryk.libs.units import Quantity class UART_Base(ModuleInterface): rx: F.ElectricLogic tx: F.ElectricLogic - baud: F.TBD[Quantity] + baud: F.TBD @L.rt_field def single_electric_reference(self): diff --git a/src/faebryk/library/UART_RS485.py b/src/faebryk/library/UART_RS485.py index 59ea95b2..aa6b19ba 100644 --- a/src/faebryk/library/UART_RS485.py +++ b/src/faebryk/library/UART_RS485.py @@ -6,7 +6,7 @@ import faebryk.library._F as F from faebryk.core.module import Module from faebryk.libs.library import L -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P logger = logging.getLogger(__name__) @@ -18,8 +18,8 @@ class UART_RS485(Module): read_enable: F.ElectricLogic write_enable: F.ElectricLogic - max_data_rate: F.TBD[Quantity] - gpio_voltage: F.TBD[Quantity] + max_data_rate: F.TBD + gpio_voltage: F.TBD def __preinit__(self): self.max_data_rate.merge(self.uart.baud) diff --git a/src/faebryk/library/USB2_0_ESD_Protection.py b/src/faebryk/library/USB2_0_ESD_Protection.py index f2adcd75..8ef60fe6 100644 --- a/src/faebryk/library/USB2_0_ESD_Protection.py +++ b/src/faebryk/library/USB2_0_ESD_Protection.py @@ -23,8 +23,8 @@ class USB2_0_ESD_Protection(Module): # ---------------------------------------- usb = L.list_field(2, F.USB2_0) - vbus_esd_protection: F.TBD[bool] - data_esd_protection: F.TBD[bool] + vbus_esd_protection: F.TBD + data_esd_protection: F.TBD # ---------------------------------------- # traits diff --git a/src/faebryk/library/_F.py b/src/faebryk/library/_F.py index c6828cfe..308fb51c 100644 --- a/src/faebryk/library/_F.py +++ b/src/faebryk/library/_F.py @@ -24,6 +24,7 @@ from faebryk.library.is_esphome_bus import is_esphome_bus from faebryk.library.has_construction_dependency import has_construction_dependency from faebryk.library.has_single_electric_reference import has_single_electric_reference +from faebryk.library.can_specialize_defined import can_specialize_defined from faebryk.library.Power import Power from faebryk.library.Signal import Signal from faebryk.library.has_footprint import has_footprint @@ -33,6 +34,7 @@ from faebryk.library.has_linked_pad import has_linked_pad from faebryk.library.has_reference import has_reference from faebryk.library.can_bridge import can_bridge +from faebryk.library.can_specialize import can_specialize from faebryk.library.has_descriptive_properties import has_descriptive_properties from faebryk.library.has_datasheet import has_datasheet from faebryk.library.has_designator import has_designator diff --git a/src/faebryk/library/can_be_decoupled_rails.py b/src/faebryk/library/can_be_decoupled_rails.py index 32a88df6..121a9a94 100644 --- a/src/faebryk/library/can_be_decoupled_rails.py +++ b/src/faebryk/library/can_be_decoupled_rails.py @@ -10,6 +10,7 @@ class can_be_decoupled_rails(F.can_be_decoupled.impl()): def __init__(self, *rails: F.ElectricPower): + super().__init__() assert rails self._rails = rails diff --git a/src/faebryk/library/can_specialize.py b/src/faebryk/library/can_specialize.py new file mode 100644 index 00000000..cc889847 --- /dev/null +++ b/src/faebryk/library/can_specialize.py @@ -0,0 +1,30 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +import logging +from abc import abstractmethod +from typing import TYPE_CHECKING, Iterable + +from faebryk.core.trait import Trait + +if TYPE_CHECKING: + from faebryk.core.module import Module + from faebryk.core.moduleinterface import ModuleInterface + +logger = logging.getLogger(__name__) + + +class can_specialize(Trait): + """ + Marks that a module can specialize other modules next to its bases. + """ + + @abstractmethod + def get_specializable_types( + self, + ) -> Iterable[type["Module"] | type["ModuleInterface"]]: + """ + Returns a list of types that can be specialized by this module (in addition to + its own type and the types of its bases). + """ + pass diff --git a/src/faebryk/library/can_specialize_defined.py b/src/faebryk/library/can_specialize_defined.py new file mode 100644 index 00000000..70952bd5 --- /dev/null +++ b/src/faebryk/library/can_specialize_defined.py @@ -0,0 +1,24 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +import logging +from typing import Iterable, Sequence + +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.library.can_specialize import can_specialize + +logger = logging.getLogger(__name__) + + +class can_specialize_defined(can_specialize.impl()): + def __init__( + self, specializable_types: Sequence[type["Module"] | type["ModuleInterface"]] + ): + super().__init__() + self._specializable_types = specializable_types + + def get_specializable_types( + self, + ) -> Iterable[type["Module"] | type["ModuleInterface"]]: + return self._specializable_types diff --git a/src/faebryk/library/has_descriptive_properties_defined.py b/src/faebryk/library/has_descriptive_properties_defined.py index 3f4ffbfc..c1d3a194 100644 --- a/src/faebryk/library/has_descriptive_properties_defined.py +++ b/src/faebryk/library/has_descriptive_properties_defined.py @@ -23,11 +23,11 @@ def __init__( def get_properties(self) -> dict[str, str]: return self.properties - def handle_duplicate(self, other: TraitImpl, node: Node) -> bool: - if not isinstance(other, has_descriptive_properties_defined): - assert isinstance(other, F.has_descriptive_properties) - self.properties.update(other.get_properties()) - return super().handle_duplicate(other, node) + def handle_duplicate(self, old: TraitImpl, node: Node) -> bool: + if not isinstance(old, has_descriptive_properties_defined): + assert isinstance(old, F.has_descriptive_properties) + self.properties.update(old.get_properties()) + return super().handle_duplicate(old, node) - other.properties.update(self.properties) + old.properties.update(self.properties) return False diff --git a/src/faebryk/library/has_linked_pad_defined.py b/src/faebryk/library/has_linked_pad_defined.py index eddcb68f..0dd8870b 100644 --- a/src/faebryk/library/has_linked_pad_defined.py +++ b/src/faebryk/library/has_linked_pad_defined.py @@ -20,10 +20,10 @@ def __init__(self, pad: "Pad") -> None: def get_pads(self) -> set["Pad"]: return self.pads - def handle_duplicate(self, other: TraitImpl, node: Node) -> bool: - if not isinstance(other, has_linked_pad_defined): - self.pads.update(other.get_pads()) - return super().handle_duplicate(other, node) + def handle_duplicate(self, old: TraitImpl, node: Node) -> bool: + if not isinstance(old, has_linked_pad_defined): + self.pads.update(old.get_pads()) + return super().handle_duplicate(old, node) - other.pads.update(self.pads) + old.pads.update(self.pads) return False diff --git a/src/faebryk/library/has_multi_picker.py b/src/faebryk/library/has_multi_picker.py index fbf934be..49d7db82 100644 --- a/src/faebryk/library/has_multi_picker.py +++ b/src/faebryk/library/has_multi_picker.py @@ -77,10 +77,10 @@ def add_pickers_by_type[T]( ) ) - def handle_duplicate(self, other: TraitImpl, node: Node) -> bool: - if not isinstance(other, has_multi_picker): - return super().handle_duplicate(other, node) + def handle_duplicate(self, old: TraitImpl, node: Node) -> bool: + if not isinstance(old, has_multi_picker): + return super().handle_duplicate(old, node) - other.pickers.extend(self.pickers) - other.pickers.sort(key=lambda x: x[0]) + old.pickers.extend(self.pickers) + old.pickers.sort(key=lambda x: x[0]) return False diff --git a/src/faebryk/library/has_pcb_routing_strategy_manual.py b/src/faebryk/library/has_pcb_routing_strategy_manual.py index 60516e8d..8a16dfce 100644 --- a/src/faebryk/library/has_pcb_routing_strategy_manual.py +++ b/src/faebryk/library/has_pcb_routing_strategy_manual.py @@ -2,11 +2,10 @@ # SPDX-License-Identifier: MIT import logging -from typing import Sequence +from typing import TYPE_CHECKING, Sequence import faebryk.library._F as F from faebryk.core.node import Node -from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer from faebryk.exporters.pcb.routing.util import ( Path, Route, @@ -14,6 +13,9 @@ get_pads_pos_of_mifs, ) +if TYPE_CHECKING: + from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer + logger = logging.getLogger(__name__) @@ -30,7 +32,7 @@ def __init__( self.relative_to = relative_to self.absolute = absolute - def calculate(self, transformer: PCB_Transformer): + def calculate(self, transformer: "PCB_Transformer"): node = self.obj nets = get_internal_nets_of_node(node) diff --git a/src/faebryk/library/has_reference.py b/src/faebryk/library/has_reference.py index 24c8fc11..2cdf58c7 100644 --- a/src/faebryk/library/has_reference.py +++ b/src/faebryk/library/has_reference.py @@ -3,11 +3,11 @@ from faebryk.core.trait import Trait -class has_reference[T: Node](Trait): +class has_reference(Trait): """Trait-attached reference""" - reference: T = Reference() + reference = Reference() - def __init__(self, reference: T): + def __init__(self, reference: Node): super().__init__() self.reference = reference diff --git a/src/faebryk/library/is_esphome_bus.py b/src/faebryk/library/is_esphome_bus.py index d42104a2..48212e80 100644 --- a/src/faebryk/library/is_esphome_bus.py +++ b/src/faebryk/library/is_esphome_bus.py @@ -15,7 +15,7 @@ def get_bus_id(self) -> str: ... @staticmethod def find_connected_bus(bus: ModuleInterface): - connected_mifs = bus.get_direct_connections() + connected_mifs = list(bus.get_connected()) try: return find(connected_mifs, lambda mif: mif.has_trait(is_esphome_bus)) except ValueError: diff --git a/src/faebryk/libs/app/designators.py b/src/faebryk/libs/app/designators.py index ca3f79ad..0ef31e0b 100644 --- a/src/faebryk/libs/app/designators.py +++ b/src/faebryk/libs/app/designators.py @@ -9,7 +9,7 @@ from typing import cast import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions from faebryk.exporters.netlist.netlist import T2Netlist from faebryk.libs.kicad.fileformats import C_kicad_pcb_file from faebryk.libs.util import duplicates, get_key, groupby @@ -22,7 +22,7 @@ def attach_random_designators(graph: Graph): sorts nodes by path and then sequentially assigns designators """ - nodes = {n for n, _ in graph.nodes_with_trait(F.has_footprint)} + nodes = {n for n, _ in GraphFunctions(graph).nodes_with_trait(F.has_footprint)} in_use = { n.get_trait(F.has_designator).get_designator() @@ -73,7 +73,7 @@ def _get_first_hole(used: list[int]): def override_names_with_designators(graph: Graph): - for n, t in graph.nodes_with_trait(F.has_designator): + for n, t in GraphFunctions(graph).nodes_with_trait(F.has_designator): name = t.get_designator() if n.has_trait(F.has_overriden_name): logger.warning( @@ -122,7 +122,7 @@ def replace_faebryk_names_with_designators_in_kicad_pcb(graph: Graph, pcbfile: P pattern = re.compile(r"^(.*)\[[^\]]*\]$") translation = { n.get_full_name(): t.get_name() - for n, t in graph.nodes_with_trait(F.has_overriden_name) + for n, t in GraphFunctions(graph).nodes_with_trait(F.has_overriden_name) } for fp in pcb.kicad_pcb.footprints: diff --git a/src/faebryk/libs/app/erc.py b/src/faebryk/libs/app/erc.py index 78b65aed..058e768b 100644 --- a/src/faebryk/libs/app/erc.py +++ b/src/faebryk/libs/app/erc.py @@ -6,12 +6,12 @@ from typing import Callable, Iterable, Sequence import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph, GraphFunctions from faebryk.core.module import Module from faebryk.core.moduleinterface import ModuleInterface from faebryk.library.Operation import Operation from faebryk.libs.picker.picker import has_part_picked -from faebryk.libs.util import groupby, print_stack +from faebryk.libs.util import groupby logger = logging.getLogger(__name__) @@ -26,14 +26,8 @@ class ERCFaultShort(ERCFault): def __init__(self, faulting_ifs: Sequence[ModuleInterface], *args: object) -> None: link = faulting_ifs[0].is_connected_to(faulting_ifs[1]) assert link - from faebryk.core.core import LINK_TB - - stack = "" - if LINK_TB: - stack = print_stack(link.tb) super().__init__(faulting_ifs, *args) - print(stack) class ERCFaultElectricPowerUndefinedVoltage(ERCFault): @@ -45,6 +39,12 @@ def __init__(self, faulting_EP: list[F.ElectricPower], *args: object) -> None: super().__init__(faulting_EP, msg, *args) +class ERCPowerSourcesShortedError(ERCFault): + """ + Multiple power sources shorted together + """ + + def simple_erc(G: Graph): """Simple ERC check. @@ -65,11 +65,20 @@ def simple_erc(G: Graph): logger.info("Checking graph for ERC violations") # power short and power with undefined voltage - electricpower = G.nodes_of_type(F.ElectricPower) + electricpower = GraphFunctions(G).nodes_of_type(F.ElectricPower) logger.info(f"Checking {len(electricpower)} Power") for ep in electricpower: if ep.lv.is_connected_to(ep.hv): raise ERCFaultShort([ep], "shorted power") + if ep.has_trait(F.Power.is_power_source): + other_sources = [ + other + for other in ep.get_connected() + if isinstance(other, F.ElectricPower) + and other.has_trait(F.Power.is_power_source) + ] + if other_sources: + raise ERCPowerSourcesShortedError([ep] + other_sources) unresolved_voltage = [ ep @@ -81,12 +90,12 @@ def simple_erc(G: Graph): raise ERCFaultElectricPowerUndefinedVoltage(unresolved_voltage) # shorted nets - nets = G.nodes_of_type(F.Net) - logger.info(f"Checking {len(nets)} nets") + nets = GraphFunctions(G).nodes_of_type(F.Net) + logger.info(f"Checking {len(nets)} explicit nets") for net in nets: collisions = { p[0] - for mif in net.part_of.get_direct_connections() + for mif in net.part_of.get_connected() if (p := mif.get_parent()) and isinstance(p[0], F.Net) } @@ -122,7 +131,8 @@ def simple_erc(G: Graph): # checked.add(mif) # if any(mif.is_connected_to(other) for other in (mifs - checked)): # raise ERCFault([mif], "shorted symmetric footprint") - comps = G.nodes_of_types((F.Resistor, F.Capacitor, F.Fuse)) + comps = GraphFunctions(G).nodes_of_types((F.Resistor, F.Capacitor, F.Fuse)) + logger.info(f"Checking {len(comps)} passives") for comp in comps: assert isinstance(comp, (F.Resistor, F.Capacitor, F.Fuse)) # TODO make prettier diff --git a/src/faebryk/libs/app/kicad_netlist.py b/src/faebryk/libs/app/kicad_netlist.py index 8d44c470..48ed9798 100644 --- a/src/faebryk/libs/app/kicad_netlist.py +++ b/src/faebryk/libs/app/kicad_netlist.py @@ -4,7 +4,7 @@ import logging from pathlib import Path -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph from faebryk.exporters.netlist.graph import attach_nets_and_kicad_info from faebryk.exporters.netlist.kicad.netlist_kicad import from_faebryk_t2_netlist from faebryk.exporters.netlist.netlist import make_t2_netlist_from_graph diff --git a/src/faebryk/libs/app/pcb.py b/src/faebryk/libs/app/pcb.py index aca2f587..bec525a8 100644 --- a/src/faebryk/libs/app/pcb.py +++ b/src/faebryk/libs/app/pcb.py @@ -10,7 +10,7 @@ import psutil import faebryk.library._F as F -from faebryk.core.graphinterface import Graph +from faebryk.core.graph import Graph from faebryk.core.module import Module from faebryk.core.node import Node from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer diff --git a/src/faebryk/libs/brightness.py b/src/faebryk/libs/brightness.py index 678e31b4..48804ed2 100644 --- a/src/faebryk/libs/brightness.py +++ b/src/faebryk/libs/brightness.py @@ -6,7 +6,7 @@ import faebryk.library._F as F from faebryk.core.parameter import Parameter -from faebryk.libs.units import P, Quantity +from faebryk.libs.units import P """ luminous intensity in candela (candela) @@ -36,7 +36,7 @@ class _Unit: - def __init__(self, value: Parameter[Quantity]): + def __init__(self, value: Parameter): self._value = value def __repr__(self): @@ -57,20 +57,20 @@ class LuminousFlux(_Unit): def from_intensity( cls, intensity: LuminousIntensity, - solid_angle: Parameter[Quantity], + solid_angle: Parameter, ) -> "LuminousFlux": return LuminousFlux(intensity.value * solid_angle) - def to_intensity(self, solid_angle: Parameter[Quantity]) -> LuminousIntensity: + def to_intensity(self, solid_angle: Parameter) -> LuminousIntensity: return LuminousIntensity(self.value / solid_angle) class Illuminance(_Unit): @classmethod - def from_flux(cls, flux: LuminousFlux, area: Parameter[Quantity]) -> "Illuminance": + def from_flux(cls, flux: LuminousFlux, area: Parameter) -> "Illuminance": return Illuminance(flux.value / area) - def to_luminous_flux(self, area: Parameter[Quantity]) -> LuminousFlux: + def to_luminous_flux(self, area: Parameter) -> LuminousFlux: return LuminousFlux(self.value * area) diff --git a/src/faebryk/libs/e_series.py b/src/faebryk/libs/e_series.py index 728cd2db..fad7be80 100644 --- a/src/faebryk/libs/e_series.py +++ b/src/faebryk/libs/e_series.py @@ -435,8 +435,8 @@ class ParamNotResolvedError(Exception): ... def e_series_intersect[T: float | Quantity]( - value: Parameter[T], e_series: E_SERIES = E_SERIES_VALUES.E_ALL -) -> F.Set[T]: + value: Parameter, e_series: E_SERIES = E_SERIES_VALUES.E_ALL +) -> F.Set: # TODO this got really uglu, need to clean up value = value.get_most_narrow() diff --git a/src/faebryk/libs/examples/pickers.py b/src/faebryk/libs/examples/pickers.py index c3fd5e37..cab62c0c 100644 --- a/src/faebryk/libs/examples/pickers.py +++ b/src/faebryk/libs/examples/pickers.py @@ -265,7 +265,7 @@ def pick_battery(module: F.Battery): ) -def pick_switch(module: "_TSwitch[F.Electrical]"): +def pick_switch(module: "_TSwitch"): module.add(F.can_attach_to_footprint_symmetrically()) pick_module_by_params( module, diff --git a/src/faebryk/libs/picker/jlcpcb/jlcpcb.py b/src/faebryk/libs/picker/jlcpcb/jlcpcb.py index 41fe28fd..c4cab9cb 100644 --- a/src/faebryk/libs/picker/jlcpcb/jlcpcb.py +++ b/src/faebryk/libs/picker/jlcpcb/jlcpcb.py @@ -365,6 +365,7 @@ def attach( ): p.override(value) + attach(module, self.partno) module.add( F.has_descriptive_properties_defined( { @@ -380,7 +381,6 @@ def attach( ) ) - attach(module, self.partno) module.add(has_part_picked_defined(JLCPCB_Part(self.partno))) if logger.isEnabledFor(logging.DEBUG): logger.debug( diff --git a/src/faebryk/libs/picker/jlcpcb/picker_lib.py b/src/faebryk/libs/picker/jlcpcb/picker_lib.py index 25b6a340..77c9ef47 100644 --- a/src/faebryk/libs/picker/jlcpcb/picker_lib.py +++ b/src/faebryk/libs/picker/jlcpcb/picker_lib.py @@ -31,21 +31,21 @@ # - should be classes instead of functions -def str_to_enum[T: Enum](enum: type[T], x: str) -> F.Constant[T]: +def str_to_enum[T: Enum](enum: type[T], x: str) -> F.Constant: name = x.replace(" ", "_").replace("-", "_").upper() if name not in [e.name for e in enum]: raise ValueError(f"Enum translation error: {x}[={name}] not in {enum}") return F.Constant(enum[name]) -def str_to_enum_func[T: Enum](enum: type[T]) -> Callable[[str], F.Constant[T]]: - def f(x: str) -> F.Constant[T]: +def str_to_enum_func[T: Enum](enum: type[T]) -> Callable[[str], F.Constant]: + def f(x: str) -> F.Constant: return str_to_enum(enum, x) return f -def enum_to_str[T: Enum](x: Parameter[T], force: bool = True) -> set[str]: +def enum_to_str(x: Parameter, force: bool = True) -> set[str]: val = x.get_most_narrow() if isinstance(val, F.Constant): val = F.Set([val]) diff --git a/src/faebryk/libs/picker/util.py b/src/faebryk/libs/picker/util.py index 1ca8e3fb..6094dcb0 100644 --- a/src/faebryk/libs/picker/util.py +++ b/src/faebryk/libs/picker/util.py @@ -2,26 +2,30 @@ # SPDX-License-Identifier: MIT import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.core.parameter import Parameter from faebryk.library.Set import Set from faebryk.libs.e_series import ( + E_SERIES, E_SERIES_VALUES, ParamNotResolvedError, e_series_intersect, ) from faebryk.libs.picker.picker import PickError -from faebryk.libs.units import Quantity, to_si_str +from faebryk.libs.units import to_si_str from faebryk.libs.util import cast_assert def generate_si_values( - value: Parameter[Quantity], si_unit: str, e_series: set[float] | None = None + value: Parameter, si_unit: str, e_series: E_SERIES | None = None ): """ Generate a list of permissible SI values for the given parameter from an E-series """ + module = value.get_parent_of_type(Module) + assert module is not None value = value.get_most_narrow() try: @@ -29,7 +33,7 @@ def generate_si_values( [e_series_intersect(value, e_series or E_SERIES_VALUES.E_ALL)] ).params except ParamNotResolvedError as e: - raise PickError(value, f"Could not run e_series_intersect: {e}") from e + raise PickError(f"Could not resolve {value} in {e_series}", module) from e return [ to_si_str(cast_assert(F.Constant, r).value, si_unit) diff --git a/src/faebryk/libs/pycodegen.py b/src/faebryk/libs/pycodegen.py index 2b89a729..4af1ba91 100644 --- a/src/faebryk/libs/pycodegen.py +++ b/src/faebryk/libs/pycodegen.py @@ -110,6 +110,4 @@ def format_and_write(code: str, path: Path): code = black.format_file_contents(code, fast=True, mode=black.FileMode()) path.write_text(code) - print("Ruff----") - subprocess.run(["ruff", "check", "--fix", path], check=True) - print("--------") + subprocess.check_output(["ruff", "check", "--fix", path]) diff --git a/src/faebryk/libs/test/times.py b/src/faebryk/libs/test/times.py index 16b1a78f..0cb9808c 100644 --- a/src/faebryk/libs/test/times.py +++ b/src/faebryk/libs/test/times.py @@ -1,35 +1,50 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT +import io import time -from textwrap import indent + +from rich.console import Console +from rich.table import Table + +from faebryk.libs.units import P class Times: - def __init__(self) -> None: - self.times = {} - self.last_time = time.time() + def __init__(self, cnt: int = 1, unit: str = "ms") -> None: + self.times: dict[str, float] = {} + self.last_time = time.perf_counter() + + self.unit = unit + self.cnt = cnt def add(self, name: str): - now = time.time() + now = time.perf_counter() if name not in self.times: self.times[name] = now - self.last_time self.last_time = now def _format_val(self, val: float): - return f"{val * 1000:.2f}ms" + return f"{((val / self.cnt)*P.s).to(self.unit).m:.2f}", self.unit def __repr__(self): - formatted = { - k: self._format_val(v) - for k, v in self.times.items() - if not k.startswith("_") - } - longest_name = max(len(k) for k in formatted) - return "Timings: \n" + indent( - "\n".join(f"{k:>{longest_name}}: {v:<10}" for k, v in formatted.items()), - " " * 4, - ) + table = Table(title="Timings") + table.add_column("Category", style="cyan") + table.add_column("Subcategory", style="magenta") + table.add_column("Value", justify="right", style="green") + table.add_column("Unit", style="yellow") + + for k, v in self.times.items(): + if not k.startswith("_"): + value, unit = self._format_val(v) + categories = k.split(":", 1) + if len(categories) == 1: + categories.append("") + table.add_row(categories[0].strip(), categories[1].strip(), value, unit) + + console = Console(record=True, file=io.StringIO()) + console.print(table) + return console.export_text(styles=True) class Context: def __init__(self, name: str, times: "Times"): @@ -38,10 +53,10 @@ def __init__(self, name: str, times: "Times"): def __enter__(self): self.times.add("_" + self.name) - self.start = time.time() + self.start = time.perf_counter() def __exit__(self, exc_type, exc_value, traceback): - self.times.times[self.name] = time.time() - self.start + self.times.times[self.name] = time.perf_counter() - self.start def context(self, name: str): return Times.Context(name, self) diff --git a/src/faebryk/libs/util.py b/src/faebryk/libs/util.py index e6a44653..e1d6b23e 100644 --- a/src/faebryk/libs/util.py +++ b/src/faebryk/libs/util.py @@ -4,17 +4,20 @@ import asyncio import collections.abc import inspect +import itertools import logging import os import select import subprocess import sys +import time from abc import abstractmethod from collections import defaultdict from contextlib import contextmanager from dataclasses import dataclass, fields from enum import StrEnum from itertools import chain +from pathlib import Path from textwrap import indent from typing import ( Any, @@ -33,6 +36,7 @@ get_origin, ) +import psutil from tortoise import Model from tortoise.queryset import QuerySet @@ -68,9 +72,9 @@ def __eq__(self, other): return hash(self) == hash(other) -def unique(it, key): - seen = [] - out = [] +def unique[T, U](it: Iterable[T], key: Callable[[T], U]) -> list[T]: + seen: list[U] = [] + out: list[T] = [] for i in it: v = key(i) if v in seen: @@ -646,7 +650,7 @@ def __call__(self, *args, **kwargs) -> Any: return self.f(*args, **kwargs) -def at_exit(func: Callable): +def at_exit(func: Callable, on_exception: bool = True): import atexit import sys @@ -654,7 +658,8 @@ def at_exit(func: Callable): atexit.register(f) hook = sys.excepthook - sys.excepthook = lambda *args: (f(), hook(*args)) + if on_exception: + sys.excepthook = lambda *args: (f(), hook(*args)) # get main thread import threading @@ -714,6 +719,23 @@ def __init_subclass__(cls) -> None: def once[T, **P](f: Callable[P, T]) -> Callable[P, T]: + # TODO add flag for this optimization + # might not be desirable if different instances with same hash + # return same values here + # check if f is a method with only self + if list(inspect.signature(f).parameters) == ["self"]: + name = f.__name__ + attr_name = f"_{name}_once" + + def wrapper_single(self) -> Any: + if not hasattr(self, attr_name): + setattr(self, attr_name, f(self)) + return getattr(self, attr_name) + + return wrapper_single + + # TODO optimization: if takes self + args, use self as cache + def wrapper(*args: P.args, **kwargs: P.kwargs) -> Any: lookup = (args, tuple(kwargs.items())) if lookup in wrapper.cache: @@ -764,6 +786,7 @@ def __init__(self, name: str, default: T, descr: str = ""): self.default = default self.descr = descr self._type: type[T] = type(default) + self.get() @property def name(self) -> str: @@ -794,6 +817,10 @@ def __hash__(self) -> int: def _convert(self, raw_val: str) -> T: ... def __eq__(self, other) -> bool: + # catch cache lookup + if isinstance(other, _ConfigFlagBase): + return id(other) == id(self) + return self.get() == other @@ -816,18 +843,29 @@ def __bool__(self): class ConfigFlagEnum[E: StrEnum](_ConfigFlagBase[E]): def __init__(self, enum: type[E], name: str, default: E, descr: str = "") -> None: - super().__init__(name, default, descr) self.enum = enum + super().__init__(name, default, descr) def _convert(self, raw_val: str) -> E: return self.enum[raw_val.upper()] class ConfigFlagString(_ConfigFlagBase[str]): + def __init__(self, name: str, default: str = "", descr: str = "") -> None: + super().__init__(name, default, descr) + def _convert(self, raw_val: str) -> str: return raw_val +class ConfigFlagInt(_ConfigFlagBase[int]): + def __init__(self, name: str, default: int = 0, descr: str = "") -> None: + super().__init__(name, default, descr) + + def _convert(self, raw_val: str) -> int: + return int(raw_val) + + def zip_dicts_by_key(*dicts): keys = {k for d in dicts for k in d} return {k: tuple(d.get(k) for d in dicts) for k in keys} @@ -869,6 +907,34 @@ def __call__(cls, *args, **kwargs): return obj +def post_init_decorator(cls): + """ + Class decorator that calls __post_init__ after the last (of derived classes) + __init__ has been called. + Attention: Needs to be called on cls in __init_subclass__ of decorated class. + """ + post_init_base = getattr(cls, "__post_init_decorator", None) + # already decorated + if post_init_base is cls: + return + + original_init = cls.__init__ + + # inherited constructor + if post_init_base and post_init_base.__init__ == cls.__init__: + original_init = post_init_base.__original_init__ + + def new_init(self, *args, **kwargs): + original_init(self, *args, **kwargs) + if hasattr(self, "__post_init__") and type(self) is cls: + self.__post_init__(*args, **kwargs) + + cls.__init__ = new_init + cls.__original_init__ = original_init + cls.__post_init_decorator = cls + return cls + + class Tree[T](dict[T, "Tree[T]"]): def iter_by_depth(self) -> Iterable[Sequence[T]]: yield list(self.keys()) @@ -1135,3 +1201,60 @@ def run_live( ) return "\n".join(stdout), process + + +@contextmanager +def global_lock(lock_file_path: Path, timeout_s: float | None = None): + # TODO consider using filelock instead + + lock_file_path.parent.mkdir(parents=True, exist_ok=True) + + start_time = time.time() + while try_or( + lambda: bool(lock_file_path.touch(exist_ok=False)), + default=True, + catch=FileExistsError, + ): + # check if pid still alive + try: + pid = int(lock_file_path.read_text()) + except ValueError: + lock_file_path.unlink(missing_ok=True) + continue + assert pid != os.getpid() + if not psutil.pid_exists(pid): + lock_file_path.unlink(missing_ok=True) + continue + if timeout_s and time.time() - start_time > timeout_s: + raise TimeoutError() + time.sleep(0.1) + + # write our pid to the lock file + lock_file_path.write_text(str(os.getpid())) + try: + yield + finally: + lock_file_path.unlink(missing_ok=True) + + +def typename(x: object | type) -> str: + if not isinstance(x, type): + x = type(x) + return x.__name__ + + +def consume(iter: Iterable, n: int) -> list: + assert n >= 0 + out = list(itertools.islice(iter, n)) + return out if len(out) == n else [] + + +class DefaultFactoryDict[T, U](dict[T, U]): + def __init__(self, factory: Callable[[T], U], *args, **kwargs): + self.factory = factory + super().__init__(*args, **kwargs) + + def __missing__(self, key: T) -> U: + res = self.factory(key) + self[key] = res + return res diff --git a/test/core/cpp/test_importcpp.py b/test/core/cpp/test_importcpp.py index 9a3e2d69..ea4636b0 100644 --- a/test/core/cpp/test_importcpp.py +++ b/test/core/cpp/test_importcpp.py @@ -2,7 +2,167 @@ # SPDX-License-Identifier: MIT +from abc import abstractmethod + +import pytest + + def test_add(): - from faebryk.core.cpp import add + from faebryk.core.cpp import add, call_python_function assert add(1, 2) == 3 + assert add(1) == 2 + + assert call_python_function(lambda: 1) == 1 + + +def test_cnodes(): + from faebryk.core.cpp import LinkNamedParent, Node + + n1 = Node() + n1.transfer_ownership(n1) + n2 = Node() + n2.transfer_ownership(n2) + + class _Node(Node): + def __init__(self) -> None: + super().__init__() + self.transfer_ownership(self) + + n3 = _Node() + + n1.children.connect(n2.parent, LinkNamedParent("test1")) + n2.children.connect(n3.parent, LinkNamedParent("test2")) + print(n1) + print(n2) + print(n3) + print(n1.children.get_children()) + print(n1.get_children(direct_only=True, sort=True)) + print(n2.get_children(direct_only=True, sort=True)) + print(n1.get_children(include_root=True, direct_only=False, sort=True)) + + +def test_pynode(): + from faebryk.core.node import Node + + n = Node() + print(n) + print("---") + + class SubNode(Node): + a: Node + b: Node + + sn = SubNode() + print(sn.a) + + print(sn.get_children(direct_only=True, types=Node)) + + +def test_derived_pynodes(): + from faebryk.core.module import Module + from faebryk.core.moduleinterface import ModuleInterface + + class App(Module): + mif1: ModuleInterface + mif2: ModuleInterface + + app = App() + app.mif1.connect(app.mif2) + + print(app.mif1) + print(app.mif1.get_connected()) + + +def test_traits_basic(): + from faebryk.core.node import Node + from faebryk.core.trait import Trait + + class T(Trait): + @abstractmethod + def do(self): ... + + class T_do(T.impl()): + def do(self): + print("do") + + class A(Node): + t: T_do + + a = A() + + print(a.t) + print(a.get_trait(T)) + a.get_trait(T).do() + + +def test_forgotten_superinit(): + from faebryk.core.node import Node + + class A(Node): + def __init__(self): + pass + + with pytest.raises(Exception): + A() + + +def test_library_nodes(): + import faebryk.library._F as F + + x = F.Electrical() + + print(x) + + +def test_cobject(): + from faebryk.core.cpp import ( + GraphInterface, + GraphInterfaceHierarchical, + GraphInterfaceSelf, + LinkDirect, + LinkNamedParent, + ) + + g1 = GraphInterfaceSelf() + g2 = GraphInterfaceHierarchical(True) + g3 = GraphInterface() + g4 = GraphInterfaceHierarchical(False) + + g2.connect(g3) + g1.connect(g2, LinkDirect()) + g2.connect(g4, LinkNamedParent("test")) + + print(g1.edges) + print(g2.edges) + print(g1.get_graph()) + + g1.get_graph().invalidate() + + +def test_link(): + from faebryk.core.cpp import GraphInterface, LinkDirect + + g1 = GraphInterface() + g2 = GraphInterface() + g1.connect(g2, LinkDirect()) + + +def test_mif_link(): + from faebryk.core.link import LinkDirectConditional + from faebryk.core.moduleinterface import ModuleInterface + + mif1 = ModuleInterface() + mif2 = ModuleInterface() + mif1.connect_shallow(mif2) + assert isinstance(mif1.is_connected_to(mif2), LinkDirectConditional) + + +if __name__ == "__main__": + # test_add() + # test_cobject() + # test_cnodes() + # test_link() + test_mif_link() + # test_pynode() + # test_derived_pynodes() diff --git a/test/core/test_core.py b/test/core/test_core.py index 0d3e784a..19a66d43 100644 --- a/test/core/test_core.py +++ b/test/core/test_core.py @@ -18,19 +18,19 @@ def test_gifs(self): gif1.connect(gif2) self.assertIn(gif2, gif1.edges) - self.assertTrue(gif1.is_connected(gif2) is not None) + self.assertTrue(gif1.is_connected_to(gif2)) gif3 = GIF() class linkcls(LinkDirect): pass - gif1.connect(gif3, linkcls) - self.assertIsInstance(gif1.is_connected(gif3), linkcls) - self.assertEqual(gif1.is_connected(gif3), gif3.is_connected(gif1)) + gif1.connect(gif3, linkcls()) + self.assertIsInstance(gif1.is_connected_to(gif3), linkcls) + self.assertEqual(gif1.is_connected_to(gif3), gif3.is_connected_to(gif1)) - self.assertRaises(AssertionError, lambda: gif1.connect(gif3)) - gif1.connect(gif3, linkcls) + self.assertRaises(RuntimeError, lambda: gif1.connect(gif3)) + self.assertRaises(RuntimeError, lambda: gif1.connect(gif3, linkcls())) self.assertEqual(gif1.G, gif2.G) @@ -39,13 +39,13 @@ def test_node_gifs(self): n1 = Node() - self.assertIsInstance(n1.self_gif.is_connected(n1.parent), LinkSibling) - self.assertIsInstance(n1.self_gif.is_connected(n1.children), LinkSibling) + self.assertIsInstance(n1.self_gif.is_connected_to(n1.parent), LinkSibling) + self.assertIsInstance(n1.self_gif.is_connected_to(n1.children), LinkSibling) n2 = Node() n1.add(n2, name="n2") - self.assertIsInstance(n1.children.is_connected(n2.parent), LinkParent) + self.assertIsInstance(n1.children.is_connected_to(n2.parent), LinkParent) print(n1.get_graph()) @@ -79,7 +79,9 @@ def test_fab_ll_chain_names(self): x.add(y, f"i{i}") x = y - self.assertEqual(x.get_full_name(), "*.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9") + self.assertRegex( + x.get_full_name(), "[*][0-9A-F]{4}.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9" + ) def test_fab_ll_chain_tree(self): root = Node() @@ -91,7 +93,9 @@ def test_fab_ll_chain_tree(self): x.add(z, f"j{i}") x = y - self.assertEqual(x.get_full_name(), "*.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9") + self.assertRegex( + x.get_full_name(), "[*][0-9A-F]{4}.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9" + ) if __name__ == "__main__": diff --git a/test/core/test_hierarchy_connect.py b/test/core/test_hierarchy_connect.py index fbdf2b59..f6291130 100644 --- a/test/core/test_hierarchy_connect.py +++ b/test/core/test_hierarchy_connect.py @@ -7,11 +7,15 @@ import faebryk.library._F as F from faebryk.core.core import logger as core_logger -from faebryk.core.link import LinkDirect, LinkDirectShallow, _TLinkDirectShallow +from faebryk.core.link import ( + LinkDirect, + LinkDirectConditional, + LinkDirectConditionalFilterResult, +) from faebryk.core.module import Module from faebryk.core.moduleinterface import ModuleInterface from faebryk.libs.library import L -from faebryk.libs.util import print_stack, times +from faebryk.libs.util import times logger = logging.getLogger(__name__) core_logger.setLevel(logger.getEffectiveLevel()) @@ -47,20 +51,20 @@ def test_chains(self): mifs[0].connect_shallow(mifs[1]) mifs[1].connect_shallow(mifs[2]) self.assertTrue(mifs[0].is_connected_to(mifs[2])) - self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), _TLinkDirectShallow) + self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), LinkDirectConditional) mifs = times(3, ModuleInterface) mifs[0].connect_shallow(mifs[1]) mifs[1].connect(mifs[2]) self.assertTrue(mifs[0].is_connected_to(mifs[2])) - self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), _TLinkDirectShallow) + self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), LinkDirectConditional) # Test hierarchy down filter & chain resolution mifs = times(3, F.ElectricLogic) mifs[0].connect_shallow(mifs[1]) mifs[1].connect(mifs[2]) self.assertTrue(mifs[0].is_connected_to(mifs[2])) - self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), _TLinkDirectShallow) + self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), LinkDirectConditional) self.assertTrue(mifs[1].signal.is_connected_to(mifs[2].signal)) self.assertTrue(mifs[1].reference.is_connected_to(mifs[2].reference)) @@ -132,18 +136,13 @@ def single_electric_reference(self): F.ElectricLogic.connect_all_module_references(self) ) - import faebryk.core.core as c - # Enable to see the stack trace of invalid connections # c.LINK_TB = True app = UARTBuffer() def _assert_no_link(mif1, mif2): link = mif1.is_connected_to(mif2) - err = "" - if link and c.LINK_TB: - err = "\n" + print_stack(link.tb) - self.assertFalse(link, err) + self.assertFalse(link) def _assert_link(mif1: ModuleInterface, mif2: ModuleInterface, link=None): out = mif1.is_connected_to(mif2) @@ -182,7 +181,7 @@ def _assert_link(mif1: ModuleInterface, mif2: ModuleInterface, link=None): _assert_link(bus2.tx, buf.outs_l[0], LinkDirect) # connect shallow - _assert_link(buf.ins_l[0], buf.outs_l[0], _TLinkDirectShallow) + _assert_link(buf.ins_l[0], buf.outs_l[0], LinkDirectConditional) # Check that the two buffer sides are connected logically _assert_link(bus1.tx, bus2.tx) @@ -217,7 +216,11 @@ class Specialized(ModuleInterface): ... self.assertTrue(mifs[0].is_connected_to(mifs[2])) # test special link - class _Link(LinkDirectShallow(lambda link, gif: True)): ... + class _Link(LinkDirectConditional): + def __init__(self): + super().__init__( + lambda src, dst: LinkDirectConditionalFilterResult.FILTER_PASS + ) mifs = times(3, ModuleInterface) mifs_special = times(3, Specialized) diff --git a/test/core/test_parameters.py b/test/core/test_parameters.py index 9db7050d..a8e0d569 100644 --- a/test/core/test_parameters.py +++ b/test/core/test_parameters.py @@ -52,8 +52,8 @@ def assertIsInstance[T: Parameter](obj: Parameter, cls: type[T]) -> T: ) # TBD Range - a = F.TBD[int]() - b = F.TBD[int]() + a = F.TBD() + b = F.TBD() R_TBD = F.Range(a, b) add = R_ONE_TEN + R_TBD mul = R_ONE_TEN * R_TBD @@ -95,8 +95,8 @@ def assertIsInstance[T: Parameter](obj: Parameter, cls: type[T]) -> T: self.assertEqual(R_ONE_TEN & {1, 2, 11}, F.Set([1, 2])) self.assertEqual(R_ONE_TEN & F.Range(12, 13), F.Set([])) # with tbd - a = F.TBD[int]() - b = F.TBD[int]() + a = F.TBD() + b = F.TBD() RTBD = F.Range(a, b) r_one_ten_con_tbd = R_ONE_TEN & RTBD assertIsInstance(r_one_ten_con_tbd, F.Operation) @@ -154,16 +154,16 @@ def assertIsInstance[T](obj, cls: type[T]) -> T: self.assertEqual(F.ANY(), F.ANY()) def test_merge( - a: Parameter[int] | set[int] | int | tuple[int, int], - b: Parameter[int] | set[int] | int | tuple[int, int], + a: Parameter | set[int] | int | tuple[int, int], + b: Parameter | set[int] | int | tuple[int, int], expected, ): - a = Parameter[int].from_literal(a) - expected = Parameter[int].from_literal(expected) + a = Parameter.from_literal(a) + expected = Parameter.from_literal(expected) self.assertEqual(a.merge(b), expected) def fail_merge(a, b): - a = Parameter[int].from_literal(a) + a = Parameter.from_literal(a) self.assertRaises(Parameter.MergeException, lambda: a.merge(b)) # F.Sets ---- @@ -196,11 +196,11 @@ def fail_merge(a, b): def test_specific(self): def test_spec( - a: Parameter[int] | set[int] | int | tuple[int, int], - b: Parameter[int] | set[int] | int | tuple[int, int], + a: Parameter | set[int] | int | tuple[int, int], + b: Parameter | set[int] | int | tuple[int, int], expected: bool = True, ): - b = Parameter[int].from_literal(b) + b = Parameter.from_literal(b) if expected: self.assertTrue(b.is_subset_of(a)) else: @@ -231,11 +231,11 @@ def test_spec( def test_compress(self): def test_comp( - a: Parameter[int].LIT_OR_PARAM, - expected: Parameter[int].LIT_OR_PARAM, + a: Parameter.LIT_OR_PARAM, + expected: Parameter.LIT_OR_PARAM, ): - a = Parameter[int].from_literal(a) - expected = Parameter[int].from_literal(expected) + a = Parameter.from_literal(a) + expected = Parameter.from_literal(expected) self.assertEqual(a.get_most_narrow(), expected) test_comp(1, 1) diff --git a/test/core/test_performance.py b/test/core/test_performance.py index 090f461c..727a5f16 100644 --- a/test/core/test_performance.py +++ b/test/core/test_performance.py @@ -1,6 +1,7 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT +import logging import time import unittest from itertools import pairwise @@ -15,6 +16,8 @@ from faebryk.libs.test.times import Times from faebryk.libs.util import times +logger = logging.getLogger(__name__) + class TestPerformance(unittest.TestCase): def test_get_all(self): @@ -84,9 +87,9 @@ def _common_timings( n.get_children(direct_only=True, types=ModuleInterface) timings.add(f"get_mifs {name}") - print(f"{test_name:-<80}") - print(f"{timings!r}") - print(str(G)) + logger.info(f"{test_name:-<80}") + logger.info(f"{timings!r}") + logger.info(str(G)) return timings # _common_timings(lambda: _factory_simple_resistors(100), "simple") @@ -98,9 +101,9 @@ def _common_timings( lambda: _factory_simple_resistors(count), f"Simple resistors: {count}" ) per_resistor = timings.times["instance"] / count - print(f"----> Avg/resistor: {per_resistor*1e3:.2f} ms") + logger.info(f"----> Avg/resistor: {per_resistor*1e3:.2f} ms") - print("=" * 80) + logger.info("=" * 80) for i in range(2, 5): count = 10 * 2**i timings = _common_timings( @@ -108,12 +111,12 @@ def _common_timings( f"Connected resistors: {count}", ) per_resistor = timings.times["instance"] / count - print(f"----> Avg/resistor: {per_resistor*1e3:.2f} ms") + logger.info(f"----> Avg/resistor: {per_resistor*1e3:.2f} ms") def test_graph_merge_rec(self): timings = Times() count = 2**14 - print(f"Count: {count}") + logger.info(f"Count: {count}") gs = times(count, GraphInterface) timings.add("instance") @@ -146,16 +149,13 @@ def rec_connect(gs_sub: list[GraphInterface]): # self.assertLess(timings.times["split 1024"], 50e-3) # self.assertLess(timings.times["instance"], 300e-3) # self.assertLess(timings.times["connect"], 1200e-3) - print(timings) - print(f"----> Avg/connect: {per_connect*1e6:.2f} us") - from faebryk.core.graphinterface import GraphImpl - - print("Counter", GraphImpl.counter, GraphImpl.counter - count) + logger.info(timings) + logger.info(f"----> Avg/connect: {per_connect*1e6:.2f} us") def test_graph_merge_it(self): timings = Times() count = 2**14 - print(f"Count: {count}") + logger.info(f"Count: {count}") gs = times(count, GraphInterface) timings.add("instance") @@ -165,18 +165,113 @@ def test_graph_merge_it(self): timings.add("connect") - self.assertEqual(gs[0].G.node_cnt, count) + self.assertEqual(gs[0].G.node_count, count) per_connect = timings.times["connect"] / count # self.assertLess(timings.times["connect"], 500e-3) # self.assertLess(timings.times["instance"], 200e-3) # self.assertLess(per_connect, 25e-6) - print(timings) - print(f"----> Avg/connect: {per_connect*1e6:.2f} us") + logger.info(timings) + logger.info(f"----> Avg/connect: {per_connect*1e6:.2f} us") + + def test_mif_connect_check(self): + cnt = 100 + + timings = Times(cnt=cnt, unit="us") + + for t in [ + GraphInterface, + ModuleInterface, + F.Electrical, + F.ElectricPower, + F.ElectricLogic, + F.I2C, + ]: + instances = [(t(), t()) for _ in range(cnt)] + timings.add(f"{t.__name__}: construct") + + for inst1, inst2 in instances: + inst1.connect(inst2) + timings.add(f"{t.__name__}: connect") + + for inst1, inst2 in instances: + self.assertTrue(inst1.is_connected_to(inst2)) + timings.add(f"{t.__name__}: is_connected") + + logger.info(f"\n{timings}") + + def test_mif_connect_hull(self): + cnt = 30 + + timings = Times(cnt=1, unit="ms") + + for t in [ + GraphInterface, + ModuleInterface, + F.Electrical, + F.ElectricPower, + F.ElectricLogic, + F.I2C, + ]: + instances = [t() for _ in range(cnt)] + timings.add(f"{t.__name__}: construct") + + for other in instances[1:]: + instances[0].connect(other) + timings.add(f"{t.__name__}: connect") + + self.assertTrue(instances[0].is_connected_to(instances[-1])) + timings.add(f"{t.__name__}: is_connected") + + if issubclass(t, ModuleInterface): + list(instances[0].get_connected()) + else: + instances[0].edges + timings.add(f"{t.__name__}: get_connected") + + self.assertTrue(instances[0].is_connected_to(instances[-1])) + timings.add(f"{t.__name__}: is_connected cached") + + logger.info(f"\n{timings}") + + def test_complex_module(self): + timings = Times() + + modules = [F.USB2514B, F.RP2040] + + for t in modules: + app = t() # noqa: F841 + timings.add(f"{t.__name__}: construct") + + # resolve_dynamic_parameters(app.get_graph()) + timings.add(f"{t.__name__}: resolve") + + logger.info(f"\n{timings}") + + def test_no_connect(self): + CNT = 30 + + timings = Times() + + app = F.RP2040_ReferenceDesign() + timings.add("construct") + + for i in range(CNT): + list(app.rp2040.power_core.get_connected()) + timings.add(f"_get_connected {i}") + + all_times = [ + timings.times[k] for k in timings.times if k.startswith("_get_connected") + ] - from faebryk.core.graphinterface import GraphImpl + timings.times["min"] = min(all_times) + timings.times["max"] = max(all_times) + timings.times["avg"] = sum(all_times) / len(all_times) + timings.times["median"] = sorted(all_times)[len(all_times) // 2] + timings.times["80%"] = sorted(all_times)[int(0.8 * len(all_times))] + timings.times["total"] = sum(all_times) - print("Counter", GraphImpl.counter, GraphImpl.counter - count) + logger.info(f"\n{timings}") if __name__ == "__main__": diff --git a/test/exporters/netlist/kicad/test_netlist_kicad.py b/test/exporters/netlist/kicad/test_netlist_kicad.py index a3bf37e5..70bfb944 100644 --- a/test/exporters/netlist/kicad/test_netlist_kicad.py +++ b/test/exporters/netlist/kicad/test_netlist_kicad.py @@ -47,11 +47,11 @@ def _test_netlist_graph(): ) # make netlist - G = resistor1.get_graph() - attach_random_designators(G) - override_names_with_designators(G) - attach_nets_and_kicad_info(G) - t2 = make_t2_netlist_from_graph(G) + G = resistor1.get_graph + attach_random_designators(G()) + override_names_with_designators(G()) + attach_nets_and_kicad_info(G()) + t2 = make_t2_netlist_from_graph(G()) for comp in t2.comps: del comp.properties["faebryk_name"] netlist = from_faebryk_t2_netlist(t2) diff --git a/test/library/test_basic.py b/test/library/test_basic.py index c31eb986..fb071af1 100644 --- a/test/library/test_basic.py +++ b/test/library/test_basic.py @@ -7,7 +7,7 @@ from faebryk.core.core import Namespace from faebryk.core.node import Node -from faebryk.core.trait import Trait, TraitImpl +from faebryk.core.trait import Trait from faebryk.libs.library import L try: @@ -46,20 +46,32 @@ def test_symbol_types(name: str, module): name.startswith("_") or not isinstance(module, type) or not issubclass(module, Node) - or (issubclass(module, Trait) and not issubclass(module, TraitImpl)) + or (issubclass(module, Trait)) ) ], ) +@pytest.mark.timeout(60) # TODO lower def test_init_args(name: str, module): """Make sure we can instantiate all classes without error""" - # check if constructor has no args & no varargs - init_signature = inspect.signature(module.__init__) - if len(init_signature.parameters) > 1 or any( - param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) - for param in init_signature.parameters.values() + # handle post_init_decorator + init = ( + module.__init__ + if not hasattr(module, "__original_init__") + else module.__original_init__ + ) + init_signature = inspect.signature(init) + args = [p for p in init_signature.parameters.values() if p.name != "self"] + + # check if constructor has no non-default args + if any( + p.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) + and p.default is inspect.Parameter.empty + for p in args ): - pytest.skip("Skipped module with init args because we can't instantiate it") + pytest.skip( + f"Skipped module with init args because we can't instantiate it: {args}" + ) try: module() diff --git a/test/library/test_reference.py b/test/library/test_reference.py index efb76f2b..b12155cf 100644 --- a/test/library/test_reference.py +++ b/test/library/test_reference.py @@ -64,6 +64,7 @@ class B(Node): x = Reference(A) def __init__(self, x): + super().__init__() self.x = x a = A() @@ -80,6 +81,7 @@ class B(Node): y = Reference(A) def __init__(self, x): + super().__init__() self.x = x self.y = self.x diff --git a/test/libs/kicad/test_fileformats.py b/test/libs/kicad/test_fileformats.py index f218b64c..95bc83d3 100644 --- a/test/libs/kicad/test_fileformats.py +++ b/test/libs/kicad/test_fileformats.py @@ -6,6 +6,7 @@ import pytest +import faebryk.library._F as F # noqa: F401 from faebryk.libs.kicad.fileformats import ( C_effects, C_footprint,