From 7eff26155cb082e4c6750d2cf8dda9c4fadb303c Mon Sep 17 00:00:00 2001 From: Guillaume Date: Sun, 22 Oct 2023 14:56:50 +0200 Subject: [PATCH 01/13] use ruff --- .pre-commit-config.yaml | 73 +--- poetry.lock | 651 +++++++++++++++----------------- pyproject.toml | 79 +++- src/muscad/base.py | 108 +++--- src/muscad/helpers.py | 2 + src/muscad/part.py | 7 + src/muscad/transformations.py | 1 + src/muscad/utils/surface.py | 8 +- src/muscad/vitamins/bolts.py | 1 + src/muscad/vitamins/steppers.py | 3 + tests/test_part.py | 1 + 11 files changed, 464 insertions(+), 470 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ab5d883..115a2ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,79 +1,38 @@ repos: -- repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.5.1 - hooks: - - id: forbid-crlf - - id: remove-crlf - - id: forbid-tabs - - id: remove-tabs - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - - id: debug-statements - - id: mixed-line-ending - - id: name-tests-test - args: [--pytest-test-first] - - id: trailing-whitespace - - id: end-of-file-fixer - id: check-merge-conflict - id: check-yaml args: [--unsafe] -- repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.10.0 - hooks: - - id: python-check-blanket-noqa - - id: python-check-blanket-type-ignore - - id: python-no-eval - - id: python-use-type-annotations - - id: text-unicode-replacement-char -- repo: https://github.com/executablebooks/mdformat - rev: 0.7.16 - hooks: - - id: mdformat - args: - - --number - additional_dependencies: - - mdformat-black +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: python-use-type-annotations + - id: text-unicode-replacement-char - repo: https://github.com/myint/docformatter - rev: v1.7.3 + rev: v1.7.5 hooks: - id: docformatter args: - --in-place - --wrap-summaries=100 - --wrap-descriptions=100 -- repo: https://github.com/hadialqattan/pycln - rev: v2.1.5 +- repo: https://github.com/psf/black + rev: 23.10.0 hooks: - - id: pycln - args: [--config=pyproject.toml] -- repo: https://github.com/pre-commit/mirrors-isort - rev: v5.10.1 - hooks: - - id: isort -- repo: https://github.com/ambv/black - rev: 23.3.0 - hooks: - - id: black -- repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - additional_dependencies: [flake8-typing-imports==1.14.0] + - id: black - repo: https://github.com/asottile/blacken-docs - rev: 1.14.0 + rev: 1.16.0 hooks: - id: blacken-docs +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.1 + hooks: + - id: ruff - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.4.0 + rev: v1.6.1 hooks: - id: mypy - args: - - --strict - - --implicit-reexport - - --show-error-codes - - --show-error-context - - --show-column-numbers - - --disable-error-code=attr-defined additional_dependencies: - pytest-mypy diff --git a/poetry.lock b/poetry.lock index c4eb2cf..a24d712 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -23,7 +22,6 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "bandit" version = "1.7.5" description = "Security oriented static analyser for python code." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -45,37 +43,29 @@ yaml = ["PyYAML"] [[package]] name = "black" -version = "23.3.0" +version = "23.10.0" description = "The uncompromising code formatter." -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, - {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, - {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, - {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, - {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, - {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, - {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, - {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, - {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, - {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, - {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, - {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, - {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, - {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, - {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, - {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, - {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, - {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, + {file = "black-23.10.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:f8dc7d50d94063cdfd13c82368afd8588bac4ce360e4224ac399e769d6704e98"}, + {file = "black-23.10.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:f20ff03f3fdd2fd4460b4f631663813e57dc277e37fb216463f3b907aa5a9bdd"}, + {file = "black-23.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3d9129ce05b0829730323bdcb00f928a448a124af5acf90aa94d9aba6969604"}, + {file = "black-23.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:960c21555be135c4b37b7018d63d6248bdae8514e5c55b71e994ad37407f45b8"}, + {file = "black-23.10.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:30b78ac9b54cf87bcb9910ee3d499d2bc893afd52495066c49d9ee6b21eee06e"}, + {file = "black-23.10.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:0e232f24a337fed7a82c1185ae46c56c4a6167fb0fe37411b43e876892c76699"}, + {file = "black-23.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31946ec6f9c54ed7ba431c38bc81d758970dd734b96b8e8c2b17a367d7908171"}, + {file = "black-23.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:c870bee76ad5f7a5ea7bd01dc646028d05568d33b0b09b7ecfc8ec0da3f3f39c"}, + {file = "black-23.10.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:6901631b937acbee93c75537e74f69463adaf34379a04eef32425b88aca88a23"}, + {file = "black-23.10.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:481167c60cd3e6b1cb8ef2aac0f76165843a374346aeeaa9d86765fe0dd0318b"}, + {file = "black-23.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74892b4b836e5162aa0452393112a574dac85e13902c57dfbaaf388e4eda37c"}, + {file = "black-23.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:47c4510f70ec2e8f9135ba490811c071419c115e46f143e4dce2ac45afdcf4c9"}, + {file = "black-23.10.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:76baba9281e5e5b230c9b7f83a96daf67a95e919c2dfc240d9e6295eab7b9204"}, + {file = "black-23.10.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:a3c2ddb35f71976a4cfeca558848c2f2f89abc86b06e8dd89b5a65c1e6c0f22a"}, + {file = "black-23.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db451a3363b1e765c172c3fd86213a4ce63fb8524c938ebd82919bf2a6e28c6a"}, + {file = "black-23.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:7fb5fc36bb65160df21498d5a3dd330af8b6401be3f25af60c6ebfe23753f747"}, + {file = "black-23.10.0-py3-none-any.whl", hash = "sha256:e223b731a0e025f8ef427dd79d8cd69c167da807f5710add30cdf131f13dd62e"}, + {file = "black-23.10.0.tar.gz", hash = "sha256:31b9f87b277a68d0e99d2905edae08807c007973eaa609da5f0c62def6b7c0bd"}, ] [package.dependencies] @@ -85,7 +75,7 @@ packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -95,26 +85,24 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cfgv" -version = "3.3.1" +version = "3.4.0" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8" files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, + {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 = "click" -version = "8.1.3" +version = "8.1.7" description = "Composable command line interface toolkit" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -124,7 +112,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -134,72 +121,63 @@ files = [ [[package]] name = "coverage" -version = "7.2.7" +version = "7.3.2" description = "Code coverage measurement for Python" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, - {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, - {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, - {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, - {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, - {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, - {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, - {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, - {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, - {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, - {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, - {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, - {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, - {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, - {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, - {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, - {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, - {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, - {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, - {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, - {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, - {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, - {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, - {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, - {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, - {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, - {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, - {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, - {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, - {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, - {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, - {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, - {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, + {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, + {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, + {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, + {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, + {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, + {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, + {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, + {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, + {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, + {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, + {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, + {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, + {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, + {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, + {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, + {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, + {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, + {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, + {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, + {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, + {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, + {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, + {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, + {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, + {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, + {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, + {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, ] [package.dependencies] @@ -210,21 +188,19 @@ toml = ["tomli"] [[package]] name = "distlib" -version = "0.3.6" +version = "0.3.7" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, - {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, + {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, + {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, ] [[package]] name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -234,14 +210,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.1" +version = "1.1.3" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, - {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, ] [package.extras] @@ -249,42 +224,40 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.12.2" +version = "3.12.4" description = "A platform independent file lock." -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, - {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, + {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, + {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] +typing = ["typing-extensions (>=4.7.1)"] [[package]] name = "flake8" -version = "6.0.0" +version = "6.1.0" description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, - {file = "flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, + {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, + {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.10.0,<2.11.0" -pyflakes = ">=3.0.0,<3.1.0" +pycodestyle = ">=2.11.0,<2.12.0" +pyflakes = ">=3.1.0,<3.2.0" [[package]] name = "flake8-bandit" version = "4.1.1" description = "Automated security testing with bandit and flake8." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -298,14 +271,13 @@ flake8 = ">=5.0.0" [[package]] name = "flake8-bugbear" -version = "23.6.5" +version = "23.9.16" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." -category = "dev" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-bugbear-23.6.5.tar.gz", hash = "sha256:8631e1071c9d85d24a615f235565c16c9a2ac57add4a14636d331bf9f4ef14fa"}, - {file = "flake8_bugbear-23.6.5-py3-none-any.whl", hash = "sha256:1d9eae6d262a3823765f4579cdab169963d1d2288b02f0f5c6e829b03dded509"}, + {file = "flake8-bugbear-23.9.16.tar.gz", hash = "sha256:90cf04b19ca02a682feb5aac67cae8de742af70538590509941ab10ae8351f71"}, + {file = "flake8_bugbear-23.9.16-py3-none-any.whl", hash = "sha256:b182cf96ea8f7a8595b2f87321d7d9b28728f4d9c3318012d896543d19742cb5"}, ] [package.dependencies] @@ -319,7 +291,6 @@ dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", name = "flake8-docstrings" version = "1.7.0" description = "Extension for flake8 which uses pydocstyle to check docstrings" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -335,7 +306,6 @@ pydocstyle = ">=2.1" name = "flake8-rst-docstrings" version = "0.3.0" description = "Python docstring reStructuredText (RST) validator for flake8" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -353,14 +323,13 @@ develop = ["build", "twine"] [[package]] name = "gitdb" -version = "4.0.10" +version = "4.0.11" description = "Git Object Database" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "gitdb-4.0.10-py3-none-any.whl", hash = "sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7"}, - {file = "gitdb-4.0.10.tar.gz", hash = "sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a"}, + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, ] [package.dependencies] @@ -368,29 +337,30 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.31" +version = "3.1.40" description = "GitPython is a Python library used to interact with Git repositories" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "GitPython-3.1.31-py3-none-any.whl", hash = "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d"}, - {file = "GitPython-3.1.31.tar.gz", hash = "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573"}, + {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, + {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, ] [package.dependencies] gitdb = ">=4.0.1,<5" +[package.extras] +test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] + [[package]] name = "identify" -version = "2.5.24" +version = "2.5.30" description = "File identification library for Python" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, + {file = "identify-2.5.30-py2.py3-none-any.whl", hash = "sha256:afe67f26ae29bab007ec21b03d4114f41316ab9dd15aa8736a167481e108da54"}, + {file = "identify-2.5.30.tar.gz", hash = "sha256:f302a4256a15c849b91cfcdcec052a8ce914634b2f77ae87dad29cd749f2d88d"}, ] [package.extras] @@ -400,7 +370,6 @@ license = ["ukkonen"] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -412,7 +381,6 @@ files = [ name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -437,7 +405,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -449,7 +416,6 @@ files = [ name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -459,56 +425,54 @@ files = [ [[package]] name = "mypy" -version = "1.4.0" +version = "1.6.1" description = "Optional static typing for Python" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mypy-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3af348e0925a59213244f28c7c0c3a2c2088b4ba2fe9d6c8d4fbb0aba0b7d05"}, - {file = "mypy-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0b2e0da7ff9dd8d2066d093d35a169305fc4e38db378281fce096768a3dbdbf"}, - {file = "mypy-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210fe0f39ec5be45dd9d0de253cb79245f0a6f27631d62e0c9c7988be7152965"}, - {file = "mypy-1.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f7a5971490fd4a5a436e143105a1f78fa8b3fe95b30fff2a77542b4f3227a01f"}, - {file = "mypy-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:50f65f0e9985f1e50040e603baebab83efed9eb37e15a22a4246fa7cd660f981"}, - {file = "mypy-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b1b5c875fcf3e7217a3de7f708166f641ca154b589664c44a6fd6d9f17d9e7e"}, - {file = "mypy-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b4c734d947e761c7ceb1f09a98359dd5666460acbc39f7d0a6b6beec373c5840"}, - {file = "mypy-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5984a8d13d35624e3b235a793c814433d810acba9eeefe665cdfed3d08bc3af"}, - {file = "mypy-1.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0f98973e39e4a98709546a9afd82e1ffcc50c6ec9ce6f7870f33ebbf0bd4f26d"}, - {file = "mypy-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:19d42b08c7532d736a7e0fb29525855e355fa51fd6aef4f9bbc80749ff64b1a2"}, - {file = "mypy-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6ba9a69172abaa73910643744d3848877d6aac4a20c41742027dcfd8d78f05d9"}, - {file = "mypy-1.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a34eed094c16cad0f6b0d889811592c7a9b7acf10d10a7356349e325d8704b4f"}, - {file = "mypy-1.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:53c2a1fed81e05ded10a4557fe12bae05b9ecf9153f162c662a71d924d504135"}, - {file = "mypy-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bba57b4d2328740749f676807fcf3036e9de723530781405cc5a5e41fc6e20de"}, - {file = "mypy-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:653863c75f0dbb687d92eb0d4bd9fe7047d096987ecac93bb7b1bc336de48ebd"}, - {file = "mypy-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7461469e163f87a087a5e7aa224102a30f037c11a096a0ceeb721cb0dce274c8"}, - {file = "mypy-1.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cf0ca95e4b8adeaf07815a78b4096b65adf64ea7871b39a2116c19497fcd0dd"}, - {file = "mypy-1.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:94a81b9354545123feb1a99b960faeff9e1fa204fce47e0042335b473d71530d"}, - {file = "mypy-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:67242d5b28ed0fa88edd8f880aed24da481929467fdbca6487167cb5e3fd31ff"}, - {file = "mypy-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3f2b353eebef669529d9bd5ae3566905a685ae98b3af3aad7476d0d519714758"}, - {file = "mypy-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62bf18d97c6b089f77f0067b4e321db089d8520cdeefc6ae3ec0f873621c22e5"}, - {file = "mypy-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca33ab70a4aaa75bb01086a0b04f0ba8441e51e06fc57e28585176b08cad533b"}, - {file = "mypy-1.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5a0ee54c2cb0f957f8a6f41794d68f1a7e32b9968675ade5846f538504856d42"}, - {file = "mypy-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:6c34d43e3d54ad05024576aef28081d9d0580f6fa7f131255f54020eb12f5352"}, - {file = "mypy-1.4.0-py3-none-any.whl", hash = "sha256:f051ca656be0c179c735a4c3193f307d34c92fdc4908d44fd4516fbe8b10567d"}, - {file = "mypy-1.4.0.tar.gz", hash = "sha256:de1e7e68148a213036276d1f5303b3836ad9a774188961eb2684eddff593b042"}, + {file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"}, + {file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"}, + {file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"}, + {file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"}, + {file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"}, + {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"}, + {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"}, + {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"}, + {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"}, + {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"}, + {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"}, + {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"}, + {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"}, + {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"}, + {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"}, + {file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"}, + {file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"}, + {file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"}, + {file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"}, + {file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"}, + {file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"}, + {file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"}, + {file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"}, + {file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"}, + {file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"}, + {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"}, + {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] -python2 = ["typed-ast (>=1.4.0,<2)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -520,7 +484,6 @@ files = [ name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -533,33 +496,30 @@ setuptools = "*" [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pathspec" -version = "0.11.1" +version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, - {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, + {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, + {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, ] [[package]] name = "pbr" version = "5.11.1" description = "Python Build Reasonableness" -category = "dev" optional = false python-versions = ">=2.6" files = [ @@ -571,7 +531,6 @@ files = [ name = "pep8-naming" version = "0.13.3" description = "Check PEP-8 naming conventions, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -584,30 +543,28 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "3.8.0" +version = "3.11.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"}, - {file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"}, + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, ] [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.3.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] @@ -616,14 +573,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.3.3" +version = "3.5.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"}, - {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"}, + {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, + {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, ] [package.dependencies] @@ -635,14 +591,13 @@ virtualenv = ">=20.10.0" [[package]] name = "pre-commit-hooks" -version = "4.4.0" +version = "4.5.0" description = "Some out-of-the-box hooks for pre-commit." -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pre_commit_hooks-4.4.0-py2.py3-none-any.whl", hash = "sha256:fc8837335476221ccccda3d176ed6ae29fe58753ce7e8b7863f5d0f987328fc6"}, - {file = "pre_commit_hooks-4.4.0.tar.gz", hash = "sha256:7011eed8e1a25cde94693da009cba76392194cecc2f3f06c51a44ea6ad6c2af9"}, + {file = "pre_commit_hooks-4.5.0-py2.py3-none-any.whl", hash = "sha256:b779d5c44ede9b1fda48e2d96b08e9aa5b1d2fdb8903ca09f0dbaca22d529edb"}, + {file = "pre_commit_hooks-4.5.0.tar.gz", hash = "sha256:ffbe2af1c85ac9a7695866955680b4dee98822638b748a6f3debefad79748c8a"}, ] [package.dependencies] @@ -651,21 +606,19 @@ tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} [[package]] name = "pycodestyle" -version = "2.10.0" +version = "2.11.1" description = "Python style guide checker" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, - {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, + {file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"}, + {file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"}, ] [[package]] name = "pydocstyle" version = "6.3.0" description = "Python docstring style checker" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -681,26 +634,24 @@ toml = ["tomli (>=1.2.3)"] [[package]] name = "pyflakes" -version = "3.0.1" +version = "3.1.0" description = "passive checker of Python programs" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, - {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, + {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, + {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] [[package]] name = "pygments" -version = "2.15.1" +version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, - {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, + {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, + {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, ] [package.extras] @@ -708,14 +659,13 @@ plugins = ["importlib-metadata"] [[package]] name = "pytest" -version = "7.4.0" +version = "7.4.2" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, + {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, ] [package.dependencies] @@ -733,7 +683,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -750,59 +699,57 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pyyaml" -version = "6.0" +version = "6.0.1" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, - {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, - {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, - {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, - {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "restructuredtext-lint" version = "1.4.0" description = "reStructuredText linter" -category = "dev" optional = false python-versions = "*" files = [ @@ -814,14 +761,13 @@ docutils = ">=0.11,<1.0" [[package]] name = "rich" -version = "13.4.2" +version = "13.6.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.4.2-py3-none-any.whl", hash = "sha256:8f87bc7ee54675732fa66a05ebfe489e27264caeeff3728c945d25971b6485ec"}, - {file = "rich-13.4.2.tar.gz", hash = "sha256:d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898"}, + {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, + {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, ] [package.dependencies] @@ -834,103 +780,98 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" -version = "0.17.32" +version = "0.17.40" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -category = "dev" optional = false python-versions = ">=3" files = [ - {file = "ruamel.yaml-0.17.32-py3-none-any.whl", hash = "sha256:23cd2ed620231677564646b0c6a89d138b6822a0d78656df7abda5879ec4f447"}, - {file = "ruamel.yaml-0.17.32.tar.gz", hash = "sha256:ec939063761914e14542972a5cba6d33c23b0859ab6342f61cf070cfc600efc2"}, + {file = "ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c"}, + {file = "ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d"}, ] [package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""} +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} [package.extras] -docs = ["ryd"] +docs = ["mercurial (>5.7)", "ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] [[package]] name = "ruamel-yaml-clib" -version = "0.2.7" +version = "0.2.8" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, - {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"}, - {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"}, - {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"}, - {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"}, - {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"}, - {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, ] [[package]] name = "setuptools" -version = "68.0.0" +version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, + {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "smmap" -version = "5.0.0" +version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" -category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, - {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, ] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" optional = false python-versions = "*" files = [ @@ -942,7 +883,6 @@ files = [ name = "stevedore" version = "5.1.0" description = "Manage dynamic plugins for Python applications" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -957,7 +897,6 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -967,36 +906,34 @@ files = [ [[package]] name = "typing-extensions" -version = "4.6.3" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "dev" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, - {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] name = "virtualenv" -version = "20.23.1" +version = "20.24.5" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.23.1-py3-none-any.whl", hash = "sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419"}, - {file = "virtualenv-20.23.1.tar.gz", hash = "sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1"}, + {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, + {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, ] [package.dependencies] -distlib = ">=0.3.6,<1" -filelock = ">=3.12,<4" -platformdirs = ">=3.5.1,<4" +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<4" [package.extras] -docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "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.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "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)"] [metadata] lock-version = "2.0" diff --git a/pyproject.toml b/pyproject.toml index 6481894..b480543 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,10 +43,6 @@ source = ["src", "*/site-packages"] branch = true source = ["muscad"] -[tool.coverage.report] -show_missing = true -fail_under = 80 - [tool.mypy] strict = true pretty = true @@ -54,6 +50,81 @@ show_column_numbers = true show_error_codes = true show_error_context = true implicit_reexport = true +disable_error_code = ["attr-defined"] + +[tool.docformatter] +recursive = true +wrap-summaries = 120 +wrap-descriptions = 120 +blank = true + +[tool.ruff] +target-version = "py38" +line-length = 120 +select = [ + "A", + "B", + "C", + "C4", + "D", + "DTZ", + "E", + "EM", + "ERA", + "F", + "FA", + "FBT", + "I", + "ICN", + "ISC", + "N", + "PGH", + "PLC", + "PLE", + "PLR", + "PLW", + "PTH", + "Q", + "RUF", + "S", + "SIM", + "T", + "TID", + "TRY", + "UP", + "W", + "YTT", +] +exclude = [ + "tests" +] + + +[tool.ruff.pydocstyle] +convention = "google" +ignore-decorators = ['override'] + +[tool.ruff.pylint] +max-args = 10 + +[tool.coverage.report] +show_missing = true +fail_under = 80 +exclude_lines = [ + "pragma: no cover", + "def __repr__", + "if self.debug:", + "if settings.DEBUG", + "raise AssertionError", + "raise NotImplementedError", + "if 0:", + "if __name__ == .__main__.:", + "def main", + "...", + "assert False", + "pytest.skip", + "pass", +] [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/src/muscad/base.py b/src/muscad/base.py index e6f421a..60fb4d6 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -41,12 +41,12 @@ class MuSCADError(Exception): def indent(s: str, token: str = " ") -> str: - """ - Indents a given string, with characters from ``token`` - Each line will be prefixed by token. + """Indents a given string, with characters from ``token`` Each line will be prefixed by token. + :param s: the string to indent (may contain multiple lines, separated by '\n' :param token: the string to use as indentation :return: the indented string + """ return token + s.replace("\n", f"\n{token}") @@ -77,6 +77,7 @@ class Object(MuSCAD): """Base class for all OpenSCAD geometry objects. Do not instantiate this class directly. + """ object_name: str @@ -87,6 +88,7 @@ def __init_subclass__(cls, name: Optional[str] = None): :param name: a string (if explicitly declared) :param kwargs: remaining attributes (unused) :return: a subclass with a `name` attribute + """ super().__init_subclass__() if name is None: @@ -98,6 +100,7 @@ def __init__(self) -> None: """Base constructor for Objects. :param children: + """ self.modifier: str = "" self.comment: Optional[str] = None @@ -107,6 +110,7 @@ def set_modifier(self, m: Optional[str]) -> Object: :param m: one of OpenSCAD's modifiers, as a single char str, or None to remove the modifier. :return: the same object, with modifier applied + """ if not m: self.modifier = "" @@ -134,6 +138,7 @@ def remove_modifier(self) -> Object: """Remove any previously applied modifier. :return: the same object, with any modifier removed + """ return self.set_modifier(None) @@ -143,11 +148,8 @@ def __add__(self, other: Object | Iterable[Object]) -> Object: return Union(self, other) def __radd__(self, other: Literal[0]) -> Object: - """ - Makes sure sum(*[object, ...]) works - :param other: another object, or 0 - :return: a Union of both objects - """ + """Makes sure sum(*[object, ...]) works :param other: another object, or 0 :return: a Union + of both objects.""" assert other == 0 return self @@ -156,6 +158,7 @@ def __sub__(self, other: Object | Hole | Misc | Iterable[Object]) -> Object: :param other: another object :return: a Difference of self - other + """ return Difference(self, other) @@ -171,6 +174,7 @@ def translate(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: :param y: y axis translation :param z: z axis translation :return: a translated object + """ if x == y == z == 0: return self @@ -186,51 +190,40 @@ def z_translate(self, z: float) -> Object: return self.translate(z=z) def rightward(self, dist: float) -> Object: - """ - Helper method to apply a Translation to the right on X axis on the current object - :param dist: distance in mm - :return: an object, translated to the right by `dist` mm + """Helper method to apply a Translation to the right on X axis on the current object :param + dist: distance in mm :return: an object, translated to the right by `dist` mm. """ return self.x_translate(dist) def leftward(self, dist: float) -> Object: - """ - Helper method to apply a Translation to the left on X axis on the current object - :param dist: distance in mm - :return: an object, translated to the left by `dist` mm - """ + """Helper method to apply a Translation to the left on X axis on the current object :param + dist: distance in mm :return: an object, translated to the left by `dist` mm.""" return self.x_translate(-dist) def forward(self, dist: float) -> Object: - """ - Helper method to apply a forward Translation on Y axis on the current object - :param dist: distance in mm - :return: an object, translated forwards by `dist` mm + """Helper method to apply a forward Translation on Y axis on the current object :param dist: + + distance in mm :return: an object, translated forwards by `dist` mm. + """ return self.y_translate(dist) def backward(self, dist: float) -> Object: - """ - Helper method to apply a backward Translation on Y axis on the current object - :param dist: distance in mm - :return: an object, translated backwards by `dist` mm - """ + """Helper method to apply a backward Translation on Y axis on the current object :param + dist: distance in mm :return: an object, translated backwards by `dist` mm.""" return self.y_translate(-dist) def up(self, dist: float) -> Object: - """ - Helper method to apply a upwards Translation on Z axis on the current object - :param dist: distance in mm - :return: an object, translated upwards by `dist` mm + """Helper method to apply a upwards Translation on Z axis on the current object :param dist: + + distance in mm :return: an object, translated upwards by `dist` mm. + """ return self.z_translate(dist) def down(self, dist: float) -> Object: - """ - Helper method to apply a downwards Translation on Z axis on the current object - :param dist: distance in mm - :return: an object, translated downwards by `dist` mm - """ + """Helper method to apply a downwards Translation on Z axis on the current object :param + dist: distance in mm :return: an object, translated downwards by `dist` mm.""" return self.z_translate(-dist) def rotate( @@ -249,6 +242,7 @@ def rotate( :param y: y angle :param z: z angle :return: a rotated object + """ x = normalize_angle(x) y = normalize_angle(y) @@ -266,31 +260,22 @@ def rotate( def x_rotate( self, angle: float, center_y: float = 0, center_z: float = 0 ) -> Object: - """ - Helper method to apply a Rotation on X axis on the current object - :param angle: angle in degrees - :return: an object, rotated by `angle` degrees on X axis - """ + """Helper method to apply a Rotation on X axis on the current object :param angle: angle in + degrees :return: an object, rotated by `angle` degrees on X axis.""" return self.rotate(x=angle, center_y=center_y, center_z=center_z) def y_rotate( self, angle: float, center_x: float = 0, center_z: float = 0 ) -> Object: - """ - Helper method to apply a Rotation on Y axis on the current object - :param angle: angle in degrees - :return: an object, rotated by `angle` degrees on Y axis - """ + """Helper method to apply a Rotation on Y axis on the current object :param angle: angle in + degrees :return: an object, rotated by `angle` degrees on Y axis.""" return self.rotate(y=angle, center_x=center_x, center_z=center_z) def z_rotate( self, angle: float, center_x: float = 0, center_y: float = 0 ) -> Object: - """ - Helper method to apply a Rotation on Z axis on the current object - :param angle: angle in degrees - :return: an object, rotated by `angle` degrees on Z axis - """ + """Helper method to apply a Rotation on Z axis on the current object :param angle: angle in + degrees :return: an object, rotated by `angle` degrees on Z axis.""" return self.rotate(z=angle, center_x=center_x, center_y=center_y) def left_to_right(self) -> Object: @@ -418,6 +403,7 @@ def upside_down(self, x_axis: bool = False) -> Object: Equivalent to self.y_rotate(180). If x_axis is True, rotate on x axis instead (like top_to_bottom()). :return: an object rotated 180° on X or Y axis + """ if x_axis: return self.x_rotate(180) @@ -431,6 +417,7 @@ def scale(self, *, x: float = 1.0, y: float = 1.0, z: float = 1.0) -> Object: :param y: y ratio :param z: z ratio :return: a scaled object + """ return Scaling(x=x, y=y, z=z)(self) @@ -441,6 +428,7 @@ def mirror(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: :param y: y mirror factor :param z: z mirror factor :return: a mirrored object + """ return Mirroring(x=x, y=y, z=z)(self) @@ -450,6 +438,7 @@ def x_mirror(self, center: float = 0.0, keep: bool = False) -> Object: :param center: the X coordinate of the axis to mirror on :param keep: if True, the initial object is kept in addition to its mirror :return: a mirrored object + """ if keep: return self.x_mirror(center, keep=False) + self @@ -461,6 +450,7 @@ def y_mirror(self, center: float = 0.0, keep: bool = False) -> Object: :param center: the Y coordinate of the axis to mirror on :param keep: if True, the initial object is kept in addition to its mirror :return: a mirrored object + """ if keep: return self.backward(center).mirror(y=1).forward(center) + self @@ -472,6 +462,7 @@ def z_mirror(self, center: float = 0.0, keep: bool = False) -> Object: :param center: the Y coordinate of the axis to mirror on :param keep: if True, the initial object is kept in addition to its mirror :return: a mirrored object + """ if keep: return self.down(center).mirror(z=1).up(center) + self @@ -501,6 +492,7 @@ def linear_extrude( :param scale: :param segments: number of segments. If None, automatically determines the number of segments to get a good-looking round result. + """ return LinearExtrusion( height=height, @@ -606,6 +598,7 @@ def rotational_extrude( :param segments: number of segments. If None, automatically determines the number of segments to get a good-looking round result. :return: + """ return RotationalExtrusion( angle=angle, @@ -669,6 +662,7 @@ def color(self, name: str, alpha: Optional[float] = None) -> Object: :param name: :param alpha: :return: + """ return Color(name, alpha=alpha)(self) @@ -676,6 +670,7 @@ def hole(self) -> Hole: """Turns this object into a Hole. :return: a Hole + """ return Hole(self) @@ -683,6 +678,7 @@ def misc(self) -> Misc: """Turns this object into a Misc item. :return: a Misc + """ return Misc(self) @@ -690,6 +686,7 @@ def __invert__(self) -> Hole: """Operator alternative to .hole(). :return: a Hole based on this object + """ return self.hole() @@ -815,6 +812,7 @@ class Primitive(Object): """Base class for simple objects with no children. Those are the primitive types such as Cube, Sphere, etc. Do not instantiate this class directly. + """ def __init__(self, **kwargs: Any): @@ -826,6 +824,7 @@ def _arguments(self) -> Dict[str | None, Any]: This must be implemented by subclasses :return: a dict of arguments as {"param_name": arg_value} + """ return self.arguments # type: ignore[return-value] @@ -835,6 +834,7 @@ def _iter_arguments( """Iterates over arguments. :return: a iterator of (key, val) tuples + """ for key, val in self._arguments().items(): if val is None: @@ -861,6 +861,7 @@ def _render_arguments(cls, params: Iterable[Tuple[Optional[str], Any]]) -> str: (anything between the parenthesis) :return: a str + """ return ", ".join(f"{key}={val}" if key else f"{val}" for key, val in params) @@ -869,6 +870,7 @@ def render(self) -> str: """Render this object as OpenSCAD code. :return: a str of OpenSCAD code + """ return f"{self.modifier}{self.object_name}({self._render_arguments(self._iter_arguments())});" @@ -889,6 +891,7 @@ def add_child(self, child: Object | Iterable[Object] | Literal[0]) -> Object: """Add a children object to this Composite :param child: :return: + """ if child == 0: # for sum(*Objects) return self @@ -923,6 +926,7 @@ def _render_children(cls, children: Iterable[Object]) -> str: """Renders the children of this object as OpenSCAD code (anything between the brackets). :return: a str + """ return ( "{" + "".join(f"\n{indent(child.render())}" for child in children) + "\n}" @@ -933,6 +937,7 @@ def render(self) -> str: """Render this composite as valid OpenSCAD code. :return: a str + """ return ( f"{self.modifier}{self.object_name}() " @@ -1084,6 +1089,7 @@ class Transformation(Primitive): """Base class for transformations. MuSCAD Transformations can have 1 single child (which can be a Union of multiple children) + """ def __init__(self, *children: Object): @@ -1275,6 +1281,7 @@ def __invert__(self) -> Hole: """Operator alternative to .hole(). :return: a Hole based on this object + """ return self.object.hole() @@ -1365,6 +1372,7 @@ def calc( """Given at least 2 of from_, center, and distance, returns all 4. If only distance is given, default to center = 0 + """ if distance is not None and from_ is None and to is None and center is None: center = 0 diff --git a/src/muscad/helpers.py b/src/muscad/helpers.py index aaca520..3c881ea 100644 --- a/src/muscad/helpers.py +++ b/src/muscad/helpers.py @@ -11,6 +11,7 @@ def normalize_angle(angle: float) -> float: :param angle: an angle value, in degrees :return: an angle between 0 included and 360 excluded + """ while angle < 0: angle = angle + 360 @@ -77,6 +78,7 @@ def camel_to_snake(name: str) -> str: :param name: a name in CamelCase :return: a name in snake_case + """ name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() diff --git a/src/muscad/part.py b/src/muscad/part.py index c675687..4931171 100644 --- a/src/muscad/part.py +++ b/src/muscad/part.py @@ -38,6 +38,7 @@ class Part(Composite): Those objects can be added to that Part using add_child(), add_misc() or add_hole(). All class attributes that are instances of Object, Misc or Hole will be added to all instances of this Part. + """ class_parts: list[Object] @@ -63,6 +64,7 @@ def _init_element(cls, name: str, obj: Misc | Hole | Object) -> None: :param name: the attribute name :param obj: the attribute value :return: + """ if isinstance(obj, Misc): obj = obj.object @@ -88,6 +90,7 @@ def __init__( :param args: :param kwargs: + """ super().__init__() self.children = self.class_parts.copy() if hasattr(self, "class_parts") else [] @@ -106,6 +109,7 @@ def init( :param kwargs: :return: + """ for o in args: if isinstance(o, Misc): @@ -152,6 +156,7 @@ def revert(self) -> Part: Note that misc items are untouched, so it probably makes no sense to revert a part containing misc items. :return: the same part, with holes and children reverted + """ self.children, self.holes = self.holes, self.children return self @@ -207,6 +212,7 @@ def postprocess(self, renderable: Object) -> Object: can be overridden in subclasses. By default, it does nothing. :param renderable: the part to postprocess for rendering :return: the postprocessed renderable + """ return renderable @@ -355,6 +361,7 @@ class RotationalExtrudedPart(Part): You must build your part flat along the Y axis and have the shape defined on the positive X axis. + """ def init( # type: ignore[override] diff --git a/src/muscad/transformations.py b/src/muscad/transformations.py index 84de648..05a7560 100644 --- a/src/muscad/transformations.py +++ b/src/muscad/transformations.py @@ -671,6 +671,7 @@ class Slide(Transformation): If the object is a Composite, each part component is hulled to its translated self. This is useful for parts that must be slided into their final position, such as screws. Bounding box of the original object is untouched.. + """ def __init__(self, *, x: float = 0, y: float = 0, z: float = 0): diff --git a/src/muscad/utils/surface.py b/src/muscad/utils/surface.py index cf4775c..3c03fd9 100644 --- a/src/muscad/utils/surface.py +++ b/src/muscad/utils/surface.py @@ -88,6 +88,7 @@ def rounded_corners( """Makes a square surface with rounded corners. Each corner can have a different radius + """ return cls.custom_corners( fl=Circle(fl) if fl > 0 else Square(1, 1), @@ -122,6 +123,7 @@ def regular_rounded_corners( :param d: :param kwargs: :return: + """ return cls.rounded_corners( d, @@ -151,6 +153,7 @@ def circle_from_3_points( :param x3: third point X coordinate :param y3: third point Y coordiante :return: a Surface that is a Circle touching the 3 points + """ temp = x2**2 + y2**2 bc = (x1**2 + y1**2 - temp) / 2 @@ -176,11 +179,12 @@ def regular_polygon(cls, nb_sides: int, radius: float) -> Object: """Returns the largest regular Polygon with `nb_sides` sides contained in a circle of `radius`. - Difference compared to a simple Circle(segments=nb_sides) is that the object dimensions - are those of the Polygon, not the circle. + Difference compared to a simple Circle(segments=nb_sides) is that the object dimensions are + those of the Polygon, not the circle. :param nb_sides: :param radius: :return: a 2D Polygon + """ if nb_sides <= 2: return Square(radius, 0) diff --git a/src/muscad/vitamins/bolts.py b/src/muscad/vitamins/bolts.py index 3b1f4cc..86c4995 100644 --- a/src/muscad/vitamins/bolts.py +++ b/src/muscad/vitamins/bolts.py @@ -2,6 +2,7 @@ Some measurements are taken from the MCAD library at https://github.com/SolidCode/MCAD/blob/master/nuts_and_bolts.scad + """ from typing_extensions import Self diff --git a/src/muscad/vitamins/steppers.py b/src/muscad/vitamins/steppers.py index 8f6f873..b9005f8 100644 --- a/src/muscad/vitamins/steppers.py +++ b/src/muscad/vitamins/steppers.py @@ -45,6 +45,7 @@ def add_central_bulge(self, d: float, h: float) -> Self: :param d: diameter of the bulge :param h: height of the bulge :return: the stepper object, with bulge added + """ self.central_bulge = ( Cylinder(d=d, h=h + 1) @@ -87,6 +88,7 @@ def add_shaft(self, d: float, length: float) -> Self: :param d: diameter of the shaft :param length: lenght of the shaft :return: the stepper object, with shaft added + """ self.shaft = ( Cylinder(d=d, h=length + 2) @@ -116,6 +118,7 @@ def nema17( :param holes: holes index to screw bolts in :param T: tolerance :return: a StepperMotor + """ width = 42.3 spacing = 31.04 diff --git a/tests/test_part.py b/tests/test_part.py index fa7f56a..515cc90 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -7,6 +7,7 @@ def test_part() -> None: """Basic tests for a Part. Checks that a Part contain a single child has the same bounding box as this child. + """ cube = Cube(8, 10, 12) part = Part() From af8867c003ac97b756e044dac849900441392bf7 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Fri, 10 Nov 2023 08:46:44 +0100 Subject: [PATCH 02/13] cleanups --- .github/dependabot.yml | 18 ++ .github/labels.yml | 66 ++++ .github/release-drafter.yml | 29 ++ .github/workflows/constraints.txt | 5 + .github/workflows/labeler.yml | 19 ++ .github/workflows/release.yml | 79 +++++ .github/workflows/tests.yml | 141 +++++++++ .pre-commit-config.yaml | 10 +- docs/conf.py | 2 +- examples/drying_rack_fix.py | 103 +++++++ examples/infinity_cube.py | 28 ++ examples/lauburu.py | 24 ++ examples/lauburu.stl | Bin 0 -> 43284 bytes examples/muscad_printer.py | 184 ++++++++++-- examples/numbers.py | 67 ++++- poetry.lock | 82 ++--- pyproject.toml | 3 + src/muscad/base.py | 466 +++++++++++++++++++++-------- src/muscad/part.py | 17 +- src/muscad/point.py | 16 +- src/muscad/primitives.py | 32 +- src/muscad/transformations.py | 44 +-- src/muscad/utils/stack.py | 4 +- src/muscad/utils/surface.py | 4 +- src/muscad/utils/tube.py | 35 +-- src/muscad/utils/volume.py | 25 +- src/muscad/vitamins/bearings.py | 5 +- src/muscad/vitamins/boards.py | 2 + src/muscad/vitamins/cable_chain.py | 4 +- src/muscad/vitamins/fans.py | 6 +- src/muscad/vitamins/gears.py | 10 +- src/muscad/vitamins/pulleys.py | 7 +- src/muscad/vitamins/rods.py | 2 +- src/muscad/vitamins/steppers.py | 10 +- 34 files changed, 1260 insertions(+), 289 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/labels.yml create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/constraints.txt create mode 100644 .github/workflows/labeler.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/tests.yml create mode 100644 examples/drying_rack_fix.py create mode 100644 examples/infinity_cube.py create mode 100644 examples/lauburu.py create mode 100644 examples/lauburu.stl diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a0a5c73 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: daily + - package-ecosystem: pip + directory: "/.github/workflows" + schedule: + interval: daily + - package-ecosystem: pip + directory: "/docs" + schedule: + interval: daily + - package-ecosystem: pip + directory: "/" + schedule: + interval: daily diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..f7f83aa --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,66 @@ +--- +# Labels names are important as they are used by Release Drafter to decide +# regarding where to record them in changelog or if to skip them. +# +# The repository labels will be automatically configured using this file and +# the GitHub Action https://github.com/marketplace/actions/github-labeler. +- name: breaking + description: Breaking Changes + color: bfd4f2 +- name: bug + description: Something isn't working + color: d73a4a +- name: build + description: Build System and Dependencies + color: bfdadc +- name: ci + description: Continuous Integration + color: 4a97d6 +- name: dependencies + description: Pull requests that update a dependency file + color: 0366d6 +- name: documentation + description: Improvements or additions to documentation + color: 0075ca +- name: duplicate + description: This issue or pull request already exists + color: cfd3d7 +- name: enhancement + description: New feature or request + color: a2eeef +- name: github_actions + description: Pull requests that update Github_actions code + color: "000000" +- name: good first issue + description: Good for newcomers + color: 7057ff +- name: help wanted + description: Extra attention is needed + color: 008672 +- name: invalid + description: This doesn't seem right + color: e4e669 +- name: performance + description: Performance + color: "016175" +- name: python + description: Pull requests that update Python code + color: 2b67c6 +- name: question + description: Further information is requested + color: d876e3 +- name: refactoring + description: Refactoring + color: ef67c4 +- name: removal + description: Removals and Deprecations + color: 9ae7ea +- name: style + description: Style + color: c120e5 +- name: testing + description: Testing + color: b1fc6f +- name: wontfix + description: This will not be worked on + color: ffffff diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..7a04410 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,29 @@ +categories: + - title: ":boom: Breaking Changes" + label: "breaking" + - title: ":rocket: Features" + label: "enhancement" + - title: ":fire: Removals and Deprecations" + label: "removal" + - title: ":beetle: Fixes" + label: "bug" + - title: ":racehorse: Performance" + label: "performance" + - title: ":rotating_light: Testing" + label: "testing" + - title: ":construction_worker: Continuous Integration" + label: "ci" + - title: ":books: Documentation" + label: "documentation" + - title: ":hammer: Refactoring" + label: "refactoring" + - title: ":lipstick: Style" + label: "style" + - title: ":package: Dependencies" + labels: + - "dependencies" + - "build" +template: | + ## Changes + + $CHANGES diff --git a/.github/workflows/constraints.txt b/.github/workflows/constraints.txt new file mode 100644 index 0000000..7f9f429 --- /dev/null +++ b/.github/workflows/constraints.txt @@ -0,0 +1,5 @@ +pip==21.1.3 +nox==2021.6.12 +nox-poetry==0.8.6 +poetry==1.1.7 +virtualenv==20.6.0 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..e7bd6a2 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,19 @@ +name: Labeler + +on: + push: + branches: + - main + - master + +jobs: + labeler: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2.3.4 + + - name: Run Labeler + uses: crazy-max/ghaction-github-labeler@v3.1.1 + with: + skip-delete: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9666d01 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,79 @@ +name: Release + +on: + push: + branches: + - main + - master + +jobs: + release: + name: Release + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v2.3.4 + with: + fetch-depth: 2 + + - name: Set up Python + uses: actions/setup-python@v2.2.2 + with: + python-version: "3.9" + + - name: Upgrade pip + run: | + pip install --constraint=.github/workflows/constraints.txt pip + pip --version + + - name: Install Poetry + run: | + pip install --constraint=.github/workflows/constraints.txt poetry + poetry --version + + - name: Check if there is a parent commit + id: check-parent-commit + run: | + echo "::set-output name=sha::$(git rev-parse --verify --quiet HEAD^)" + + - name: Detect and tag new version + id: check-version + if: steps.check-parent-commit.outputs.sha + uses: salsify/action-detect-and-tag-new-version@v2.0.1 + with: + version-command: | + bash -o pipefail -c "poetry version | awk '{ print \$2 }'" + + - name: Bump version for developmental release + if: "! steps.check-version.outputs.tag" + run: | + poetry version patch && + version=$(poetry version | awk '{ print $2 }') && + poetry version $version.dev.$(date +%s) + + - name: Build package + run: | + poetry build --ansi + + - name: Publish package on PyPI + if: steps.check-version.outputs.tag + uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.PYPI_TOKEN }} + + - name: Publish package on TestPyPI + if: "! steps.check-version.outputs.tag" + uses: pypa/gh-action-pypi-publish@v1.4.2 + with: + user: __token__ + password: ${{ secrets.TEST_PYPI_TOKEN }} + repository_url: https://test.pypi.org/legacy/ + + - name: Publish the release notes + uses: release-drafter/release-drafter@v5.15.0 + with: + publish: ${{ steps.check-version.outputs.tag != '' }} + tag: ${{ steps.check-version.outputs.tag }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..5eb4f28 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,141 @@ +name: Tests + +on: + - push + - pull_request + +jobs: + tests: + name: ${{ matrix.session }} ${{ matrix.python-version }} / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - { python-version: 3.9, os: ubuntu-latest, session: "pre-commit" } + - { python-version: 3.9, os: ubuntu-latest, session: "safety" } + - { python-version: 3.9, os: ubuntu-latest, session: "mypy" } + - { python-version: 3.8, os: ubuntu-latest, session: "mypy" } + - { python-version: 3.7, os: ubuntu-latest, session: "mypy" } + - { python-version: 3.6, os: ubuntu-latest, session: "mypy" } + - { python-version: 3.9, os: ubuntu-latest, session: "tests" } + - { python-version: 3.8, os: ubuntu-latest, session: "tests" } + - { python-version: 3.7, os: ubuntu-latest, session: "tests" } + - { python-version: 3.6, os: ubuntu-latest, session: "tests" } + - { python-version: 3.9, os: windows-latest, session: "tests" } + - { python-version: 3.9, os: macos-latest, session: "tests" } + - { python-version: 3.9, os: ubuntu-latest, session: "typeguard" } + - { python-version: 3.9, os: ubuntu-latest, session: "xdoctest" } + - { python-version: 3.9, os: ubuntu-latest, session: "docs-build" } + + env: + NOXSESSION: ${{ matrix.session }} + + steps: + - name: Check out the repository + uses: actions/checkout@v2.3.4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2.2.2 + with: + python-version: ${{ matrix.python-version }} + + - name: Upgrade pip + run: | + pip install --constraint=.github/workflows/constraints.txt pip + pip --version + + - name: Install Poetry + run: | + pip install --constraint=.github/workflows/constraints.txt poetry + poetry --version + + - name: Install Nox + run: | + pip install --constraint=.github/workflows/constraints.txt nox nox-poetry + nox --version + + - name: Compute pre-commit cache key + if: matrix.session == 'pre-commit' + id: pre-commit-cache + shell: python + run: | + import hashlib + import sys + + python = "py{}.{}".format(*sys.version_info[:2]) + payload = sys.version.encode() + sys.executable.encode() + digest = hashlib.sha256(payload).hexdigest() + result = "${{ runner.os }}-{}-{}-pre-commit".format(python, digest[:8]) + + print("::set-output name=result::{}".format(result)) + + - name: Restore pre-commit cache + uses: actions/cache@v2.1.6 + if: matrix.session == 'pre-commit' + with: + path: ~/.cache/pre-commit + key: ${{ steps.pre-commit-cache.outputs.result }}-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: | + ${{ steps.pre-commit-cache.outputs.result }}- + + - name: Run Nox + run: | + nox --force-color --python=${{ matrix.python-version }} + + - name: Upload coverage data + if: always() && matrix.session == 'tests' + uses: "actions/upload-artifact@v2.2.4" + with: + name: coverage-data + path: ".coverage.*" + + - name: Upload documentation + if: matrix.session == 'docs-build' + uses: actions/upload-artifact@v2.2.4 + with: + name: docs + path: docs/_build + + coverage: + runs-on: ubuntu-latest + needs: tests + steps: + - name: Check out the repository + uses: actions/checkout@v2.3.4 + + - name: Set up Python 3.9 + uses: actions/setup-python@v2.2.2 + with: + python-version: 3.9 + + - name: Upgrade pip + run: | + pip install --constraint=.github/workflows/constraints.txt pip + pip --version + + - name: Install Poetry + run: | + pip install --constraint=.github/workflows/constraints.txt poetry + poetry --version + + - name: Install Nox + run: | + pip install --constraint=.github/workflows/constraints.txt nox nox-poetry + nox --version + + - name: Download coverage data + uses: actions/download-artifact@v2.0.10 + with: + name: coverage-data + + - name: Combine coverage data and display human readable report + run: | + nox --force-color --session=coverage + + - name: Create coverage report + run: | + nox --force-color --session=coverage -- xml + + - name: Upload coverage report + uses: codecov/codecov-action@v1.5.2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 115a2ee..bcb5864 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,6 +5,10 @@ repos: - id: check-merge-conflict - id: check-yaml args: [--unsafe] +- repo: https://github.com/frostming/fix-future-annotations + rev: 0.5.0 # a released version tag + hooks: + - id: fix-future-annotations - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: @@ -19,7 +23,7 @@ repos: - --wrap-summaries=100 - --wrap-descriptions=100 - repo: https://github.com/psf/black - rev: 23.10.0 + rev: 23.11.0 hooks: - id: black - repo: https://github.com/asottile/blacken-docs @@ -27,11 +31,11 @@ repos: hooks: - id: blacken-docs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.1 + rev: v0.1.6 hooks: - id: ruff - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + rev: v1.7.1 hooks: - id: mypy additional_dependencies: diff --git a/docs/conf.py b/docs/conf.py index 90d950d..21a6b5f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ project = "MuSCAD" author = "Guillaume Pujol" -copyright = f"{datetime.now().year}, {author}" +copyright = f"{datetime.utcnow().year}, {author}" extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", diff --git a/examples/drying_rack_fix.py b/examples/drying_rack_fix.py new file mode 100644 index 0000000..1aa8784 --- /dev/null +++ b/examples/drying_rack_fix.py @@ -0,0 +1,103 @@ +from muscad import Part, Cylinder, Volume +from muscad.vitamins.bolts import Bolt + + +class DryingRackFix(Part): + """A reinforcement part for my cheap drying rack made with welded tubes. + + This will keep a tube perdendicular to the other one even after the welding snapped. + + """ + + vertical_tube = ~Cylinder(d=13, h=60).debug() + horizontal_tube = ( + ~Cylinder(d=13, h=60) + .bottom_to_left() + .align( + left=vertical_tube.center_x, + center_y=vertical_tube.center_y, + center_z=vertical_tube.center_z, + ) + .debug() + ) + + left_bolt = ( + ~Bolt.M3(12, head_clearance=10) + .add_nut(-1, inline_clearance_size=10, angle=180) + .bottom_to_back() + .align( + right=vertical_tube.left - 1, + center_y=vertical_tube.center_y, + center_z=horizontal_tube.center_z, + ) + ) + top_bolt = ( + ~Bolt.M3(12, head_clearance=10) + .add_nut(-1, inline_clearance_size=10, angle=180) + .bottom_to_back() + .align( + left=vertical_tube.right + 3, + center_y=vertical_tube.center_y, + bottom=horizontal_tube.top + 1, + ) + ) + bottom_bolt = ( + ~Bolt.M3(12, head_clearance=10) + .add_nut(-1, inline_clearance_size=10, angle=180) + .bottom_to_back() + .align( + left=vertical_tube.right + 3, + center_y=vertical_tube.center_y, + top=horizontal_tube.bottom - 1, + ) + ) + + reinforcement = ( + Volume( + left=left_bolt.left - 1, + right=top_bolt.right + 1, + front=vertical_tube.front + 2, + back=vertical_tube.back - 2, + bottom=bottom_bolt.bottom - 1, + top=top_bolt.top + 1, + ) + .fillet_depth(4, right=True) + .fillet_depth(14, left=True) + ) + + +drying_rack_fix_bottom, drying_rack_fix_top = ( + DryingRackFix().back_to_bottom().align(center_z=0).divide(z=0, T=0.2) +) + +drying_rack_fix_bottom.render_to_file("drying_rack_fix_bottom.scad") +drying_rack_fix_top.render_to_file("drying_rack_fix_top.scad") + + +# drying_rack_fix_top.export_stl("drying_rack_fix_top.scad") + + +class DryingRackFoot(Part): + """A replacement foot for that same drying rack. + + It initially had wheels but one of them feel and got lost. + + """ + + vertical_tube = ~Cylinder(d=13.8, h=60).debug() + feet = Cylinder(d=24, d2=18, h=60).align(top=vertical_tube.bottom + 20) + bolt = ( + ~Bolt.M3(20) + .add_nut(-2) + .bottom_to_left() + .align( + center_x=vertical_tube.center_x + 2, + center_y=vertical_tube.center_y, + center_z=feet.top - 10, + ) + .debug() + ) + + +drying_rack_foot = DryingRackFoot() +drying_rack_foot.render_to_file() diff --git a/examples/infinity_cube.py b/examples/infinity_cube.py new file mode 100644 index 0000000..df9ad04 --- /dev/null +++ b/examples/infinity_cube.py @@ -0,0 +1,28 @@ +from muscad import TT, Part, Volume + + +class Frame(Part): + mirror_x = ~Volume(width=300 + TT, height=300 + TT, depth=3 + TT, left=5) + mirror_y = ~Volume(depth=300 + TT, height=300 + TT, width=3 + TT, back=5) + + outer_shell = ( + Volume(width=20, depth=20, height=300) + .chamfer_height(5, left=True, back=True) + .chamfer_height(10, right=True, front=True) + .align(left=mirror_y.left - 3, back=mirror_x.back - 3) + ) + cable_hole = ( + ~Volume( + width=10, + depth=10, + height=300 + TT, + left=mirror_y.right + 2, + back=mirror_x.front + 2, + ) + .chamfer_height(2, left=True, back=True) + .chamfer_height(9, right=True, front=True) + ) + + +if __name__ == "__main__": + Frame().render_to_file() diff --git a/examples/lauburu.py b/examples/lauburu.py new file mode 100644 index 0000000..1a2aade --- /dev/null +++ b/examples/lauburu.py @@ -0,0 +1,24 @@ +from muscad import Circle, Part, Square + + +class Lauburu(Part): + # base = Import("lauburu.stl").scale(x=2, y=2, z=.1).background() + + def init(self, r: float = 100, width: float = 10) -> None: # type: ignore[override] + shape = ( + Circle(d=r / 2).align(center_x=r * 0.75 + width, center_y=0) + + ( + Circle(d=r + width).align(center_x=(r + width) / 2, center_y=0) + - Circle(d=r / 2).align(center_x=r * 0.25 + width, center_y=0) + & Square(r + width * 2, r).align(left=0, back=0) + ) + ).offset(r=0.4, invert=True) + self.cutter = shape.z_linear_extrude(6) + self.wing = shape.offset(1).z_linear_extrude(5).align(bottom=self.cutter.top) + self.reinforcement = ( + shape.offset(3).z_linear_extrude(3).align(bottom=self.wing.top) + ) + + +if __name__ == "__main__": + Lauburu().render_to_file() diff --git a/examples/lauburu.stl b/examples/lauburu.stl new file mode 100644 index 0000000000000000000000000000000000000000..fa217525fd3ccd0ca25c2ad09165594320a3a23a GIT binary patch literal 43284 zcmb823$R^PdB;~U2sOb5L==f|Ey}}UN$vrO0q)urA(TdW8YvWqLZFaI0)vD%Ve_a@ z3REpZ>%^2Y4rApt3L=twjtrQTSHXf0zz(gvUs`Z@M(DTqIobc;_rc!MJHznL{eS=U zU*FoV^{utL{@3^QFZtV}0*)Sv zDzntY(POabtCKh0x_VXh`@_$DdUD2Z_L%fIdMK*QQWHlHWuDT0WuDk(x3h{WMmTzq zk-E&n(LBhU)Uep=bDKsMmTzqk-E&n(LD4hSvY!}|I~v1;d?w*_np}aM-MVmmsvP^ z=&Cj5(5W|_x@%d~F%6C$iYl|z#L+{Ur&Qgmsw=6A5sn^Yq%N~?^qBwdUHZk9m)2v& zY=xr-8L7)G96gkIN|!u-`{Z6zmKRlwaP)8tN3_AwLz$@00xW0Cz zBO`U0g%vwQ+wC)>zjDe>^;j`m;g|;*sqJ2Zmb{BHPw9-bSO20LPAaMh;pjm|>M{#^ z2Rna0^!v#_+37!auYS*-2uBYxQrlReCGRr!NBd3AJ>|9Gn2%4r>G_jVQAad5dL%GX zms!|5a7@jiZ=zb*}J;+F1W?}D;(&cvLEIjn7daRhOaP%M}b(w{uhcZuTjm<@W@U?kG z6(bxy$VhG1AV%7|D04fPK6b&x4x{IwaP%M}wOt<=Y434Ub#FiZfqJZ%t#I@pBXyaD zy#vQ2rH5?}f58c}iz-GqdXV9`2}ciQp3+NGubX&!G`|v#9%Q7p^9m#FT^_eN!&UEC zQ;!w16^Gc~Qj(M-MU_H{s}^%u|~4z`YZ9kLK;d(SwZC zWfqPe)4#s7f5sEn)?>wNg`)=w0iv z?XyiiIKt7xW?&5?b(w{|LrMo*jkEaCOY5;>w!+bajMQZojvjh)+T7=yJaOx_Q;I4^ zIC?k+&t10UU6i@~ev7pe6W_kIuA_yc2N|jBdaxO3@1g3>kDD@&9%Q60v#@vIn51;d zo&AZe7OY=XF~ZS<49874dPre)=JwO`>|$E&Asju(NL^;(=<(ijQ{L!5zc9D@{SBMG zIM{#Q4^?;eJl}9ifB(Ce>{`VLM-MVm+c8Hq>0Ok0O7H#Mc)oe( zcNJBHaP%M}wbh&$Y44%T?Yy!pc#b_Ws|exfK}PB_3wsClH>LTv9gx5B_{^e;5sn^Y zn3HhyP_Jy>Zuj=5>}giT2uBYxQro=*qYzz+IU9dD+5M^#cuXoriGrAS1QaY8Yv? zUm;~atjRxXqgwsGsvw6rdXSOY=It2iss;ThbHBc;wchAKMrvD6#7L|C3Mq4&&)GRO zVHsDy7mgleq_+7LM!IT2KgxW#)-!wLQLRi2ar7V~^{_r)H_~dqLdrZPu5t@U4>D5Q zb?#Qu>7vYS+@9TI;+WAf7mgleq%N~C+pn-PZ`Kg&<1t$CG8{d~NIhKlSvS(&MVZ?( z5i9?~(SwZCWft}hetZu9)#}}!-fnHx_i^BQUL+kEj+?Mzhe(+>>(upFjaE|MWUR|)86_M&$S^12=%HSvwD#W* z$S1GAu&82$qX!wOZN7_9@Gk0=UuV)vQS=}q^>E){-6(hu^~&~*o*&P1Zdg)O5yH`f zjMQZo_71-0m|+#f_}aVbb5%HckYOz%96i*ll(b7m96c0e`&>=t#L+{&@_TIDG4mQS zQrmqOqu>>q_1QBKdu+ln4>D5QS_WpB-osxx+S_bD6FnnE`!V;76dGyYC!TQ?BZZDr zRQRW`6zxwOKgW^6w?rR5gOtLzM1`L_OW|9h!dE6z_?D>f^^z36B`SP1Dur)}3P0{r zwC|;QMhflFu>A^qMhfn7c+IS7`%P{++6VSgP&*0N?wuOvm=jwaCm@@>hZedW$GQnasM zc}9x%Cyr;NXur36MhZP8+~Ko-ct(o$cMY_=fTO5gYuo?Ap11cP!*5T#(Z-*k!|x?lMys=Wsp zo$Y+{EPCiJ>Z7Rk9%OWNaTXpubZ_@jRC^CHx*p5Smb^kw93MrsSCG*)$X%J3hn|Bz zifZpcMptc_*^*c2>FlGZ_6jn(&TSQ^*&mL1XqMrlsP-OYG~?hda`e#L$461^J;-RT z#a;dAp?RW@8m#xqEk|yA+I4h25oKl&bhH1*5c5(4}CMR%p8vjM8dLmx57R zZRt`lN~b=xz#m1GS!!ZNS9sOpXYvt8uIN|z>|5^3JAHg%z|lidWtN(_YKuSWRkFm3 z$)7*Bd{FVi(SwZCRuy9u=-~Ul^Rw6G4}N#EIuGIKL54XAdxw+`dhCpR%PpU(+dc4! zGH-Hd8F2JaRGFnFjvne&N`Jidh54A*ue7H#ar97BnWZL<9uI$dY5w};mUZ9NE9MtH z6lIlgqb80X>Qzc7{&>$kV{*$n58>!RM(Q#PM~};|T#(ni^l;s-dS!3WqK95XMrwP@ z8!dT-&P4nDlTYk=_h#jwj%aYqLs4axn%Fy}wD9L2&0jp?8+9J+iE#8FBXyaDqlbEB zZ%{3{ed75aoLT1~96cPvY#SUscAGvUU$l6~IuG_lIC_wgy3E4SL%p)&eDj`p*W*sA z^AL_6WTdtl7o*@^jvi#Bw%IaT z@*e6{O1Ij*{eQ1Mq|QS)dXSO2%);Iwr3dZYy>ZQxbsp@AaP%M}b(w{uhkBLL^Y*Mg zY@c~`9>URsjMQZojvi0jz2xFw-CyUyo(M+|GE$dWIC`j8DNS4QOzv$zyUs&6dXSO2 z%)-%Q=2Z*wdZ(uol#^AL_6WTY;$aP-)1!P5NG z1=rSjuqVRNgN)R6Pee=JMZK~&YfenXveCUnIC_wgy3E4fA*J(d?sM~jm)3c(C&JN# zjMQZojvl(h+1&8dVshCrQwCLxaP)9Ya~HL!21gIg$b3%fRrDYub(w{uhkBLLhJP`b zyl49QgDOTidXSOY<_s7G?;=G?w^*I<$P4eNJ%pnN8L7)G>>Yf&_s;J1mp#2n?I9dJ z$k0zXdPrfruNxlN&)=F^dk9AlGE$dWIC@Bt(g&|u)W7fQZEFwV=s`wm`%5JB(7Qp|6q(SwZCWft}hzFk(6!qI~a{e+{36!t~}DpTR;K}KquQ=>xmE>hSW?&?MTOaJZQ zK@}k!J;+FHZ}y{y-a`ueYtRi2?EmRaN7f#~(SwZCHupgfy@wPjeZsDfr(?f_6M<~oxTx%(0nt|g~% zcS|T-OHSeL;ZV4ioWea>pl~fYg?rvX;aYME_oRoywd53T+@WY+TXGB(s_vbE{lhU( zunPB@yEol;;?B{!n2Vy?Nm#!+^;=SK1+u-ma}3&TucK7m)x)5*(AF6l6{@L%+GWlt zRWDr%*I(q(UPn0wiuQ_yJy9*v)hpK)Jld-X$3W3OZ+8q7?N0;8K+%5Rbqo}`gS*3L z|8NWx?Q5^QVQopfS@su|_zPF>LPo1NDY=%Mge#3&nd(xoEqbseXOXl*Yx5GUI?~T8 z$go|unw#z836#Mv;ST8Y(YOhh{<<;#9)ttV%Ygee+w&RQn zyIsL+o9cE2@3mBSD4I1yfA;Boi?b!iG-``#upe8$qo8w_Yp-1%IxD$C9~C;6d=%Bb zUu1MvayKJ-XcT=E)!u`Q&f_w(C9lx^%12S{6=Za_bC)dUp}VM$qS||q(beT=%U+>- zyN{yUE6C`2WHk}<&=bc;QSCj*=o;i|RP@ku&__}2J;>;)<;q?3(9_vRQSCj*=sM>r zXY|l4!$(o=J;*%RtXXqKIC^OAXYavY z(L=MrE(N37J=iN3)yz1e2Nk+&bFU~WH1iHotsa`)mziq%`LR+pVeZZDo8eXuRV>_h z>{6&I;x2oaLUk?QFX&S6sHtx2QZPytU6+DUsv5f#j8bLVrC^k*-!28CRAF~17^Nz{ zOTj3u6m%&VrPaDF1*5du(xqUOR$IChjM8dLmx56|E8DM+nY$OoIsbEJwUX8w40^?< zUOc77@BB$F(lPTsR{h>zgH$l;lpoG7wi&6t;=#`?D_+}Tdfo0rhn-&h<@fs4&UjDQ za&kXpq_!PJwB!V7lXF(&AOFKMRXe?Jp0zNa`^0?#M-N5W`zwu_IA*S1rQV$N&MF@L zWUliNjvi#Bws$Tu3O*0@D)r8PVza#SyRNVE5RM*Xq%N~?%wwnZ_RR}spINti+o@&# z^gGWEIC?0`{>G$H6GspAD)sh0?W&25_P((85RM*Xq_)4Iz$o~!n!0w&{OGrq*6pfS zsV5vg$VhE}kA;@JhkBKIzy01;6N{(qRr?7?4>D4hSvZc>6`y-#;<6VP)a|k-!qJ0_ z)b_kWOFj>siK$m^Sr#kL-e5P20FEAtvNsZ%vz<8Ramug0G4a_C>{92!o(M+|GE&>; zc%dbqhkBKI`>#H;xO2)K6IC9<(SwZCWfqQk9Dl^DiH|OrQRl&)2uBYxQrq9hp(US( zdX;*YEI+B(WTWAEB^*7-NL^;(n8!g=U(1Kg-LD>>DZ8g)1)s-$Ywyo%N7p%fA{;%)NNs>Q(C9xmzms{6hWt)WFe0QDv5zIC`9R!ln6w(Y%B`5sn^Yq%N~? z^w1qH_3k@)O0i)7;T%pldXSO2%)-$_v!&EyPlTff8L7)G96i*l)El3UkiCZ#skipVgNw1mL9AO7N9dB@RnP&j&!k-E&n(L=pTJ+2f8M-MVm zmsvP^s8^|{RRiMap{O!TO&mRVHgJWd{RonwqWyS~p`tx=GE`^`V;;N%P|<$X^1eg` zTk_{F@0L`sC9lx^JoTtxOJ2dJ7Zq&DEBH*Mf-QLkXA@MgC9mMPQ_;S*M1~4g_is3Y zP2KOus=apJ!4&_r`>^I@6#aZ2tY4{qYYIlSSCC|=Xs^{&LHf*joO~WssBZIWuV9oa zq%H;f%RJg^H8NDRS4HfJ>Yc7$u`PPES1e?xP@NFZL^4#gKMlxGp?h%jAVY=j;88(_ ziuScvtqxXgHGj!$Gu8%I^`ZL_Bei}0E>4}1=+4p zMW1?9w<~yUQ{ArM8BTS(LdTisE7wKiY)NM#SAn^D9u+#yK1%0XRFKgb!<~kxP=-E= zYJbeh=p5s&O!Uyb#79x>J;>-xhZ4QSCj*=-lP(HG1g2>!Yal9%OW_aHbzU z^fd5MRC^CHx<0rX6+QHf@=;WK4>G!Fn! za78$JXkOx@sP-OYG!NqHdGyd6&PV;tU~X~67O&XuKvb|-^w3PLOTnmi5B7>tx>rUI zDj21Ca8zhk%-x^}w1*24LbtxF7impq+C{>MJ3P!0i?NTsG)o+)AQL3=J6pT_8-=$!bR?@l@jM7R- zmx57R3F%TWN-H5<3P$m)Y`+H2*t;0K{nuwyv-#fG9lgQ*FP>cEWA4d=;^n{lQT2O& z)l$KzU)(Z(@a>V>E9RWFY;gR{X?4519ees<+wZTccS(igy@ZU^b_Js)?{Uo~EBcE! zezsrr_rdQj?0@*!p9LH}6lL$+HEQCRxq6j)7j1ObV8y<>)_Dj=4>D5QJKGoqpND#t zdUtR3p8hYsdVQUTaP%M}wcUv@3f|+q9rx|8oVC1e_xOv-{@TAkJK*S{DEoWxMok>Y zO1-i-sP4UL@~YJr)*iyqgN)Sn&Lu{{=W)>XTlIJN)^T;a>Q(9qM-MVm+nWPu$$O|* zsaG63HhIN6=GA_}(SwZC_AJ3Dc#pmBdSvq97d}(B%bo~F4>D3)Wr3Ewht9;*+u?)d zVCB*CZn6mA=%Fb4w9w{kCyvK_Y|bT*V7S%v-6GY8M5 zyC$nVgrf%;scpuAQScsjzi0O3iJ#uM&VxM>jvi#BwsRLPc@Om}^`8FHNrTI#56>&% z=s`wmdz%WQ;5~FER!6G&R7YFIX>j%mGE$dWI3Dw~#cTcNb~&g%1_$q*24iEx^GZ18 z;h08kQ4NkB$~^VfT(@X&@tWbiL^yhok=i~Z1taYXp6ydD|g_Z+>lfZx@apWTduFp2jHnJjPyovcKPg;km0`*}V%r^a?Uk+ck)m zyh6Q7y{Sjd8{9H_)(Xcw$VhD~2p9$L@zN#_^#A$;!+QyPA{;%)NNt}3ik7^GdS!El zJ!TJnwr>8=;OL?T3|NM)?dpmm~96iWLZJ#xYmV6%Sm0cgj=7abB z>)QI>B^*7-NNsb5Ci9rbtn;t!uNXaR*%RUDK}Kr(1Z}kB^H8sB$KCEFTduA@V+%NX zD9WyhCUfGL$Jm!H?LR!4`>-d%(SwZCc732FpNH;nsWx zU6eTHp&6OY+tCx@=s`wmTjj(I(C4B4+S_g0u0NP|{czqc96iWLZO6RHFXkad>RrC^ zw81CdKdcjkqX!wOZDk5Q^m*_pj88h|b1=M%gXiElpM7k}d#G3TX7>i~8XWNW?CRMH z9P?0=eV#~@IdL2-DU7*s=HT;_`_~@A(SwZC_C`E<=<|>w^}c@Fwu7%8eQfO^96cP< z^va?d9P^OE*PN<9qX!wO%Pbr{q)0thtisWQjMR3Wqbl}eB}MA7`W22IWTY;$aLhxB z)I0bm2M^|sj)!pcAS1PXYiMhIE``lSe|F^HxmCmSN;rCuk=pK8=%F7gDQvF(r6q$e zKQ_ERgrf%;scjC29(oTw4eU9%;;#OGT{e0SHaL1H%HE)Ao;bwuc&JzQb{keBgrf%; zscqhlQSf=FSE;9!3F7FXD0}uHTj7`o&jzmIv>!n-RJ0!tGE}r@PKFANVa$Vf04mzA zTHcqaU`zgZ@NP*3Tk;Cs&r^>Iw&WFjdQriayn@eED%g@&XlCJi#g@E+<4%wE^(!(| zXayl2K{8ad*XQTiYQ&?v4r@+EQSI|!jZF1hQ!uK%f+Ryld#$Dl(jN~VC!Yrus@uHU zD;T8;sY}8BGLQCJjSLm4km50CPgL)8^@?rLqrGAwLxt*u*efzr=*bWjWT$qC? zvl(FPS|1s<%a*(c$3$zS_P3p_>!M`XE?e>*Y?o15TaHJN6&9nY_8zL$?0qQIXYC4A zslFa;SExR>Dit+(yMk9L)$IzNuT-}ybY5}Qv6}C^l{HILcUXIkvn8EN-hxScQSCj*=&a=aNA%Dr`Y5Ws2N|8m+$)M6y8HMjs=Wspo$cILiypcs`Y5Ws z2N_)-e(viPx>Nfos=b1Yu1D4eZ^#2HQ1qkPdyvsJ$n~q}p{JUUqS||q(N)W}zUZN6 zt&gJGdyvs}&UMk~p*e$(qS||q(fomH$k9VH86QQp_aLL0lZ|w<-X1+P>+@09-#&M+ z;(cpvKOrjE7Ckh#>rybP-GfoO2iu!ft-WF%RA}ZLd!>0a_n_=kb(%K^qC#_Z+c|FL z@LdYk2HdafQmFpmPGy%uH4gXXyA-NY`9?yQg2zL%`z{5eRG)Pz7^NDpOTj4BnOzD- zsg~_hFiQ1umx58M$-5McQr+LBV3gM0x)hAk+DVs!QCd6cQZPzuCtV6g@vLmW2Jr^} zINzby8_jQ+z2eg${9ms&Z~TYVbIiiVHFIJ$5$_C&Dl^o?!9$;b=s#0bpCTGP$Vkm^ zjPcKclvqbeT}7#;@*0hzf0G=eUah9PpJo*Kx54;Kj+lq9;?xs;*Pd(M{^1!Z^j&*V z;Tb9PU3*dC87cH#dr{#TDf~I+Rn1t9`zWb>JjUJ5A4TcA_F}weq|kTmMTKXi(0A=c zg=eJDckM-mXQa?~?L~!Wq|kTmMTKXi(0A%Zg}#L^B_AcV*HGb($EL4N-gxWkRX)1< zm%hcd`j^_RdRvWr^;`er87W31@+Jz;NHH4qH&J*-it5M?ztPqADJ9QHQRPteIL6zE zIz|fIAHzM-Gg6>-4hqjm@lf+!t+8L8kwV|q8WoV|)fyF^ zk;0C}=(}2@!ZT8|e`2qDX1LGwh1%Khxy>1Q`@FrnCSHHtJ_XO}41O1S`!}ZZ{eQlP S-u{j-Ka10SCZzj3%l`wLILTH3 literal 0 HcmV?d00001 diff --git a/examples/muscad_printer.py b/examples/muscad_printer.py index 4bbcf9d..9654091 100644 --- a/examples/muscad_printer.py +++ b/examples/muscad_printer.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import sys from typing import Literal @@ -707,7 +709,7 @@ class XCarriage(Part): ).fillet_depth(2) fan = ( - ~Fan.fan40x40x20(bolts=False) + ~Fan.fan40x40x20(bolt=None) .add_bolts( bolt=Bolt.M3(30).add_nut(-8.5, side_clearance_size=10, angle=180, T=0.1), spacing=32, @@ -1492,8 +1494,8 @@ class XYIdler(Part): inner_y_pulley = ( ~Pulley.placeholder(18, 10.3) .add_clearance(10, 270) - .add_belt_clearance(10, 270, True) - .add_belt_clearance(40, 180) + .add_belt_clearance(10, angle=270, left=True) + .add_belt_clearance(40, angle=180) .align( center_x=gantry.y_stepper.center_x + 10, center_y=z_extrusion.back, @@ -1566,10 +1568,10 @@ def __stl__(self) -> Object: class XYIdlerLeft(XYIdler): outer_y_pulley = ( - ~Pulley.placeholder(18, 10.3) + ~Pulley.placeholder(18, height=10.3) .add_clearance(20, 0) - .add_belt_clearance(40, 180) - .add_belt_clearance(70, 270, True) + .add_belt_clearance(40, angle=180) + .add_belt_clearance(70, angle=270, left=True) .align( center_x=XYIdler.y_rod.center_x + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER, center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, @@ -1589,10 +1591,10 @@ class XYIdlerLeft(XYIdler): ) x_pulley = ( - ~Pulley.placeholder(18, 10.3) + ~Pulley.placeholder(18, height=10.3) .add_clearance(10, 270) - .add_belt_clearance(10, 270, True) - .add_belt_clearance(40, 180) + .add_belt_clearance(10, angle=270, left=True) + .add_belt_clearance(40, angle=180) .align( center_x=outer_y_pulley.center_x + 10, center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, @@ -1616,10 +1618,10 @@ class XYIdlerLeft(XYIdler): class XYIdlerRight(XYIdler): outer_x_pulley = ( - ~Pulley.placeholder(18, 10.3) + ~Pulley.placeholder(18, height=10.3) .add_clearance(20, 0) - .add_belt_clearance(40, 180) - .add_belt_clearance(40, 270, True) + .add_belt_clearance(40, angle=180) + .add_belt_clearance(40, angle=270, left=True) .align( center_x=gantry.y_stepper.center_x, center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, @@ -1638,10 +1640,10 @@ class XYIdlerRight(XYIdler): ) outer_y_pulley = ( - ~Pulley.placeholder(18, 10.3) - .add_clearance(20, 0) - .add_belt_clearance(40, 180) - .add_belt_clearance(70, 270, True) + ~Pulley.placeholder(18, height=10.3) + .add_clearance(20, angle=0) + .add_belt_clearance(40, angle=180) + .add_belt_clearance(70, angle=270, left=True) .align( center_x=gantry.y_stepper.center_x, center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, @@ -2630,7 +2632,7 @@ class ZBracketBottom(Part): ) -class ZBracketTop(Part): +class ZBracketTopLeft(Part): extrusion = ~frame.y_extrusion_left_middle rod = ~gantry.z_rod_left_front @@ -2711,7 +2713,7 @@ def __stl__(self) -> Object: return self.upside_down() -z_bracket_top_left_front = ZBracketTop() +z_bracket_top_left_front = ZBracketTopLeft() z_bracket_top_left_back = z_bracket_top_left_front.y_mirror( gantry.z_threaded_rod_left.center_y ) @@ -2719,6 +2721,77 @@ def __stl__(self) -> Object: z_bracket_top_right_back = z_bracket_top_left_back.x_mirror(bed.center_x) +class ZBracketTop(MirroredPart, y=True, keep_y=True): + extrusion = ~frame.y_extrusion_left_middle.debug() + rod = ~gantry.z_rod_left_front.debug() + + body = ( + Volume( + left=extrusion.left + 3, + right=extrusion.right - 1, + back=0, + front=rod.front - 10, + bottom=extrusion.bottom + 4, + top=rod.top - E, + ) + .fillet_height(4, left=True, front=True) + .fillet_width(4, bottom=True) + ) + + rod_holder = Surface.free( + Circle(d=16).align(center_x=extrusion.right - 1, front=rod.back), + Circle(d=6).align(right=rod.right + 3, back=rod.back - 5), + Circle(d=6).align(right=rod.right + 3, front=rod.front + 3), + Circle(d=4).align(left=rod.left - 15, front=rod.front + 3), + Circle(d=4).align(center_x=body.right, front=body.front), + ).z_linear_extrude(top=body.top, distance=10) + + rod_holder_bolt = ( + ~Bolt.M3(20, head_clearance=20) + .add_nut(-4, angle=90, inline_clearance_size=20) + .bottom_to_front() + .align( + center_x=rod.left - 5, + center_y=rod.center_y - 2, + center_z=rod_holder.center_z, + ) + ) + rod_holder_clearance = ~Volume( + right=rod.left + 1, + left=extrusion.left, + center_y=rod.center_y, + depth=2, + center_z=rod_holder.center_z, + height=rod_holder.height + 1, + ) + + top_bolt = ( + ~Bolt.M6(12, head_clearance=40) + .top_to_bottom() + .align( + center_x=extrusion.center_x, + center_y=body.front - 10, + center_z=extrusion.top + 3, + ) + ) + + side_bolt = ( + ~Bolt.M6(12) + .bottom_to_left() + .align( + center_x=extrusion.left, + center_y=0, + center_z=extrusion.center_z, + ) + .slide(z=-20) + .debug() + ) + + +z_bracket_top = ZBracketTop() +z_bracket_top.render_to_file() + + class ZStepperMount(Part): extrusion = ~frame.y_extrusion_left_bottom stepper = ~gantry.z_stepper_left @@ -3267,6 +3340,79 @@ class CableClip(Part): cable_clip = CableClip() +class MotorCableGuide(Part): + body = ( + Volume( + left=frame.y_extrusion_right_middle.right, + width=15, + front=frame.z_extrusion_right_front.front + 15, + back=mainboard_mount.back, + top=frame.y_extrusion_right_middle.top - 2, + bottom=frame.y_extrusion_right_middle.bottom + 2, + ) + .fillet_width(back=True, r=8) + .fillet_height(front=True, right=True) + ) + front_part = Volume( + left=frame.z_extrusion_right_front.left + 2, + right=body.left, + back=frame.z_extrusion_right_front.front, + front=body.front, + top=body.top, + bottom=body.bottom, + ).fillet_depth(left=True, r=8) + + bolt_back = ( + ~Bolt.M5(30) + .bottom_to_right() + .align( + right=body.right + E, + back=body.back + 4, + center_z=frame.y_extrusion_right_middle.center_z, + ) + ) + bolt_front = ( + ~Bolt.M5(30) + .bottom_to_front() + .align( + center_x=frame.z_extrusion_right_front.center_x, + front=front_part.front + E, + center_z=front_part.center_z, + ) + ) + cables = ~Volume( + left=frame.z_extrusion_right_front.left + 6, + right=body.right - 4, + back=bolt_back.front + 4, + front=body.front - 5, + top=body.top + E, + bottom=body.bottom + 4, + ).fillet_height() + cutout = ~( + Union( + Volume( + left=frame.y_extrusion_right_middle.right - E, + width=4, + center_y=frame.z_extrusion_right_front.back - 110 + 20 * x, + depth=10, + bottom=frame.y_extrusion_right_middle.bottom - 5, + top=frame.y_extrusion_right_middle.top + E, + ).fillet_height(right=True) + for x in range(5) + ) + + Volume( + center_x=frame.z_extrusion_right_front.center_x, + width=10, + back=frame.z_extrusion_right_front.front - E, + depth=4, + bottom=body.bottom - E, + top=body.top + E, + ).fillet_height(front=True) + ) + + +motor_cable_guide = MotorCableGuide() + frame_spacer = Volume( center_x=frame.y_extrusion_right_top.center_x, width=30, @@ -3360,6 +3506,7 @@ class CableClip(Part): + z_stepper_mount_right + z_top_endstop + z_bottom_endstop + + motor_cable_guide ).color("#505050") + ( x_carriage @@ -3400,6 +3547,7 @@ class CableClip(Part): z_bottom_endstop.render_to_file() z_bracket_top_right_front.render_to_file("z_bracket_top_right_front") z_bracket_top_right_back.render_to_file("z_bracket_top_right_back") + motor_cable_guide.render_to_file() if "--stl" in sys.argv: for part in ( diff --git a/examples/numbers.py b/examples/numbers.py index d874ef9..91752bb 100644 --- a/examples/numbers.py +++ b/examples/numbers.py @@ -2,11 +2,11 @@ class Screw(Part): - body = Cylinder(h=8, d=3.8) + body = Cylinder(h=8, d=3.8).align(top=1) head = ( Polygon((0, 0), (3.9, 0), (3.9, -0.4), (1.9, -3), (0, -2.8)) .z_rotational_extrude() - .align(bottom=body.top - E) + .align(bottom=body.top - 1) ) @@ -77,10 +77,71 @@ class Number5(Part): screw3 = ~Screw().align(center_x=-16, center_y=-20, top=number.top + T) +class Number6(Part): + number = ( + Text("6", size=75, direction="ttb", valign="center", font="Sancreek") + .z_linear_extrude(5) + .leftward(0.3) + .backward(0.8) + ) + # screw1 = ~Screw().align(center_x=17, center_y=28, top=number.top + T) + screw2 = ~Screw().align(center_x=-15, center_y=-13, top=number.top + T) + screw3 = ~Screw().align(center_x=16, center_y=-13, top=number.top + T) + + +class Number7(Part): + number = ( + Text("7", size=75, direction="ttb", valign="center", font="Sancreek") + .z_linear_extrude(5) + .leftward(0.3) + .backward(0.8) + ) + screw1 = ~Screw().align(center_x=8, center_y=35, top=number.top + T) + screw2 = ~Screw().align(center_x=-8, center_y=-25, top=number.top + T) + + +class Number8(Part): + number = ( + Text("8", size=75, direction="ttb", valign="center", font="Sancreek") + .z_linear_extrude(5) + .leftward(0.3) + .backward(0.8) + ) + screw1 = ~Screw().align(center_x=-16, center_y=23, top=number.top + T) + screw3 = ~Screw().align(center_x=16, center_y=-23, top=number.top + T) + + +class Number9(Part): + number = ( + Text("9", size=75, direction="ttb", valign="center", font="Sancreek") + .z_linear_extrude(5) + .leftward(0.3) + .backward(0.8) + ) + # screw1 = ~Screw().align(center_x=-17, center_y=-27, top=number.top + T) + screw2 = ~Screw().align(center_x=-15, center_y=13, top=number.top + T) + screw3 = ~Screw().align(center_x=16, center_y=13, top=number.top + T) + + +class Number0(Part): + number = ( + Text("0", size=75, direction="ttb", valign="center", font="Sancreek") + .z_linear_extrude(5) + .leftward(0.8) + ) + screw1 = ~Screw().align(center_x=-16, center_y=0, top=number.top + T) + screw2 = ~Screw().align(center_x=16, center_y=0, top=number.top + T) + + if __name__ == "__main__": - Screw().render_to_file("screw.scad") + # Screw().render_to_file("screw.scad") Number1().render_to_file("number1.scad") Number2().render_to_file("number2.scad") Number3().render_to_file("number3.scad") Number4().render_to_file("number4.scad") Number5().render_to_file("number5.scad") + Number6().render_to_file("number6.scad") + Number7().render_to_file("number7.scad") + Number8().render_to_file("number8.scad") + Number9().render_to_file("number9.scad") + Number0().render_to_file("number0.scad") diff --git a/poetry.lock b/poetry.lock index a24d712..67faa63 100644 --- a/poetry.lock +++ b/poetry.lock @@ -43,29 +43,29 @@ yaml = ["PyYAML"] [[package]] name = "black" -version = "23.10.0" +version = "23.11.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.10.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:f8dc7d50d94063cdfd13c82368afd8588bac4ce360e4224ac399e769d6704e98"}, - {file = "black-23.10.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:f20ff03f3fdd2fd4460b4f631663813e57dc277e37fb216463f3b907aa5a9bdd"}, - {file = "black-23.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3d9129ce05b0829730323bdcb00f928a448a124af5acf90aa94d9aba6969604"}, - {file = "black-23.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:960c21555be135c4b37b7018d63d6248bdae8514e5c55b71e994ad37407f45b8"}, - {file = "black-23.10.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:30b78ac9b54cf87bcb9910ee3d499d2bc893afd52495066c49d9ee6b21eee06e"}, - {file = "black-23.10.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:0e232f24a337fed7a82c1185ae46c56c4a6167fb0fe37411b43e876892c76699"}, - {file = "black-23.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31946ec6f9c54ed7ba431c38bc81d758970dd734b96b8e8c2b17a367d7908171"}, - {file = "black-23.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:c870bee76ad5f7a5ea7bd01dc646028d05568d33b0b09b7ecfc8ec0da3f3f39c"}, - {file = "black-23.10.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:6901631b937acbee93c75537e74f69463adaf34379a04eef32425b88aca88a23"}, - {file = "black-23.10.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:481167c60cd3e6b1cb8ef2aac0f76165843a374346aeeaa9d86765fe0dd0318b"}, - {file = "black-23.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74892b4b836e5162aa0452393112a574dac85e13902c57dfbaaf388e4eda37c"}, - {file = "black-23.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:47c4510f70ec2e8f9135ba490811c071419c115e46f143e4dce2ac45afdcf4c9"}, - {file = "black-23.10.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:76baba9281e5e5b230c9b7f83a96daf67a95e919c2dfc240d9e6295eab7b9204"}, - {file = "black-23.10.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:a3c2ddb35f71976a4cfeca558848c2f2f89abc86b06e8dd89b5a65c1e6c0f22a"}, - {file = "black-23.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db451a3363b1e765c172c3fd86213a4ce63fb8524c938ebd82919bf2a6e28c6a"}, - {file = "black-23.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:7fb5fc36bb65160df21498d5a3dd330af8b6401be3f25af60c6ebfe23753f747"}, - {file = "black-23.10.0-py3-none-any.whl", hash = "sha256:e223b731a0e025f8ef427dd79d8cd69c167da807f5710add30cdf131f13dd62e"}, - {file = "black-23.10.0.tar.gz", hash = "sha256:31b9f87b277a68d0e99d2905edae08807c007973eaa609da5f0c62def6b7c0bd"}, + {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, + {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, + {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, + {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, + {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, + {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, + {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, + {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, + {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, + {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, + {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, + {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, + {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, + {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, + {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, + {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, + {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, + {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, ] [package.dependencies] @@ -224,19 +224,19 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.12.4" +version = "3.13.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, - {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] -typing = ["typing-extensions (>=4.7.1)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" @@ -354,13 +354,13 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre [[package]] name = "identify" -version = "2.5.30" +version = "2.5.31" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.30-py2.py3-none-any.whl", hash = "sha256:afe67f26ae29bab007ec21b03d4114f41316ab9dd15aa8736a167481e108da54"}, - {file = "identify-2.5.30.tar.gz", hash = "sha256:f302a4256a15c849b91cfcdcec052a8ce914634b2f77ae87dad29cd749f2d88d"}, + {file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"}, + {file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"}, ] [package.extras] @@ -518,13 +518,13 @@ files = [ [[package]] name = "pbr" -version = "5.11.1" +version = "6.0.0" description = "Python Build Reasonableness" optional = false python-versions = ">=2.6" files = [ - {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, - {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, ] [[package]] @@ -659,13 +659,13 @@ plugins = ["importlib-metadata"] [[package]] name = "pytest" -version = "7.4.2" +version = "7.4.3" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, + {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, + {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, ] [package.dependencies] @@ -780,13 +780,13 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" -version = "0.17.40" +version = "0.18.5" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false -python-versions = ">=3" +python-versions = ">=3.7" files = [ - {file = "ruamel.yaml-0.17.40-py3-none-any.whl", hash = "sha256:b16b6c3816dff0a93dca12acf5e70afd089fa5acb80604afd1ffa8b465b7722c"}, - {file = "ruamel.yaml-0.17.40.tar.gz", hash = "sha256:6024b986f06765d482b5b07e086cc4b4cd05dd22ddcbc758fa23d54873cf313d"}, + {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, + {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, ] [package.dependencies] @@ -917,13 +917,13 @@ files = [ [[package]] name = "virtualenv" -version = "20.24.5" +version = "20.24.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, + {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, + {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index b480543..d9a01de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,6 +98,9 @@ select = [ exclude = [ "tests" ] +extend-ignore = [ + "D100", "D101", "D102", "D103", "D104", "D105", "N802", "N803" +] [tool.ruff.pydocstyle] diff --git a/src/muscad/base.py b/src/muscad/base.py index 60fb4d6..0502c4f 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -8,13 +8,9 @@ from typing import ( Any, Callable, - Dict, Iterable, - List, Literal, - Optional, Protocol, - Tuple, ) from muscad.helpers import camel_to_snake, normalize_angle @@ -24,8 +20,11 @@ class MuSCAD: """Base class for all MuSCAD objects.""" def render(self) -> str: - """Returns the SCAD code to render this object :return: (str) the SCAD code for this - object.""" + """Returns the SCAD code to render this object. + + :return: the SCAD code for this object. + + """ raise NotImplementedError() # pragma: no cover def __str__(self) -> str: @@ -51,7 +50,7 @@ def indent(s: str, token: str = " ") -> str: return token + s.replace("\n", f"\n{token}") -def add_comment(code: str, comment: Optional[str] = None) -> str: +def add_comment(code: str, comment: str | None = None) -> str: """Adds comment to a rendered code.""" if not comment: return code @@ -82,7 +81,7 @@ class Object(MuSCAD): object_name: str - def __init_subclass__(cls, name: Optional[str] = None): + def __init_subclass__(cls, name: str | None = None): """Derive a name from the subclass name, if not explicitly declared. :param name: a string (if explicitly declared) @@ -103,9 +102,9 @@ def __init__(self) -> None: """ self.modifier: str = "" - self.comment: Optional[str] = None + self.comment: str | None = None - def set_modifier(self, m: Optional[str]) -> Object: + def set_modifier(self, m: str | None) -> Object: """Set or remove a modifier for this object. :param m: one of OpenSCAD's modifiers, as a single char str, or None to remove the modifier. @@ -209,21 +208,30 @@ def forward(self, dist: float) -> Object: return self.y_translate(dist) def backward(self, dist: float) -> Object: - """Helper method to apply a backward Translation on Y axis on the current object :param - dist: distance in mm :return: an object, translated backwards by `dist` mm.""" + """Helper method to apply a backward Translation on Y axis on the current object. + + :param dist: distance in mm + :return: an object, translated backwards by `dist` mm. + + """ return self.y_translate(-dist) def up(self, dist: float) -> Object: - """Helper method to apply a upwards Translation on Z axis on the current object :param dist: + """Helper method to apply an upwards Translation on Z axis on the current object. - distance in mm :return: an object, translated upwards by `dist` mm. + :param dist: distance in mm + :return: an object, translated upwards by `dist` mm. """ return self.z_translate(dist) def down(self, dist: float) -> Object: - """Helper method to apply a downwards Translation on Z axis on the current object :param - dist: distance in mm :return: an object, translated downwards by `dist` mm.""" + """Helper method to apply a downwards Translation on Z axis on the current object. + + :param dist: distance in mm + :return: an object, translated downwards by `dist` mm. + + """ return self.z_translate(-dist) def rotate( @@ -241,6 +249,9 @@ def rotate( :param x: x angle :param y: y angle :param z: z angle + :param center_x: center of rotation on X axis + :param center_y: center of rotation on Y axis + :param center_z: center of rotation on Z axis :return: a rotated object """ @@ -260,147 +271,275 @@ def rotate( def x_rotate( self, angle: float, center_y: float = 0, center_z: float = 0 ) -> Object: - """Helper method to apply a Rotation on X axis on the current object :param angle: angle in - degrees :return: an object, rotated by `angle` degrees on X axis.""" + """Helper method to apply a Rotation on X axis on the current object. + + :param angle: angle in degrees + :return: an object, rotated by `angle` degrees on X axis. + + """ return self.rotate(x=angle, center_y=center_y, center_z=center_z) def y_rotate( self, angle: float, center_x: float = 0, center_z: float = 0 ) -> Object: - """Helper method to apply a Rotation on Y axis on the current object :param angle: angle in - degrees :return: an object, rotated by `angle` degrees on Y axis.""" + """Helper method to apply a Rotation on Y axis on the current object. + + :param angle: angle in degrees + :return: an object, rotated by `angle` degrees on Y axis. + + """ return self.rotate(y=angle, center_x=center_x, center_z=center_z) def z_rotate( self, angle: float, center_x: float = 0, center_y: float = 0 ) -> Object: - """Helper method to apply a Rotation on Z axis on the current object :param angle: angle in - degrees :return: an object, rotated by `angle` degrees on Z axis.""" + """Helper method to apply a Rotation on Z axis on the current object. + + :param angle: angle in degrees + :return: an object, rotated by `angle` degrees on Z axis. + + """ return self.rotate(z=angle, center_x=center_x, center_y=center_y) def left_to_right(self) -> Object: - """Alias for self.z_rotate(180) :return: a rotated Object.""" + """Alias for self.z_rotate(180) + + :return: a rotated Object. + + """ return self.z_rotate(180) def left_to_bottom(self) -> Object: - """Alias for self.y_rotate(-90).z_rotate(90) :return: a rotated Object.""" + """Alias for self.y_rotate(-90).z_rotate(90) + + :return: a rotated Object. + + """ return self.y_rotate(-90).z_rotate(90) def left_to_top(self) -> Object: - """Alias for self.y_rotate(90).z_rotate(90) :return: a rotated Object.""" + """Alias for self.y_rotate(90).z_rotate(90) + + :return: a rotated Object. + + """ return self.y_rotate(90).z_rotate(90) def left_to_front(self) -> Object: - """Alias for self.z_rotate(-90) :return: a rotated Object.""" + """Alias for self.z_rotate(-90) + + :return: a rotated Object. + + """ return self.z_rotate(-90) def left_to_back(self) -> Object: - """Alias for self.z_rotate(90) :return: a rotated Object.""" + """Alias for self.z_rotate(90) + + :return: a rotated Object. + + """ return self.z_rotate(90) def right_to_bottom(self) -> Object: - """Alias for self.y_rotate(90).z_rotate(-90) :return: a rotated Object.""" + """Alias for self.y_rotate(90).z_rotate(-90) + + :return: a rotated Object. + + """ return self.y_rotate(90).z_rotate(-90) def right_to_top(self) -> Object: - """Alias for self.y_rotate(-90).z_rotate(90) :return: a rotated Object.""" + """Alias for self.y_rotate(-90).z_rotate(90) + + :return: a rotated Object. + + """ return self.y_rotate(-90).z_rotate(90) def right_to_front(self) -> Object: - """Alias for self.z_rotate(90) :return: a rotated Object.""" + """Alias for self.z_rotate(90) + + :return: a rotated Object. + + """ return self.z_rotate(90) def right_to_back(self) -> Object: - """Alias for self.z_rotate(-90) :return: a rotated Object.""" + """Alias for self.z_rotate(-90) + + :return: a rotated Object. + + """ return self.z_rotate(-90) def right_to_left(self) -> Object: - """Alias for self.z_rotate(180) :return: a rotated Object.""" + """Alias for self.z_rotate(180) + + :return: a rotated Object. + + """ return self.z_rotate(180) def front_to_left(self) -> Object: - """Alias for self.z_rotate(90) :return: a rotated Object.""" + """Alias for self.z_rotate(90) + + :return: a rotated Object. + + """ return self.z_rotate(90) def front_to_right(self) -> Object: - """Alias for self.z_rotate(-90) :return: a rotated Object.""" + """Alias for self.z_rotate(-90) + + :return: a rotated Object. + + """ return self.z_rotate(-90) def front_to_top(self) -> Object: - """Alias for self.x_rotate(90).z_rotate(180) :return: a rotated Object.""" + """Alias for self.x_rotate(90).z_rotate(180) + + :return: a rotated Object. + + """ return self.x_rotate(90).z_rotate(180) def front_to_bottom(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(180) :return: a rotated Object.""" + """Alias for self.x_rotate(-90).z_rotate(180) + + :return: a rotated Object. + + """ return self.x_rotate(-90).z_rotate(180) def front_to_back(self) -> Object: - """Alias for self.z_rotate(180) :return: a rotated Object.""" + """Alias for self.z_rotate(180) + + :return: a rotated Object. + + """ return self.z_rotate(180) def back_to_left(self) -> Object: - """Alias for self.z_rotate(-90) :return: a rotated Object.""" + """Alias for self.z_rotate(-90) + + :return: a rotated Object. + + """ return self.z_rotate(-90) def back_to_right(self) -> Object: - """Alias for self.z_rotate(90) :return: a rotated Object.""" + """Alias for self.z_rotate(90) + + :return: a rotated Object. + + """ return self.z_rotate(90) def back_to_front(self) -> Object: return self.z_rotate(180) def back_to_top(self) -> Object: - """Alias for self.x_rotate(-90) :return: a rotated Object.""" + """Alias for self.x_rotate(-90) + + :return: a rotated Object. + + """ return self.x_rotate(-90) def back_to_bottom(self) -> Object: - """Alias for self.x_rotate(90) :return: a rotated Object.""" + """Alias for self.x_rotate(90) + + :return: a rotated Object. + + """ return self.x_rotate(90) def bottom_to_left(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(-90) :return: a rotated Object.""" + """Alias for self.x_rotate(-90).z_rotate(-90) + + :return: a rotated Object. + + """ return self.x_rotate(-90).z_rotate(-90) def bottom_to_right(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(90) :return: a rotated Object.""" + """Alias for self.x_rotate(-90).z_rotate(90) + + :return: a rotated Object. + + """ return self.x_rotate(-90).z_rotate(90) def bottom_to_front(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(180) :return: a rotated Object.""" + """Alias for self.x_rotate(-90).z_rotate(180) + + :return: a rotated Object. + + """ return self.x_rotate(-90).z_rotate(180) def bottom_to_back(self) -> Object: - """Alias for self.x_rotate(-90) :return: a rotated Object.""" + """Alias for self.x_rotate(-90) + + :return: a rotated Object. + + """ return self.x_rotate(-90) def bottom_to_top(self) -> Object: - """Alias for self.x_rotate(180) :return: a rotated Object.""" + """Alias for self.x_rotate(180) + + :return: a rotated Object. + + """ return self.x_rotate(180) def top_to_left(self) -> Object: - """Alias for self.x_rotate(90).z_rotate(-90) :return: a rotated Object.""" + """Alias for self.x_rotate(90).z_rotate(-90) + + :return: a rotated Object. + + """ return self.x_rotate(90).z_rotate(-90) def top_to_right(self) -> Object: - """Alias for self.x_rotate(90).z_rotate(90) :return: a rotated Object.""" + """Alias for self.x_rotate(90).z_rotate(90) + + :return: a rotated Object. + + """ return self.x_rotate(90).z_rotate(90) def top_to_back(self) -> Object: - """Alias for self.x_rotate(90) :return: a rotated Object.""" + """Alias for self.x_rotate(90) + + :return: a rotated Object. + + """ return self.x_rotate(90) def top_to_front(self) -> Object: - """Alias for self.x_rotate(-90).y_rotate(180) :return: a rotated Object.""" + """Alias for self.x_rotate(-90).y_rotate(180) + + :return: a rotated Object. + + """ return self.x_rotate(-90).y_rotate(180) def top_to_bottom(self) -> Object: - """Alias for self.x_rotate(180) :return: a rotated Object.""" + """Alias for self.x_rotate(180) + + :return: a rotated Object. + + """ return self.x_rotate(180) def upside_down(self, x_axis: bool = False) -> Object: - """Turns the object upside down on its y axis. + """Turns the object upside down on its Y axis. - Equivalent to self.y_rotate(180). If x_axis is True, rotate on x axis instead (like + Equivalent to self.y_rotate(180). If x_axis is True, rotate on X axis instead (like top_to_bottom()). :return: an object rotated 180° on X or Y axis @@ -478,9 +617,9 @@ def linear_extrude( center: bool = False, convexity: int = 10, twist: float = 0.0, - slices: Optional[int] = None, + slices: int | None = None, scale: float = 1.0, - segments: Optional[int] = None, + segments: int | None = None, ) -> Object: """Applies a linear extrusion transformation to this object. @@ -506,16 +645,16 @@ def linear_extrude( def z_linear_extrude( self, - distance: Optional[float] = None, + distance: float | None = None, *, - bottom: Optional[float] = None, - center_z: Optional[float] = None, - top: Optional[float] = None, + bottom: float | None = None, + center_z: float | None = None, + top: float | None = None, convexity: int = 10, twist: float = 0.0, - slices: Optional[int] = None, + slices: int | None = None, scale: float = 1.0, - segments: Optional[int] = None, + segments: int | None = None, downwards: bool = False, ) -> Object: bottom, center_z, top, distance = calc(bottom, center_z, top, distance) @@ -533,16 +672,16 @@ def z_linear_extrude( def y_linear_extrude( self, - distance: Optional[float] = None, + distance: float | None = None, *, - back: Optional[float] = None, - center_y: Optional[float] = None, - front: Optional[float] = None, + back: float | None = None, + center_y: float | None = None, + front: float | None = None, convexity: int = 10, twist: float = 0.0, - slices: Optional[int] = None, + slices: int | None = None, scale: float = 1.0, - segments: Optional[int] = None, + segments: int | None = None, backwards: bool = False, ) -> Object: back, center_y, front, distance = calc(back, center_y, front, distance) @@ -560,16 +699,16 @@ def y_linear_extrude( def x_linear_extrude( self, - distance: Optional[float] = None, + distance: float | None = None, *, - left: Optional[float] = None, - center_x: Optional[float] = None, - right: Optional[float] = None, + left: float | None = None, + center_x: float | None = None, + right: float | None = None, convexity: int = 10, twist: float = 0.0, - slices: Optional[int] = None, + slices: int | None = None, scale: float = 1.0, - segments: Optional[int] = None, + segments: int | None = None, leftwards: bool = False, ) -> Object: left, center_x, right, distance = calc(left, center_x, right, distance) @@ -588,8 +727,8 @@ def x_linear_extrude( def rotational_extrude( self, angle: float = 360, - convexity: Optional[int] = None, - segments: Optional[int] = None, + convexity: int | None = None, + segments: int | None = None, ) -> Object: """Applies a rotational extrusion to this object. @@ -608,17 +747,17 @@ def rotational_extrude( def z_rotational_extrude( self, - angle: Optional[float] = None, - angle_from: Optional[float] = None, - angle_to: Optional[float] = None, + angle: float | None = None, + angle_from: float | None = None, + angle_to: float | None = None, radius: float = 0, - convexity: Optional[int] = None, - segments: Optional[int] = None, + convexity: int | None = None, + segments: int | None = None, center_x: float = 0, center_y: float = 0, - bottom: Optional[float] = None, - center_z: Optional[float] = None, - top: Optional[float] = None, + bottom: float | None = None, + center_z: float | None = None, + top: float | None = None, ) -> Object: bottom, center_z, top, _ = calc( from_=bottom, center=center_z, to=top, distance=self.width @@ -656,7 +795,7 @@ def offset( return self.offset(r, delta, chamfer) - self return Offset(r=r, delta=delta, chamfer=chamfer)(self) - def color(self, name: str, alpha: Optional[float] = None) -> Object: + def color(self, name: str, alpha: float | None = None) -> Object: """Applies a color to this object. :param name: @@ -749,15 +888,15 @@ def slide(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: def align( self, *, - left: Optional[float] = None, - center_x: Optional[float] = None, - right: Optional[float] = None, - back: Optional[float] = None, - center_y: Optional[float] = None, - front: Optional[float] = None, - bottom: Optional[float] = None, - center_z: Optional[float] = None, - top: Optional[float] = None, + left: float | None = None, + center_x: float | None = None, + right: float | None = None, + back: float | None = None, + center_y: float | None = None, + front: float | None = None, + bottom: float | None = None, + center_z: float | None = None, + top: float | None = None, ) -> Object: x = y = z = 0.0 if left is not None: @@ -781,6 +920,93 @@ def align( return self.translate(x=x, y=y, z=z) + def divide( + self, + x: float | None = None, + y: float | None = None, + z: float | None = None, + T: float = 0, + ) -> tuple[Object, Object]: + """Cut this Object into 2 object, on either a x, or y or z plane.""" + from muscad import Volume + + if x is not None: + if not self.left < x < self.right: + msg = f"x must be the x coordinate of a pane that divides the object in 2, between {self.left} and {self.right}" + raise ValueError(msg) + return ( + self + & Volume( + left=self.left, + right=x - T, + back=self.back, + front=self.front, + bottom=self.bottom, + top=self.top, + ), + self + & Volume( + left=x + T, + right=self.right, + back=self.back, + front=self.front, + bottom=self.bottom, + top=self.top, + ), + ) + elif y is not None: + if not self.back < y < self.front: + msg = f"y must be the y coordinate of a pane that divides the object in 2, between {self.back} and {self.front}" + raise ValueError(msg) + return ( + self + & Volume( + left=self.left, + right=self.right, + back=self.back, + front=y - T, + bottom=self.bottom, + top=self.top, + ), + self + & Volume( + left=self.left, + right=self.right, + back=y + T, + front=self.front, + bottom=self.bottom, + top=self.top, + ), + ) + elif z is not None: + if not self.bottom < z < self.top: + msg = f"z must be the z coordinate of a pane that divides the object in 2, between {self.bottom} and {self.top}" + raise ValueError(msg) + return ( + self + & Volume( + left=self.left, + right=self.right, + back=self.back, + front=self.front, + bottom=self.bottom, + top=z - T, + ), + self + & Volume( + left=self.left, + right=self.right, + back=self.back, + front=self.front, + bottom=z + T, + top=self.top, + ), + ) + else: + raise ValueError( + "You must provide one plane to divide the Object, defined by one X, Y, Z coordinate" + ) + def __stl__(self) -> Object: return self # pragma: no cover @@ -789,7 +1015,7 @@ def file_name(self) -> str: return camel_to_snake(self.__class__.__name__) # pragma: no cover def render_to_file( - self, path: Optional[str] = None, mode: str = "wt", openscad: bool = False + self, path: str | None = None, mode: str = "wt", openscad: bool = False ) -> str: # pragma: no cover if path is None: path = self.file_name @@ -797,7 +1023,7 @@ def render_to_file( mode = self.mode return render_to_file(self, mode, path, openscad=openscad) - def export_stl(self, path: Optional[str] = None) -> str: # pragma: no cover + def export_stl(self, path: str | None = None) -> str: # pragma: no cover obj = self.__stl__() if path is None: path = self.file_name @@ -819,7 +1045,7 @@ def __init__(self, **kwargs: Any): super().__init__() self.arguments = kwargs - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: """Get an argument dict for this object. This must be implemented by subclasses @@ -830,7 +1056,7 @@ def _arguments(self) -> Dict[str | None, Any]: def _iter_arguments( self, - ) -> Iterable[Tuple[Optional[str], typing.Union[str, float]]]: + ) -> Iterable[tuple[str | None, str | float]]: """Iterates over arguments. :return: a iterator of (key, val) tuples @@ -856,7 +1082,7 @@ def _iter_arguments( yield key, val @classmethod - def _render_arguments(cls, params: Iterable[Tuple[Optional[str], Any]]) -> str: + def _render_arguments(cls, params: Iterable[tuple[str | None, Any]]) -> str: """Render the parameters for this object as OpenSCAD code. (anything between the parenthesis) @@ -883,7 +1109,7 @@ class Composite(Object): def __init__(self, *children: Object | Iterable[Object]): super().__init__() - self.children: List[Object] = [] + self.children: list[Object] = [] if children: self.apply(*children) @@ -901,7 +1127,7 @@ def add_child(self, child: Object | Iterable[Object] | Literal[0]) -> Object: self.children.extend(child) return self - def apply(self, *children: typing.Union[Object, Iterable[Object]]) -> Object: + def apply(self, *children: Object | Iterable[Object]) -> Object: for child in children: self.add_child(child) return self @@ -1094,7 +1320,7 @@ class Transformation(Primitive): def __init__(self, *children: Object): super().__init__() - self._child: Optional[Object] = None + self._child: Object | None = None self.apply(*children) @property @@ -1107,7 +1333,7 @@ def child(self) -> Object: def child(self, value: Object) -> None: self._child = value - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {} def apply(self, *children: Object) -> Object: @@ -1246,7 +1472,7 @@ def render(self) -> str: return self.object.render() @property - def comment(self) -> Optional[str]: + def comment(self) -> str | None: return self.object.comment @comment.setter @@ -1266,7 +1492,7 @@ def render(self) -> str: return self.object.render() @property - def comment(self) -> Optional[str]: + def comment(self) -> str | None: return self.object.comment @comment.setter @@ -1315,11 +1541,11 @@ def render_to_file(obj: Object, mode: str, path: str, openscad: bool = False) -> class Calc(Protocol): def __call__( self, - from_: Optional[float] = None, - center: Optional[float] = None, - to: Optional[float] = None, - distance: Optional[float] = None, - ) -> Tuple[float, float, float, float]: + from_: float | None = None, + center: float | None = None, + to: float | None = None, + distance: float | None = None, + ) -> tuple[float, float, float, float]: ... # pragma: no cover @@ -1327,11 +1553,11 @@ def validate_calc( f: Calc, ) -> Calc: def wrapper( - from_: Optional[float] = None, - center: Optional[float] = None, - to: Optional[float] = None, - distance: Optional[float] = None, - ) -> Tuple[float, float, float, float]: + from_: float | None = None, + center: float | None = None, + to: float | None = None, + distance: float | None = None, + ) -> tuple[float, float, float, float]: _from, _center, _to, _distance = f(from_, center, to, distance) if center is not None and _center != center: raise ValueError( @@ -1364,11 +1590,11 @@ def wrapper( @validate_calc def calc( - from_: Optional[float] = None, - center: Optional[float] = None, - to: Optional[float] = None, - distance: Optional[float] = None, -) -> Tuple[float, float, float, float]: + from_: float | None = None, + center: float | None = None, + to: float | None = None, + distance: float | None = None, +) -> tuple[float, float, float, float]: """Given at least 2 of from_, center, and distance, returns all 4. If only distance is given, default to center = 0 @@ -1430,7 +1656,7 @@ def calc( raise ValueError("no sufficient input to calculate all params") -def export_stl(scad_path: str, stl_path: Optional[str] = None) -> str: +def export_stl(scad_path: str, stl_path: str | None = None) -> str: if not stl_path: stl_path = os.path.splitext(scad_path)[0] + ".stl" subprocess.call(["openscad", "-o", stl_path, scad_path]) diff --git a/src/muscad/part.py b/src/muscad/part.py index 4931171..94ec64e 100644 --- a/src/muscad/part.py +++ b/src/muscad/part.py @@ -1,13 +1,12 @@ from __future__ import annotations from itertools import chain -from typing import Any, Iterable, Iterator, Literal, Type +from typing import Any, Iterable, Iterator, Literal, ClassVar from muscad import ( EE, Composite, Hole, - List, Misc, Object, Union, @@ -21,7 +20,7 @@ ) -def walk_mro_until(cls: Type[Any], supercls: Type[Any]) -> Iterator[Type[Any]]: +def walk_mro_until(cls: type[Any], supercls: type[Any]) -> Iterator[type[Any]]: for c in cls.mro(): if c == supercls: break @@ -33,17 +32,17 @@ class Part(Composite): A Part is made of 3 kind of objects: - instances of Object, which will form the "main" structure of this Part - - instances of Misc, which are miscellaneous items that will not be taken into account when evaluating this Part dimensions - - instances of Hole, which will be "unfillable" holes which will be substracted from that Part. + - instances of Misc, which are misc items that will not be taken into account when evaluating this Part dimensions + - instances of Hole, which will be "unfillable" holes which will be substracted from the other objects Those objects can be added to that Part using add_child(), add_misc() or add_hole(). All class attributes that are instances of Object, Misc or Hole will be added to all instances of this Part. """ - class_parts: list[Object] - class_misc: list[Object] - class_holes: list[Object] + class_parts: ClassVar[list[Object]] + class_misc: ClassVar[list[Object]] + class_holes: ClassVar[list[Object]] def __init_subclass__(cls, **kwargs: Any) -> None: """When creating an inherited class, sort all class-level attributes and make lists of all @@ -353,7 +352,7 @@ def top(self) -> float: return top(self.children) -from muscad.primitives import Square +from muscad.primitives import Square, Cube class RotationalExtrudedPart(Part): diff --git a/src/muscad/point.py b/src/muscad/point.py index be4798f..b452496 100644 --- a/src/muscad/point.py +++ b/src/muscad/point.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from muscad.helpers import atan2, cos, radians, sin @@ -20,36 +22,36 @@ class Point2D(Vector): def __init__(self, x: float, y: float): super().__init__(x=x, y=y) - def z_rotate(self, angle: float) -> "Point2D": + def z_rotate(self, angle: float) -> Point2D: return Point2D( cos(angle) * self.x + sin(angle) * self.y, cos(angle) * self.y - sin(angle) * self.x, ) - def x_mirror(self) -> "Point2D": + def x_mirror(self) -> Point2D: return Point2D(-self.x, self.y) - def y_mirror(self) -> "Point2D": + def y_mirror(self) -> Point2D: return Point2D(self.x, -self.y) - def opposite(self) -> "Point2D": + def opposite(self) -> Point2D: return Point2D(-self.x, -self.y) def angle(self) -> float: return atan2(self.y, self.x) @classmethod - def from_radius_and_angle(cls, radius: float, angle: float) -> "Point2D": + def from_radius_and_angle(cls, radius: float, angle: float) -> Point2D: return cls(radius * cos(angle), radius * sin(angle)) @classmethod - def involute(cls, radius: float, angle: float) -> "Point2D": + def involute(cls, radius: float, angle: float) -> Point2D: return cls( radius * (cos(angle) + radians(angle) * sin(angle)), radius * (sin(angle) - radians(angle) * cos(angle)), ) - def to_3d(self, z: float = 0) -> "Point3D": + def to_3d(self, z: float = 0) -> Point3D: """Turns this Point2D into a Point3D.""" return Point3D(self.x, self.y, z) diff --git a/src/muscad/primitives.py b/src/muscad/primitives.py index 86e19d4..26a9848 100644 --- a/src/muscad/primitives.py +++ b/src/muscad/primitives.py @@ -1,7 +1,9 @@ """This modules contains 2D & 3D Primitives classes that match OpenSCAD primitives.""" +from __future__ import annotations + import os import sys -from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple +from typing import Any, Iterable, Sequence from muscad.base import Object, Primitive from muscad.point import Point2D, Point3D @@ -52,7 +54,7 @@ def front(self) -> float: def top(self) -> float: return self.height / 2 - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return { "size": Point3D(self._width, self._depth, self._height), "center": True, @@ -64,8 +66,8 @@ def __init__( self, h: float, d: float, - d2: Optional[float] = None, - segments: Optional[int] = None, + d2: float | None = None, + segments: int | None = None, ): super().__init__() self._height = h @@ -75,7 +77,7 @@ def __init__( segments = int(d * 3.14 / 0.4) self.segments = segments - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: if self.top_diameter is None: return { "h": self._height, @@ -142,7 +144,7 @@ def __init__(self, d: float, segments: int | None = None) -> None: segments = int(d * 3.14 / 0.4) self._segments = segments - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"d": self._diameter, "$fn": self._segments} @property @@ -208,7 +210,7 @@ def unpack_points(points: Iterable[Point3D | Sequence[float]]) -> Iterable[Point point, ) - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return { "points": self.points, "faces": self.faces, @@ -239,7 +241,7 @@ def __init__(self, d: float, segments: int | None = None) -> None: segments = int(d * 3.14 / 0.4) self._segments = segments - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"d": self._diameter, "$fn": self._segments} @property @@ -265,7 +267,7 @@ def __init__(self, width: float, depth: float) -> None: self._width = width self._depth = depth - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"size": Point2D(self._width, self._depth), "center": True} @property @@ -319,7 +321,7 @@ def __init__( self.script = script self.segments = segments - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return { "text": self.text, "size": self.size, @@ -369,17 +371,17 @@ def front(self) -> float: class Polygon(Primitive2D): def __init__( self, - *points: Point2D | Tuple[float, float], + *points: Point2D | tuple[float, float], path: Iterable[int] | None = None, hole_paths: Iterable[Iterable[int]] | None = None, - convexity: int | None = None + convexity: int | None = None, ) -> None: super().__init__() self.points = list(self.unpack_points(points)) if hole_paths: if not path: path = list(range(len(points))) - self.paths: Optional[List[List[int]]] = [list(path)] if path else None + self.paths: list[list[int]] | None = [list(path)] if path else None if hole_paths and self.paths: for hole_path in hole_paths: self.paths.append(list(hole_path)) @@ -387,7 +389,7 @@ def __init__( @staticmethod def unpack_points( - points: Iterable[Point2D | Tuple[float, float]] + points: Iterable[Point2D | tuple[float, float]] ) -> Iterable[Point2D]: for point in points: if isinstance(point, Point2D): @@ -401,7 +403,7 @@ def unpack_points( point, ) - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return { "points": self.points, "paths": self.paths, diff --git a/src/muscad/transformations.py b/src/muscad/transformations.py index 05a7560..83559fa 100644 --- a/src/muscad/transformations.py +++ b/src/muscad/transformations.py @@ -1,4 +1,6 @@ -from typing import Any, Dict, Optional, Tuple +from __future__ import annotations + +from typing import Any from muscad.helpers import normalize_angle @@ -13,7 +15,7 @@ def __init__(self, *, x: float = 0, y: float = 0, z: float = 0): self.y = y self.z = z - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"v": Point3D(self.x, self.y, self.z)} def combine(self, other: Transformation) -> Transformation: @@ -62,7 +64,7 @@ def __init__(self, *, x: float = 0, y: float = 0, z: float = 0): self.y = normalize_angle(y) self.z = normalize_angle(z) - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"a": Point3D(self.x, self.y, self.z)} def combine(self, other: Transformation) -> Transformation: @@ -339,7 +341,7 @@ def __init__(self, *, x: float = 0, y: float = 0, z: float = 0) -> None: self.y = y self.z = z - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"v": Point3D(self.x, self.y, self.z)} @property @@ -374,7 +376,7 @@ def __init__(self, *, x: float = 0, y: float = 0, z: float = 0): self.y = y self.z = z - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"v": Point3D(self.x, self.y, self.z)} @property @@ -444,16 +446,16 @@ def copy(self) -> Transformation: class Multmatrix(Transformation): def __init__( self, - matrix: Tuple[ - Tuple[float, float, float, float], - Tuple[float, float, float, float], - Tuple[float, float, float, float], + matrix: tuple[ + tuple[float, float, float, float], + tuple[float, float, float, float], + tuple[float, float, float, float], ], ): super().__init__() self.matrix = matrix - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"m": self.matrix} @@ -463,7 +465,7 @@ def __init__(self, colorname: str, alpha: float | None = None) -> None: self.colorname = colorname self.alpha = alpha - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {None: self.colorname, "alpha": self.alpha} def copy(self) -> Transformation: @@ -473,9 +475,9 @@ def copy(self) -> Transformation: class Offset(Transformation): def __init__( self, - r: Optional[float] = None, - delta: Optional[float] = None, - chamfer: Optional[bool] = False, + r: float | None = None, + delta: float | None = None, + chamfer: bool | None = False, ): super().__init__() if r and delta: @@ -486,7 +488,7 @@ def __init__( self.delta = delta self.chamfer = chamfer - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"r": self.radius, "delta": self.delta, "chamfer": self.chamfer} @@ -525,7 +527,7 @@ def __init__(self, cut: bool = False) -> None: super().__init__() self.cut = cut - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"cut": self.cut} @property @@ -542,7 +544,7 @@ def __init__(self, convexity: int = 2) -> None: super().__init__() self._convexity = convexity - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return {"convexity": self._convexity} @@ -568,7 +570,7 @@ def __init__( segments = int(twist * 3.14 / 0.4) self._segments = segments - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return { "height": self._height, "center": self._center, @@ -615,8 +617,8 @@ class RotationalExtrusion(Transformation, name="rotate_extrude"): def __init__( self, angle: float = 360, - convexity: Optional[int] = None, - segments: Optional[int] = None, + convexity: int | None = None, + segments: int | None = None, ): super().__init__() self.angle = angle @@ -625,7 +627,7 @@ def __init__( segments = int(angle / 3.14 * 0.4) self.segments = segments - def _arguments(self) -> Dict[str | None, Any]: + def _arguments(self) -> dict[str | None, Any]: return { "angle": self.angle, "convexity": self.convexity, diff --git a/src/muscad/utils/stack.py b/src/muscad/utils/stack.py index 342226f..0a5cd46 100644 --- a/src/muscad/utils/stack.py +++ b/src/muscad/utils/stack.py @@ -1,9 +1,11 @@ +from __future__ import annotations + import typing from muscad import Misc, Object, Part -def stack(*parts: typing.Union[Object, Misc], overlap: float = 0.01) -> Part: +def stack(*parts: Object | Misc, overlap: float = 0.01) -> Part: """Stack each part on top of each other :param parts: all parts :param overlap: separation between each part (positive value: pieces will overlap, negative value: pieces will be separated) :return: parts stacked on top of each other, bottom to top.""" diff --git a/src/muscad/utils/surface.py b/src/muscad/utils/surface.py index 3c03fd9..5d9bae1 100644 --- a/src/muscad/utils/surface.py +++ b/src/muscad/utils/surface.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from muscad import Circle, Hull, Object, Point2D, Polygon, Square, calc @@ -211,7 +213,7 @@ def fillet(cls, radius: float) -> Object: ).align(left=0, back=0) @classmethod - def chamfer(self, radius: float) -> Object: + def chamfer(cls, radius: float) -> Object: chamfer_width = ((radius**2) * 2) ** 0.5 return Square(radius, radius).align(back=0, left=0) - Square( chamfer_width, chamfer_width diff --git a/src/muscad/utils/tube.py b/src/muscad/utils/tube.py index 9eccbff..440b9e6 100644 --- a/src/muscad/utils/tube.py +++ b/src/muscad/utils/tube.py @@ -1,29 +1,26 @@ from __future__ import annotations -import typing -from typing import Optional - from muscad import Cylinder, E, Hole, Misc, Object, Part, Volume, calc class Tube(Part): def init( # type: ignore[override] self, - *args: typing.Union[Misc, Hole, Object], - diameter: Optional[float] = None, - top_diameter: Optional[float] = None, - radius: Optional[float] = None, - left: Optional[float] = None, - center_x: Optional[float] = None, - right: Optional[float] = None, - back: Optional[float] = None, - center_y: Optional[float] = None, - front: Optional[float] = None, - bottom: Optional[float] = None, - center_z: Optional[float] = None, - top: Optional[float] = None, - height: Optional[float] = None, - **kwargs: typing.Union[Misc, Hole, Object], + *args: Misc | Hole | Object, + diameter: float | None = None, + top_diameter: float | None = None, + radius: float | None = None, + left: float | None = None, + center_x: float | None = None, + right: float | None = None, + back: float | None = None, + center_y: float | None = None, + front: float | None = None, + bottom: float | None = None, + center_z: float | None = None, + top: float | None = None, + height: float | None = None, + **kwargs: Misc | Hole | Object, ) -> None: if diameter is None and radius is not None: diameter = radius * 2 @@ -43,7 +40,7 @@ def init( # type: ignore[override] super().init(*args, **kwargs) def tunnel( - self, diameter: Optional[float] = None, radius: Optional[float] = None + self, diameter: float | None = None, radius: float | None = None ) -> Tube: """Hollows the center of this tube, making it a tunnel.""" if diameter is None and radius is None: diff --git a/src/muscad/utils/volume.py b/src/muscad/utils/volume.py index 77b0c42..365ee87 100644 --- a/src/muscad/utils/volume.py +++ b/src/muscad/utils/volume.py @@ -1,6 +1,5 @@ from __future__ import annotations -from typing import Optional from muscad import EE, Cube, E, Object, Part, calc from muscad.utils.fillet import Chamfer, Fillet @@ -10,18 +9,18 @@ class Volume(Part): def init( # type: ignore[override] self, *, - left: Optional[float] = None, - center_x: Optional[float] = None, - right: Optional[float] = None, - width: Optional[float] = None, - back: Optional[float] = None, - center_y: Optional[float] = None, - front: Optional[float] = None, - depth: Optional[float] = None, - bottom: Optional[float] = None, - center_z: Optional[float] = None, - top: Optional[float] = None, - height: Optional[float] = None, + left: float | None = None, + center_x: float | None = None, + right: float | None = None, + width: float | None = None, + back: float | None = None, + center_y: float | None = None, + front: float | None = None, + depth: float | None = None, + bottom: float | None = None, + center_z: float | None = None, + top: float | None = None, + height: float | None = None, ) -> None: self._left, self._center_x, self._right, self._width = calc( left, center_x, right, width diff --git a/src/muscad/vitamins/bearings.py b/src/muscad/vitamins/bearings.py index 304e8a1..5dd1a5e 100644 --- a/src/muscad/vitamins/bearings.py +++ b/src/muscad/vitamins/bearings.py @@ -1,6 +1,5 @@ from __future__ import annotations -from typing import Dict, Optional from muscad import EE, Cylinder, Object, Part, Union from muscad.utils.volume import Volume @@ -87,7 +86,7 @@ def add_bolts( return self def add_rod_clearance( - self, length: float = 20, slide: Optional[Dict[str, float]] = None, T: float = 1 + self, length: float = 20, slide: dict[str, float] | None = None, T: float = 1 ) -> BushingLinearBearing: slide = slide or {} self.rod_clearance = ( @@ -145,7 +144,7 @@ def SC12UU( class RotationBearing(Part): def init( # type: ignore[override] - self, inner_diam: float, outer_diam: float, height: float, hole: bool = True + self, inner_diam: float, outer_diam: float, height: float, *, hole: bool = True ) -> None: self._outer_diameter = outer_diam self._inner_diameter = inner_diam diff --git a/src/muscad/vitamins/boards.py b/src/muscad/vitamins/boards.py index b5936f7..8a59648 100644 --- a/src/muscad/vitamins/boards.py +++ b/src/muscad/vitamins/boards.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing_extensions import Self from muscad import TT, Cylinder, E, Object, Part, Union diff --git a/src/muscad/vitamins/cable_chain.py b/src/muscad/vitamins/cable_chain.py index 24b83d9..f626dc1 100644 --- a/src/muscad/vitamins/cable_chain.py +++ b/src/muscad/vitamins/cable_chain.py @@ -1,4 +1,4 @@ -from typing import Tuple +from __future__ import annotations from muscad.utils.tube import Tube @@ -42,7 +42,7 @@ def male( @classmethod def couple( cls, outer_diameter: float = 16, inner_diameter: float = 6, T: float = 0.2 - ) -> Tuple[Tube, Tube]: + ) -> tuple[Tube, Tube]: return cls.female(outer_diameter, inner_diameter, T), cls.male( outer_diameter, inner_diameter, T ) diff --git a/src/muscad/vitamins/fans.py b/src/muscad/vitamins/fans.py index 50a6c28..eb30412 100644 --- a/src/muscad/vitamins/fans.py +++ b/src/muscad/vitamins/fans.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Iterable from typing_extensions import Self @@ -109,9 +111,9 @@ def add_tunnel( @classmethod def fan40x40x20( - cls, bolts: bool = True, bolt: Object = Bolt.M3(25).add_nut(-E), T: float = 0.2 + cls, bolt: Object | None = Bolt.M3(25).add_nut(-E), T: float = 0.2 ) -> Self: fan = cls(width=40 + 2 * T, height=20 + 2 * T, r=2) - if bolts: + if bolt: fan.add_bolts(bolt, 32) return fan diff --git a/src/muscad/vitamins/gears.py b/src/muscad/vitamins/gears.py index 7a24ca8..56e5ce5 100644 --- a/src/muscad/vitamins/gears.py +++ b/src/muscad/vitamins/gears.py @@ -1,4 +1,6 @@ """A port of https://www.thingiverse.com/thing:3575 in MuSCAD.""" +from __future__ import annotations + from typing import Iterable, Literal from muscad import ( @@ -40,9 +42,8 @@ def init( # type: ignore[override] circular_pitch = 180 / diametral_pitch if not circular_pitch: - raise ValueError( - "gear module needs either a diametral_pitch or circular_pitch" - ) + msg = "gear module needs either a diametral_pitch or circular_pitch" + raise ValueError(msg) # Pitch diameter: Diameter of pitch circle pitch_diameter = nb_teeth * circular_pitch / 180 @@ -254,7 +255,8 @@ def init( # type: ignore[override] - face_cone_descent / tan(pitch_angle) ) - # For the bevel_gear_flat finish option, calculate the height of a cube to select the portion of the gear that includes the full pitch face. + # For the bevel_gear_flat finish option, calculate the height of a cube + # to select the portion of the gear that includes the full pitch face. bevel_gear_flat_height = pitch_apex - (cone_distance - face_width) * cos( pitch_angle ) diff --git a/src/muscad/vitamins/pulleys.py b/src/muscad/vitamins/pulleys.py index 524f84c..1b6d73c 100644 --- a/src/muscad/vitamins/pulleys.py +++ b/src/muscad/vitamins/pulleys.py @@ -22,7 +22,7 @@ def tooth(self, profile: Polygon, count: int) -> Self: return self def add_flange( - self, diameter: float, height: float, top: bool = True, bottom: bool = True + self, diameter: float, *, height: float, top: bool = True, bottom: bool = True ) -> Self: if height > 0 and top: self.top_flange = Cylinder(d=diameter, h=height).align( @@ -56,7 +56,7 @@ def add_clearance(self, length: float, angle: float) -> Self: return self def add_belt_clearance( - self, length: float, angle: float, left: bool = False + self, length: float, *, angle: float, left: bool = False ) -> Self: self.belt_clearance = ( Cube(self.body.width / 2, length, self.body.height) @@ -81,7 +81,8 @@ def GT2( cls, tooth_count: int, height: float = 6, shaft_dia: float = 3, T: float = 0.2 ) -> Self: if tooth_count < 10: - raise ValueError("Unable to draw a GT2 pulley with less than 10 tooth") + msg = "Unable to draw a GT2 pulley with less than 10 tooth" + raise ValueError(msg) outer_dia = tooth_outer_diameter(tooth_count, 2, 0.254) return cls(outer_dia, height).add_shaft_clearance(shaft_dia, T=T) diff --git a/src/muscad/vitamins/rods.py b/src/muscad/vitamins/rods.py index f6f557e..eb250f4 100644 --- a/src/muscad/vitamins/rods.py +++ b/src/muscad/vitamins/rods.py @@ -55,7 +55,7 @@ def init(self, diameter: float, length: float) -> None: # type: ignore[override def T8(cls, length: float = 300, T: float = 0.2) -> Self: return cls(diameter=8 + 2 * T, length=length) - def add_brass_nut(self, brass_nut: Object, align: bool = True) -> Self: + def add_brass_nut(self, brass_nut: Object, *, align: bool = True) -> Self: if align: self.brass_nut = brass_nut.align( center_x=self.rod.center_x, center_y=self.rod.center_y diff --git a/src/muscad/vitamins/steppers.py b/src/muscad/vitamins/steppers.py index b9005f8..0c58c61 100644 --- a/src/muscad/vitamins/steppers.py +++ b/src/muscad/vitamins/steppers.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Iterable from typing_extensions import Self @@ -63,8 +65,12 @@ def add_gearbox( holes: Iterable[int] = (0, 1, 2, 3), depth: float = 5, ) -> Self: - """Adds the gearbox on the shaft side of the stepper :param d: diameter of the gearbox - :param h: height of the gearbox :return: the stepper object, with gearbox added. + """Adds the gearbox on the shaft side of the stepper. + + :param d: diameter of the gearbox + :param h: height of the gearbox + :return: the stepper object, with gearbox added. + """ self.gearbox = Cylinder(d=d, h=h + E).align( center_x=self.body.center_x, From d8f892b4b5459802610a7bf0b9b94844f0803296 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Sat, 2 Dec 2023 18:40:29 +0100 Subject: [PATCH 03/13] more cleanups --- .pre-commit-config.yaml | 11 +- docs/conf.py | 4 +- examples/drying_rack_fix.py | 50 +- examples/infinity_cube.py | 28 - examples/lauburu.py | 24 - examples/muscad_printer.py | 855 ++++++++++------------------- examples/numbers.py | 40 +- pyproject.toml | 4 - src/muscad/base.py | 92 ++-- src/muscad/helpers.py | 29 +- src/muscad/part.py | 34 +- src/muscad/primitives.py | 24 +- src/muscad/transformations.py | 58 +- src/muscad/utils/fillet.py | 4 +- src/muscad/utils/shapes.py | 14 +- src/muscad/utils/stack.py | 11 +- src/muscad/utils/surface.py | 32 +- src/muscad/utils/tube.py | 19 +- src/muscad/utils/volume.py | 13 +- src/muscad/vitamins/bearings.py | 37 +- src/muscad/vitamins/belts.py | 4 +- src/muscad/vitamins/boards.py | 10 +- src/muscad/vitamins/bolts.py | 28 +- src/muscad/vitamins/brackets.py | 12 +- src/muscad/vitamins/cable_chain.py | 16 +- src/muscad/vitamins/endstops.py | 87 +-- src/muscad/vitamins/extrusions.py | 4 +- src/muscad/vitamins/fans.py | 25 +- src/muscad/vitamins/gears.py | 40 +- src/muscad/vitamins/pulleys.py | 20 +- src/muscad/vitamins/rods.py | 4 +- src/muscad/vitamins/steppers.py | 22 +- 32 files changed, 523 insertions(+), 1132 deletions(-) delete mode 100644 examples/infinity_cube.py delete mode 100644 examples/lauburu.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bcb5864..0300441 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,12 +20,8 @@ repos: - id: docformatter args: - --in-place - - --wrap-summaries=100 - - --wrap-descriptions=100 -- repo: https://github.com/psf/black - rev: 23.11.0 - hooks: - - id: black + - --wrap-summaries=120 + - --wrap-descriptions=120 - repo: https://github.com/asottile/blacken-docs rev: 1.16.0 hooks: @@ -33,7 +29,10 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.1.6 hooks: + - id: ruff-format - id: ruff + args: + - --fix - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.7.1 hooks: diff --git a/docs/conf.py b/docs/conf.py index 21a6b5f..0d5777b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,9 +1,9 @@ """Sphinx configuration.""" -from datetime import datetime +from datetime import UTC, datetime project = "MuSCAD" author = "Guillaume Pujol" -copyright = f"{datetime.utcnow().year}, {author}" +copyright = f"{datetime.now(tz=UTC).year}, {author}" extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", diff --git a/examples/drying_rack_fix.py b/examples/drying_rack_fix.py index 1aa8784..cd991e3 100644 --- a/examples/drying_rack_fix.py +++ b/examples/drying_rack_fix.py @@ -1,4 +1,4 @@ -from muscad import Part, Cylinder, Volume +from muscad import Cylinder, Part, Volume from muscad.vitamins.bolts import Bolt @@ -21,35 +21,22 @@ class DryingRackFix(Part): .debug() ) - left_bolt = ( - ~Bolt.M3(12, head_clearance=10) - .add_nut(-1, inline_clearance_size=10, angle=180) - .bottom_to_back() - .align( - right=vertical_tube.left - 1, - center_y=vertical_tube.center_y, - center_z=horizontal_tube.center_z, - ) + left_bolt = ~Bolt.M3(12, head_clearance=10).add_nut(-1, inline_clearance_size=10, angle=180).bottom_to_back().align( + right=vertical_tube.left - 1, + center_y=vertical_tube.center_y, + center_z=horizontal_tube.center_z, ) - top_bolt = ( - ~Bolt.M3(12, head_clearance=10) - .add_nut(-1, inline_clearance_size=10, angle=180) - .bottom_to_back() - .align( - left=vertical_tube.right + 3, - center_y=vertical_tube.center_y, - bottom=horizontal_tube.top + 1, - ) + top_bolt = ~Bolt.M3(12, head_clearance=10).add_nut(-1, inline_clearance_size=10, angle=180).bottom_to_back().align( + left=vertical_tube.right + 3, + center_y=vertical_tube.center_y, + bottom=horizontal_tube.top + 1, ) - bottom_bolt = ( - ~Bolt.M3(12, head_clearance=10) - .add_nut(-1, inline_clearance_size=10, angle=180) - .bottom_to_back() - .align( - left=vertical_tube.right + 3, - center_y=vertical_tube.center_y, - top=horizontal_tube.bottom - 1, - ) + bottom_bolt = ~Bolt.M3(12, head_clearance=10).add_nut( + -1, inline_clearance_size=10, angle=180 + ).bottom_to_back().align( + left=vertical_tube.right + 3, + center_y=vertical_tube.center_y, + top=horizontal_tube.bottom - 1, ) reinforcement = ( @@ -66,17 +53,12 @@ class DryingRackFix(Part): ) -drying_rack_fix_bottom, drying_rack_fix_top = ( - DryingRackFix().back_to_bottom().align(center_z=0).divide(z=0, T=0.2) -) +drying_rack_fix_bottom, drying_rack_fix_top = DryingRackFix().back_to_bottom().align(center_z=0).divide(z=0, T=0.2) drying_rack_fix_bottom.render_to_file("drying_rack_fix_bottom.scad") drying_rack_fix_top.render_to_file("drying_rack_fix_top.scad") -# drying_rack_fix_top.export_stl("drying_rack_fix_top.scad") - - class DryingRackFoot(Part): """A replacement foot for that same drying rack. diff --git a/examples/infinity_cube.py b/examples/infinity_cube.py deleted file mode 100644 index df9ad04..0000000 --- a/examples/infinity_cube.py +++ /dev/null @@ -1,28 +0,0 @@ -from muscad import TT, Part, Volume - - -class Frame(Part): - mirror_x = ~Volume(width=300 + TT, height=300 + TT, depth=3 + TT, left=5) - mirror_y = ~Volume(depth=300 + TT, height=300 + TT, width=3 + TT, back=5) - - outer_shell = ( - Volume(width=20, depth=20, height=300) - .chamfer_height(5, left=True, back=True) - .chamfer_height(10, right=True, front=True) - .align(left=mirror_y.left - 3, back=mirror_x.back - 3) - ) - cable_hole = ( - ~Volume( - width=10, - depth=10, - height=300 + TT, - left=mirror_y.right + 2, - back=mirror_x.front + 2, - ) - .chamfer_height(2, left=True, back=True) - .chamfer_height(9, right=True, front=True) - ) - - -if __name__ == "__main__": - Frame().render_to_file() diff --git a/examples/lauburu.py b/examples/lauburu.py deleted file mode 100644 index 1a2aade..0000000 --- a/examples/lauburu.py +++ /dev/null @@ -1,24 +0,0 @@ -from muscad import Circle, Part, Square - - -class Lauburu(Part): - # base = Import("lauburu.stl").scale(x=2, y=2, z=.1).background() - - def init(self, r: float = 100, width: float = 10) -> None: # type: ignore[override] - shape = ( - Circle(d=r / 2).align(center_x=r * 0.75 + width, center_y=0) - + ( - Circle(d=r + width).align(center_x=(r + width) / 2, center_y=0) - - Circle(d=r / 2).align(center_x=r * 0.25 + width, center_y=0) - & Square(r + width * 2, r).align(left=0, back=0) - ) - ).offset(r=0.4, invert=True) - self.cutter = shape.z_linear_extrude(6) - self.wing = shape.offset(1).z_linear_extrude(5).align(bottom=self.cutter.top) - self.reinforcement = ( - shape.offset(3).z_linear_extrude(3).align(bottom=self.wing.top) - ) - - -if __name__ == "__main__": - Lauburu().render_to_file() diff --git a/examples/muscad_printer.py b/examples/muscad_printer.py index 9654091..06b1618 100644 --- a/examples/muscad_printer.py +++ b/examples/muscad_printer.py @@ -63,9 +63,7 @@ Z_RODS_DISTANCE = 65 * 2 Z_HEIGHT = 480 # Maximum Z travel -Y_ROD_OFFSET = ( - 17.5 # Z distance between an Y rod center and the upper Y extrusion bottom -) +Y_ROD_OFFSET = 17.5 # Z distance between an Y rod center and the upper Y extrusion bottom X_ROD_OFFSET = 47 # Z distance between the 2 X rods X_ROD_LENGTH = 500 # length of the X rods @@ -126,9 +124,7 @@ class Bed(Part): ) .misc() ) - x_extrusion_back = x_extrusion_front.y_mirror( - center=y_extrusion_right.center_y - ).misc() + x_extrusion_back = x_extrusion_front.y_mirror(center=y_extrusion_right.center_y).misc() bolt_front = ( Bolt.M3(40, thread_clearance=20) @@ -179,9 +175,7 @@ class GlassPlateCorner(Part): glass_plate_corner_left_front = GlassPlateCorner() -glass_plate_corner_right_front = glass_plate_corner_left_front.x_mirror().align( - right=bed.right, front=bed.front -) +glass_plate_corner_right_front = glass_plate_corner_left_front.x_mirror().align(right=bed.right, front=bed.front) class GlassPlateFrontFix(Part): @@ -273,9 +267,7 @@ class Frame(Part): back=z_extrusion_left_back.front, center_z=y_extrusion_left_top.center_z - MIDDLE_Y_EXTRUSION_OFFSET, ) - y_extrusion_right_middle = y_extrusion_left_middle.x_mirror( - x_extrusion_front_top.center_x - ) + y_extrusion_right_middle = y_extrusion_left_middle.x_mirror(x_extrusion_front_top.center_x) x_extrusion_front_bottom = X_EXTRUSION.align( left=z_extrusion_left_front.right, @@ -381,13 +373,9 @@ class Gantry(Part): class Extrusion3030Endcap(Part): base = Volume(width=30, depth=30, height=6).fillet_height(4) x_insert = ( - Extrusion3030Insert(25) - .front_to_right() - .align(left=base.right, center_y=base.center_y, bottom=base.bottom) - ) - y_insert = Extrusion3030Insert(25).align( - center_x=base.center_x, back=base.front, bottom=base.bottom + Extrusion3030Insert(25).front_to_right().align(left=base.right, center_y=base.center_y, bottom=base.bottom) ) + y_insert = Extrusion3030Insert(25).align(center_x=base.center_x, back=base.front, bottom=base.bottom) x_insert_left = ( Extrusion3030Insert(25) .back_to_top() @@ -395,9 +383,7 @@ class Extrusion3030Endcap(Part): .align(left=base.left, center_y=base.center_y, bottom=base.top) ) z_insert_back = ( - Extrusion3030Insert(25) - .bottom_to_back() - .align(center_x=base.center_x, back=base.back, bottom=base.top) + Extrusion3030Insert(25).bottom_to_back().align(center_x=base.center_x, back=base.back, bottom=base.top) ) def __stl__(self) -> Object: @@ -405,27 +391,14 @@ def __stl__(self) -> Object: endcap_left_back = ( - Extrusion3030Endcap() - .upside_down() - .z_rotate(-90) - .align(left=frame.left, back=frame.back, top=frame.top) + Extrusion3030Endcap().upside_down().z_rotate(-90).align(left=frame.left, back=frame.back, top=frame.top) ) endcap_left_front = ( - Extrusion3030Endcap() - .upside_down() - .z_rotate(180) - .align(left=frame.left, front=frame.front, top=frame.top) -) -endcap_right_back = ( - Extrusion3030Endcap() - .upside_down() - .align(right=frame.right, back=frame.back, top=frame.top) + Extrusion3030Endcap().upside_down().z_rotate(180).align(left=frame.left, front=frame.front, top=frame.top) ) +endcap_right_back = Extrusion3030Endcap().upside_down().align(right=frame.right, back=frame.back, top=frame.top) endcap_right_front = ( - Extrusion3030Endcap() - .upside_down() - .z_rotate(90) - .align(right=frame.right, front=frame.front, top=frame.top) + Extrusion3030Endcap().upside_down().z_rotate(90).align(right=frame.right, front=frame.front, top=frame.top) ) @@ -456,38 +429,25 @@ class BedBracket(Part): body = Hull(_base, _bolt_holder) - bottom_bolt = ( - ~Bolt.M5(10) - .slide(y=-10) - .align( - center_x=_base.center_x, - center_y=extrusion.center_y, - center_z=extrusion.bottom - 2, - ) + bottom_bolt = ~Bolt.M5(10).slide(y=-10).align( + center_x=_base.center_x, + center_y=extrusion.center_y, + center_z=extrusion.bottom - 2, ) - front_bolt = ( - ~Bolt.M5(10, head_clearance=20) - .bottom_to_front() - .slide(z=10) - .align( - center_x=_base.center_x, - center_y=extrusion.front, - center_z=extrusion.center_z, - ) + front_bolt = ~Bolt.M5(10, head_clearance=20).bottom_to_front().slide(z=10).align( + center_x=_base.center_x, + center_y=extrusion.front, + center_z=extrusion.center_z, ) - clearance = ( - ~Volume( - center_x=bed_bolt.center_x, - width=10, - back=extrusion.front + 6, - front=_bolt_holder.front, - bottom=extrusion.bottom, - height=extrusion.height, - ) - .fillet_height(r=2, back=True) - .fillet_depth(bottom=True) - ) + clearance = ~Volume( + center_x=bed_bolt.center_x, + width=10, + back=extrusion.front + 6, + front=_bolt_holder.front, + bottom=extrusion.bottom, + height=extrusion.height, + ).fillet_height(r=2, back=True).fillet_depth(bottom=True) bed_bracket_front = BedBracket() @@ -525,21 +485,15 @@ def init(self) -> None: # type: ignore[override] thumbwheel_front = ThumbWheel() -thumbwheel_left = thumbwheel_front.align( - center_x=bed.bolt_left.center_x, center_y=bed.bolt_left.center_y -) -thumbwheel_right = thumbwheel_front.align( - center_x=bed.bolt_right.center_x, center_y=bed.bolt_right.center_y -) +thumbwheel_left = thumbwheel_front.align(center_x=bed.bolt_left.center_x, center_y=bed.bolt_left.center_y) +thumbwheel_right = thumbwheel_front.align(center_x=bed.bolt_right.center_x, center_y=bed.bolt_right.center_y) class ZTopEndStop(Part): bed_extrusion = ~bed.x_extrusion_front.debug() frame_extrusion = ~frame.y_extrusion_right_middle.debug() endstop = ( - ~MechanicalEndstopOnPCB( - Bolt.M3(10).add_nut(-3, side_clearance_size=10, angle=-90) - ) + ~MechanicalEndstopOnPCB(Bolt.M3(10).add_nut(-3, side_clearance_size=10, angle=-90)) .front_to_bottom() .front_to_right() .align( @@ -550,16 +504,11 @@ class ZTopEndStop(Part): .debug() ) - bolt = ( - ~Bolt.M5(12, head_clearance=30) - .bottom_to_left() - .align( - center_x=frame_extrusion.left, - center_y=endstop.back - 10, - center_z=frame_extrusion.center_z + 20, - ) - .slide(z=-30) - ) + bolt = ~Bolt.M5(12, head_clearance=30).bottom_to_left().align( + center_x=frame_extrusion.left, + center_y=endstop.back - 10, + center_z=frame_extrusion.center_z + 20, + ).slide(z=-30) plate = Volume( width=6, right=frame_extrusion.left - TT, @@ -603,18 +552,11 @@ class XCarriage(Part): depth=8, bottom=x_bearing_bottom.bottom + E, top=x_bearing_bottom.top + 1, - ).fillet_depth( - bottom=True, left=True - ).reverse_fillet_top( - left=True, right=True - ) + ).fillet_depth(bottom=True, left=True).reverse_fillet_top(left=True, right=True) - center_pulleys_bolt = ( - ~Bolt.M3(20, head_clearance=100) - .add_nut(-1, inline_clearance_size=10) - .bottom_to_front() - .align(center_x=body.center_x, back=body.back, center_z=body.center_z) - ) + center_pulleys_bolt = ~Bolt.M3(20, head_clearance=100).add_nut( + -1, inline_clearance_size=10 + ).bottom_to_front().align(center_x=body.center_x, back=body.back, center_z=body.center_z) extruder_holder = Volume( center_x=body.center_x, @@ -777,14 +719,10 @@ class XAxisPulleys(Part): center_pulleys_bolt = x_carriage.center_pulleys_bolt bottom_bearing = ~gantry.x_bearing_bottom - left_pulley = ( - ~Pulley.placeholder(15, 10.3) - .add_clearance(20, angle=90) - .align( - right=gantry.x_bearing_bottom.left - 0.5, - center_y=gantry.x_rod_top.center_y, - top=gantry.y_rod_left.center_z + X_PULLEYS_Z_OFFSET, - ) + left_pulley = ~Pulley.placeholder(15, 10.3).add_clearance(20, angle=90).align( + right=gantry.x_bearing_bottom.left - 0.5, + center_y=gantry.x_rod_top.center_y, + top=gantry.y_rod_left.center_z + X_PULLEYS_Z_OFFSET, ) right_pulley = ~left_pulley.x_mirror(center=bed.center_x) @@ -835,9 +773,7 @@ class XAxisPulleys(Part): right_endstop = ( ~MechanicalEndstopOnPCB( - bolt=Bolt.M3(12, thread_clearance=4) - .add_nut(-1, side_clearance_size=10, angle=-90) - .upside_down(), + bolt=Bolt.M3(12, thread_clearance=4).add_nut(-1, side_clearance_size=10, angle=-90).upside_down(), z_offset=5.5, ) .front_to_right() @@ -847,26 +783,18 @@ class XAxisPulleys(Part): ) left_endstop = ( - ~MechanicalEndstopOnPCB( - bolt=Bolt.M3(8).add_nut(-2, side_clearance_size=10, angle=-90) - ) + ~MechanicalEndstopOnPCB(bolt=Bolt.M3(8).add_nut(-2, side_clearance_size=10, angle=-90)) .back_to_right() .back_to_top() .align(left=body.left, back=body.front, bottom=body.bottom - 1) .debug() ) - cable_guide_bolts = ( - ~Bolt.M3(16, head_clearance=10) - .add_nut(-3, inline_clearance_size=20) - .bottom_to_front() - .align( - center_x=body.center_x + 8, - front=body.front + 8, - center_z=center_pulleys_bolt.center_z, - ) - .x_mirror(center=center_pulleys_bolt.center_x, keep=True) - ) + cable_guide_bolts = ~Bolt.M3(16, head_clearance=10).add_nut(-3, inline_clearance_size=20).bottom_to_front().align( + center_x=body.center_x + 8, + front=body.front + 8, + center_z=center_pulleys_bolt.center_z, + ).x_mirror(center=center_pulleys_bolt.center_x, keep=True) def __stl__(self) -> Object: return self.bottom_to_back() @@ -945,18 +873,14 @@ class ExtruderClamp(Part): back=clamp.back, bottom=clamp.bottom, height=6, - ).fillet_depth(1, right=True).reverse_fillet_left(top=True).reverse_fillet_bottom( - left=True - ) - Volume( + ).fillet_depth(1, right=True).reverse_fillet_left(top=True).reverse_fillet_bottom(left=True) - Volume( left=clamp.right, width=4, front=clamp.front + E, back=clamp.back - E, center_z=clamp.bottom + 3, height=3, - ).fillet_depth( - 1, left=True - ) + ).fillet_depth(1, left=True) sensor_holder_up = ( Volume( @@ -1017,9 +941,7 @@ class Tunnel(Part): back=x_carriage.blower.blower.back + NOZZLE_SIZE * 3.1 + T, top=x_carriage.extruder_holder.center_z + E, bottom=x_carriage.bottom - E, - ).fillet_height( - NOZZLE_SIZE * 2 - ) + ).fillet_height(NOZZLE_SIZE * 2) blower = Hull( Volume( @@ -1057,14 +979,10 @@ class Tunnel(Part): ).fillet_height(NOZZLE_SIZE * 3), ) - bolt_left = ( - ~Bolt.M2(10) - .bottom_to_back() - .align( - center_x=tunnel.left - 3, - center_y=x_carriage.body.back, - center_z=x_carriage.bottom + 20, - ) + bolt_left = ~Bolt.M2(10).bottom_to_back().align( + center_x=tunnel.left - 3, + center_y=x_carriage.body.back, + center_z=x_carriage.bottom + 20, ) bolt_holder_left = ( Volume( @@ -1280,24 +1198,16 @@ class XYStepperMount(Part): .fillet_height(r=5, back=True, right=True) ) - front_upper_bolt = ( - ~Bolt.M6(8) - .bottom_to_front() - .align( - center_x=z_extrusion.center_x, - front=base.front + E, - center_z=y_extrusion.bottom - 13, - ) + front_upper_bolt = ~Bolt.M6(8).bottom_to_front().align( + center_x=z_extrusion.center_x, + front=base.front + E, + center_z=y_extrusion.bottom - 13, ) - right_upper_bolt = ( - ~Bolt.M6(8) - .bottom_to_right() - .align( - center_x=z_extrusion.right, - center_y=z_extrusion.center_y, - center_z=y_extrusion.bottom - 13, - ) + right_upper_bolt = ~Bolt.M6(8).bottom_to_right().align( + center_x=z_extrusion.right, + center_y=z_extrusion.center_y, + center_z=y_extrusion.bottom - 13, ) right_lower_bolt = ~right_upper_bolt.align(center_z=y_rod.center_z) @@ -1329,15 +1239,10 @@ class XYStepperMount(Part): ) ) - clamp_bolt = ( - ~Bolt.M3(length=16) - .add_nut(-1, angle=-15, side_clearance_size=10) - .bottom_to_left() - .align( - center_x=clamp_clearance.right, - center_y=walls.front - 7, - top=y_rod.bottom - 3, - ) + clamp_bolt = ~Bolt.M3(length=16).add_nut(-1, angle=-15, side_clearance_size=10).bottom_to_left().align( + center_x=clamp_clearance.right, + center_y=walls.front - 7, + top=y_rod.bottom - 3, ) inner_y_pulley = ~Pulley.placeholder(13, 10.3).align( @@ -1421,11 +1326,7 @@ class XYStepperMountRight(XYStepperMount): front=XYStepperMount.base.front + E, bottom=XYStepperMount.base.top, top=XYStepperMount.inner_y_pulley.top + 0.3, - ).reverse_fillet_front( - left=True, right=True - ).reverse_fillet_back( - left=True, right=True - ) + ).reverse_fillet_front(left=True, right=True).reverse_fillet_back(left=True, right=True) y_pulley_support = Volume( center_x=XYStepperMount.stepper.center_x, @@ -1460,25 +1361,17 @@ class XYIdler(Part): .fillet_depth(right=True) ) - back_top_bolt = ( - ~Bolt.M6(12) - .bottom_to_back() - .align( - center_x=z_extrusion.center_x, - back=body.back - E, - center_z=x_extrusion.bottom - 13, - ) + back_top_bolt = ~Bolt.M6(12).bottom_to_back().align( + center_x=z_extrusion.center_x, + back=body.back - E, + center_z=x_extrusion.bottom - 13, ) back_bottom_bolt = ~back_top_bolt.z_mirror(center=y_rod.center_z) - right_top_bolt = ( - ~Bolt.M6(12) - .bottom_to_right() - .align( - right=body.right + E, - center_y=z_extrusion.center_y, - center_z=x_extrusion.bottom - 13, - ) + right_top_bolt = ~Bolt.M6(12).bottom_to_right().align( + right=body.right + E, + center_y=z_extrusion.center_y, + center_z=x_extrusion.bottom - 13, ) right_bottom_bolt = ~right_top_bolt.z_mirror(y_rod.center_z) @@ -1491,27 +1384,18 @@ class XYIdler(Part): height=34, ).fillet_depth(right=True) - inner_y_pulley = ( - ~Pulley.placeholder(18, 10.3) - .add_clearance(10, 270) - .add_belt_clearance(10, angle=270, left=True) - .add_belt_clearance(40, angle=180) - .align( - center_x=gantry.y_stepper.center_x + 10, - center_y=z_extrusion.back, - bottom=y_rod.center_z + Y_PULLEYS_Z_OFFSET, - ) + inner_y_pulley = ~Pulley.placeholder(18, 10.3).add_clearance(10, 270).add_belt_clearance( + 10, angle=270, left=True + ).add_belt_clearance(40, angle=180).align( + center_x=gantry.y_stepper.center_x + 10, + center_y=z_extrusion.back, + bottom=y_rod.center_z + Y_PULLEYS_Z_OFFSET, ) - inner_y_pulley_bolt = ( - ~Bolt.M3(20) - .add_nut(-2, side_clearance_size=20) - .top_to_bottom() - .align( - center_x=inner_y_pulley.center_x, - center_y=inner_y_pulley.center_y, - top=pulleys_holder.top + E, - ) + inner_y_pulley_bolt = ~Bolt.M3(20).add_nut(-2, side_clearance_size=20).top_to_bottom().align( + center_x=inner_y_pulley.center_x, + center_y=inner_y_pulley.center_y, + top=pulleys_holder.top + E, ) clamp = ~( @@ -1525,20 +1409,15 @@ class XYIdler(Part): ) ) - clamp_bolt_top = ( - ~Bolt.M3(16, head_clearance=10) - .add_nut(-2, side_clearance_size=20, angle=195) - .bottom_to_left() - .align( - center_x=y_rod.center_x, - center_y=middle_of(body.back, z_extrusion.back), - center_z=(back_top_bolt.center_z + y_rod.center_z) / 2 - 2, - ) + clamp_bolt_top = ~Bolt.M3(16, head_clearance=10).add_nut( + -2, side_clearance_size=20, angle=195 + ).bottom_to_left().align( + center_x=y_rod.center_x, + center_y=middle_of(body.back, z_extrusion.back), + center_z=(back_top_bolt.center_z + y_rod.center_z) / 2 - 2, ) - clamp_bolt_bottom = ~clamp_bolt_top.align( - center_z=(back_bottom_bolt.center_z + y_rod.center_z) / 2 + 2 - ) + clamp_bolt_bottom = ~clamp_bolt_top.align(center_z=(back_bottom_bolt.center_z + y_rod.center_z) / 2 + 2) def __stl__(self) -> Object: return self.back_to_bottom() @@ -1567,49 +1446,32 @@ def __stl__(self) -> Object: class XYIdlerLeft(XYIdler): - outer_y_pulley = ( - ~Pulley.placeholder(18, height=10.3) - .add_clearance(20, 0) - .add_belt_clearance(40, angle=180) - .add_belt_clearance(70, angle=270, left=True) - .align( - center_x=XYIdler.y_rod.center_x + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER, - center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, - bottom=XYIdler.y_rod.center_z + Y_PULLEYS_Z_OFFSET, - ) + outer_y_pulley = ~Pulley.placeholder(18, height=10.3).add_clearance(20, 0).add_belt_clearance( + 40, angle=180 + ).add_belt_clearance(70, angle=270, left=True).align( + center_x=XYIdler.y_rod.center_x + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER, + center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, + bottom=XYIdler.y_rod.center_z + Y_PULLEYS_Z_OFFSET, ) - outer_y_pulley_bolt = ( - ~Bolt.M3(20) - .add_nut(-2, side_clearance_size=20, angle=-90) - .top_to_bottom() - .align( - center_x=outer_y_pulley.center_x, - center_y=outer_y_pulley.center_y, - top=XYIdler.pulleys_holder.top + E, - ) + outer_y_pulley_bolt = ~Bolt.M3(20).add_nut(-2, side_clearance_size=20, angle=-90).top_to_bottom().align( + center_x=outer_y_pulley.center_x, + center_y=outer_y_pulley.center_y, + top=XYIdler.pulleys_holder.top + E, ) - x_pulley = ( - ~Pulley.placeholder(18, height=10.3) - .add_clearance(10, 270) - .add_belt_clearance(10, angle=270, left=True) - .add_belt_clearance(40, angle=180) - .align( - center_x=outer_y_pulley.center_x + 10, - center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, - top=XYIdler.y_rod.center_z + X_PULLEYS_Z_OFFSET - 1, - ) + x_pulley = ~Pulley.placeholder(18, height=10.3).add_clearance(10, 270).add_belt_clearance( + 10, angle=270, left=True + ).add_belt_clearance(40, angle=180).align( + center_x=outer_y_pulley.center_x + 10, + center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, + top=XYIdler.y_rod.center_z + X_PULLEYS_Z_OFFSET - 1, ) - x_pulley_bolt = ( - ~Bolt.M3(16) - .add_nut(-1, inline_clearance_size=3) - .align( - center_x=x_pulley.center_x, - center_y=x_pulley.center_y, - bottom=XYIdler.pulleys_holder.bottom - E, - ) + x_pulley_bolt = ~Bolt.M3(16).add_nut(-1, inline_clearance_size=3).align( + center_x=x_pulley.center_x, + center_y=x_pulley.center_y, + bottom=XYIdler.pulleys_holder.bottom - E, ) @@ -1617,49 +1479,32 @@ class XYIdlerLeft(XYIdler): class XYIdlerRight(XYIdler): - outer_x_pulley = ( - ~Pulley.placeholder(18, height=10.3) - .add_clearance(20, 0) - .add_belt_clearance(40, angle=180) - .add_belt_clearance(40, angle=270, left=True) - .align( - center_x=gantry.y_stepper.center_x, - center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, - top=XYIdler.y_rod.center_z + X_PULLEYS_Z_OFFSET, - ) + outer_x_pulley = ~Pulley.placeholder(18, height=10.3).add_clearance(20, 0).add_belt_clearance( + 40, angle=180 + ).add_belt_clearance(40, angle=270, left=True).align( + center_x=gantry.y_stepper.center_x, + center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, + top=XYIdler.y_rod.center_z + X_PULLEYS_Z_OFFSET, ) - inner_x_belt = ( - ~Belt.GT2(42, 16) - .front_to_left() - .align( - center_x=XYIdler.inner_y_pulley.center_x - 5, - front=outer_x_pulley.back, - top=XYIdler.y_rod.center_z + X_PULLEYS_Z_OFFSET - 1, - ) + inner_x_belt = ~Belt.GT2(42, 16).front_to_left().align( + center_x=XYIdler.inner_y_pulley.center_x - 5, + front=outer_x_pulley.back, + top=XYIdler.y_rod.center_z + X_PULLEYS_Z_OFFSET - 1, ) - outer_y_pulley = ( - ~Pulley.placeholder(18, height=10.3) - .add_clearance(20, angle=0) - .add_belt_clearance(40, angle=180) - .add_belt_clearance(70, angle=270, left=True) - .align( - center_x=gantry.y_stepper.center_x, - center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, - bottom=XYIdler.y_rod.center_z + Y_PULLEYS_Z_OFFSET, - ) + outer_y_pulley = ~Pulley.placeholder(18, height=10.3).add_clearance(20, angle=0).add_belt_clearance( + 40, angle=180 + ).add_belt_clearance(70, angle=270, left=True).align( + center_x=gantry.y_stepper.center_x, + center_y=XYIdler.z_extrusion.center_y - BACK_BELT_Y_OFFSET, + bottom=XYIdler.y_rod.center_z + Y_PULLEYS_Z_OFFSET, ) - outer_y_pulley_bolt = ( - ~Bolt.M3(35) - .add_nut(29, inline_clearance_size=20) - .top_to_bottom() - .align( - center_x=outer_y_pulley.center_x, - center_y=outer_y_pulley.center_y, - top=XYIdler.pulleys_holder.top + E, - ) + outer_y_pulley_bolt = ~Bolt.M3(35).add_nut(29, inline_clearance_size=20).top_to_bottom().align( + center_x=outer_y_pulley.center_x, + center_y=outer_y_pulley.center_y, + top=XYIdler.pulleys_holder.top + E, ) @@ -1683,19 +1528,13 @@ class YCarriage(Part): .debug() ) - front_x_pulley = ( - ~Pulley.placeholder(18, 10.3) - .add_bolt( - Bolt.M3(25).add_nut(-1, side_clearance_size=20), - z_offset=2, - ) - .add_clearance(20, 270) - .add_clearance(20, 0) - .align( - center_x=y_bearing.center_x + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER + 10, - center_y=y_bearing.center_y + 10, - top=y_bearing.center_z + X_PULLEYS_Z_OFFSET, - ) + front_x_pulley = ~Pulley.placeholder(18, 10.3).add_bolt( + Bolt.M3(25).add_nut(-1, side_clearance_size=20), + z_offset=2, + ).add_clearance(20, 270).add_clearance(20, 0).align( + center_x=y_bearing.center_x + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER + 10, + center_y=y_bearing.center_y + 10, + top=y_bearing.center_z + X_PULLEYS_Z_OFFSET, ) back_x_pulley = ~front_x_pulley.y_mirror(y_bearing.center_y) @@ -1718,15 +1557,12 @@ class YCarriage(Part): bottom=x_rod_bottom.bottom - 5, ).fillet_width(r=10, front=True) - y_clamp_bolt_top_front = ( - ~Bolt.M3(16, head_clearance=10) - .add_nut(placement=-2, side_clearance_size=30, angle=180) - .bottom_to_left() - .align( - left=body.left - 8, - center_y=body.front - 4.5, - center_z=y_bearing.top + 2.4, - ) + y_clamp_bolt_top_front = ~Bolt.M3(16, head_clearance=10).add_nut( + placement=-2, side_clearance_size=30, angle=180 + ).bottom_to_left().align( + left=body.left - 8, + center_y=body.front - 4.5, + center_z=y_bearing.top + 2.4, ) y_clamp_bolt_top_back = ~y_clamp_bolt_top_front.y_mirror(y_bearing.center_y) @@ -1744,11 +1580,8 @@ class YCarriage(Part): clamp_clearance_up = ~clamp_clearance_bottom.z_mirror(y_bearing.center_z) - clamp_bolt_up = ( - ~Bolt.M3(12) - .add_nut(-E, side_clearance_size=30, angle=90) - .top_to_bottom() - .align(center_x=body.center_x, center_y=body.back + 9, top=body.top) + clamp_bolt_up = ~Bolt.M3(12).add_nut(-E, side_clearance_size=30, angle=90).top_to_bottom().align( + center_x=body.center_x, center_y=body.back + 9, top=body.top ) clamp_bolt_down = ~clamp_bolt_up.z_mirror(y_bearing.center_z) @@ -1776,18 +1609,14 @@ def __stl__(self) -> Object: class YCarriageLeft(YCarriage): - belt_fix_clearance_front = ( - ~Volume( - left=YCarriage.y_belt_inner_clearance.left, - width=20, - back=YCarriage.body.front - 16, - front=YCarriage.body.front + E, - bottom=YCarriage.y_belt_inner_clearance.bottom, - top=YCarriage.y_belt_inner_clearance.top, - ) - .fillet_depth(1, right=True) - .fillet_width(1, back=True) - ) + belt_fix_clearance_front = ~Volume( + left=YCarriage.y_belt_inner_clearance.left, + width=20, + back=YCarriage.body.front - 16, + front=YCarriage.body.front + E, + bottom=YCarriage.y_belt_inner_clearance.bottom, + top=YCarriage.y_belt_inner_clearance.top, + ).fillet_depth(1, right=True).fillet_width(1, back=True) y_carriage_left = YCarriageLeft() @@ -1863,21 +1692,14 @@ class YCarriageRight(YCarriage): bottom=YCarriage.y_belt_outer_clearance.bottom, top=YCarriage.y_belt_outer_clearance.top, ).fillet_depth(1, left=True) - y_fix_bolt = ( - ~Bolt.M3(25) - .add_nut(-2, inline_clearance_size=20, angle=90) - .bottom_to_front() - .align( - center_x=YCarriage.y_belt_outer_clearance.center_x - 9, - center_y=YCarriage.body.center_y, - center_z=YCarriage.y_belt_outer_clearance.center_z, - ) + y_fix_bolt = ~Bolt.M3(25).add_nut(-2, inline_clearance_size=20, angle=90).bottom_to_front().align( + center_x=YCarriage.y_belt_outer_clearance.center_x - 9, + center_y=YCarriage.body.center_y, + center_z=YCarriage.y_belt_outer_clearance.center_z, ) x_belt_clearance = ~Volume( - center_x=YCarriage.y_bearing.center_x - + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER - - 5, + center_x=YCarriage.y_bearing.center_x + Y_ROD_CENTER_TO_STEPPER_SHAFT_CENTER - 5, width=8, back=YCarriage.body.back - 1, front=YCarriage.body.front + 1, @@ -1889,11 +1711,7 @@ def __stl__(self) -> Object: return self.front_to_bottom() -y_carriage_right = ( - YCarriageRight() - .x_mirror(center=bed.center_x) - .y_mirror(center=y_carriage_left.center_y) -) +y_carriage_right = YCarriageRight().x_mirror(center=bed.center_x).y_mirror(center=y_carriage_left.center_y) class YBeltFixFront(Part): @@ -2009,9 +1827,7 @@ class YEndstopAttachmentBack(Part): stepper_mount = ~xy_stepper_mount_right y_endstop = ( - ~MechanicalEndstopOnPCB( - bolt=Bolt.M3(8).add_nut(-1, inline_clearance_size=10), z_offset=-3 - ) + ~MechanicalEndstopOnPCB(bolt=Bolt.M3(8).add_nut(-1, inline_clearance_size=10), z_offset=-3) .bottom_to_left() .bottom_to_front() .align( @@ -2243,14 +2059,10 @@ class ExtruderStepperMount(Part): .align(left=attachment.right, back=stepper_holder.front) ) - bolt_top_back = ( - ~Bolt.M6(12) - .upside_down() - .align( - center_x=y_extrusion.center_x, - center_y=stepper_holder.center_y, - center_z=y_extrusion.top, - ) + bolt_top_back = ~Bolt.M6(12).upside_down().align( + center_x=y_extrusion.center_x, + center_y=stepper_holder.center_y, + center_z=y_extrusion.top, ) bolt_top_front = ~bolt_top_back.y_mirror(attachment.center_y) @@ -2324,15 +2136,10 @@ class SpoolHolder(Part): .fillet_width(12, back=True, bottom=True) ) - bolt_right = ( - ~Bolt.M6(16) - .bottom_to_right() - .slide(y=20) - .align( - center_x=y_extrusion.right, - center_y=stopper_right.center_y + 10, - center_z=y_extrusion.center_z, - ) + bolt_right = ~Bolt.M6(16).bottom_to_right().slide(y=20).align( + center_x=y_extrusion.right, + center_y=stopper_right.center_y + 10, + center_z=y_extrusion.center_z, ) arm = ( @@ -2398,14 +2205,10 @@ class SpoolHolder(Part): top=bearing_right.top + 2, ).fillet_width(12, top=True) - bolt_top = ( - ~Bolt.M6(12) - .upside_down() - .align( - center_x=y_extrusion.center_x, - center_y=fix.center_y, - center_z=y_extrusion.top, - ) + bolt_top = ~Bolt.M6(12).upside_down().align( + center_x=y_extrusion.center_x, + center_y=fix.center_y, + center_z=y_extrusion.top, ) def __stl__(self) -> Object: @@ -2431,17 +2234,11 @@ class ZBedMount(MirroredPart, y=True, keep_y=True): .debug() ) - bolts = ( - ~Bolt.M5(10, head_clearance=30) - .bottom_to_left() - .align( - left=extrusion.left - 10, - center_y=bearing.back - 7, - center_z=extrusion.center_z, - ) - .slide(y=-10) - .y_mirror(center=z_rod.center_y, keep=True) - ) + bolts = ~Bolt.M5(10, head_clearance=30).bottom_to_left().align( + left=extrusion.left - 10, + center_y=bearing.back - 7, + center_z=extrusion.center_z, + ).slide(y=-10).y_mirror(center=z_rod.center_y, keep=True) base = Surface.free( Circle(d=30).align(center_x=bearing.center_x, center_y=bearing.center_y), @@ -2502,13 +2299,8 @@ class ZBedMount(MirroredPart, y=True, keep_y=True): top=arm.bottom, height=7, ) & Union( - Tube(diameter=8 * i, center_x=0, center_y=0, bottom=0, height=7).tunnel( - 8 * i - 4 - ) - for i in range(3, 15) - ).align( - center_x=t8.center_x, center_y=t8.center_y, top=arm.bottom - ) + Tube(diameter=8 * i, center_x=0, center_y=0, bottom=0, height=7).tunnel(8 * i - 4) for i in range(3, 15) + ).align(center_x=t8.center_x, center_y=t8.center_y, top=arm.bottom) brass_nut_base = Tube( diameter=21.8, @@ -2540,17 +2332,11 @@ class ZBedMount(MirroredPart, y=True, keep_y=True): ) ) - top_bolts = ( - ~Bolt.M5(10) - .bottom_to_top() - .align( - center_x=extrusion.center_x, - center_y=bearing.center_y + 16, - center_z=extrusion.top + 2, - ) - .slide(y=20) - .y_mirror(bearing.center_y, keep=True) - ) + top_bolts = ~Bolt.M5(10).bottom_to_top().align( + center_x=extrusion.center_x, + center_y=bearing.center_y + 16, + center_z=extrusion.top + 2, + ).slide(y=20).y_mirror(bearing.center_y, keep=True) def __stl__(self) -> Object: return self.upside_down() @@ -2564,15 +2350,10 @@ class ZBracketBottom(Part): extrusion = ~frame.y_extrusion_left_bottom rod = ~gantry.z_rod_left_back - front_bolt = ( - ~Bolt.M5(10, head_clearance=10) - .bottom_to_right() - .slide(z=20) - .align( - right=extrusion.right + 8, - back=rod.front, - center_z=extrusion.center_z, - ) + front_bolt = ~Bolt.M5(10, head_clearance=10).bottom_to_right().slide(z=20).align( + right=extrusion.right + 8, + back=rod.front, + center_z=extrusion.center_z, ) bottom_bolt = ~Bolt.M6(10).align( @@ -2610,26 +2391,19 @@ class ZBracketBottom(Part): bottom=rod_holder.bottom - E, top=rod_holder.top + E, ) - rod_holder_bolt = ( - ~Bolt.M3(16, head_clearance=10) - .add_nut(-0.1, angle=90, inline_clearance_size=20) - .bottom_to_back() - .align( - center_x=rod.left - 5, - center_y=rod_holder_clearance.center_y, - center_z=rod_holder.center_z, - ) + rod_holder_bolt = ~Bolt.M3(16, head_clearance=10).add_nut( + -0.1, angle=90, inline_clearance_size=20 + ).bottom_to_back().align( + center_x=rod.left - 5, + center_y=rod_holder_clearance.center_y, + center_z=rod_holder.center_z, ) z_bracket_bottom_left_back = ZBracketBottom() -z_bracket_bottom_left_front = z_bracket_bottom_left_back.y_mirror( - center=gantry.z_threaded_rod_left.center_y -) +z_bracket_bottom_left_front = z_bracket_bottom_left_back.y_mirror(center=gantry.z_threaded_rod_left.center_y) z_bracket_bottom_right_back = z_bracket_bottom_left_back.x_mirror(center=bed.center_x) -z_bracket_bottom_right_front = z_bracket_bottom_right_back.y_mirror( - center=gantry.z_threaded_rod_right.center_y -) +z_bracket_bottom_right_front = z_bracket_bottom_right_back.y_mirror(center=gantry.z_threaded_rod_right.center_y) class ZBracketTopLeft(Part): @@ -2657,15 +2431,12 @@ class ZBracketTopLeft(Part): Circle(d=4).align(left=rod.left - 15, front=rod.front + 3), ).z_linear_extrude(top=body.top, distance=10) - rod_holder_bolt = ( - ~Bolt.M3(20, head_clearance=20) - .add_nut(-4, angle=90, inline_clearance_size=20) - .bottom_to_front() - .align( - center_x=rod.left - 5, - center_y=rod.center_y - 2, - center_z=rod_holder.center_z, - ) + rod_holder_bolt = ~Bolt.M3(20, head_clearance=20).add_nut( + -4, angle=90, inline_clearance_size=20 + ).bottom_to_front().align( + center_x=rod.left - 5, + center_y=rod.center_y - 2, + center_z=rod_holder.center_z, ) rod_holder_clearance = ~Volume( right=rod.left + 1, @@ -2676,14 +2447,10 @@ class ZBracketTopLeft(Part): height=rod_holder.height + 1, ) - top_bolt = ( - ~Bolt.M6(12, head_clearance=40) - .top_to_bottom() - .align( - center_x=extrusion.center_x, - center_y=body.center_y, - center_z=extrusion.top + 3, - ) + top_bolt = ~Bolt.M6(12, head_clearance=40).top_to_bottom().align( + center_x=extrusion.center_x, + center_y=body.center_y, + center_z=extrusion.top + 3, ) front_bolt = ( @@ -2714,9 +2481,7 @@ def __stl__(self) -> Object: z_bracket_top_left_front = ZBracketTopLeft() -z_bracket_top_left_back = z_bracket_top_left_front.y_mirror( - gantry.z_threaded_rod_left.center_y -) +z_bracket_top_left_back = z_bracket_top_left_front.y_mirror(gantry.z_threaded_rod_left.center_y) z_bracket_top_right_front = z_bracket_top_left_front.x_mirror(bed.center_x) z_bracket_top_right_back = z_bracket_top_left_back.x_mirror(bed.center_x) @@ -2746,15 +2511,12 @@ class ZBracketTop(MirroredPart, y=True, keep_y=True): Circle(d=4).align(center_x=body.right, front=body.front), ).z_linear_extrude(top=body.top, distance=10) - rod_holder_bolt = ( - ~Bolt.M3(20, head_clearance=20) - .add_nut(-4, angle=90, inline_clearance_size=20) - .bottom_to_front() - .align( - center_x=rod.left - 5, - center_y=rod.center_y - 2, - center_z=rod_holder.center_z, - ) + rod_holder_bolt = ~Bolt.M3(20, head_clearance=20).add_nut( + -4, angle=90, inline_clearance_size=20 + ).bottom_to_front().align( + center_x=rod.left - 5, + center_y=rod.center_y - 2, + center_z=rod_holder.center_z, ) rod_holder_clearance = ~Volume( right=rod.left + 1, @@ -2765,14 +2527,10 @@ class ZBracketTop(MirroredPart, y=True, keep_y=True): height=rod_holder.height + 1, ) - top_bolt = ( - ~Bolt.M6(12, head_clearance=40) - .top_to_bottom() - .align( - center_x=extrusion.center_x, - center_y=body.front - 10, - center_z=extrusion.top + 3, - ) + top_bolt = ~Bolt.M6(12, head_clearance=40).top_to_bottom().align( + center_x=extrusion.center_x, + center_y=body.front - 10, + center_z=extrusion.top + 3, ) side_bolt = ( @@ -2806,38 +2564,25 @@ class ZStepperMount(Part): .debug() ) - side_bolt_front = ( - ~Bolt.M5(10) - .bottom_to_right() - .slide(y=20) - .align( - right=extrusion.right + 7, - center_y=stepper.front + 15, - center_z=extrusion.center_z, - ) + side_bolt_front = ~Bolt.M5(10).bottom_to_right().slide(y=20).align( + right=extrusion.right + 7, + center_y=stepper.front + 15, + center_z=extrusion.center_z, ) side_bolt_back = ~side_bolt_front.y_mirror(stepper.center_y) - top_bolt_front = ( - ~Bolt.M5(10) - .top_to_bottom() - .align( - center_x=extrusion.center_x, - center_y=side_bolt_front.center_y - 3, - center_z=extrusion.top + 2, - ) + top_bolt_front = ~Bolt.M5(10).top_to_bottom().align( + center_x=extrusion.center_x, + center_y=side_bolt_front.center_y - 3, + center_z=extrusion.top + 2, ) top_bolt_back = ~top_bolt_front.y_mirror(stepper.center_y) - top_bold_center = ( - ~Bolt.M5(10) - .top_to_bottom() - .align( - center_x=extrusion.center_x, - center_y=stepper.center_y, - center_z=extrusion.top + 2, - ) + top_bold_center = ~Bolt.M5(10).top_to_bottom().align( + center_x=extrusion.center_x, + center_y=stepper.center_y, + center_z=extrusion.top + 2, ) body = ( @@ -2927,17 +2672,11 @@ class ZBottomEndStop(Part): bottom=endstop_attachment.bottom, top=frame.y_extrusion_right_bottom.top - 2, ).reverse_fillet_left(top=True) - bolt = ( - ~Bolt.M5(10) - .bottom_to_left() - .align( - center_x=frame.y_extrusion_right_bottom.left, - center_y=arm.back - 10, - center_z=frame.y_extrusion_right_bottom.center_z, - ) - .debug() - .slide(z=20) - ) + bolt = ~Bolt.M5(10).bottom_to_left().align( + center_x=frame.y_extrusion_right_bottom.left, + center_y=arm.back - 10, + center_z=frame.y_extrusion_right_bottom.center_z, + ).debug().slide(z=20) frame_attachment = Volume( right=frame.y_extrusion_right_bottom.left - T, width=6, @@ -2988,24 +2727,16 @@ def init( # type: ignore[override] bottom=self.board.bottom + 10, ).fillet_width() ) - self.z_bolt_top = ( - ~Bolt.M6(10) - .bottom_to_front() - .align( - center_x=self.z_extrusion.center_x, - center_y=self.z_extrusion.front, - center_z=self.mount.top + 10, - ) + self.z_bolt_top = ~Bolt.M6(10).bottom_to_front().align( + center_x=self.z_extrusion.center_x, + center_y=self.z_extrusion.front, + center_z=self.mount.top + 10, ) - self.z_bolt_bottom = ( - ~Bolt.M6(10) - .bottom_to_front() - .align( - center_x=self.z_extrusion.center_x, - center_y=self.z_extrusion.front, - center_z=self.mount.bottom - 10, - ) + self.z_bolt_bottom = ~Bolt.M6(10).bottom_to_front().align( + center_x=self.z_extrusion.center_x, + center_y=self.z_extrusion.front, + center_z=self.mount.bottom - 10, ) self.z_attach = Volume( @@ -3046,24 +2777,16 @@ def init( # type: ignore[override] ).fillet_width() ) - self.z_bolt_top = ( - ~Bolt.M6(10) - .bottom_to_back() - .align( - center_x=self.z_extrusion.center_x, - center_y=self.z_extrusion.back, - center_z=self.mount.top + bolt_spacing, - ) + self.z_bolt_top = ~Bolt.M6(10).bottom_to_back().align( + center_x=self.z_extrusion.center_x, + center_y=self.z_extrusion.back, + center_z=self.mount.top + bolt_spacing, ) - self.z_bolt_bottom = ( - ~Bolt.M6(10) - .bottom_to_back() - .align( - center_x=self.z_extrusion.center_x, - center_y=self.z_extrusion.back, - center_z=self.mount.bottom - bolt_spacing, - ) + self.z_bolt_bottom = ~Bolt.M6(10).bottom_to_back().align( + center_x=self.z_extrusion.center_x, + center_y=self.z_extrusion.back, + center_z=self.mount.bottom - bolt_spacing, ) self.z_attach = Volume( @@ -3119,14 +2842,10 @@ def init( # type: ignore[override] ).fillet_width() ) - self.z_bolt = ( - ~Bolt.M6(10) - .bottom_to_front() - .align( - center_x=self.z_extrusion.center_x, - center_y=self.z_extrusion.front, - center_z=self.mount.top + 10, - ) + self.z_bolt = ~Bolt.M6(10).bottom_to_front().align( + center_x=self.z_extrusion.center_x, + center_y=self.z_extrusion.front, + center_z=self.mount.top + 10, ) self.z_attach = ( Volume( @@ -3141,14 +2860,10 @@ def init( # type: ignore[override] .fillet_width(back=True, bottom=True) ) - self.y_bolt = ( - ~Bolt.M6(10) - .upside_down() - .align( - center_x=self.y_extrusion.center_x, - center_y=self.mount.front + 10, - center_z=self.y_extrusion.top, - ) + self.y_bolt = ~Bolt.M6(10).upside_down().align( + center_x=self.y_extrusion.center_x, + center_y=self.mount.front + 10, + center_z=self.y_extrusion.top, ) self.y_attach = ( Volume( @@ -3220,9 +2935,7 @@ def init(self) -> None: # type: ignore[override] class RaspberryMount(BoardMountSide): def init(self) -> None: # type: ignore[override] - mainboard = Board.raspberry_pi_3b( - Bolt.M3(26).add_nut(-E, inline_clearance_size=10).upside_down() - ) + mainboard = Board.raspberry_pi_3b(Bolt.M3(26).add_nut(-E, inline_clearance_size=10).upside_down()) return super().init(mainboard) @@ -3311,11 +3024,7 @@ def __stl__(self) -> Object: class CableClip(Part): - body = ( - Volume(width=12, depth=5, back=-1.5, height=6) - .fillet_height(1, back=True) - .fillet_height(4, front=True) - ) + body = Volume(width=12, depth=5, back=-1.5, height=6).fillet_height(1, back=True).fillet_height(4, front=True) center_clearance = ~Volume( center_x=body.center_x, @@ -3362,23 +3071,15 @@ class MotorCableGuide(Part): bottom=body.bottom, ).fillet_depth(left=True, r=8) - bolt_back = ( - ~Bolt.M5(30) - .bottom_to_right() - .align( - right=body.right + E, - back=body.back + 4, - center_z=frame.y_extrusion_right_middle.center_z, - ) + bolt_back = ~Bolt.M5(30).bottom_to_right().align( + right=body.right + E, + back=body.back + 4, + center_z=frame.y_extrusion_right_middle.center_z, ) - bolt_front = ( - ~Bolt.M5(30) - .bottom_to_front() - .align( - center_x=frame.z_extrusion_right_front.center_x, - front=front_part.front + E, - center_z=front_part.center_z, - ) + bolt_front = ~Bolt.M5(30).bottom_to_front().align( + center_x=frame.z_extrusion_right_front.center_x, + front=front_part.front + E, + center_z=front_part.center_z, ) cables = ~Volume( left=frame.z_extrusion_right_front.left + 6, @@ -3453,13 +3154,9 @@ class MotorCableGuide(Part): + cable_clamp_frame ).render_to_file("right_side") - ( - y_carriage_left - + xy_stepper_mount_left - + xy_idler_left - + xy_idler_clamp_left - + y_belt_fix_left - ).render_to_file("left_side") + (y_carriage_left + xy_stepper_mount_left + xy_idler_left + xy_idler_clamp_left + y_belt_fix_left).render_to_file( + "left_side" + ) ( (bed + glass_plate + gantry + frame).background() diff --git a/examples/numbers.py b/examples/numbers.py index 91752bb..a88bce9 100644 --- a/examples/numbers.py +++ b/examples/numbers.py @@ -1,12 +1,10 @@ -from muscad import Cylinder, E, Part, Polygon, T, Text +from muscad import Cylinder, Part, Polygon, T, Text class Screw(Part): body = Cylinder(h=8, d=3.8).align(top=1) head = ( - Polygon((0, 0), (3.9, 0), (3.9, -0.4), (1.9, -3), (0, -2.8)) - .z_rotational_extrude() - .align(bottom=body.top - 1) + Polygon((0, 0), (3.9, 0), (3.9, -0.4), (1.9, -3), (0, -2.8)).z_rotational_extrude().align(bottom=body.top - 1) ) @@ -17,9 +15,7 @@ class Number1(Part): .leftward(0.3) .backward(0.8) ) - screws = ( - ~Screw().align(center_x=0, center_y=30, top=number.top + T).y_mirror(keep=True) - ) + screws = ~Screw().align(center_x=0, center_y=30, top=number.top + T).y_mirror(keep=True) class Number2(Part): @@ -41,12 +37,7 @@ class Number3(Part): .leftward(0.3) .backward(0.8) ) - screws = ( - ~Screw() - .align(center_x=16, center_y=24, top=number.top + T) - .y_mirror(keep=True) - .x_mirror(keep=True) - ) + screws = ~Screw().align(center_x=16, center_y=24, top=number.top + T).y_mirror(keep=True).x_mirror(keep=True) class Number4(Part): @@ -56,11 +47,7 @@ class Number4(Part): .leftward(0.3) .backward(0.8) ) - screws_right = ( - ~Screw() - .align(center_x=10, center_y=10, top=number.top + T) - .y_mirror(-10, keep=True) - ) + screws_right = ~Screw().align(center_x=10, center_y=10, top=number.top + T).y_mirror(-10, keep=True) screw_top = ~Screw().align(center_x=-4, center_y=32, top=number.top + T) screw_left = ~Screw().align(center_x=-22, center_y=-19, top=number.top + T) @@ -84,9 +71,8 @@ class Number6(Part): .leftward(0.3) .backward(0.8) ) - # screw1 = ~Screw().align(center_x=17, center_y=28, top=number.top + T) - screw2 = ~Screw().align(center_x=-15, center_y=-13, top=number.top + T) - screw3 = ~Screw().align(center_x=16, center_y=-13, top=number.top + T) + screw1 = ~Screw().align(center_x=-15, center_y=-13, top=number.top + T) + screw2 = ~Screw().align(center_x=16, center_y=-13, top=number.top + T) class Number7(Part): @@ -118,23 +104,17 @@ class Number9(Part): .leftward(0.3) .backward(0.8) ) - # screw1 = ~Screw().align(center_x=-17, center_y=-27, top=number.top + T) - screw2 = ~Screw().align(center_x=-15, center_y=13, top=number.top + T) - screw3 = ~Screw().align(center_x=16, center_y=13, top=number.top + T) + screw1 = ~Screw().align(center_x=-15, center_y=13, top=number.top + T) + screw2 = ~Screw().align(center_x=16, center_y=13, top=number.top + T) class Number0(Part): - number = ( - Text("0", size=75, direction="ttb", valign="center", font="Sancreek") - .z_linear_extrude(5) - .leftward(0.8) - ) + number = Text("0", size=75, direction="ttb", valign="center", font="Sancreek").z_linear_extrude(5).leftward(0.8) screw1 = ~Screw().align(center_x=-16, center_y=0, top=number.top + T) screw2 = ~Screw().align(center_x=16, center_y=0, top=number.top + T) if __name__ == "__main__": - # Screw().render_to_file("screw.scad") Number1().render_to_file("number1.scad") Number2().render_to_file("number2.scad") Number3().render_to_file("number3.scad") diff --git a/pyproject.toml b/pyproject.toml index d9a01de..8c47ed9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,6 @@ target-version = "py38" line-length = 120 select = [ "A", - "B", "C", "C4", "D", @@ -76,17 +75,14 @@ select = [ "FBT", "I", "ICN", - "ISC", "N", "PGH", "PLC", "PLE", - "PLR", "PLW", "PTH", "Q", "RUF", - "S", "SIM", "T", "TID", diff --git a/src/muscad/base.py b/src/muscad/base.py index 0502c4f..94e73c7 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -30,8 +30,8 @@ def render(self) -> str: def __str__(self) -> str: return self.render() - def _repr_pretty_(self, p: Any, cycle: bool) -> None: - """Helper method for Jupyter Notebook to show the rendered code instead of __repr__""" + def _repr_pretty_(self, p: Any, cycle: bool) -> None: # no-qa: FBT001 + """Helper method for Jupyter Notebook to show the rendered code instead of `__repr__`.""" p.text(str(self)) # pragma: no cover @@ -40,7 +40,7 @@ class MuSCADError(Exception): def indent(s: str, token: str = " ") -> str: - """Indents a given string, with characters from ``token`` Each line will be prefixed by token. + """Indents a given string, with characters from `token`. Each line will be prefixed by token. :param s: the string to indent (may contain multiple lines, separated by '\n' :param token: the string to use as indentation @@ -142,13 +142,13 @@ def remove_modifier(self) -> Object: return self.set_modifier(None) def __add__(self, other: Object | Iterable[Object]) -> Object: - """Adding two objects together creates a Union of those objects :param other: another object - :return: a Union of both objects.""" + """Adding two objects together creates a Union of those objects :param other: another object :return: a Union of + both objects. + """ return Union(self, other) def __radd__(self, other: Literal[0]) -> Object: - """Makes sure sum(*[object, ...]) works :param other: another object, or 0 :return: a Union - of both objects.""" + """Makes sure sum(*[object, ...]) works :param other: another object, or 0 :return: a Union of both objects.""" assert other == 0 return self @@ -163,7 +163,8 @@ def __sub__(self, other: Object | Hole | Misc | Iterable[Object]) -> Object: def __and__(self, other: Object) -> Object: """Logical and between two objects creates an Intersection between those objects :param - other: another object :return: an Intersection of self and other.""" + other: another object :return: an Intersection of self and other. + """ return Intersection(self, other) def translate(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: @@ -196,7 +197,8 @@ def rightward(self, dist: float) -> Object: def leftward(self, dist: float) -> Object: """Helper method to apply a Translation to the left on X axis on the current object :param - dist: distance in mm :return: an object, translated to the left by `dist` mm.""" + dist: distance in mm :return: an object, translated to the left by `dist` mm. + """ return self.x_translate(-dist) def forward(self, dist: float) -> Object: @@ -262,15 +264,11 @@ def rotate( return self if center_x or center_y or center_z: return Translation(x=center_x, y=center_y, z=center_z)( - Rotation(x=x, y=y, z=z)( - Translation(x=-center_x, y=-center_y, z=-center_z)(self) - ) + Rotation(x=x, y=y, z=z)(Translation(x=-center_x, y=-center_y, z=-center_z)(self)) ) return Rotation(x=x, y=y, z=z)(self) - def x_rotate( - self, angle: float, center_y: float = 0, center_z: float = 0 - ) -> Object: + def x_rotate(self, angle: float, center_y: float = 0, center_z: float = 0) -> Object: """Helper method to apply a Rotation on X axis on the current object. :param angle: angle in degrees @@ -279,9 +277,7 @@ def x_rotate( """ return self.rotate(x=angle, center_y=center_y, center_z=center_z) - def y_rotate( - self, angle: float, center_x: float = 0, center_z: float = 0 - ) -> Object: + def y_rotate(self, angle: float, center_x: float = 0, center_z: float = 0) -> Object: """Helper method to apply a Rotation on Y axis on the current object. :param angle: angle in degrees @@ -290,9 +286,7 @@ def y_rotate( """ return self.rotate(y=angle, center_x=center_x, center_z=center_z) - def z_rotate( - self, angle: float, center_x: float = 0, center_y: float = 0 - ) -> Object: + def z_rotate(self, angle: float, center_x: float = 0, center_y: float = 0) -> Object: """Helper method to apply a Rotation on Z axis on the current object. :param angle: angle in degrees @@ -539,8 +533,7 @@ def top_to_bottom(self) -> Object: def upside_down(self, x_axis: bool = False) -> Object: """Turns the object upside down on its Y axis. - Equivalent to self.y_rotate(180). If x_axis is True, rotate on X axis instead (like - top_to_bottom()). + Equivalent to self.y_rotate(180). If x_axis is True, rotate on X axis instead (like top_to_bottom()). :return: an object rotated 180° on X or Y axis """ @@ -571,7 +564,7 @@ def mirror(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: """ return Mirroring(x=x, y=y, z=z)(self) - def x_mirror(self, center: float = 0.0, keep: bool = False) -> Object: + def x_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: """Helper method to mirror this object on the X axis or a parallel. :param center: the X coordinate of the axis to mirror on @@ -629,8 +622,8 @@ def linear_extrude( :param twist: :param slices: :param scale: - :param segments: number of segments. If None, automatically determines the number of - segments to get a good-looking round result. + :param segments: number of segments. If None, automatically determines the number of segments to get a good- + looking round result. """ return LinearExtrusion( @@ -734,8 +727,8 @@ def rotational_extrude( :param angle: :param convexity: - :param segments: number of segments. If None, automatically determines the number of - segments to get a good-looking round result. + :param segments: number of segments. If None, automatically determines the number of segments to get a good- + looking round result. :return: """ @@ -759,16 +752,12 @@ def z_rotational_extrude( center_z: float | None = None, top: float | None = None, ) -> Object: - bottom, center_z, top, _ = calc( - from_=bottom, center=center_z, to=top, distance=self.width - ) + bottom, center_z, top, _ = calc(from_=bottom, center=center_z, to=top, distance=self.width) if angle is None and angle_from is None and angle_to is None: angle = 360 angle_from = 0 else: - angle_from, _, angle_to, angle = calc( - from_=angle_from, to=angle_to, distance=angle - ) + angle_from, _, angle_to, angle = calc(from_=angle_from, to=angle_to, distance=angle) return ( self.x_translate(radius) .rotational_extrude(angle, convexity, segments) @@ -878,9 +867,7 @@ def center_z(self) -> float: return (self.top + self.bottom) / 2 def bounding_box(self) -> Object: - return Cube(self.width, self.depth, self.height).translate( - x=self.left, y=self.back, z=self.bottom - ) + return Cube(self.width, self.depth, self.height).translate(x=self.left, y=self.back, z=self.bottom) def slide(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: return Slide(x=x, y=y, z=z)(self) @@ -1003,9 +990,7 @@ def divide( ), ) else: - raise ValueError( - "You must provide one plane to divide the Object, defined by one X, Y, Z coordinate" - ) + raise ValueError("You must provide one plane to divide the Object, defined by one X, Y, Z coordinate") def __stl__(self) -> Object: return self # pragma: no cover @@ -1154,9 +1139,7 @@ def _render_children(cls, children: Iterable[Object]) -> str: :return: a str """ - return ( - "{" + "".join(f"\n{indent(child.render())}" for child in children) + "\n}" - ) + return "{" + "".join(f"\n{indent(child.render())}" for child in children) + "\n}" @render_comment def render(self) -> str: @@ -1165,10 +1148,7 @@ def render(self) -> str: :return: a str """ - return ( - f"{self.modifier}{self.object_name}() " - f"{self._render_children(self._iter_children())}" - ) + return f"{self.modifier}{self.object_name}() " f"{self._render_children(self._iter_children())}" def walk(self) -> Iterable[Object]: for child in self.children: @@ -1203,8 +1183,11 @@ class Union(Composite): """OpenSCAD union()""" def __add__(self, other: Object | Iterable[Object]) -> Object: - """Adding to a Union adds a children instead of creating a new Union :param other: a - children object :return: the same union with children appended.""" + """Adding to a Union adds a children instead of creating a new Union :param other: a children object :return: + + the same union with children appended. + + """ return self.add_child(other) @property @@ -1247,8 +1230,8 @@ class Difference(Composite): """OpenSCAD difference()""" def __sub__(self, other: Object | Misc | Hole | Iterable[Object]) -> Object: - """Substracting from a Difference adds a children instead of creating a new Difference - :param other: a children object :return: the same difference with children appended. + """Substracting from a Difference adds a children instead of creating a new Difference :param other: a children + object :return: the same difference with children appended. """ return self.add_child(other) @@ -1281,8 +1264,8 @@ class Intersection(Composite): """OpenSCAD intersection()""" def __and__(self, other: Object) -> Object: - """Intersecting with an Intersection adds a children instead of creating a new Intersection - :param other: a children object :return: the same intersection with children appended. + """Intersecting with an Intersection adds a children instead of creating a new Intersection :param other: a + children object :return: the same intersection with children appended. """ return self.add_child(other) @@ -1377,7 +1360,8 @@ def file_name(self) -> str: def combine(self, child: Transformation) -> Transformation: """When applying multiple transformations of the same type, those may be combined :param - child: another Primitive :return: an object combining all transformations.""" + child: another Primitive :return: an object combining all transformations. + """ self.child = child return self diff --git a/src/muscad/helpers.py b/src/muscad/helpers.py index 3c881ea..a04dced 100644 --- a/src/muscad/helpers.py +++ b/src/muscad/helpers.py @@ -21,26 +21,22 @@ def normalize_angle(angle: float) -> float: def cos(deg: float) -> float: - """Returns the cosinus of angle in degrees :param deg: an angle, in degrees :return: a cosine, - between -1 and 1.""" + """Returns the cosinus of angle in degrees :param deg: an angle, in degrees :return: a cosine, between -1 and 1.""" return math.cos(math.radians(deg)) def sin(deg: float) -> float: - """Returns the sinus of an angle in degrees :param deg: an angle, in degrees :return: a sine, - between -1 and 1.""" + """Returns the sinus of an angle in degrees :param deg: an angle, in degrees :return: a sine, between -1 and 1.""" return math.sin(math.radians(deg)) def tan(deg: float) -> float: - """Returns the arc tangent of an angle in degrees :param deg: an angle, in degrees :return: an - arc tangent.""" + """Returns the arc tangent of an angle in degrees :param deg: an angle, in degrees :return: an arc tangent.""" return math.tan(math.radians(deg)) def acos(x: float) -> float: - """Returns the arc cosine of x, in degrees :param x: a float :return: the arc cosine in - degrees.""" + """Returns the arc cosine of x, in degrees :param x: a float :return: the arc cosine in degrees.""" return math.degrees(math.acos(x)) @@ -50,8 +46,7 @@ def asin(x: float) -> float: def atan(x: float) -> float: - """Returns the arc tangeant of x, in degrees :param x: a float :return: the arc tangent in - degrees.""" + """Returns the arc tangeant of x, in degrees :param x: a float :return: the arc tangent in degrees.""" return math.degrees(math.atan(x)) @@ -60,21 +55,25 @@ def atan2(x: float, y: float) -> float: def hypotenuse(leg1: float, leg2: float) -> float: - """Returns the length of the hypotenuse based on the length of both legs :param leg1: length of - one leg :param leg2: length of the other leg :return: the hypotenuse length.""" + """Returns the length of the hypotenuse based on the length of both legs :param leg1: length of one leg :param leg2: + + length of the other leg + :return: the hypotenuse length. + + """ return (leg1**2 + leg2**2) ** 0.5 # type: ignore[no-any-return] def catheti(hypot: float, leg: float) -> float: """Returns the length of one leg based on the length of the hypotenuse and the other leg :param hypot: length of the hypotenuse :param leg: length of the other leg :return: the length of the - missing leg.""" + missing leg. + """ return (hypot**2 - leg**2) ** 0.5 # type: ignore[no-any-return] def camel_to_snake(name: str) -> str: - """Transform a CamelCase name (like Python class names) into a snake_case name (like OpenSCAD - object names). + """Transform a CamelCase name (like Python class names) into a snake_case name (like OpenSCAD object names). :param name: a name in CamelCase :return: a name in snake_case diff --git a/src/muscad/part.py b/src/muscad/part.py index 94ec64e..ba40544 100644 --- a/src/muscad/part.py +++ b/src/muscad/part.py @@ -1,7 +1,7 @@ from __future__ import annotations from itertools import chain -from typing import Any, Iterable, Iterator, Literal, ClassVar +from typing import Any, ClassVar, Iterable, Iterator, Literal from muscad import ( EE, @@ -45,8 +45,9 @@ class Part(Composite): class_holes: ClassVar[list[Object]] def __init_subclass__(cls, **kwargs: Any) -> None: - """When creating an inherited class, sort all class-level attributes and make lists of all - Objects, Misc and Holes.""" + """When creating an inherited class, sort all class-level attributes and make lists of all Objects, Misc and + Holes. + """ super().__init_subclass__(**kwargs) cls.class_parts = [] cls.class_misc = [] @@ -94,9 +95,7 @@ def __init__( super().__init__() self.children = self.class_parts.copy() if hasattr(self, "class_parts") else [] self.holes = self.class_holes.copy() if hasattr(self, "class_holes") else [] - self.miscellaneous = ( - self.class_misc.copy() if hasattr(self, "class_misc") else [] - ) + self.miscellaneous = self.class_misc.copy() if hasattr(self, "class_misc") else [] self.init(*args, **kwargs) def init( @@ -125,9 +124,7 @@ def init( elif isinstance(o, Object): self.add_child(o, comment) - def add_child( - self, obj: Object | Iterable[Object] | Literal[0], comment: str | None = None - ) -> Object: + def add_child(self, obj: Object | Iterable[Object] | Literal[0], comment: str | None = None) -> Object: if comment and isinstance(obj, Object): obj.comment = comment super().add_child(obj) @@ -152,8 +149,7 @@ def add_misc(self, obj: Object | Misc | Hole, comment: str | None = None) -> Obj def revert(self) -> Part: """Turns all holes to children, and all children to holes. - Note that misc items are untouched, so it probably makes no sense to revert a part - containing misc items. + Note that misc items are untouched, so it probably makes no sense to revert a part containing misc items. :return: the same part, with holes and children reverted """ @@ -205,10 +201,9 @@ def render(self, postprocess: bool = True) -> str: def postprocess(self, renderable: Object) -> Object: """Applies some postprocessing transformation to the part, at render time. - Postprocessing will not be taken into account when calculating this part dimension or - position. You use it for example to position the Part to make printing easier. - Postprocessing can be disabled by passing `postprocess=False` to ` render()`. This method - can be overridden in subclasses. By default, it does nothing. + Postprocessing will not be taken into account when calculating this part dimension or position. You use it for + example to position the Part to make printing easier. Postprocessing can be disabled by passing + `postprocess=False` to ` render()`. This method can be overridden in subclasses. By default, it does nothing. :param renderable: the part to postprocess for rendering :return: the postprocessed renderable @@ -352,14 +347,13 @@ def top(self) -> float: return top(self.children) -from muscad.primitives import Square, Cube +from muscad.primitives import Square class RotationalExtrudedPart(Part): """A part that will be transformed with a RotationalExtrusion as postprocessing. - You must build your part flat along the Y axis and have the shape defined on the positive X - axis. + You must build your part flat along the Y axis and have the shape defined on the positive X axis. """ @@ -369,9 +363,7 @@ def init( # type: ignore[override] **kwargs: Misc | Hole | Object, ) -> None: # mask will hide the shape on negative X axis. - mask = Square(width=self.width + EE, depth=self.depth + EE).align( - right=self.center_x, center_y=self.center_y - ) + mask = Square(width=self.width + EE, depth=self.depth + EE).align(right=self.center_x, center_y=self.center_y) self.add_hole(mask) def postprocess(self, renderable: Object) -> Object: diff --git a/src/muscad/primitives.py b/src/muscad/primitives.py index 26a9848..22ff2a2 100644 --- a/src/muscad/primitives.py +++ b/src/muscad/primitives.py @@ -339,33 +339,25 @@ def _arguments(self) -> dict[str | None, Any]: def left(self) -> float: if self.halign in (None, "left"): return 0 - raise NotImplementedError( - "use halign='left' (default) to be able to align a Text to left" - ) + raise NotImplementedError("use halign='left' (default) to be able to align a Text to left") @property def right(self) -> float: if self.halign == "right": return 0 - raise NotImplementedError( - "use halign='right' to be able to align a Text to right" - ) + raise NotImplementedError("use halign='right' to be able to align a Text to right") @property def back(self) -> float: if self.valign in (None, "baseline", "bottom"): return 0 - raise NotImplementedError( - "use valign='baseline' or 'bottom' to be able to align a Text to back" - ) + raise NotImplementedError("use valign='baseline' or 'bottom' to be able to align a Text to back") @property def front(self) -> float: if self.valign == "top": return 0 - raise NotImplementedError( - "use valign='top' to be able to align a Text to front" - ) + raise NotImplementedError("use valign='top' to be able to align a Text to front") class Polygon(Primitive2D): @@ -388,9 +380,7 @@ def __init__( self.convexity = convexity @staticmethod - def unpack_points( - points: Iterable[Point2D | tuple[float, float]] - ) -> Iterable[Point2D]: + def unpack_points(points: Iterable[Point2D | tuple[float, float]]) -> Iterable[Point2D]: for point in points: if isinstance(point, Point2D): yield point @@ -428,9 +418,7 @@ def back(self) -> float: class Import(Primitive): - def __init__( - self, file: str, convexity: int | None = None, layer: str | None = None - ) -> None: + def __init__(self, file: str, convexity: int | None = None, layer: str | None = None) -> None: if not os.path.isabs(file): file = os.path.join(os.path.dirname(sys.argv[0]), file) super().__init__(file=file, convexity=convexity, layer=layer) diff --git a/src/muscad/transformations.py b/src/muscad/transformations.py index 83559fa..86ca22e 100644 --- a/src/muscad/transformations.py +++ b/src/muscad/transformations.py @@ -147,9 +147,7 @@ def left(self) -> float: ]: return -self.child.top - raise NotImplementedError( - "Only simple 90° rotations are supported", self.x, self.y, self.z - ) + raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) @property def right(self) -> float: @@ -195,9 +193,7 @@ def right(self) -> float: ]: return -self.child.bottom - raise NotImplementedError( - "Only simple 90° rotations are supported", self.x, self.y, self.z - ) + raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) @property def back(self) -> float: @@ -243,9 +239,7 @@ def back(self) -> float: ]: return -self.child.top - raise NotImplementedError( - "Only simple 90° rotations are supported", self.x, self.y, self.z - ) + raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) @property def front(self) -> float: @@ -291,9 +285,7 @@ def front(self) -> float: ]: return -self.child.bottom - raise NotImplementedError( - "Only simple 90° rotations are supported", self.x, self.y, self.z - ) + raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) @property def bottom(self) -> float: @@ -310,9 +302,7 @@ def bottom(self) -> float: if (self.x == 90 and self.y == 180) or (self.x == 270 and self.y == 0): return -self.child.front - raise NotImplementedError( - "Only simple 90° rotations are supported", self.x, self.y, self.z - ) + raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) @property def top(self) -> float: @@ -329,9 +319,7 @@ def top(self) -> float: if (self.x == 90 and self.y == 180) or (self.x == 270 and self.y == 0): return -self.child.back - raise NotImplementedError( - "Only simple 90° rotations are supported", self.x, self.y, self.z - ) + raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) class Scaling(Transformation, name="scale"): @@ -384,9 +372,7 @@ def left(self) -> float: if self.x: if not self.y and not self.z: return -self.child.right - raise NotImplementedError( - "Only single axis mirror vectors are supported" - ) # pragma: no cover + raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover return self.child.left @property @@ -394,9 +380,7 @@ def right(self) -> float: if self.x: if not self.y and not self.z: return -self.child.left - raise NotImplementedError( - "Only single axis mirror vectors are supported" - ) # pragma: no cover + raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover return self.child.right @property @@ -404,9 +388,7 @@ def front(self) -> float: if self.y: if not self.x and not self.z: return -self.child.back - raise NotImplementedError( - "Only single axis mirror vectors are supported" - ) # pragma: no cover + raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover return self.child.front @property @@ -414,9 +396,7 @@ def back(self) -> float: if self.y: if not self.x and not self.z: return -self.child.front - raise NotImplementedError( - "Only single axis mirror vectors are supported" - ) # pragma: no cover + raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover return self.child.back @property @@ -424,9 +404,7 @@ def top(self) -> float: if self.z: if not self.x and not self.y: return -self.child.bottom - raise NotImplementedError( - "Only single axis mirror vectors are supported" - ) # pragma: no cover + raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover return self.child.top @property @@ -434,9 +412,7 @@ def bottom(self) -> float: if self.z: if not self.x and not self.y: return -self.child.top - raise NotImplementedError( - "Only single axis mirror vectors are supported" - ) # pragma: no cover + raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover return self.child.bottom def copy(self) -> Transformation: @@ -670,9 +646,8 @@ def bottom(self) -> float: class Slide(Transformation): """Custom transformation that translate an object then Hulls the result to itself. - If the object is a Composite, each part component is hulled to its translated self. This is - useful for parts that must be slided into their final position, such as screws. Bounding box of - the original object is untouched.. + If the object is a Composite, each part component is hulled to its translated self. This is useful for parts that + must be slided into their final position, such as screws. Bounding box of the original object is untouched. """ @@ -683,7 +658,4 @@ def __init__(self, *, x: float = 0, y: float = 0, z: float = 0): self.z = z def render(self) -> str: - return Union( - Hull(child, child.translate(x=self.x, y=self.y, z=self.z)) - for child in self.child.walk() - ).render() + return Union(Hull(child, child.translate(x=self.x, y=self.y, z=self.z)) for child in self.child.walk()).render() diff --git a/src/muscad/utils/fillet.py b/src/muscad/utils/fillet.py index 0da467b..579b603 100644 --- a/src/muscad/utils/fillet.py +++ b/src/muscad/utils/fillet.py @@ -14,9 +14,7 @@ def init(self, radius: float = 4, angle: float = 45) -> None: # type: ignore[ov chamfer_width = (radius**2 * 2) ** 0.5 self.chamfer = ~Square(chamfer_width, chamfer_width + 1).z_rotate(angle) else: - chamfer_width = ( - (radius * cos(angle)) ** 2 + (radius * sin(angle)) ** 2 - ) ** 0.5 + chamfer_width = ((radius * cos(angle)) ** 2 + (radius * sin(angle)) ** 2) ** 0.5 self.chamfer = ~Square(chamfer_width, chamfer_width).z_rotate( angle, center_x=-cos(45 + angle), center_y=-cos(angle) ) diff --git a/src/muscad/utils/shapes.py b/src/muscad/utils/shapes.py index f2701c3..c94b54a 100644 --- a/src/muscad/utils/shapes.py +++ b/src/muscad/utils/shapes.py @@ -10,9 +10,7 @@ def pipe( outer_diameter: float, inner_diameter: float, ) -> Object: - return Cylinder(d=outer_diameter, h=height) - Cylinder( - d=inner_diameter, h=height + EE - ) + return Cylinder(d=outer_diameter, h=height) - Cylinder(d=inner_diameter, h=height + EE) @classmethod def cone(cls, height: float, diameter: float) -> Object: @@ -23,12 +21,10 @@ def oval_prism(cls, height: float, x_diameter: float, y_diameter: float) -> Obje return Cylinder(h=height, d=y_diameter).scale(y=x_diameter / y_diameter) @classmethod - def oval_tube( - cls, height: float, x_diameter: float, y_diameter: float, wall: float - ) -> Object: - return Cylinder(h=height, d=x_diameter).scale( - y=y_diameter / x_diameter - ) - Cylinder(h=height + EE, d=x_diameter).scale( + def oval_tube(cls, height: float, x_diameter: float, y_diameter: float, wall: float) -> Object: + return Cylinder(h=height, d=x_diameter).scale(y=y_diameter / x_diameter) - Cylinder( + h=height + EE, d=x_diameter + ).scale( x=(x_diameter - wall * 2) / x_diameter, y=(y_diameter - wall * 2) / x_diameter, ) diff --git a/src/muscad/utils/stack.py b/src/muscad/utils/stack.py index 0a5cd46..7beeaac 100644 --- a/src/muscad/utils/stack.py +++ b/src/muscad/utils/stack.py @@ -1,14 +1,15 @@ from __future__ import annotations -import typing - from muscad import Misc, Object, Part def stack(*parts: Object | Misc, overlap: float = 0.01) -> Part: - """Stack each part on top of each other :param parts: all parts :param overlap: separation - between each part (positive value: pieces will overlap, negative value: pieces will be - separated) :return: parts stacked on top of each other, bottom to top.""" + """Stack each part on top of each other. + + :param parts: all parts + :param overlap: separation between each part (positive value: pieces will overlap, negative value: pieces will be separated) + :return: parts stacked on top of each other, bottom to top. + """ s = Part() top = 0.0 for part in parts: diff --git a/src/muscad/utils/surface.py b/src/muscad/utils/surface.py index 5d9bae1..19a5bce 100644 --- a/src/muscad/utils/surface.py +++ b/src/muscad/utils/surface.py @@ -143,9 +143,7 @@ def regular_rounded_corners( ) @classmethod - def circle_from_3_points( - cls, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float - ) -> Object: + def circle_from_3_points(cls, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float) -> Object: """Draws a circle from 3 points. :param x1: first point X coordinate @@ -163,7 +161,8 @@ def circle_from_3_points( det = (x1 - x2) * (y2 - y3) - (x2 - x3) * (y1 - y2) if abs(det) < 1.0e-6: - raise ValueError("Unable to draw a circle from those 3 points") + msg = "Unable to draw a circle from those 3 points" + raise ValueError(msg) cx = (bc * (y2 - y3) - cd * (y1 - y2)) / det cy = ((x1 - x2) * cd - (x2 - x3) * bc) / det @@ -178,11 +177,11 @@ def triangle_in_circle(cls, radius: float) -> Polygon: @classmethod def regular_polygon(cls, nb_sides: int, radius: float) -> Object: - """Returns the largest regular Polygon with `nb_sides` sides contained in a circle of - `radius`. + """Returns the largest regular Polygon with `nb_sides` sides fitting in a circle of `radius`. + + Difference compared to a simple Circle(segments=nb_sides) is that the object dimensions are those of the + Polygon, not the circle. - Difference compared to a simple Circle(segments=nb_sides) is that the object dimensions are - those of the Polygon, not the circle. :param nb_sides: :param radius: :return: a 2D Polygon @@ -195,12 +194,7 @@ def regular_polygon(cls, nb_sides: int, radius: float) -> Object: if nb_sides == 4: width = ((radius * 2) ** 2 / 2) ** 0.5 return Square(width, width) - return Polygon( - *( - Point2D.from_radius_and_angle(radius, angle=i * 360 / nb_sides) - for i in range(nb_sides) - ) - ) + return Polygon(*(Point2D.from_radius_and_angle(radius, angle=i * 360 / nb_sides) for i in range(nb_sides))) @classmethod def ellipse(cls, width: float, height: float) -> Object: @@ -208,13 +202,11 @@ def ellipse(cls, width: float, height: float) -> Object: @classmethod def fillet(cls, radius: float) -> Object: - return Square(radius, radius).align(left=0, back=0) - Circle( - d=radius * 2 - ).align(left=0, back=0) + return Square(radius, radius).align(left=0, back=0) - Circle(d=radius * 2).align(left=0, back=0) @classmethod def chamfer(cls, radius: float) -> Object: chamfer_width = ((radius**2) * 2) ** 0.5 - return Square(radius, radius).align(back=0, left=0) - Square( - chamfer_width, chamfer_width - ).z_rotate(45).align(center_x=radius, center_y=radius) + return Square(radius, radius).align(back=0, left=0) - Square(chamfer_width, chamfer_width).z_rotate(45).align( + center_x=radius, center_y=radius + ) diff --git a/src/muscad/utils/tube.py b/src/muscad/utils/tube.py index 440b9e6..2c9a7e9 100644 --- a/src/muscad/utils/tube.py +++ b/src/muscad/utils/tube.py @@ -1,5 +1,7 @@ from __future__ import annotations +import contextlib + from muscad import Cylinder, E, Hole, Misc, Object, Part, Volume, calc @@ -24,11 +26,9 @@ def init( # type: ignore[override] ) -> None: if diameter is None and radius is not None: diameter = radius * 2 - try: + with contextlib.suppress(ValueError): # first try to get the cylinder diameter from the x coordinates left, center_x, right, diameter = calc(left, center_x, right, diameter) - except ValueError: - pass # fallback to the y coordinates back, center_y, front, diameter = calc(back, center_y, front, diameter) @@ -39,16 +39,13 @@ def init( # type: ignore[override] ) super().init(*args, **kwargs) - def tunnel( - self, diameter: float | None = None, radius: float | None = None - ) -> Tube: + def tunnel(self, diameter: float | None = None, radius: float | None = None) -> Tube: """Hollows the center of this tube, making it a tunnel.""" - if diameter is None and radius is None: - raise ValueError("at least one of diameter or radius must be specified") - if diameter is None and radius is not None: - diameter = radius * 2 if diameter is None: - raise ValueError("this is just to make mypy happy") + if radius is None: + msg = "at least one of diameter or radius must be specified" + raise ValueError(msg) + diameter = radius * 2 self.tunnel_hole = ~Cylinder(d=diameter, h=self.height + 2).align( center_x=self.center_x, center_y=self.center_y, diff --git a/src/muscad/utils/volume.py b/src/muscad/utils/volume.py index 365ee87..b73d869 100644 --- a/src/muscad/utils/volume.py +++ b/src/muscad/utils/volume.py @@ -1,6 +1,5 @@ from __future__ import annotations - from muscad import EE, Cube, E, Object, Part, calc from muscad.utils.fillet import Chamfer, Fillet @@ -22,15 +21,9 @@ def init( # type: ignore[override] top: float | None = None, height: float | None = None, ) -> None: - self._left, self._center_x, self._right, self._width = calc( - left, center_x, right, width - ) - self._back, self._center_y, self._front, self._depth = calc( - back, center_y, front, depth - ) - self._bottom, self._center_z, self._top, self._height = calc( - bottom, center_z, top, height - ) + self._left, self._center_x, self._right, self._width = calc(left, center_x, right, width) + self._back, self._center_y, self._front, self._depth = calc(back, center_y, front, depth) + self._bottom, self._center_z, self._top, self._height = calc(bottom, center_z, top, height) self.volume = Cube(self.width, self.depth, self.height).align( center_x=self.center_x, center_y=self.center_y, diff --git a/src/muscad/vitamins/bearings.py b/src/muscad/vitamins/bearings.py index 5dd1a5e..8739da3 100644 --- a/src/muscad/vitamins/bearings.py +++ b/src/muscad/vitamins/bearings.py @@ -1,6 +1,5 @@ from __future__ import annotations - from muscad import EE, Cylinder, Object, Part, Union from muscad.utils.volume import Volume from muscad.vitamins.bolts import Bolt @@ -8,7 +7,12 @@ class LinearBearing(Part): def init( # type: ignore[override] - self, inner_diam: float, outer_diam: float, length: float, hollow: bool = True + self, + *, + inner_diam: float, + outer_diam: float, + length: float, + hollow: bool = True, ) -> None: self._outer_diam = outer_diam self._inner_diam = inner_diam @@ -18,24 +22,18 @@ def init( # type: ignore[override] if hollow: self.inner = ~Cylinder(d=inner_diam, h=length * 2) - def add_throats( - self, diameter: float, width: float, offset: float - ) -> LinearBearing: - throat = Cylinder(d=self._outer_diam + 1, h=width) - Cylinder( - d=diameter, h=width + 1 - ) + def add_throats(self, diameter: float, width: float, offset: float) -> LinearBearing: + throat = Cylinder(d=self._outer_diam + 1, h=width) - Cylinder(d=diameter, h=width + 1) self.left_throat = ~throat.align(bottom=self.outer.bottom + offset) self.right_throat = ~throat.align(top=self.outer.top - offset) return self def add_rod_clearance(self, length: float = 20, T: float = 1) -> LinearBearing: - self.rod_clearance = Cylinder( - d=self._inner_diam + 2 * T, h=self._length + 2 * length - ).misc() + self.rod_clearance = Cylinder(d=self._inner_diam + 2 * T, h=self._length + 2 * length).misc() return self @classmethod - def LM8UU(cls, hollow: bool = True, T: float = 0.2) -> LinearBearing: + def LM8UU(cls, *, hollow: bool = True, T: float = 0.2) -> LinearBearing: return cls( inner_diam=8 + 2 * T, outer_diam=15 + 2 * T, @@ -44,7 +42,7 @@ def LM8UU(cls, hollow: bool = True, T: float = 0.2) -> LinearBearing: ).add_throats(14 + 2 * T, 1.1 - T, 3.25 + T) @classmethod - def LM12UU(cls, hollow: bool = True, T: float = 0.2) -> LinearBearing: + def LM12UU(cls, *, hollow: bool = True, T: float = 0.2) -> LinearBearing: return cls( inner_diam=12 + 2 * T, outer_diam=21 + T, @@ -90,10 +88,7 @@ def add_rod_clearance( ) -> BushingLinearBearing: slide = slide or {} self.rod_clearance = ( - Cylinder(d=self.rod_diameter + 2 * T, h=self.width + 2 * length) - .slide(**slide) - .x_rotate(90) - .misc() + Cylinder(d=self.rod_diameter + 2 * T, h=self.width + 2 * length).slide(**slide).x_rotate(90).misc() ) return self @@ -155,15 +150,13 @@ def init( # type: ignore[override] self.inner = ~Cylinder(d=inner_diam, h=height + EE) @classmethod - def b605zz(cls, T: float = 0.2, hole: bool = True) -> RotationBearing: + def b605zz(cls, *, T: float = 0.2, hole: bool = True) -> RotationBearing: return cls(5 + 2 * T, 14 + 2 * T, 5 + 2 * T, hole=hole) @classmethod - def b608zz(cls, T: float = 0.2, hole: bool = True) -> RotationBearing: + def b608zz(cls, *, T: float = 0.2, hole: bool = True) -> RotationBearing: return cls(8 + 2 * T, 22 + 2 * T, 7 + 2 * T, hole=hole) def add_clearance(self, size: float) -> RotationBearing: - self.clearance = ~( - Cylinder(d=self._outer_diameter + size, h=self._height) - self.inner - ) + self.clearance = ~(Cylinder(d=self._outer_diameter + size, h=self._height) - self.inner) return self diff --git a/src/muscad/vitamins/belts.py b/src/muscad/vitamins/belts.py index d6da6a1..318f221 100644 --- a/src/muscad/vitamins/belts.py +++ b/src/muscad/vitamins/belts.py @@ -45,9 +45,7 @@ def init( # type: ignore[override] T: float = 0.3, ) -> None: nb_tooth = int(length / pitch) - self.tooth = Union( - profile.leftward(i * pitch) for i in range(nb_tooth) - ).z_linear_extrude(width, center_z=0) + self.tooth = Union(profile.leftward(i * pitch) for i in range(nb_tooth)).z_linear_extrude(width, center_z=0) self.tolerance = Volume( left=self.tooth.left, right=self.tooth.right, diff --git a/src/muscad/vitamins/boards.py b/src/muscad/vitamins/boards.py index 8a59648..9051d3c 100644 --- a/src/muscad/vitamins/boards.py +++ b/src/muscad/vitamins/boards.py @@ -29,15 +29,9 @@ def init( # type: ignore[override] self.bolts = Union().misc() def add_bolt(self, bolt: Object, x: float, y: float) -> Self: - if x < 0: - x = self.board.right + x - else: - x = self.board.left + x + x = self.board.right + x if x < 0 else self.board.left + x + y = self.board.front + y if y < 0 else self.board.back + y - if y < 0: - y = self.board.front + y - else: - y = self.board.back + y self.bolts.add_child(bolt.align(center_x=x, center_y=y)) return self diff --git a/src/muscad/vitamins/bolts.py b/src/muscad/vitamins/bolts.py index 86c4995..9007265 100644 --- a/src/muscad/vitamins/bolts.py +++ b/src/muscad/vitamins/bolts.py @@ -25,6 +25,7 @@ class Bolt(Part): def init( # type: ignore[override] self, diameter: float, + *, length: float, head_height: float, head_diameter: float, @@ -35,21 +36,11 @@ def init( # type: ignore[override] ) -> None: self.thread = Cylinder(d=diameter, h=length) if thread_clearance: - self.tread_clearance = ( - Cylinder(d=diameter, h=thread_clearance) - .align(bottom=self.thread.top) - .misc() - ) + self.tread_clearance = Cylinder(d=diameter, h=thread_clearance).align(bottom=self.thread.top).misc() if head: - self.head = Cylinder(d=head_diameter, h=head_height).align( - top=self.thread.bottom + E - ) + self.head = Cylinder(d=head_diameter, h=head_height).align(top=self.thread.bottom + E) if head_clearance: - self.head_clearance = ( - Cylinder(d=head_diameter, h=head_clearance) - .align(top=self.head.bottom + E) - .misc() - ) + self.head_clearance = Cylinder(d=head_diameter, h=head_clearance).align(top=self.head.bottom + E).misc() self.diameter = diameter self.length = length @@ -70,17 +61,10 @@ def add_nut( if placement < 0: placement = self.length + placement - nut_thickness + 0.1 - nut = ( - Nut(nut_width, nut_thickness) - .align(bottom=self.thread.bottom + placement) - .z_rotate(angle) - .misc() - ) + nut = Nut(nut_width, nut_thickness).align(bottom=self.thread.bottom + placement).z_rotate(angle).misc() self.add_misc(nut) if inline_clearance_size > 0: - self.inline_nut_clearance = Hull( - nut.object, nut.up(inline_clearance_size) - ).misc() + self.inline_nut_clearance = Hull(nut.object, nut.up(inline_clearance_size)).misc() if side_clearance_size > 0: self.nut_clearance = ( Cube( diff --git a/src/muscad/vitamins/brackets.py b/src/muscad/vitamins/brackets.py index f9184e6..9581a13 100644 --- a/src/muscad/vitamins/brackets.py +++ b/src/muscad/vitamins/brackets.py @@ -7,14 +7,10 @@ class CastBracket(Part): def init(self, width: float, height: float) -> None: # type: ignore[override] self.body = Volume(width=width, depth=width, height=height) - self.clearance = ( - ~Volume(width=width * 1.25, depth=width * 1.25, height=height + EE) - .z_rotate(45) - .align( - center_x=self.body.right, - center_y=self.body.front, - center_z=self.body.center_z, - ) + self.clearance = ~Volume(width=width * 1.25, depth=width * 1.25, height=height + EE).z_rotate(45).align( + center_x=self.body.right, + center_y=self.body.front, + center_z=self.body.center_z, ) @classmethod diff --git a/src/muscad/vitamins/cable_chain.py b/src/muscad/vitamins/cable_chain.py index f626dc1..830fc9a 100644 --- a/src/muscad/vitamins/cable_chain.py +++ b/src/muscad/vitamins/cable_chain.py @@ -5,12 +5,8 @@ class CableChain: @classmethod - def female( - cls, outer_diameter: float = 16, inner_diameter: float = 6, T: float = 0.2 - ) -> Tube: - return Tube(diameter=outer_diameter - T, bottom=T, top=1 - T).tunnel( - inner_diameter + T - ) + def female(cls, outer_diameter: float = 16, inner_diameter: float = 6, T: float = 0.2) -> Tube: + return Tube(diameter=outer_diameter - T, bottom=T, top=1 - T).tunnel(inner_diameter + T) @classmethod def male( @@ -40,9 +36,5 @@ def male( ) @classmethod - def couple( - cls, outer_diameter: float = 16, inner_diameter: float = 6, T: float = 0.2 - ) -> tuple[Tube, Tube]: - return cls.female(outer_diameter, inner_diameter, T), cls.male( - outer_diameter, inner_diameter, T - ) + def couple(cls, outer_diameter: float = 16, inner_diameter: float = 6, T: float = 0.2) -> tuple[Tube, Tube]: + return cls.female(outer_diameter, inner_diameter, T), cls.male(outer_diameter, inner_diameter, T) diff --git a/src/muscad/vitamins/endstops.py b/src/muscad/vitamins/endstops.py index 4571539..c04c640 100644 --- a/src/muscad/vitamins/endstops.py +++ b/src/muscad/vitamins/endstops.py @@ -25,33 +25,21 @@ class OptoSwitch(Part): bottom=base.bottom, ) - left_hole = ( - ~Cylinder(d=3, h=4.5) - .bottom_to_front() - .align( - center_x=base.left + 2.75, - center_y=base.center_y, - center_z=base.center_z, - ) + left_hole = ~Cylinder(d=3, h=4.5).bottom_to_front().align( + center_x=base.left + 2.75, + center_y=base.center_y, + center_z=base.center_z, ) - right_hole = ( - ~Cylinder(d=3, h=4.5) - .bottom_to_front() - .align( - center_x=base.right - 2.75, - center_y=base.center_y, - center_z=base.center_z, - ) + right_hole = ~Cylinder(d=3, h=4.5).bottom_to_front().align( + center_x=base.right - 2.75, + center_y=base.center_y, + center_z=base.center_z, ) class OpticalEndstop(Part): base = Volume(width=33, depth=1.6, height=10.5).color("red") - switch = ( - OptoSwitch() - .align(right=base.right - 0.1, back=base.front, center_z=base.center_z) - .color("grey") - ) + switch = OptoSwitch().align(right=base.right - 0.1, back=base.front, center_z=base.center_z).color("grey") connector = ( Volume( width=5.8, @@ -72,23 +60,15 @@ class OpticalEndstop(Part): back=base.front, center_z=base.center_z, ).color("blue") - left_hole = ( - ~Cylinder(d=3, h=4.5) - .bottom_to_front() - .align( - center_x=switch.left + 2.75, - center_y=base.center_y, - center_z=base.center_z, - ) + left_hole = ~Cylinder(d=3, h=4.5).bottom_to_front().align( + center_x=switch.left + 2.75, + center_y=base.center_y, + center_z=base.center_z, ) - right_hole = ( - ~Cylinder(d=3, h=4.5) - .bottom_to_front() - .align( - center_x=switch.right - 2.75, - center_y=base.center_y, - center_z=base.center_z, - ) + right_hole = ~Cylinder(d=3, h=4.5).bottom_to_front().align( + center_x=switch.right - 2.75, + center_y=base.center_y, + center_z=base.center_z, ) def add_bolts(self, bolt: Object) -> Self: @@ -114,9 +94,9 @@ def add_bolts(self, bolt: Object) -> Self: class BIQUEndstop(Part): - body = Surface.free( - Circle(d=7).align(center_x=-10), Circle(d=7).align(center_x=10) - ).z_linear_extrude(bottom=0, top=1) + body = Surface.free(Circle(d=7).align(center_x=-10), Circle(d=7).align(center_x=10)).z_linear_extrude( + bottom=0, top=1 + ) endstop = Volume( width=13, depth=6, @@ -141,10 +121,7 @@ class MechanicalSwitchEndstop(Part): lever = ( ( Volume(width=13.5, depth=0.5, height=6).align(right=0, back=0) - + ( - Cube(10, 10, 20).y_translate(5.5) - & (Cylinder(d=4, h=6) - Cylinder(d=3, h=7)) - ).translate(x=1.5, y=-0.5) + + (Cube(10, 10, 20).y_translate(5.5) & (Cylinder(d=4, h=6) - Cylinder(d=3, h=7))).translate(x=1.5, y=-0.5) ) .align(left=body.left, back=body.front) .z_rotate(15, center_x=body.left, center_y=body.front) @@ -163,12 +140,8 @@ def LJ12A3(cls, T: float = 0.2) -> Self: class MechanicalEndstopOnPCB(Part): pcb = Volume(width=40, depth=16, height=1.6) - switch = MechanicalSwitchEndstop().align( - right=pcb.right - 6, front=pcb.front, bottom=pcb.top - ) - connector = Volume(width=10, depth=10, height=6).align( - left=pcb.left, center_y=pcb.center_y, bottom=pcb.top - ) + switch = MechanicalSwitchEndstop().align(right=pcb.right - 6, front=pcb.front, bottom=pcb.top) + connector = Volume(width=10, depth=10, height=6).align(left=pcb.left, center_y=pcb.center_y, bottom=pcb.top) switch_welds = ( Volume( @@ -215,23 +188,17 @@ class BLTouchClassic(Part): attachment = ( Hull( Surface.square(width=6, depth=11.53), - Surface.circle(radius=4, center_x=9).x_mirror(0, True), + Surface.circle(radius=4, center_x=9).x_mirror(0, keep=True), ) - - Surface.circle(diameter=3.2, center_x=9).x_mirror(0, True) + - Surface.circle(diameter=3.2, center_x=9).x_mirror(0, keep=True) ).z_linear_extrude(2.3) cylinder = Tube(diameter=11, top=attachment.bottom, height=7.7) body = Tube(diameter=13, top=cylinder.bottom, height=26.3) & Volume( width=13, depth=11.53, top=cylinder.bottom, height=27 - ) - Surface.chamfer(12).align(right=0).z_rotational_extrude( - bottom=cylinder.bottom - 30 - ) + ) - Surface.chamfer(12).align(right=0).z_rotational_extrude(bottom=cylinder.bottom - 30) pin = Tube(diameter=2, top=body.bottom, height=11) def init( # type: ignore[override] self, bolt: Object = Bolt.M3(20).add_nut(-4, inline_clearance_size=10) ) -> None: - self.bolts = ( - bolt.align(center_x=9, bottom=self.attachment.bottom - 3) - .x_mirror(0, True) - .misc() - ) + self.bolts = bolt.align(center_x=9, bottom=self.attachment.bottom - 3).x_mirror(0, keep=True).misc() diff --git a/src/muscad/vitamins/extrusions.py b/src/muscad/vitamins/extrusions.py index 566ebd3..7ac2c4f 100644 --- a/src/muscad/vitamins/extrusions.py +++ b/src/muscad/vitamins/extrusions.py @@ -23,9 +23,7 @@ def e2020(cls, length: float, rounding: float = 2, T: float = 0.1) -> Self: class Extrusion3030Insert(Part): def init(self, length: float = 50) -> None: # type: ignore[override] - self.body = Volume(width=8 - TT, depth=length, height=6).fillet_depth( - 0.5, bottom=True - ) + self.body = Volume(width=8 - TT, depth=length, height=6).fillet_depth(0.5, bottom=True) self.wings = Surface.free( Circle(d=1.5, segments=20).align(center_x=6, back=-0.5), Circle(d=1.5, segments=20).align(center_x=-6, back=-0.5), diff --git a/src/muscad/vitamins/fans.py b/src/muscad/vitamins/fans.py index eb30412..4352ed7 100644 --- a/src/muscad/vitamins/fans.py +++ b/src/muscad/vitamins/fans.py @@ -77,27 +77,22 @@ def init(self, *, width: float, height: float, r: float) -> None: # type: ignor center_z=0, ).fillet_height(r) - def add_bolts( - self, bolt: Object, spacing: float, holes: Iterable[int] = (0, 1, 2, 3) - ) -> Self: - """Add up to 4 bolts in the stepper fixing holes (as miscellaneous items) :param bolt: the - bolt to add (must be head up) :param spacing: edge distance between 2 bolt centers :param - depth: depth of the fixing holes inside the stepper :param holes: index of the bolts to add. + def add_bolts(self, bolt: Object, spacing: float, holes: Iterable[int] = (0, 1, 2, 3)) -> Self: + """Add up to 4 bolts in the stepper fixing holes (as miscellaneous items). - Modify it if you only want 2 or 3 bolts. + :param bolt: the bolt to add (must be head up) + :param spacing: edge distance between 2 bolt centers + :param holes: index of the bolts to add. Modify it if you only want 2 or 3 bolts. :return: the stepper object, with bolts added + """ radius = ((spacing**2) * 2) ** 0.5 / 2 self.bolts = ( - Union(bolt.rightward(radius).z_rotate(45 + 90 * i) for i in holes) - .align(bottom=self.body.bottom) - .misc() + Union(bolt.rightward(radius).z_rotate(45 + 90 * i) for i in holes).align(bottom=self.body.bottom).misc() ) return self - def add_tunnel( - self, diameter: float, length: float, d2: float | None = None - ) -> Self: + def add_tunnel(self, diameter: float, length: float, d2: float | None = None) -> Self: self.tunnel = ( Cylinder(d=diameter, d2=d2, h=length) .align( @@ -110,9 +105,7 @@ def add_tunnel( return self @classmethod - def fan40x40x20( - cls, bolt: Object | None = Bolt.M3(25).add_nut(-E), T: float = 0.2 - ) -> Self: + def fan40x40x20(cls, bolt: Object | None = Bolt.M3(25).add_nut(-E), T: float = 0.2) -> Self: fan = cls(width=40 + 2 * T, height=20 + 2 * T, r=2) if bolt: fan.add_bolts(bolt, 32) diff --git a/src/muscad/vitamins/gears.py b/src/muscad/vitamins/gears.py index 56e5ce5..54898df 100644 --- a/src/muscad/vitamins/gears.py +++ b/src/muscad/vitamins/gears.py @@ -76,18 +76,14 @@ def init( # type: ignore[override] involute_facets=involute_facets, ).linear_extrude(rim_thickness, convexity=10, twist=twist) if gear_thickness < rim_thickness: - gear -= Cylinder( - d=rim_radius * 2, h=rim_thickness - gear_thickness + E - ).align(bottom=gear_thickness) + gear -= Cylinder(d=rim_radius * 2, h=rim_thickness - gear_thickness + E).align(bottom=gear_thickness) self.add_child(gear, "gear") if gear_thickness > rim_thickness: self.add_child(Cylinder(d=rim_radius * 2, h=gear_thickness).align(bottom=0)) if hub_thickness > gear_thickness: self.add_child( - Cylinder(d=hub_diameter, h=hub_thickness - gear_thickness).align( - bottom=gear_thickness - ), + Cylinder(d=hub_diameter, h=hub_thickness - gear_thickness).align(bottom=gear_thickness), "axis", ) @@ -151,9 +147,7 @@ def involute_gear_tooth( involute_facets: int | Literal["auto"] = "auto", ) -> Object: min_radius = max(base_radius, root_radius) - pitch_angle = Point2D.involute( - base_radius, involute_intersect_angle(base_radius, pitch_radius) - ).angle() + pitch_angle = Point2D.involute(base_radius, involute_intersect_angle(base_radius, pitch_radius)).angle() center_angle = pitch_angle + half_thick_angle start_angle = involute_intersect_angle(base_radius, min_radius) @@ -166,8 +160,7 @@ def iter_facets(involute_facets: int) -> Iterable[Polygon]: for i in range(1, involute_facets + 1): point1 = Point2D.involute( base_radius, - start_angle - + (stop_angle - start_angle) * (i - 1) / involute_facets, + start_angle + (stop_angle - start_angle) * (i - 1) / involute_facets, ).z_rotate(center_angle) point2 = Point2D.involute( base_radius, @@ -230,19 +223,13 @@ def init( # type: ignore[override] root_cone_full_radius = tan(root_angle) * apex_to_apex back_cone_full_radius = apex_to_apex / tan(pitch_angle) - back_cone_end_radius = ( - outside_pitch_radius - - dedendum * cos(pitch_angle) - - gear_thickness / tan(pitch_angle) - ) + back_cone_end_radius = outside_pitch_radius - dedendum * cos(pitch_angle) - gear_thickness / tan(pitch_angle) back_cone_descent = dedendum * sin(pitch_angle) + gear_thickness # Root diameter: Diameter of bottom of tooth spaces. root_radius = back_cone_radius - dedendum - half_tooth_thickness = ( - outside_pitch_radius * sin(360 / (4 * nb_teeth)) - backlash / 4 - ) + half_tooth_thickness = outside_pitch_radius * sin(360 / (4 * nb_teeth)) - backlash / 4 half_thick_angle = asin(half_tooth_thickness / back_cone_radius) face_cone_height = apex_to_apex - face_width / cos(pitch_angle) @@ -250,16 +237,12 @@ def init( # type: ignore[override] face_cone_descent = dedendum * sin(pitch_angle) face_cone_end_radius = ( - outside_pitch_radius - - face_width / sin(pitch_angle) - - face_cone_descent / tan(pitch_angle) + outside_pitch_radius - face_width / sin(pitch_angle) - face_cone_descent / tan(pitch_angle) ) # For the bevel_gear_flat finish option, calculate the height of a cube # to select the portion of the gear that includes the full pitch face. - bevel_gear_flat_height = pitch_apex - (cone_distance - face_width) * cos( - pitch_angle - ) + bevel_gear_flat_height = pitch_apex - (cone_distance - face_width) * cos(pitch_angle) base = Cylinder( d=root_cone_full_radius * 2, @@ -333,9 +316,7 @@ def init( # type: ignore[override] ) self.add_hole( - Cylinder(d=bore_diameter, h=apex_to_apex, segments=8).align( - bottom=pitch_apex - apex_to_apex - ), + Cylinder(d=bore_diameter, h=apex_to_apex, segments=8).align(bottom=pitch_apex - apex_to_apex), "bore", ) @@ -411,8 +392,7 @@ def iter_facets(involute_facets: int) -> Iterable[Polyhedron]: for i in range(1, involute_facets + 1): point1 = Point2D.involute( base_radius * 2, - start_angle - + (stop_angle - start_angle) * (i - 1) / involute_facets, + start_angle + (stop_angle - start_angle) * (i - 1) / involute_facets, ).z_rotate(center_angle) point2 = Point2D.involute( base_radius * 2, diff --git a/src/muscad/vitamins/pulleys.py b/src/muscad/vitamins/pulleys.py index 1b6d73c..b386df8 100644 --- a/src/muscad/vitamins/pulleys.py +++ b/src/muscad/vitamins/pulleys.py @@ -21,9 +21,7 @@ def tooth(self, profile: Polygon, count: int) -> Self: ) return self - def add_flange( - self, diameter: float, *, height: float, top: bool = True, bottom: bool = True - ) -> Self: + def add_flange(self, diameter: float, *, height: float, top: bool = True, bottom: bool = True) -> Self: if height > 0 and top: self.top_flange = Cylinder(d=diameter, h=height).align( center_x=self.body.center_x, @@ -55,9 +53,7 @@ def add_clearance(self, length: float, angle: float) -> Self: ) return self - def add_belt_clearance( - self, length: float, *, angle: float, left: bool = False - ) -> Self: + def add_belt_clearance(self, length: float, *, angle: float, left: bool = False) -> Self: self.belt_clearance = ( Cube(self.body.width / 2, length, self.body.height) .align( @@ -70,16 +66,12 @@ def add_belt_clearance( ) return self - def add_shaft_clearance( - self, d: float = 5, lenght: float = 20, T: float = 0.2, **align: float - ) -> Self: + def add_shaft_clearance(self, d: float = 5, lenght: float = 20, T: float = 0.2, **align: float) -> Self: self.shaft = Cylinder(d=d + 2 * T, h=lenght).align(**align).misc() return self @classmethod - def GT2( - cls, tooth_count: int, height: float = 6, shaft_dia: float = 3, T: float = 0.2 - ) -> Self: + def GT2(cls, tooth_count: int, height: float = 6, shaft_dia: float = 3, T: float = 0.2) -> Self: if tooth_count < 10: msg = "Unable to draw a GT2 pulley with less than 10 tooth" raise ValueError(msg) @@ -91,7 +83,5 @@ def placeholder(cls, diameter: float, height: float, T: float = 0.2) -> Self: return cls(outer_dia=diameter + 2 * T, height=height + 2 * T) -def tooth_outer_diameter( - tooth_count: int, tooth_pitch: float, pitch_line_offset: float -) -> float: +def tooth_outer_diameter(tooth_count: int, tooth_pitch: float, pitch_line_offset: float) -> float: return 2 * ((tooth_count * tooth_pitch) / (3.141_592_65 * 2) - pitch_line_offset) diff --git a/src/muscad/vitamins/rods.py b/src/muscad/vitamins/rods.py index eb250f4..e3304ea 100644 --- a/src/muscad/vitamins/rods.py +++ b/src/muscad/vitamins/rods.py @@ -57,7 +57,5 @@ def T8(cls, length: float = 300, T: float = 0.2) -> Self: def add_brass_nut(self, brass_nut: Object, *, align: bool = True) -> Self: if align: - self.brass_nut = brass_nut.align( - center_x=self.rod.center_x, center_y=self.rod.center_y - ) + self.brass_nut = brass_nut.align(center_x=self.rod.center_x, center_y=self.rod.center_y) return self diff --git a/src/muscad/vitamins/steppers.py b/src/muscad/vitamins/steppers.py index 0c58c61..376129f 100644 --- a/src/muscad/vitamins/steppers.py +++ b/src/muscad/vitamins/steppers.py @@ -26,12 +26,14 @@ def add_bolts( depth: float = 3, holes: Iterable[int] = (0, 1, 2, 3), ) -> Self: - """Add up to 4 bolts in the stepper fixing holes (as miscellaneous items) :param bolt: the - bolt to add (must be head up) :param spacing: edge distance between 2 bolt centers :param - depth: depth of the fixing holes inside the stepper :param holes: index of the bolts to add. + """Add up to 4 bolts in the stepper fixing holes (as miscellaneous items). - Modify it if you only want 2 or 3 bolts. + :param bolt: the bolt to add (must be head up) + :param spacing: edge distance between 2 bolt centers + :param depth: depth of the fixing holes inside the stepper + :param holes: index of the bolts to add. Modify it if you only want 2 or 3 bolts. :return: the stepper object, with bolts added + """ radius = ((spacing**2) * 2) ** 0.5 / 2 self.bolts = ( @@ -49,11 +51,7 @@ def add_central_bulge(self, d: float, h: float) -> Self: :return: the stepper object, with bulge added """ - self.central_bulge = ( - Cylinder(d=d, h=h + 1) - .align(center_x=0, center_y=0, top=self.top + h) - .misc() - ) + self.central_bulge = Cylinder(d=d, h=h + 1).align(center_x=0, center_y=0, top=self.top + h).misc() return self def add_gearbox( @@ -96,11 +94,7 @@ def add_shaft(self, d: float, length: float) -> Self: :return: the stepper object, with shaft added """ - self.shaft = ( - Cylinder(d=d, h=length + 2) - .align(center_x=0, center_y=0, bottom=self.top - E) - .misc() - ) + self.shaft = Cylinder(d=d, h=length + 2).align(center_x=0, center_y=0, bottom=self.top - E).misc() return self @classmethod From 9182353916ea21cf3fb31f862ebb42d62f158fac Mon Sep 17 00:00:00 2001 From: Guillaume Date: Sat, 2 Dec 2023 22:51:01 +0100 Subject: [PATCH 04/13] even more cleanups --- pyproject.toml | 3 +- src/muscad/base.py | 270 +++++++++++++++++++------------- src/muscad/helpers.py | 14 +- src/muscad/part.py | 25 +-- src/muscad/primitives.py | 29 ++-- src/muscad/transformations.py | 76 +++++---- src/muscad/utils/stack.py | 4 +- tests/test_utils/test_volume.py | 2 +- 8 files changed, 253 insertions(+), 170 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8c47ed9..6cd1dd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,6 @@ target-version = "py38" line-length = 120 select = [ "A", - "C", "C4", "D", "DTZ", @@ -95,7 +94,7 @@ exclude = [ "tests" ] extend-ignore = [ - "D100", "D101", "D102", "D103", "D104", "D105", "N802", "N803" + "D100", "D101", "D102", "D103", "D104", "D105", "E402", "N802", "N803" ] diff --git a/src/muscad/base.py b/src/muscad/base.py index 94e73c7..dda8d06 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -5,6 +5,7 @@ import subprocess import typing from functools import wraps +from pathlib import Path from typing import ( Any, Callable, @@ -30,7 +31,7 @@ def render(self) -> str: def __str__(self) -> str: return self.render() - def _repr_pretty_(self, p: Any, cycle: bool) -> None: # no-qa: FBT001 + def _repr_pretty_(self, p: Any, cycle: bool) -> None: # noqa: FBT001 """Helper method for Jupyter Notebook to show the rendered code instead of `__repr__`.""" p.text(str(self)) # pragma: no cover @@ -40,7 +41,7 @@ class MuSCADError(Exception): def indent(s: str, token: str = " ") -> str: - """Indents a given string, with characters from `token`. Each line will be prefixed by token. + r"""Indents a given string, with characters from `token`. Each line will be prefixed by token. :param s: the string to indent (may contain multiple lines, separated by '\n' :param token: the string to use as indentation @@ -58,7 +59,7 @@ def add_comment(code: str, comment: str | None = None) -> str: return f"{comment}{code}" -def render_comment(f: Callable[[O], str]) -> Callable[[O], str]: +def render_comment(f: Callable[[O_contra], str]) -> Callable[[O_contra], str]: """A helper decorator to add comments to rendered code.""" @wraps(f) @@ -69,7 +70,7 @@ def wrapper(self: Any) -> str: return wrapper -O = typing.TypeVar("O", contravariant=True) +O_contra = typing.TypeVar("O_contra", contravariant=True) class Object(MuSCAD): @@ -142,13 +143,21 @@ def remove_modifier(self) -> Object: return self.set_modifier(None) def __add__(self, other: Object | Iterable[Object]) -> Object: - """Adding two objects together creates a Union of those objects :param other: another object :return: a Union of - both objects. + """Adding two objects together creates a Union of those objects. + + :param other: another object + :return: a Union of both objects. + """ return Union(self, other) def __radd__(self, other: Literal[0]) -> Object: - """Makes sure sum(*[object, ...]) works :param other: another object, or 0 :return: a Union of both objects.""" + """Makes sure sum(*[object, ...]) works. + + :param other: another object, or 0 + :return: a Union of both objects.* + + """ assert other == 0 return self @@ -162,8 +171,11 @@ def __sub__(self, other: Object | Hole | Misc | Iterable[Object]) -> Object: return Difference(self, other) def __and__(self, other: Object) -> Object: - """Logical and between two objects creates an Intersection between those objects :param - other: another object :return: an Intersection of self and other. + """Logical and between two objects creates an Intersection between those objects. + + :param other: another object + :return: an Intersection of self and other. + """ return Intersection(self, other) @@ -190,21 +202,28 @@ def z_translate(self, z: float) -> Object: return self.translate(z=z) def rightward(self, dist: float) -> Object: - """Helper method to apply a Translation to the right on X axis on the current object :param - dist: distance in mm :return: an object, translated to the right by `dist` mm. + """Helper method to apply a Translation to the right on X axis on the current object. + + :param dist: distance in mm + :return: an object, translated to the right by `dist` mm. + """ return self.x_translate(dist) def leftward(self, dist: float) -> Object: - """Helper method to apply a Translation to the left on X axis on the current object :param - dist: distance in mm :return: an object, translated to the left by `dist` mm. + """Helper method to apply a Translation to the left on X axis on the current object. + + :param dist: distance in mm + :return: an object, translated to the left by `dist` mm. + """ return self.x_translate(-dist) def forward(self, dist: float) -> Object: - """Helper method to apply a forward Translation on Y axis on the current object :param dist: + """Helper method to apply a forward Translation on Y axis on the current object. - distance in mm :return: an object, translated forwards by `dist` mm. + :param dist: distance in mm + :return: an object, translated forwards by `dist` mm. """ return self.y_translate(dist) @@ -296,7 +315,7 @@ def z_rotate(self, angle: float, center_x: float = 0, center_y: float = 0) -> Ob return self.rotate(z=angle, center_x=center_x, center_y=center_y) def left_to_right(self) -> Object: - """Alias for self.z_rotate(180) + """Alias for `self.z_rotate(180)`. :return: a rotated Object. @@ -304,7 +323,7 @@ def left_to_right(self) -> Object: return self.z_rotate(180) def left_to_bottom(self) -> Object: - """Alias for self.y_rotate(-90).z_rotate(90) + """Alias for `self.y_rotate(-90).z_rotate(90)`. :return: a rotated Object. @@ -312,7 +331,7 @@ def left_to_bottom(self) -> Object: return self.y_rotate(-90).z_rotate(90) def left_to_top(self) -> Object: - """Alias for self.y_rotate(90).z_rotate(90) + """Alias for `self.y_rotate(90).z_rotate(90)`. :return: a rotated Object. @@ -320,7 +339,7 @@ def left_to_top(self) -> Object: return self.y_rotate(90).z_rotate(90) def left_to_front(self) -> Object: - """Alias for self.z_rotate(-90) + """Alias for `self.z_rotate(-90)`. :return: a rotated Object. @@ -328,7 +347,7 @@ def left_to_front(self) -> Object: return self.z_rotate(-90) def left_to_back(self) -> Object: - """Alias for self.z_rotate(90) + """Alias for `self.z_rotate(90)`. :return: a rotated Object. @@ -336,7 +355,7 @@ def left_to_back(self) -> Object: return self.z_rotate(90) def right_to_bottom(self) -> Object: - """Alias for self.y_rotate(90).z_rotate(-90) + """Alias for `self.y_rotate(90).z_rotate(-90)`. :return: a rotated Object. @@ -344,7 +363,7 @@ def right_to_bottom(self) -> Object: return self.y_rotate(90).z_rotate(-90) def right_to_top(self) -> Object: - """Alias for self.y_rotate(-90).z_rotate(90) + """Alias for `self.y_rotate(-90).z_rotate(90)`. :return: a rotated Object. @@ -352,7 +371,7 @@ def right_to_top(self) -> Object: return self.y_rotate(-90).z_rotate(90) def right_to_front(self) -> Object: - """Alias for self.z_rotate(90) + """Alias for `self.z_rotate(90)`. :return: a rotated Object. @@ -360,7 +379,7 @@ def right_to_front(self) -> Object: return self.z_rotate(90) def right_to_back(self) -> Object: - """Alias for self.z_rotate(-90) + """Alias for `self.z_rotate(-90)`. :return: a rotated Object. @@ -368,7 +387,7 @@ def right_to_back(self) -> Object: return self.z_rotate(-90) def right_to_left(self) -> Object: - """Alias for self.z_rotate(180) + """Alias for `self.z_rotate(180)`. :return: a rotated Object. @@ -376,7 +395,7 @@ def right_to_left(self) -> Object: return self.z_rotate(180) def front_to_left(self) -> Object: - """Alias for self.z_rotate(90) + """Alias for `self.z_rotate(90)`. :return: a rotated Object. @@ -384,7 +403,7 @@ def front_to_left(self) -> Object: return self.z_rotate(90) def front_to_right(self) -> Object: - """Alias for self.z_rotate(-90) + """Alias for `self.z_rotate(-90)`. :return: a rotated Object. @@ -392,7 +411,7 @@ def front_to_right(self) -> Object: return self.z_rotate(-90) def front_to_top(self) -> Object: - """Alias for self.x_rotate(90).z_rotate(180) + """Alias for `self.x_rotate(90).z_rotate(180)`. :return: a rotated Object. @@ -400,7 +419,7 @@ def front_to_top(self) -> Object: return self.x_rotate(90).z_rotate(180) def front_to_bottom(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(180) + """Alias for `self.x_rotate(-90).z_rotate(180)`. :return: a rotated Object. @@ -408,7 +427,7 @@ def front_to_bottom(self) -> Object: return self.x_rotate(-90).z_rotate(180) def front_to_back(self) -> Object: - """Alias for self.z_rotate(180) + """Alias for `self.z_rotate(180)`. :return: a rotated Object. @@ -416,7 +435,7 @@ def front_to_back(self) -> Object: return self.z_rotate(180) def back_to_left(self) -> Object: - """Alias for self.z_rotate(-90) + """Alias for `self.z_rotate(-90)`. :return: a rotated Object. @@ -424,7 +443,7 @@ def back_to_left(self) -> Object: return self.z_rotate(-90) def back_to_right(self) -> Object: - """Alias for self.z_rotate(90) + """Alias for `self.z_rotate(90)`. :return: a rotated Object. @@ -432,10 +451,15 @@ def back_to_right(self) -> Object: return self.z_rotate(90) def back_to_front(self) -> Object: + """Alias for `self.z_rotate(180)`. + + :return: a rotated Object. + + """ return self.z_rotate(180) def back_to_top(self) -> Object: - """Alias for self.x_rotate(-90) + """Alias for `self.x_rotate(-90)`. :return: a rotated Object. @@ -443,7 +467,7 @@ def back_to_top(self) -> Object: return self.x_rotate(-90) def back_to_bottom(self) -> Object: - """Alias for self.x_rotate(90) + """Alias for `self.x_rotate(90)`. :return: a rotated Object. @@ -451,7 +475,7 @@ def back_to_bottom(self) -> Object: return self.x_rotate(90) def bottom_to_left(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(-90) + """Alias for `self.x_rotate(-90).z_rotate(-90)`. :return: a rotated Object. @@ -459,7 +483,7 @@ def bottom_to_left(self) -> Object: return self.x_rotate(-90).z_rotate(-90) def bottom_to_right(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(90) + """Alias for `self.x_rotate(-90).z_rotate(90)`. :return: a rotated Object. @@ -467,7 +491,7 @@ def bottom_to_right(self) -> Object: return self.x_rotate(-90).z_rotate(90) def bottom_to_front(self) -> Object: - """Alias for self.x_rotate(-90).z_rotate(180) + """Alias for `self.x_rotate(-90).z_rotate(180)`. :return: a rotated Object. @@ -475,7 +499,7 @@ def bottom_to_front(self) -> Object: return self.x_rotate(-90).z_rotate(180) def bottom_to_back(self) -> Object: - """Alias for self.x_rotate(-90) + """Alias for `self.x_rotate(-90)`. :return: a rotated Object. @@ -483,7 +507,7 @@ def bottom_to_back(self) -> Object: return self.x_rotate(-90) def bottom_to_top(self) -> Object: - """Alias for self.x_rotate(180) + """Alias for `self.x_rotate(180)`. :return: a rotated Object. @@ -491,7 +515,7 @@ def bottom_to_top(self) -> Object: return self.x_rotate(180) def top_to_left(self) -> Object: - """Alias for self.x_rotate(90).z_rotate(-90) + """Alias for `self.x_rotate(90).z_rotate(-90)`. :return: a rotated Object. @@ -499,7 +523,7 @@ def top_to_left(self) -> Object: return self.x_rotate(90).z_rotate(-90) def top_to_right(self) -> Object: - """Alias for self.x_rotate(90).z_rotate(90) + """Alias for `self.x_rotate(90).z_rotate(90)`. :return: a rotated Object. @@ -507,7 +531,7 @@ def top_to_right(self) -> Object: return self.x_rotate(90).z_rotate(90) def top_to_back(self) -> Object: - """Alias for self.x_rotate(90) + """Alias for `self.x_rotate(90)`. :return: a rotated Object. @@ -515,7 +539,7 @@ def top_to_back(self) -> Object: return self.x_rotate(90) def top_to_front(self) -> Object: - """Alias for self.x_rotate(-90).y_rotate(180) + """Alias for `self.x_rotate(-90).y_rotate(180)`. :return: a rotated Object. @@ -523,14 +547,14 @@ def top_to_front(self) -> Object: return self.x_rotate(-90).y_rotate(180) def top_to_bottom(self) -> Object: - """Alias for self.x_rotate(180) + """Alias for `self.x_rotate(180)`. :return: a rotated Object. """ return self.x_rotate(180) - def upside_down(self, x_axis: bool = False) -> Object: + def upside_down(self, *, x_axis: bool = False) -> Object: """Turns the object upside down on its Y axis. Equivalent to self.y_rotate(180). If x_axis is True, rotate on X axis instead (like top_to_bottom()). @@ -576,7 +600,7 @@ def x_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: return self.x_mirror(center, keep=False) + self return self.leftward(center).mirror(x=1).rightward(center) - def y_mirror(self, center: float = 0.0, keep: bool = False) -> Object: + def y_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: """Helper method to mirror this object on the Y axis or a parallel. :param center: the Y coordinate of the axis to mirror on @@ -588,7 +612,7 @@ def y_mirror(self, center: float = 0.0, keep: bool = False) -> Object: return self.backward(center).mirror(y=1).forward(center) + self return self.backward(center).mirror(y=1).forward(center) - def z_mirror(self, center: float = 0.0, keep: bool = False) -> Object: + def z_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: """Helper method to mirror this object on the Z axis or a parallel. :param center: the Y coordinate of the axis to mirror on @@ -600,13 +624,14 @@ def z_mirror(self, center: float = 0.0, keep: bool = False) -> Object: return self.down(center).mirror(z=1).up(center) + self return self.down(center).mirror(z=1).up(center) - def project(self, cut: bool = False) -> Object: + def project(self, *, cut: bool = False) -> Object: """Transform this object into a 2D shape by projecting it to the (x,y) plane.""" return Projection(cut=cut)(self) def linear_extrude( self, height: float, + *, center: bool = False, convexity: int = 10, twist: float = 0.0, @@ -773,15 +798,16 @@ def hull(self) -> Hull: def offset( self, r: float, + *, delta: float | None = None, chamfer: bool = False, invert: bool = False, ) -> Object: if invert: if r < 0: - return self - self.offset(r, delta, chamfer) + return self - self.offset(r, delta=delta, chamfer=chamfer) else: - return self.offset(r, delta, chamfer) - self + return self.offset(r, delta=delta, chamfer=chamfer) - self return Offset(r=r, delta=delta, chamfer=chamfer)(self) def color(self, name: str, alpha: float | None = None) -> Object: @@ -832,11 +858,11 @@ def height(self) -> float: @property def left(self) -> float: - raise NotImplementedError("left", self.__class__) # pragma: no cover + raise NotImplementedError() # pragma: no cover @property def right(self) -> float: - raise NotImplementedError("right", self.__class__) # pragma: no cover + raise NotImplementedError() # pragma: no cover @property def center_x(self) -> float: @@ -844,11 +870,11 @@ def center_x(self) -> float: @property def back(self) -> float: - raise NotImplementedError("back", self.__class__) # pragma: no cover + raise NotImplementedError() # pragma: no cover @property def front(self) -> float: - raise NotImplementedError("front", self.__class__) # pragma: no cover + raise NotImplementedError() # pragma: no cover @property def center_y(self) -> float: @@ -856,11 +882,11 @@ def center_y(self) -> float: @property def bottom(self) -> float: - raise NotImplementedError("bottom", self.__class__) # pragma: no cover + raise NotImplementedError() # pragma: no cover @property def top(self) -> float: - raise NotImplementedError("top", self.__class__) # pragma: no cover + raise NotImplementedError() # pragma: no cover @property def center_z(self) -> float: @@ -919,7 +945,10 @@ def divide( if x is not None: if not self.left < x < self.right: - msg = f"x must be the x coordinate of a pane that divides the object in 2, between {self.left} and {self.right}" + msg = ( + "x must be the x coordinate of a pane that divides the object in 2," + f" between {self.left} and {self.right}" + ) raise ValueError(msg) return ( self @@ -943,7 +972,10 @@ def divide( ) elif y is not None: if not self.back < y < self.front: - msg = f"y must be the y coordinate of a pane that divides the object in 2, between {self.back} and {self.front}" + msg = ( + "y must be the y coordinate of a pane that divides the object in 2," + f" between {self.back} and {self.front}" + ) raise ValueError(msg) return ( self @@ -967,7 +999,10 @@ def divide( ) elif z is not None: if not self.bottom < z < self.top: - msg = f"z must be the z coordinate of a pane that divides the object in 2, between {self.bottom} and {self.top}" + msg = ( + "z must be the z coordinate of a pane that divides the object in 2," + f" between {self.bottom} and {self.top}" + ) raise ValueError(msg) return ( self @@ -990,7 +1025,8 @@ def divide( ), ) else: - raise ValueError("You must provide one plane to divide the Object, defined by one X, Y, Z coordinate") + msg = "You must provide one plane to divide the Object, defined by one X, Y, Z coordinate" + raise ValueError(msg) def __stl__(self) -> Object: return self # pragma: no cover @@ -1000,15 +1036,13 @@ def file_name(self) -> str: return camel_to_snake(self.__class__.__name__) # pragma: no cover def render_to_file( - self, path: str | None = None, mode: str = "wt", openscad: bool = False - ) -> str: # pragma: no cover + self, path: str | Path | None = None, *, mode: str = "wt", openscad: bool = False + ) -> Path: # pragma: no cover if path is None: path = self.file_name - if mode is None: - mode = self.mode - return render_to_file(self, mode, path, openscad=openscad) + return render_to_file(self, path, mode=mode, openscad=openscad) - def export_stl(self, path: str | None = None) -> str: # pragma: no cover + def export_stl(self, path: str | Path | None = None) -> Path: # pragma: no cover obj = self.__stl__() if path is None: path = self.file_name @@ -1090,7 +1124,7 @@ def walk(self) -> Iterable[Object]: class Composite(Object): - """Base class for Boolean operations (Union, Difference, Intersection)""" + """Base class for Boolean operations (Union, Difference, Intersection).""" def __init__(self, *children: Object | Iterable[Object]): super().__init__() @@ -1099,9 +1133,10 @@ def __init__(self, *children: Object | Iterable[Object]): self.apply(*children) def add_child(self, child: Object | Iterable[Object] | Literal[0]) -> Object: - """Add a children object to this Composite :param child: + """Add a children object to this Composite. - :return: + :param child: the child object to add + :return: another composite with the additional child """ if child == 0: # for sum(*Objects) @@ -1180,12 +1215,13 @@ def top(children: Iterable[Object]) -> float: class Union(Composite): - """OpenSCAD union()""" + """OpenSCAD `union()`.""" def __add__(self, other: Object | Iterable[Object]) -> Object: - """Adding to a Union adds a children instead of creating a new Union :param other: a children object :return: + """Adding to a Union adds a children instead of creating a new Union. - the same union with children appended. + :param other: a children object + :return: the same union with children appended. """ return self.add_child(other) @@ -1227,11 +1263,14 @@ def render(self) -> str: class Difference(Composite): - """OpenSCAD difference()""" + """OpenSCAD `difference()`.""" def __sub__(self, other: Object | Misc | Hole | Iterable[Object]) -> Object: - """Substracting from a Difference adds a children instead of creating a new Difference :param other: a children - object :return: the same difference with children appended. + """Substracting from a Difference adds a children instead of creating a new Difference. + + :param other: a children object + :return: the same difference with children appended. + """ return self.add_child(other) @@ -1261,11 +1300,14 @@ def top(self) -> float: class Intersection(Composite): - """OpenSCAD intersection()""" + """OpenSCAD `intersection()`.""" def __and__(self, other: Object) -> Object: - """Intersecting with an Intersection adds a children instead of creating a new Intersection :param other: a - children object :return: the same intersection with children appended. + """Intersecting with an Intersection adds a children instead of creating a new Intersection. + + :param other: a children object + :return: the same intersection with children appended. + """ return self.add_child(other) @@ -1309,7 +1351,8 @@ def __init__(self, *children: Object): @property def child(self) -> Object: if self._child is None: - raise RuntimeError("This Transformation has no child") + msg = "This Transformation has no child" + raise RuntimeError(msg) return self._child @child.setter @@ -1338,13 +1381,16 @@ def __call__(self, *children: Object) -> Object: @render_comment def render(self) -> str: - return f"{self.modifier}{self.object_name}({self._render_arguments(self._iter_arguments())})\n{self._render_child()}" + return f"""\ +{self.modifier}{self.object_name}({self._render_arguments(self._iter_arguments())}) +{self._render_child()}""" def childattr(self, item: str) -> Any: - """Makes properties from the transformed object accessible through the Transformation :param - item: + """Makes properties from the transformed object accessible through the Transformation. + + :param item: name of an attribute on the child + :return: the child attribute, with transformation applied - :return: """ return self.copy()(getattr(self.child, item)) @@ -1359,8 +1405,11 @@ def file_name(self) -> str: return self.child.file_name def combine(self, child: Transformation) -> Transformation: - """When applying multiple transformations of the same type, those may be combined :param - child: another Primitive :return: an object combining all transformations. + """When applying multiple transformations of the same type, those may be combined. + + :param child: another Primitive + :return: an object combining all transformations. + """ self.child = child return self @@ -1449,7 +1498,8 @@ def __getattr__(self, key: str) -> Any: def __add__(self, other: Object) -> Hole: if not isinstance(other, Hole): - raise ValueError("Holes can only be added to Holes") + msg = "Holes can only be added to Holes" + raise TypeError(msg) return Hole(self.object + other.object) def render(self) -> str: @@ -1504,14 +1554,17 @@ def middle_of(one: float, other: float) -> float: return one + (other - one) / 2 -def render_to_file(obj: Object, mode: str, path: str, openscad: bool = False) -> str: - if not path.endswith(".scad"): - path = path + ".scad" - if not os.path.isabs(path): - path = os.path.join(os.getcwd(), path) +def render_to_file(obj: Object, path: str | Path, *, mode: str, openscad: bool = False) -> Path: + if not isinstance(path, Path): + path = Path(path) + + if path.suffix != ".scad": + path = path.with_suffix(".scad") + if not path.is_absolute(): + path = Path.cwd() / path render = obj.render() - with open(path, mode) as foutput: + with path.open(mode) as foutput: foutput.write(render) if openscad and not os.environ.get("MUSCAD_NO_OPENSCAD"): try: @@ -1544,13 +1597,17 @@ def wrapper( ) -> tuple[float, float, float, float]: _from, _center, _to, _distance = f(from_, center, to, distance) if center is not None and _center != center: - raise ValueError( - f"calculated center incompatible with specified center (specified: {center}, calculated: {_center}, difference={center - _center})" + msg = ( + "calculated center incompatible with specified center" + f" (specified: {center}, calculated: {_center}, difference={center - _center})" ) + raise ValueError(msg) if distance is not None and not math.isclose(_distance, abs(distance)): - raise ValueError( - f"calculated distance incompatible with specified from_ (specified: {distance}, calculated: {_distance}, difference={distance - _distance})" + msg = ( + "calculated distance incompatible with specified from_" + f" (specified: {distance}, calculated: {_distance}, difference={distance - _distance})" ) + raise ValueError(msg) if ( (from_ is not None and to is not None and from_ > to) or (from_ is not None and center is not None and from_ > center) @@ -1560,13 +1617,11 @@ def wrapper( from_, to = to, from_ if from_ is not None and _from != from_: - raise ValueError( - f"calculated from_ incompatible with specified from_ (specified: {from_}, calculated: {_from}, difference={from_ - _from})" - ) + msg = f"calculated from_ incompatible with specified from_ (specified: {from_}, calculated: {_from}, difference={from_ - _from})" + raise ValueError(msg) if to is not None and _to != to: - raise ValueError( - f"calculated to incompatible with specified to (specified: {to}, calculated: {_to}, difference={to - _to})" - ) + msg = f"calculated to incompatible with specified to (specified: {to}, calculated: {_to}, difference={to - _to})" + raise ValueError(msg) return _from, _center, _to, _distance return wrapper @@ -1637,12 +1692,15 @@ def calc( else: center = (from_ - to) / 2 + from_ return to, center, from_, distance - raise ValueError("no sufficient input to calculate all params") + msg = "no sufficient input to calculate all params" + raise ValueError(msg) -def export_stl(scad_path: str, stl_path: str | None = None) -> str: - if not stl_path: - stl_path = os.path.splitext(scad_path)[0] + ".stl" +def export_stl(scad_path: str | Path, stl_path: str | Path | None = None) -> Path: + if stl_path is None: + stl_path = Path(scad_path).stem + ".stl" + if not isinstance(stl_path, Path): + stl_path = Path(stl_path) subprocess.call(["openscad", "-o", stl_path, scad_path]) return stl_path diff --git a/src/muscad/helpers.py b/src/muscad/helpers.py index a04dced..2ab9c6d 100644 --- a/src/muscad/helpers.py +++ b/src/muscad/helpers.py @@ -55,9 +55,10 @@ def atan2(x: float, y: float) -> float: def hypotenuse(leg1: float, leg2: float) -> float: - """Returns the length of the hypotenuse based on the length of both legs :param leg1: length of one leg :param leg2: + """In a triangle, returns the length of the hypotenuse based on the length of both perpendicular sides. - length of the other leg + :param leg1: length of one leg + :param leg2: length of the other leg :return: the hypotenuse length. """ @@ -65,9 +66,12 @@ def hypotenuse(leg1: float, leg2: float) -> float: def catheti(hypot: float, leg: float) -> float: - """Returns the length of one leg based on the length of the hypotenuse and the other leg :param - hypot: length of the hypotenuse :param leg: length of the other leg :return: the length of the - missing leg. + """In a triangle, returns the length of one leg based on the length of the hypotenuse and the other leg. + + :param hypot: length of the hypotenuse + :param leg: length of the other leg + :return: the length of the missing leg. + """ return (hypot**2 - leg**2) ** 0.5 # type: ignore[no-any-return] diff --git a/src/muscad/part.py b/src/muscad/part.py index ba40544..7a13507 100644 --- a/src/muscad/part.py +++ b/src/muscad/part.py @@ -45,8 +45,11 @@ class Part(Composite): class_holes: ClassVar[list[Object]] def __init_subclass__(cls, **kwargs: Any) -> None: - """When creating an inherited class, sort all class-level attributes and make lists of all Objects, Misc and + """Handle class level attributes. + + When creating an inherited class, sort all class-level attributes and build lists of all Objects, Misc and Holes. + """ super().__init_subclass__(**kwargs) cls.class_parts = [] @@ -103,9 +106,10 @@ def init( *args: float | bool | Misc | Hole | Object, **kwargs: float | bool | Misc | Hole | Object, ) -> None: - """Override this to add parametric children to this Part :param args: + """Override this to add parametric children to this Part. - :param kwargs: + :param args: arguments passed to __init__ + :param kwargs: keyword arguments passed to __init__ :return: """ @@ -164,14 +168,10 @@ def __setattr__(self, key: str, value: Any) -> None: elif isinstance(value, Misc): if value.comment is None: value.comment = key - # if previous: - # self.miscellaneous.remove(previous) self.add_misc(value) elif isinstance(value, Hole): if value.comment is None: value.comment = key - # if previous: - # self.holes.remove(previous) self.add_hole(value) elif isinstance(value, Object): if value.comment is None: @@ -181,12 +181,13 @@ def __setattr__(self, key: str, value: Any) -> None: self.add_child(value) @render_comment - def render(self, postprocess: bool = True) -> str: + def render(self, *, postprocess: bool = True) -> str: if not self.children and not self.miscellaneous: if self.holes: self.children, self.holes = self.holes, self.children else: - raise RuntimeError("This part has no children") + msg = "This part has no children" + raise RuntimeError(msg) # renders children and misc renderable: Object = Union(chain(self.children, self.miscellaneous)) # if this part has holes, render a diff of all children with all holes @@ -239,7 +240,7 @@ def bottom(self) -> float: def top(self) -> float: return top(self.children) - def debug(self, include_misc: bool = False) -> Object: + def debug(self, *, include_misc: bool = False) -> Object: """Turn all children to debug, not the misc (Unless include_misc is set to True).""" if include_misc: return super().debug() @@ -261,6 +262,7 @@ class MirroredPart(Part): def __init_subclass__( cls, + *, x: bool = False, y: bool = False, z: bool = False, @@ -286,7 +288,8 @@ def __init_subclass__( @render_comment def render(self) -> str: if not self.children: - raise RuntimeError("This part has no children") + msg = "This part has no children" + raise RuntimeError(msg) children: Object = Union(self.children) if self.holes: children = children - self.holes diff --git a/src/muscad/primitives.py b/src/muscad/primitives.py index 22ff2a2..ff1d879 100644 --- a/src/muscad/primitives.py +++ b/src/muscad/primitives.py @@ -1,8 +1,8 @@ """This modules contains 2D & 3D Primitives classes that match OpenSCAD primitives.""" from __future__ import annotations -import os import sys +from pathlib import Path from typing import Any, Iterable, Sequence from muscad.base import Object, Primitive @@ -205,8 +205,9 @@ def unpack_points(points: Iterable[Point3D | Sequence[float]]) -> Iterable[Point x, y, z = point yield Point3D(x, y, z) else: + msg = "invalid point, must be a 3 floats tuple or a Point3D instance" raise ValueError( - "invalid point, must be a 3 floats tuple or a Point3D instance", + msg, point, ) @@ -339,25 +340,29 @@ def _arguments(self) -> dict[str | None, Any]: def left(self) -> float: if self.halign in (None, "left"): return 0 - raise NotImplementedError("use halign='left' (default) to be able to align a Text to left") + msg = "use halign='left' (default) to be able to align a Text to left" + raise NotImplementedError(msg) @property def right(self) -> float: if self.halign == "right": return 0 - raise NotImplementedError("use halign='right' to be able to align a Text to right") + msg = "use halign='right' to be able to align a Text to right" + raise NotImplementedError(msg) @property def back(self) -> float: if self.valign in (None, "baseline", "bottom"): return 0 - raise NotImplementedError("use valign='baseline' or 'bottom' to be able to align a Text to back") + msg = "use valign='baseline' or 'bottom' to be able to align a Text to back" + raise NotImplementedError(msg) @property def front(self) -> float: if self.valign == "top": return 0 - raise NotImplementedError("use valign='top' to be able to align a Text to front") + msg = "use valign='top' to be able to align a Text to front" + raise NotImplementedError(msg) class Polygon(Primitive2D): @@ -370,9 +375,8 @@ def __init__( ) -> None: super().__init__() self.points = list(self.unpack_points(points)) - if hole_paths: - if not path: - path = list(range(len(points))) + if hole_paths and not path: + path = list(range(len(points))) self.paths: list[list[int]] | None = [list(path)] if path else None if hole_paths and self.paths: for hole_path in hole_paths: @@ -388,8 +392,9 @@ def unpack_points(points: Iterable[Point2D | tuple[float, float]]) -> Iterable[P x, y = point yield Point2D(x, y) else: + msg = ("invalid point, must be a 2 floats tuple or a Point2D instance",) raise ValueError( - "invalid point, must be a 2 floats tuple or a Point2D instance", + msg, point, ) @@ -419,8 +424,8 @@ def back(self) -> float: class Import(Primitive): def __init__(self, file: str, convexity: int | None = None, layer: str | None = None) -> None: - if not os.path.isabs(file): - file = os.path.join(os.path.dirname(sys.argv[0]), file) + if not Path(file).is_absolute(): + file = str(Path(sys.argv[0]).parent / file) super().__init__(file=file, convexity=convexity, layer=layer) diff --git a/src/muscad/transformations.py b/src/muscad/transformations.py index 86ca22e..69af679 100644 --- a/src/muscad/transformations.py +++ b/src/muscad/transformations.py @@ -55,6 +55,9 @@ def top(self) -> float: return self.child.top + self.z +RIGHT_ANGLE_ROTATIONS_ONLY = NotImplementedError("Only simple 90° rotations are supported") + + class Rotation(Transformation, name="rotate"): """OpenSCAD rotate().""" @@ -68,17 +71,16 @@ def _arguments(self) -> dict[str | None, Any]: return {"a": Point3D(self.x, self.y, self.z)} def combine(self, other: Transformation) -> Transformation: - if isinstance(other, self.__class__): - if ( - (self.x == 0 and self.y == 0) - or (other.z == 0 and self.x == self.z == 0) - or (other.y == other.z == 0 and self.y == self.z == 0) - ): - self.x = normalize_angle(self.x + other.x) - self.y = normalize_angle(self.y + other.y) - self.z = normalize_angle(self.z + other.z) - self.child: Object = other.child - return self + if isinstance(other, self.__class__) and ( + (self.x == 0 and self.y == 0) + or (other.z == 0 and self.x == self.z == 0) + or (other.y == other.z == 0 and self.y == self.z == 0) + ): + self.x = normalize_angle(self.x + other.x) + self.y = normalize_angle(self.y + other.y) + self.z = normalize_angle(self.z + other.z) + self.child: Object = other.child + return self return super().combine(other) def copy(self) -> Transformation: @@ -147,7 +149,7 @@ def left(self) -> float: ]: return -self.child.top - raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) + raise RIGHT_ANGLE_ROTATIONS_ONLY @property def right(self) -> float: @@ -193,7 +195,7 @@ def right(self) -> float: ]: return -self.child.bottom - raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) + raise RIGHT_ANGLE_ROTATIONS_ONLY @property def back(self) -> float: @@ -239,7 +241,7 @@ def back(self) -> float: ]: return -self.child.top - raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) + raise RIGHT_ANGLE_ROTATIONS_ONLY @property def front(self) -> float: @@ -285,7 +287,7 @@ def front(self) -> float: ]: return -self.child.bottom - raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) + raise RIGHT_ANGLE_ROTATIONS_ONLY @property def bottom(self) -> float: @@ -302,7 +304,7 @@ def bottom(self) -> float: if (self.x == 90 and self.y == 180) or (self.x == 270 and self.y == 0): return -self.child.front - raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) + raise RIGHT_ANGLE_ROTATIONS_ONLY @property def top(self) -> float: @@ -319,7 +321,7 @@ def top(self) -> float: if (self.x == 90 and self.y == 180) or (self.x == 270 and self.y == 0): return -self.child.back - raise NotImplementedError("Only simple 90° rotations are supported", self.x, self.y, self.z) + raise RIGHT_ANGLE_ROTATIONS_ONLY class Scaling(Transformation, name="scale"): @@ -357,6 +359,9 @@ def top(self) -> float: return self.child.top * self.z +MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED = NotImplementedError("Only single axis mirror vectors are supported") + + class Mirroring(Transformation, name="mirror"): def __init__(self, *, x: float = 0, y: float = 0, z: float = 0): super().__init__() @@ -372,7 +377,7 @@ def left(self) -> float: if self.x: if not self.y and not self.z: return -self.child.right - raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover + raise MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED # pragma: no cover return self.child.left @property @@ -380,7 +385,7 @@ def right(self) -> float: if self.x: if not self.y and not self.z: return -self.child.left - raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover + raise MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED # pragma: no cover return self.child.right @property @@ -388,7 +393,7 @@ def front(self) -> float: if self.y: if not self.x and not self.z: return -self.child.back - raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover + raise MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED # pragma: no cover return self.child.front @property @@ -396,7 +401,7 @@ def back(self) -> float: if self.y: if not self.x and not self.z: return -self.child.front - raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover + raise MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED # pragma: no cover return self.child.back @property @@ -404,7 +409,7 @@ def top(self) -> float: if self.z: if not self.x and not self.y: return -self.child.bottom - raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover + raise MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED # pragma: no cover return self.child.top @property @@ -412,7 +417,7 @@ def bottom(self) -> float: if self.z: if not self.x and not self.y: return -self.child.top - raise NotImplementedError("Only single axis mirror vectors are supported") # pragma: no cover + raise MULTI_AXIS_MIRROR_VECTOR_NOT_SUPPORTED # pragma: no cover return self.child.bottom def copy(self) -> Transformation: @@ -452,14 +457,17 @@ class Offset(Transformation): def __init__( self, r: float | None = None, + *, delta: float | None = None, chamfer: bool | None = False, ): super().__init__() - if r and delta: - raise ValueError("can't set both 'r' and 'delta'") # pragma: no cover - if r is None and delta is None: - raise ValueError("must set either 'r' or 'delta'") # pragma: no cover + if r and delta: # pragma: no cover + msg = "can't set both 'r' and 'delta'" + raise ValueError(msg) + if r is None and delta is None: # pragma: no cover + msg = "must set either 'r' or 'delta'" + raise ValueError(msg) self.radius = r self.delta = delta self.chamfer = chamfer @@ -499,7 +507,7 @@ class Hull(Transformation): class Projection(Transformation): - def __init__(self, cut: bool = False) -> None: + def __init__(self, *, cut: bool = False) -> None: super().__init__() self.cut = cut @@ -524,10 +532,14 @@ def _arguments(self) -> dict[str | None, Any]: return {"convexity": self._convexity} +TWIST_NOT_SUPPORTED = NotImplementedError("Linear extrusion with 'twist' is not supported") + + class LinearExtrusion(Transformation, name="linear_extrude"): def __init__( self, height: float, + *, center: bool = False, convexity: int = 10, twist: float = 0, @@ -560,25 +572,25 @@ def _arguments(self) -> dict[str | None, Any]: def left(self) -> float: if self._twist == 0: return self.child.left * self._scale - raise NotImplementedError("Linear extrusion with 'twist' is not supported") + raise TWIST_NOT_SUPPORTED @property def right(self) -> float: if self._twist == 0: return self.child.right * self._scale - raise NotImplementedError("Linear extrusion with 'twist' is not supported") + raise TWIST_NOT_SUPPORTED @property def back(self) -> float: if self._twist == 0: return self.child.back * self._scale - raise NotImplementedError("Linear extrusion with 'twist' is not supported") + raise TWIST_NOT_SUPPORTED @property def front(self) -> float: if self._twist == 0: return self.child.front * self._scale - raise NotImplementedError("Linear extrusion with 'twist' is not supported") + raise TWIST_NOT_SUPPORTED @property def bottom(self) -> float: diff --git a/src/muscad/utils/stack.py b/src/muscad/utils/stack.py index 7beeaac..0cff494 100644 --- a/src/muscad/utils/stack.py +++ b/src/muscad/utils/stack.py @@ -7,8 +7,10 @@ def stack(*parts: Object | Misc, overlap: float = 0.01) -> Part: """Stack each part on top of each other. :param parts: all parts - :param overlap: separation between each part (positive value: pieces will overlap, negative value: pieces will be separated) + :param overlap: separation between each part (positive value: pieces will overlap, negative value: pieces will be + separated) :return: parts stacked on top of each other, bottom to top. + """ s = Part() top = 0.0 diff --git a/tests/test_utils/test_volume.py b/tests/test_utils/test_volume.py index 711b11d..f685b85 100644 --- a/tests/test_utils/test_volume.py +++ b/tests/test_utils/test_volume.py @@ -52,7 +52,7 @@ def test_volume() -> None: assert upside_down.bottom == -top assert upside_down.top == -bottom - upside_down_x = ref.upside_down(True) + upside_down_x = ref.upside_down(x_axis=True) assert upside_down_x.left == left assert upside_down_x.right == right assert upside_down_x.back == -front From 799cb98c45bb242e935f343498d6c482216437d5 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Wed, 3 Jan 2024 10:04:33 +0100 Subject: [PATCH 05/13] allow align Text to center_x or center_y --- src/muscad/primitives.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/muscad/primitives.py b/src/muscad/primitives.py index ff1d879..ea0214a 100644 --- a/src/muscad/primitives.py +++ b/src/muscad/primitives.py @@ -350,6 +350,13 @@ def right(self) -> float: msg = "use halign='right' to be able to align a Text to right" raise NotImplementedError(msg) + @property + def center_x(self) -> float: + if self.halign == "center": + return 0 + msg = "use halign='center' to be able to align a Text to center_x" + raise NotImplementedError(msg) + @property def back(self) -> float: if self.valign in (None, "baseline", "bottom"): @@ -364,6 +371,13 @@ def front(self) -> float: msg = "use valign='top' to be able to align a Text to front" raise NotImplementedError(msg) + @property + def center_y(self) -> float: + if self.valign == "center": + return 0 + msg = "use valign='center' to be able to align a Text to center_y" + raise NotImplementedError(msg) + class Polygon(Primitive2D): def __init__( From 746c38d761a79d63d6e7d8aa4c5c909b02788de9 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Wed, 3 Jan 2024 20:16:47 +0100 Subject: [PATCH 06/13] introduce Object.{x,y,z}_symmetry() methods instead of Object.{x,y,z}_mirror(keep=True) --- examples/muscad_printer.py | 34 ++--- examples/numbers.py | 6 +- src/muscad/base.py | 48 +++++-- src/muscad/part.py | 107 ++++++++++++---- src/muscad/primitives.py | 7 + src/muscad/utils/barrel.py | 124 ++++++++++++++++++ src/muscad/utils/star.py | 7 + src/muscad/utils/tube.py | 8 +- src/muscad/vitamins/boards.py | 2 +- src/muscad/vitamins/endstops.py | 8 +- src/muscad/vitamins/threads.py | 213 +++++++++++++++++++++++++++++++ tests/test_base.py | 10 +- tests/test_part.py | 10 +- tests/test_utils/test_surface.py | 13 +- 14 files changed, 519 insertions(+), 78 deletions(-) create mode 100644 src/muscad/utils/barrel.py create mode 100644 src/muscad/utils/star.py create mode 100644 src/muscad/vitamins/threads.py diff --git a/examples/muscad_printer.py b/examples/muscad_printer.py index 06b1618..d9b0ba5 100644 --- a/examples/muscad_printer.py +++ b/examples/muscad_printer.py @@ -13,11 +13,11 @@ E, Fillet, Hull, - MirroredPart, Object, Part, Sphere, Surface, + SymmetricPart, T, Union, Volume, @@ -490,8 +490,8 @@ def init(self) -> None: # type: ignore[override] class ZTopEndStop(Part): - bed_extrusion = ~bed.x_extrusion_front.debug() - frame_extrusion = ~frame.y_extrusion_right_middle.debug() + bed_extrusion = ~bed.x_extrusion_front + frame_extrusion = ~frame.y_extrusion_right_middle endstop = ( ~MechanicalEndstopOnPCB(Bolt.M3(10).add_nut(-3, side_clearance_size=10, angle=-90)) .front_to_bottom() @@ -625,7 +625,7 @@ class XCarriage(Part): back=cable_guide_top.back - E, center_z=cable_guide_top.top - 6, ) - .z_mirror(cable_guide_top.top - 10, keep=True) + .z_mirror(cable_guide_top.top - 10) .debug() ) @@ -794,7 +794,7 @@ class XAxisPulleys(Part): center_x=body.center_x + 8, front=body.front + 8, center_z=center_pulleys_bolt.center_z, - ).x_mirror(center=center_pulleys_bolt.center_x, keep=True) + ).x_symmetry(center=center_pulleys_bolt.center_x) def __stl__(self) -> Object: return self.bottom_to_back() @@ -1027,7 +1027,7 @@ class CableChainCarriageAttachment(Part): height=4, ) .add_corner(angle=270) - .z_mirror(center=frame.y_extrusion_right_top.center_z, keep=True) + .z_symmetry(center=frame.y_extrusion_right_top.center_z) ) shaft = ( @@ -1117,7 +1117,7 @@ class CableChainFrameAttachement(Part): ~Bolt.M5(16) .bottom_to_left() .align(left=body.left - 1, center_y=body.back + 8, center_z=body.center_z) - .y_mirror(body.center_y, keep=True) + .y_symmetry(body.center_y) .debug() ) chain_bolts = ( @@ -1125,7 +1125,7 @@ class CableChainFrameAttachement(Part): .add_nut(-1) .bottom_to_left() .align(right=body.right + 1, center_z=body.center_z) - .y_mirror(center=-7.5 / 2, keep=True) + .y_symmetry(center=-7.5 / 2) .align(center_y=body.back + 24) .debug() ) @@ -1139,7 +1139,7 @@ class CableChainFrameAttachement(Part): center_y=chain_bolts.front + 8, center_z=body.center_z + 8, ) - .z_mirror(center=body.center_z, keep=True) + .z_mirror(center=body.center_z) .debug() ) @@ -2034,8 +2034,8 @@ class ExtruderStepperMount(Part): center_y=stepper_holder.back, center_z=stepper.center_z + 15.52, ) - .x_mirror(center=stepper.center_x, keep=True) - .z_mirror(center=stepper.center_z, keep=True) + .x_symmetry(center=stepper.center_x) + .z_symmetry(center=stepper.center_z) .debug() ) @@ -2218,7 +2218,7 @@ def __stl__(self) -> Object: spool_holder = SpoolHolder() -class ZBedMount(MirroredPart, y=True, keep_y=True): +class ZBedMount(SymmetricPart, y=True): extrusion = ~bed.y_extrusion_left z_rod = ~gantry.z_rod_left_front t8 = ~gantry.z_threaded_rod_left @@ -2238,7 +2238,7 @@ class ZBedMount(MirroredPart, y=True, keep_y=True): left=extrusion.left - 10, center_y=bearing.back - 7, center_z=extrusion.center_z, - ).slide(y=-10).y_mirror(center=z_rod.center_y, keep=True) + ).slide(y=-10).y_symmetry(center=z_rod.center_y) base = Surface.free( Circle(d=30).align(center_x=bearing.center_x, center_y=bearing.center_y), @@ -2336,7 +2336,7 @@ class ZBedMount(MirroredPart, y=True, keep_y=True): center_x=extrusion.center_x, center_y=bearing.center_y + 16, center_z=extrusion.top + 2, - ).slide(y=20).y_mirror(bearing.center_y, keep=True) + ).slide(y=20).y_symmetry(bearing.center_y) def __stl__(self) -> Object: return self.upside_down() @@ -2486,8 +2486,8 @@ def __stl__(self) -> Object: z_bracket_top_right_back = z_bracket_top_left_back.x_mirror(bed.center_x) -class ZBracketTop(MirroredPart, y=True, keep_y=True): - extrusion = ~frame.y_extrusion_left_middle.debug() +class ZBracketTop(SymmetricPart, y=True): + extrusion = ~frame.y_extrusion_left_middle rod = ~gantry.z_rod_left_front.debug() body = ( @@ -3227,7 +3227,7 @@ class MotorCableGuide(Part): + thumbwheel_left + thumbwheel_right ).color("orange") - + (frame_spacer + z_rod_spacer).color("green") + # + (frame_spacer + z_rod_spacer).color("green") ).render_to_file("muscad_printer") glass_plate_corner_left_front.render_to_file() diff --git a/examples/numbers.py b/examples/numbers.py index a88bce9..6ea9ab0 100644 --- a/examples/numbers.py +++ b/examples/numbers.py @@ -15,7 +15,7 @@ class Number1(Part): .leftward(0.3) .backward(0.8) ) - screws = ~Screw().align(center_x=0, center_y=30, top=number.top + T).y_mirror(keep=True) + screws = ~Screw().align(center_x=0, center_y=30, top=number.top + T).y_symmetry() class Number2(Part): @@ -37,7 +37,7 @@ class Number3(Part): .leftward(0.3) .backward(0.8) ) - screws = ~Screw().align(center_x=16, center_y=24, top=number.top + T).y_mirror(keep=True).x_mirror(keep=True) + screws = ~Screw().align(center_x=16, center_y=24, top=number.top + T).y_symmetry().x_symmetry() class Number4(Part): @@ -47,7 +47,7 @@ class Number4(Part): .leftward(0.3) .backward(0.8) ) - screws_right = ~Screw().align(center_x=10, center_y=10, top=number.top + T).y_mirror(-10, keep=True) + screws_right = ~Screw().align(center_x=10, center_y=10, top=number.top + T).y_symmetry(-10) screw_top = ~Screw().align(center_x=-4, center_y=32, top=number.top + T) screw_left = ~Screw().align(center_x=-22, center_y=-19, top=number.top + T) diff --git a/src/muscad/base.py b/src/muscad/base.py index dda8d06..d55749d 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -588,42 +588,66 @@ def mirror(self, *, x: float = 0, y: float = 0, z: float = 0) -> Object: """ return Mirroring(x=x, y=y, z=z)(self) - def x_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: + def x_mirror(self, center: float = 0.0) -> Object: """Helper method to mirror this object on the X axis or a parallel. :param center: the X coordinate of the axis to mirror on - :param keep: if True, the initial object is kept in addition to its mirror :return: a mirrored object """ - if keep: - return self.x_mirror(center, keep=False) + self return self.leftward(center).mirror(x=1).rightward(center) - def y_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: + def y_mirror(self, center: float = 0.0) -> Object: """Helper method to mirror this object on the Y axis or a parallel. :param center: the Y coordinate of the axis to mirror on - :param keep: if True, the initial object is kept in addition to its mirror :return: a mirrored object """ - if keep: - return self.backward(center).mirror(y=1).forward(center) + self return self.backward(center).mirror(y=1).forward(center) - def z_mirror(self, center: float = 0.0, *, keep: bool = False) -> Object: + def z_mirror(self, center: float = 0.0) -> Object: """Helper method to mirror this object on the Z axis or a parallel. :param center: the Y coordinate of the axis to mirror on - :param keep: if True, the initial object is kept in addition to its mirror :return: a mirrored object """ - if keep: - return self.down(center).mirror(z=1).up(center) + self return self.down(center).mirror(z=1).up(center) + def x_symmetry(self, center: float = 0.0) -> Object: + """Helper method to create a symmetric object using a mirror on a plane parallel to the X plane. + + Similar to x_mirror(), but the original object is kept. + + :param center: the X coordinate of the axis to mirror on + :return: a symmetric object + + """ + return self.x_mirror(center) + self + + def y_symmetry(self, center: float = 0.0) -> Object: + """Helper method to create a symmetric object using a mirror on a plane parallel to the Y plane. + + Similar to y_mirror(), but the original object is kept. + + :param center: the Y coordinate of the axis to mirror on + :return: a symmetric object + + """ + return self.y_mirror(center) + self + + def z_symmetry(self, center: float = 0.0) -> Object: + """Helper method to create a symmetric object using a mirror on a plane parallel to the Z plane. + + Similar to z_mirror(), but the original object is kept. + + :param center: the Z coordinate of the axis to mirror on + :return: a symmetric object + + """ + return self.z_mirror(center) + self + def project(self, *, cut: bool = False) -> Object: """Transform this object into a 2D shape by projecting it to the (x,y) plane.""" return Projection(cut=cut)(self) diff --git a/src/muscad/part.py b/src/muscad/part.py index 7a13507..c2de549 100644 --- a/src/muscad/part.py +++ b/src/muscad/part.py @@ -256,9 +256,6 @@ class MirroredPart(Part): _center_x: float _center_y: float _center_z: float - keep_x: bool - keep_y: bool - keep_z: bool def __init_subclass__( cls, @@ -269,9 +266,6 @@ def __init_subclass__( center_x: float = 0, center_y: float = 0, center_z: float = 0, - keep_x: bool = False, - keep_y: bool = False, - keep_z: bool = False, **kwargs: Any, ): super().__init_subclass__(**kwargs) @@ -281,9 +275,6 @@ def __init_subclass__( cls._center_x = center_x cls._center_y = center_y cls._center_z = center_z - cls.keep_x = keep_x - cls.keep_y = keep_y - cls.keep_z = keep_z @render_comment def render(self) -> str: @@ -294,62 +285,130 @@ def render(self) -> str: if self.holes: children = children - self.holes if self.mirror_x: - children = children.x_mirror(center=self._center_x, keep=self.keep_x) + children = children.x_mirror(center=self._center_x) if self.mirror_y: - children = children.y_mirror(center=self._center_y, keep=self.keep_y) + children = children.y_mirror(center=self._center_y) if self.mirror_z: - children = children.z_mirror(center=self._center_z, keep=self.keep_z) + children = children.z_mirror(center=self._center_z) return Union(children, self.miscellaneous).set_modifier(self.modifier).render() @property def left(self) -> float: if self.mirror_x: - if self.keep_x: - return -max(abs(left(self.children)), abs(right(self.children))) return -right(self.children) return left(self.children) @property def right(self) -> float: if self.mirror_x: - if self.keep_x: - return max(abs(left(self.children)), abs(right(self.children))) return -left(self.children) return right(self.children) @property def back(self) -> float: if self.mirror_y: - if self.keep_y: - return -max(abs(back(self.children)), abs(front(self.children))) return -front(self.children) return back(self.children) @property def front(self) -> float: if self.mirror_y: - if self.keep_y: - return max(abs(back(self.children)), abs(front(self.children))) return -back(self.children) return front(self.children) @property def bottom(self) -> float: if self.mirror_z: - if self.keep_z: - return -max(abs(bottom(self.children)), abs(top(self.children))) return -top(self.children) return bottom(self.children) @property def top(self) -> float: if self.mirror_z: - if self.keep_z: - return max(abs(bottom(self.children)), abs(top(self.children))) return -bottom(self.children) return top(self.children) +class SymmetricPart(Part): + mirror_x: bool + mirror_y: bool + mirror_z: bool + _center_x: float + _center_y: float + _center_z: float + + def __init_subclass__( + cls, + *, + x: bool = False, + y: bool = False, + z: bool = False, + center_x: float = 0, + center_y: float = 0, + center_z: float = 0, + **kwargs: Any, + ): + super().__init_subclass__(**kwargs) + cls.mirror_x = x + cls.mirror_y = y + cls.mirror_z = z + cls._center_x = center_x + cls._center_y = center_y + cls._center_z = center_z + + @render_comment + def render(self) -> str: + if not self.children: + msg = "This part has no children" + raise RuntimeError(msg) + children: Object = Union(self.children) + if self.holes: + children = children - self.holes + if self.mirror_x: + children = children.x_symmetry(center=self._center_x) + if self.mirror_y: + children = children.y_symmetry(center=self._center_y) + if self.mirror_z: + children = children.z_symmetry(center=self._center_z) + return Union(children, self.miscellaneous).set_modifier(self.modifier).render() + + @property + def left(self) -> float: + if self.mirror_x: + return -max(abs(left(self.children)), abs(right(self.children))) + return left(self.children) + + @property + def right(self) -> float: + if self.mirror_x: + return max(abs(left(self.children)), abs(right(self.children))) + return right(self.children) + + @property + def back(self) -> float: + if self.mirror_y: + return -max(abs(back(self.children)), abs(front(self.children))) + return back(self.children) + + @property + def front(self) -> float: + if self.mirror_y: + return max(abs(back(self.children)), abs(front(self.children))) + return front(self.children) + + @property + def bottom(self) -> float: + if self.mirror_z: + return -max(abs(bottom(self.children)), abs(top(self.children))) + return bottom(self.children) + + @property + def top(self) -> float: + if self.mirror_z: + return max(abs(bottom(self.children)), abs(top(self.children))) + return top(self.children) + + from muscad.primitives import Square diff --git a/src/muscad/primitives.py b/src/muscad/primitives.py index ea0214a..bf8a02c 100644 --- a/src/muscad/primitives.py +++ b/src/muscad/primitives.py @@ -196,6 +196,13 @@ def __init__( self.faces = [list(face) for face in faces] self.convexity = convexity + self._left = min(p.x for p in self.points) + self._right = max(p.x for p in self.points) + self._back = min(p.y for p in self.points) + self._front = max(p.y for p in self.points) + self._bottom = min(p.z for p in self.points) + self._top = max(p.z for p in self.points) + @staticmethod def unpack_points(points: Iterable[Point3D | Sequence[float]]) -> Iterable[Point3D]: for point in points: diff --git a/src/muscad/utils/barrel.py b/src/muscad/utils/barrel.py new file mode 100644 index 0000000..673710e --- /dev/null +++ b/src/muscad/utils/barrel.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +from contextlib import suppress + +from muscad import Cylinder, Part, calc + + +class Barrel(Part): + def init( # type: ignore[override] + self, + d: float | None = None, + *, + r: float | None = None, + d2: float | None = None, + r2: float | None = None, + left: float | None = None, + center_x: float | None = None, + right: float | None = None, + width: float | None = None, + back: float | None = None, + center_y: float | None = None, + front: float | None = None, + depth: float | None = None, + bottom: float | None = None, + center_z: float | None = None, + top: float | None = None, + height: float | None = None, + segments: int | None = None, + ) -> None: + match d, r: + case None, None: + # no explicit diameter or radius, try to deduce it from the other params + with suppress(ValueError): + left, center_x, right, width = calc(left, center_x, right, width) + with suppress(ValueError): + back, center_y, front, depth = calc(back, center_y, front) + match width, depth: + case None, None: + msg = ( + "No sufficient parameter to calculate the barrel diameter." + " Please provide either a diameter `d`, a radius `r`," + "or at least 2 two parameters on axis X or Y." + ) + raise ValueError(msg) + case float(), None: + d = width + case None, float(): + d = depth + case float(), float() if width != depth: + msg = ( + f"width ({width}mm) is different from depth ({depth}mm)." + " I don't know which one to use as diameter." + ) + raise ValueError(msg) + case float(), None: + pass + case None, float() if r is not None: + d = r * 2 + case float(), float() if r is not None and d is not None and r * 2 != d: + msg = ( + "diameter `d` and radius `r` must be consistent." "Please fix, or provide either one or the other." + ) + raise ValueError(msg) + + assert d is not None + + match r2, d2: + case None, float(): + pass + case float(), None if r2 is not None: + d2 = r2 * 2 + case float(), float() if d2 is not None and r2 is not None and d2 != r2 * 2: + msg = ( + f"inconsistent top diameter `d2` ({d2}mm) and top radius ({r2}mm)." + "Please fix, or provide either one or the other." + ) + raise ValueError(msg) + + match width, d: + case None, float(): + width = d + case float(), float() if width != d: + msg = f"diameter `d` ({d}mm) is different from width ({width}mm)." + raise ValueError(msg) + + match depth, d: + case None, float(): + depth = d + case float(), None: + assert False + case float(), float() if depth != d: + msg = f"diameter `d` ({d}mm) is different from depth ({depth}mm)." + raise ValueError(msg) + + self._left, self._center_x, self._right, self._width = calc(left, center_x, right, width) + self._back, self._center_y, self._front, self._depth = calc(back, center_y, front, depth) + self._bottom, self._center_z, self._top, self._height = calc(bottom, center_z, top, height) + self.cylinder = Cylinder(d=d, d2=d2, h=self._height, segments=segments).align( + center_x=self._center_x, center_y=self._center_y, center_z=self._center_z + ) + + @property + def left(self) -> float: + return self._left + + @property + def right(self) -> float: + return self._right + + @property + def back(self) -> float: + return self._back + + @property + def front(self) -> float: + return self._front + + @property + def bottom(self) -> float: + return self._bottom + + @property + def top(self) -> float: + return self._top diff --git a/src/muscad/utils/star.py b/src/muscad/utils/star.py new file mode 100644 index 0000000..86e7ec7 --- /dev/null +++ b/src/muscad/utils/star.py @@ -0,0 +1,7 @@ +from muscad import Part + + +class Star(Part): + def init(self, part: Part, n: int) -> None: # type: ignore[override] + for i in range(n): + self.add_child(part.z_rotate(i * 360 / n)) diff --git a/src/muscad/utils/tube.py b/src/muscad/utils/tube.py index 2c9a7e9..4c1d18c 100644 --- a/src/muscad/utils/tube.py +++ b/src/muscad/utils/tube.py @@ -2,7 +2,7 @@ import contextlib -from muscad import Cylinder, E, Hole, Misc, Object, Part, Volume, calc +from muscad import Cylinder, E, Hole, Misc, Object, Part, calc class Tube(Part): @@ -62,6 +62,8 @@ def add_corner( top_distance: float = 0, ) -> Tube: """Turns a quarter of this tube into a cube.""" + from muscad import Volume + self.add_misc( Volume( left=self.center_x, @@ -76,6 +78,8 @@ def add_corner( def cut_corner(self, angle: float = 0) -> Tube: """Removes a quarter of this tube.""" + from muscad import Volume + self.add_hole( Volume( left=self.center_x, @@ -90,6 +94,8 @@ def cut_corner(self, angle: float = 0) -> Tube: def add_side(self, angle: float = 0, distance: float = 0) -> Tube: """Turns a quarter of this tube into a cube.""" + from muscad import Volume + self.add_misc( Volume( left=self.center_x, diff --git a/src/muscad/vitamins/boards.py b/src/muscad/vitamins/boards.py index 9051d3c..6921a49 100644 --- a/src/muscad/vitamins/boards.py +++ b/src/muscad/vitamins/boards.py @@ -132,7 +132,7 @@ def lcd12864(cls, bolt: Object | None = None) -> Self: top=lcd.lower_board.bottom + E, ) .fillet_height(r=1) - .x_mirror(lcd.lower_board.center_x, keep=True) + .x_symmetry(lcd.lower_board.center_x) .misc() ) return lcd diff --git a/src/muscad/vitamins/endstops.py b/src/muscad/vitamins/endstops.py index c04c640..0ff8355 100644 --- a/src/muscad/vitamins/endstops.py +++ b/src/muscad/vitamins/endstops.py @@ -179,7 +179,7 @@ def init(self, bolt: Object = Bolt.M3(10).add_nut(-3), z_offset: float = -3) -> center_y=self.pcb.front - 2.5, ) .z_translate(z_offset) - .x_mirror(self.switch.center_x, keep=True) + .x_symmetry(self.switch.center_x) .misc() ) @@ -188,9 +188,9 @@ class BLTouchClassic(Part): attachment = ( Hull( Surface.square(width=6, depth=11.53), - Surface.circle(radius=4, center_x=9).x_mirror(0, keep=True), + Surface.circle(radius=4, center_x=9).x_symmetry(), ) - - Surface.circle(diameter=3.2, center_x=9).x_mirror(0, keep=True) + - Surface.circle(diameter=3.2, center_x=9).x_symmetry() ).z_linear_extrude(2.3) cylinder = Tube(diameter=11, top=attachment.bottom, height=7.7) body = Tube(diameter=13, top=cylinder.bottom, height=26.3) & Volume( @@ -201,4 +201,4 @@ class BLTouchClassic(Part): def init( # type: ignore[override] self, bolt: Object = Bolt.M3(20).add_nut(-4, inline_clearance_size=10) ) -> None: - self.bolts = bolt.align(center_x=9, bottom=self.attachment.bottom - 3).x_mirror(0, keep=True).misc() + self.bolts = bolt.align(center_x=9, bottom=self.attachment.bottom - 3).x_symmetry().misc() diff --git a/src/muscad/vitamins/threads.py b/src/muscad/vitamins/threads.py new file mode 100644 index 0000000..a38a51c --- /dev/null +++ b/src/muscad/vitamins/threads.py @@ -0,0 +1,213 @@ +# based on NUT JOB by Mike Mattala: https://www.thingiverse.com/thing:193647 +from __future__ import annotations + +from math import floor, pi + +from muscad import ( + Barrel, + Cylinder, + Object, + Part, + Polygon, + Polyhedron, + Union, + cos, + sin, +) + + +class ThreadShape(Part): + def init( # type: ignore[override] + self, + *, + length: float, + outer_diameter: float, + inner_diameter: float, + segments: int, + step: float, + top_countersink: bool = False, + bottom_countersink: bool = False, + ) -> None: + if not top_countersink and not bottom_countersink: + self.shape = Barrel(height=length, d=outer_diameter, segments=segments) + else: + self.shape = Barrel(bottom=step / 2, height=length - step + 0.005, d=outer_diameter, segments=segments) + self.top_countersink = ( + Barrel(bottom=self.shape.top, height=step / 2, d=outer_diameter, d2=inner_diameter, segments=segments) + if top_countersink + else Barrel(bottom=self.shape.top, height=step / 2, d=outer_diameter, segments=segments) + ) + self.bottom_countersink = ( + Barrel(height=step / 2, top=self.shape.bottom, d=outer_diameter, d2=inner_diameter, segments=segments) + if bottom_countersink + else Barrel(height=step / 2, top=self.shape.bottom, d=outer_diameter, segments=segments) + ) + + +class ScrewThread(Part): + def init( # type: ignore[override] + self, + *, + outer_diameter: float, + length: float, + step: int, + top_countersink: bool = False, + bottom_countersink: bool = False, + shape_degrees: float = 45, + resolution: float = 0.5, + ) -> None: + inner_diameter = outer_diameter - step * cos(shape_degrees) / sin(shape_degrees) + segments = floor(pi * outer_diameter / resolution) + ttn = round(length / step + 1) + lfxy = 360 / segments + zt = step / segments + self.thread = ThreadShape( + length=length, + outer_diameter=outer_diameter, + inner_diameter=inner_diameter, + segments=segments, + step=step, + top_countersink=top_countersink, + bottom_countersink=bottom_countersink, + ) & Union( + Polyhedron( + points=[ + [0, 0, (i - 1) * step], + [ + inner_diameter / 2 * cos(j * lfxy), + inner_diameter / 2 * sin(j * lfxy), + i * step + j * zt - step, + ], + [ + inner_diameter / 2 * cos((j + 1) * lfxy), + inner_diameter / 2 * sin((j + 1) * lfxy), + i * step + (j + 1) * zt - step, + ], + [0, 0, i * step], + [ + outer_diameter / 2 * cos(j * lfxy), + outer_diameter / 2 * sin(j * lfxy), + i * step + j * zt - step / 2, + ], + [ + outer_diameter / 2 * cos((j + 1) * lfxy), + outer_diameter / 2 * sin((j + 1) * lfxy), + i * step + (j + 1) * zt - step / 2, + ], + [ + inner_diameter / 2 * cos(j * lfxy), + inner_diameter / 2 * sin(j * lfxy), + i * step + j * zt, + ], + [ + inner_diameter / 2 * cos((j + 1) * lfxy), + inner_diameter / 2 * sin((j + 1) * lfxy), + i * step + (j + 1) * zt, + ], + [0, 0, (i + 1) * step], + ], + faces=[ + [1, 0, 3], + [1, 3, 6], + [6, 3, 8], + [1, 6, 4], + [0, 1, 2], + [1, 4, 2], + [2, 4, 5], + [5, 4, 6], + [5, 6, 7], + [7, 6, 8], + [7, 8, 3], + [0, 2, 3], + [3, 2, 7], + [7, 2, 5], + ], + ) + for i in range(ttn) + for j in range(segments) + ) + + +class HexScrew(Part): + def init( # type: ignore[override] + self, + *, + thread_outer_diameter: float, + thread_step: float, + step_shape_degrees: float, + thread_length: float, + resolution: int, + head_diameter: float, + head_height: float, + non_thread_length: float, + non_thread_diameter: float | None = None, + countersink: bool = True, + ) -> None: + ntd = thread_outer_diameter - thread_step * cos(step_shape_degrees) / sin(step_shape_degrees) + self.head = HexHead(head_height, head_diameter).align(bottom=0) + non_thread: Object + if non_thread_length == 0: + non_thread = Cylinder(h=0.01, d=ntd) + else: + if non_thread_diameter == -1: + non_thread = Cylinder(h=non_thread_length + 0.01, d=ntd) + elif non_thread_diameter == 0: + non_thread = Cylinder(h=non_thread_length - thread_step / 2, d=thread_outer_diameter) + Cylinder( + h=thread_step / 2, d=thread_outer_diameter, d2=ntd + ) + else: + if non_thread_diameter is None: + non_thread_diameter = ntd + non_thread = Cylinder(h=non_thread_length, d=non_thread_diameter) + + self.non_thread = non_thread.align(bottom=self.head.top) + self.screw = ScrewThread( + outer_diameter=thread_outer_diameter, + step=thread_step, + shape_degrees=step_shape_degrees, + length=thread_length, + resolution=resolution, + top_countersink=countersink, + bottom_countersink=False, + ).align(bottom=self.non_thread.top) + + +class HexHead(Part): + def init(self, height: float, diameter: float) -> None: # type: ignore[override] + d0 = diameter / sin(60) + x0 = 0 + x1 = diameter / 2 + x2 = x1 + height / 2 + y0 = 0 + y1 = height / 2 + y2 = height + + self.head = Barrel(bottom=0, height=height, d=d0, segments=6) & Polygon( + (x0, y0), (x1, y0), (x2, y1), (x1, y2), (x0, y2) + ).z_rotational_extrude(bottom=0) + + +class HexNut(Part): + def init( # type: ignore[override] + self, + *, + diameter: float, + height: float, + thread_outer_diameter: float, + thread_step: float = 2, + step_shape_degrees: float = 45, + resolution: float = 0.5, + ) -> None: + self.nut = HexHead(height, diameter) + self.countersinks = ~Barrel( + bottom=self.nut.bottom - 0.1, + height=thread_step / 2, + d=thread_outer_diameter, + d2=thread_outer_diameter - (diameter / 2 + 0.1) * cos(step_shape_degrees) / sin(step_shape_degrees), + ).z_mirror(center=self.nut.center_z) + self.bore = ~ScrewThread( + outer_diameter=thread_outer_diameter, length=height, step=thread_step, shape_degrees=step_shape_degrees + ).align(center_z=self.nut.center_z) + + +HexNut(diameter=9, height=4, thread_outer_diameter=6.2).render_to_file() diff --git a/tests/test_base.py b/tests/test_base.py index 657ab0d..ae2a64b 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -201,22 +201,24 @@ def test_mirror() -> None: assert z_mirrored_circle.left == -5 assert z_mirrored_circle.right == 5 - x_mirrored_circle = Circle(d=10).align(left=0).x_mirror(keep=True) +def test_symmetry() -> None: + """Test for Object.?_symmetry()""" + x_mirrored_circle = Circle(d=10).align(left=0).x_symmetry() assert x_mirrored_circle.left == -10 assert x_mirrored_circle.right == 10 - y_mirrored_circle = Circle(d=10).align(back=0).y_mirror(keep=True) + y_mirrored_circle = Circle(d=10).align(back=0).y_symmetry() assert y_mirrored_circle.back == -10 assert y_mirrored_circle.front == 10 - z_mirrored_circle = Sphere(d=10).align(bottom=0).z_mirror(keep=True) + z_mirrored_circle = Sphere(d=10).align(bottom=0).z_symmetry() assert z_mirrored_circle.bottom == -10 assert z_mirrored_circle.top == 10 def test_hull() -> None: """Test for Hull.""" - hulled_mirrored_circle = Circle(d=10).align(left=0).x_mirror(keep=True).hull() + hulled_mirrored_circle = Circle(d=10).align(left=0).x_symmetry().hull() assert compare_str( hulled_mirrored_circle, """hull() diff --git a/tests/test_part.py b/tests/test_part.py index 515cc90..9658da0 100644 --- a/tests/test_part.py +++ b/tests/test_part.py @@ -1,5 +1,5 @@ """Tests for the Part class.""" -from muscad import Cube, MirroredPart, Part +from muscad import Cube, Part, SymmetricPart from tests.conftest import compare_str @@ -23,13 +23,13 @@ def test_part() -> None: assert compare_str(part, "cube(size=[8, 10, 12], center=true);") -def test_mirrored_part() -> None: - """Tests for MirroredPart.""" +def test_symmetric_part() -> None: + """Tests for SymmetricPart.""" - class TestMirroredPart(MirroredPart, x=True, keep_x=True): + class TestSymmetricPart(SymmetricPart, x=True): cube = Cube(8, 10, 12).align(left=0, back=0, bottom=0) - part = TestMirroredPart() + part = TestSymmetricPart() assert part.left == -8 assert part.right == 8 diff --git a/tests/test_utils/test_surface.py b/tests/test_utils/test_surface.py index 3315f71..5658fe4 100644 --- a/tests/test_utils/test_surface.py +++ b/tests/test_utils/test_surface.py @@ -10,7 +10,7 @@ def test_heart_z() -> None: Circle(d=20).align(right=3, front=30), Circle(d=2).align(center_x=0, back=0), ) - .x_mirror(keep=True) + .x_symmetry() .z_linear_extrude(bottom=-1, top=3) ) @@ -51,8 +51,7 @@ def test_spade_z() -> None: Square(1, 15).align(left=0, back=0), ) ) - .x_mirror(keep=True) - .z_linear_extrude(bottom=-1, top=4) + .x_symmetry() .z_linear_extrude(bottom=-1, top=4) ) assert compare_str( @@ -104,7 +103,7 @@ def test_heart_y() -> None: Circle(d=20).align(right=3, front=30), Circle(d=2).align(center_x=0, back=0), ) - .x_mirror(keep=True) + .x_symmetry() .y_linear_extrude(back=3, front=6) ) @@ -146,7 +145,7 @@ def test_spade_y() -> None: Square(1, 15).align(left=0, back=0), ), ) - .x_mirror(keep=True) + .x_symmetry() .y_linear_extrude(10, center_y=3) ) @@ -200,7 +199,7 @@ def test_heart_x() -> None: Circle(d=20).align(right=3, front=30), Circle(d=2).align(center_x=0, back=0), ) - .x_mirror(keep=True) + .x_symmetry() .x_linear_extrude(5, leftwards=True) ) @@ -242,7 +241,7 @@ def test_spade_x() -> None: Square(1, 15).align(left=0, back=0), ) ) - .x_mirror(keep=True) + .x_symmetry() .x_linear_extrude(9, left=-3) ) From 1d0a285b881e46f1e73ac064b2ffbd089aecc301 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Wed, 3 Jan 2024 20:18:10 +0100 Subject: [PATCH 07/13] deps update --- .pre-commit-config.yaml | 4 +- poetry.lock | 311 ++++++++++++++++++++-------------------- 2 files changed, 161 insertions(+), 154 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0300441..91442ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,14 +27,14 @@ repos: hooks: - id: blacken-docs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.6 + rev: v0.1.11 hooks: - id: ruff-format - id: ruff args: - --fix - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.8.0 hooks: - id: mypy additional_dependencies: diff --git a/poetry.lock b/poetry.lock index 67faa63..c8b8a98 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,37 +1,38 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "attrs" -version = "23.1.0" +version = "23.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, - {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]", "pre-commit"] +dev = ["attrs[tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] name = "bandit" -version = "1.7.5" +version = "1.7.6" description = "Security oriented static analyser for python code." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, - {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, + {file = "bandit-1.7.6-py3-none-any.whl", hash = "sha256:36da17c67fc87579a5d20c323c8d0b1643a890a2b93f00b3d1229966624694ff"}, + {file = "bandit-1.7.6.tar.gz", hash = "sha256:72ce7bc9741374d96fb2f1c9a8960829885f1243ffde743de70a19cee353e8f3"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" +GitPython = ">=3.1.30" PyYAML = ">=5.3.1" rich = "*" stevedore = ">=1.20.0" @@ -43,29 +44,33 @@ yaml = ["PyYAML"] [[package]] name = "black" -version = "23.11.0" +version = "23.12.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, - {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, - {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, - {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, - {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, - {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, - {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, - {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, - {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, - {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, - {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, - {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, - {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, - {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, - {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, - {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, - {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, - {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, + {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, + {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, + {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, + {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, + {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, + {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, + {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, + {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, + {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, + {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, + {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, + {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, + {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, + {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, + {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, + {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, + {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, + {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, + {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, + {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, + {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, + {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, ] [package.dependencies] @@ -79,7 +84,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] +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)"] @@ -121,63 +126,63 @@ files = [ [[package]] name = "coverage" -version = "7.3.2" +version = "7.4.0" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, - {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, - {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, - {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, - {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, - {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, - {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, - {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, + {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, + {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, + {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, + {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, + {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, + {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, + {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, + {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, + {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, + {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, + {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, + {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, + {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, + {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, + {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, + {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, + {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, + {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, + {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, + {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, + {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, + {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, + {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, + {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, + {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, + {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, + {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, ] [package.dependencies] @@ -188,13 +193,13 @@ toml = ["tomli"] [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.8" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] [[package]] @@ -210,13 +215,13 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.1.3" +version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] @@ -271,13 +276,13 @@ flake8 = ">=5.0.0" [[package]] name = "flake8-bugbear" -version = "23.9.16" +version = "23.12.2" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-bugbear-23.9.16.tar.gz", hash = "sha256:90cf04b19ca02a682feb5aac67cae8de742af70538590509941ab10ae8351f71"}, - {file = "flake8_bugbear-23.9.16-py3-none-any.whl", hash = "sha256:b182cf96ea8f7a8595b2f87321d7d9b28728f4d9c3318012d896543d19742cb5"}, + {file = "flake8-bugbear-23.12.2.tar.gz", hash = "sha256:32b2903e22331ae04885dae25756a32a8c666c85142e933f43512a70f342052a"}, + {file = "flake8_bugbear-23.12.2-py3-none-any.whl", hash = "sha256:83324bad4d90fee4bf64dd69c61aff94debf8073fbd807c8b6a36eec7a2f0719"}, ] [package.dependencies] @@ -354,13 +359,13 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre [[package]] name = "identify" -version = "2.5.31" +version = "2.5.33" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"}, - {file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"}, + {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, + {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, ] [package.extras] @@ -425,38 +430,38 @@ files = [ [[package]] name = "mypy" -version = "1.6.1" +version = "1.8.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"}, - {file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"}, - {file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"}, - {file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"}, - {file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"}, - {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"}, - {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"}, - {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"}, - {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"}, - {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"}, - {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"}, - {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"}, - {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"}, - {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"}, - {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"}, - {file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"}, - {file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"}, - {file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"}, - {file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"}, - {file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"}, - {file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"}, - {file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"}, - {file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"}, - {file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"}, - {file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"}, - {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"}, - {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, ] [package.dependencies] @@ -467,6 +472,7 @@ typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] install-types = ["pip"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] @@ -507,13 +513,13 @@ files = [ [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] @@ -543,13 +549,13 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "3.11.0" +version = "4.1.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, + {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, + {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, ] [package.extras] @@ -645,27 +651,28 @@ files = [ [[package]] name = "pygments" -version = "2.16.1" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -761,13 +768,13 @@ docutils = ">=0.11,<1.0" [[package]] name = "rich" -version = "13.6.0" +version = "13.7.0" 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.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, - {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, ] [package.dependencies] @@ -843,17 +850,17 @@ files = [ [[package]] name = "setuptools" -version = "68.2.2" +version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, - {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -906,30 +913,30 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "virtualenv" -version = "20.24.6" +version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, - {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<4" +platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] From c8226486892a832f09dc5176c62cef1ce3d914bc Mon Sep 17 00:00:00 2001 From: Guillaume Date: Wed, 3 Jan 2024 21:38:07 +0100 Subject: [PATCH 08/13] last fixes for ruff --- docs/conf.py | 2 +- examples/muscad_printer.py | 2 +- pyproject.toml | 2 +- src/muscad/base.py | 10 ++++++++-- src/muscad/utils/__init__.py | 2 ++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0d5777b..1b43ae2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,7 +3,7 @@ project = "MuSCAD" author = "Guillaume Pujol" -copyright = f"{datetime.now(tz=UTC).year}, {author}" +copyright = f"{datetime.now(tz=UTC).year}, {author}" # noqa: A001 extensions = [ "sphinx.ext.autodoc", "sphinx.ext.napoleon", diff --git a/examples/muscad_printer.py b/examples/muscad_printer.py index d9b0ba5..042465f 100644 --- a/examples/muscad_printer.py +++ b/examples/muscad_printer.py @@ -3227,7 +3227,7 @@ class MotorCableGuide(Part): + thumbwheel_left + thumbwheel_right ).color("orange") - # + (frame_spacer + z_rod_spacer).color("green") + + (frame_spacer + z_rod_spacer).color("green") ).render_to_file("muscad_printer") glass_plate_corner_left_front.render_to_file() diff --git a/pyproject.toml b/pyproject.toml index 6cd1dd6..657669f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,7 @@ exclude = [ "tests" ] extend-ignore = [ - "D100", "D101", "D102", "D103", "D104", "D105", "E402", "N802", "N803" + "D100", "D101", "D102", "D103", "D104", "D105", "D107", "E402", "F403", "N802", "N803" ] diff --git a/src/muscad/base.py b/src/muscad/base.py index d55749d..eada666 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -1641,10 +1641,16 @@ def wrapper( from_, to = to, from_ if from_ is not None and _from != from_: - msg = f"calculated from_ incompatible with specified from_ (specified: {from_}, calculated: {_from}, difference={from_ - _from})" + msg = ( + "calculated from_ incompatible with specified from_" + " (specified: {from_}, calculated: {_from}, difference={from_ - _from})" + ) raise ValueError(msg) if to is not None and _to != to: - msg = f"calculated to incompatible with specified to (specified: {to}, calculated: {_to}, difference={to - _to})" + msg = ( + "calculated to incompatible with specified to" + " (specified: {to}, calculated: {_to}, difference={to - _to})" + ) raise ValueError(msg) return _from, _center, _to, _distance diff --git a/src/muscad/utils/__init__.py b/src/muscad/utils/__init__.py index b8dae1c..7c207ea 100644 --- a/src/muscad/utils/__init__.py +++ b/src/muscad/utils/__init__.py @@ -1,5 +1,7 @@ +from .barrel import * from .fillet import * from .shapes import * from .stack import * +from .star import * from .surface import * from .volume import * From be68c4d96786ddfe79585bd4d8aa7c8df2c1ea81 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 1 Feb 2024 20:47:48 +0100 Subject: [PATCH 09/13] fix reverse top and front left offsets --- examples/handle_cap.py | 105 +++++++++++ examples/migo_link.py | 78 ++++++++ examples/muscad_printer.py | 368 ++++++++++++++++++++++++++++++++++++- examples/sd_usb_holder.py | 42 +++++ src/muscad/utils/volume.py | 4 +- 5 files changed, 593 insertions(+), 4 deletions(-) create mode 100644 examples/handle_cap.py create mode 100644 examples/migo_link.py create mode 100644 examples/sd_usb_holder.py diff --git a/examples/handle_cap.py b/examples/handle_cap.py new file mode 100644 index 0000000..e8d4823 --- /dev/null +++ b/examples/handle_cap.py @@ -0,0 +1,105 @@ +from muscad import Cylinder, E, Part, Text, Volume + + +class FilletMeasurement(Part): + body = ( + Volume(width=50, depth=50, height=2) + .fillet_height(6, right=True, front=True) + .fillet_height(7, right=True, back=True) + .fillet_height(8, left=True, back=True) + .fillet_height(9, left=True, front=True) + ) + + l6 = ( + Text("6", size=4, halign="center", valign="center") + .z_rotate(-45) + .align( + center_x=body.right - 4, + center_y=body.front - 4, + ) + .z_linear_extrude(1, bottom=body.top) + ) + l7 = ( + Text("7", size=4, halign="center", valign="center") + .z_rotate(-135) + .align(center_x=body.right - 4, center_y=body.back + 4) + .z_linear_extrude(1, bottom=body.top) + ) + l8 = ( + Text("8", size=4, halign="center", valign="center") + .z_rotate(135) + .align( + center_x=body.left + 4, + center_y=body.back + 4, + ) + .z_linear_extrude(1, bottom=body.top) + ) + l9 = ( + Text("9", size=4, halign="center", valign="center") + .z_rotate(45) + .align(center_x=body.left + 4.5, center_y=body.front - 4.5) + .z_linear_extrude(1, bottom=body.top) + ) + + d13 = ~Cylinder(d=13 * 2, h=body.width + 1).align( + center_x=body.center_x, center_y=body.front + 3, center_z=body.center_z + ) + l13 = ( + Text("13", size=4, halign="center", valign="center") + .align(center_x=d13.center_x, center_y=d13.back - 2) + .z_linear_extrude(1, bottom=body.top) + ) + + d14 = ~Cylinder(d=14 * 2, h=body.width + 1).align( + center_x=body.right + 3, center_y=body.center_y, center_z=body.center_z + ) + l14 = ( + Text("14", size=4, halign="center", valign="center") + .front_to_right() + .align(center_x=d14.left - 2, center_y=d14.center_y) + .z_linear_extrude(1, bottom=body.top) + ) + d15 = ~Cylinder(d=15 * 2, h=body.width + 1).align( + center_x=body.center_x, center_y=body.back - 3, center_z=body.center_z + ) + l15 = ( + Text("15", size=4, halign="center", valign="center") + .front_to_back() + .align(center_x=d15.center_x, center_y=d15.front + 2) + .z_linear_extrude(1, bottom=body.top) + ) + + d16 = ~Cylinder(d=16 * 2, h=body.width + 1).align( + center_x=body.left - 3, center_y=body.center_y, center_z=body.center_z + ) + l16 = ( + Text("16", size=4, halign="center", valign="center") + .front_to_left() + .align(center_x=d16.right + 2, center_y=d16.center_y) + .z_linear_extrude(1, bottom=body.top) + ) + + +FilletMeasurement().render_to_file() + + +class HandleCap(Part): + bulge = ~Volume(width=27, depth=60, height=9).fillet_height(13) + handle = ~Cylinder(d=23, h=10).align(center_x=bulge.center_x, center_y=bulge.center_y, bottom=bulge.top - E) + + cap = ( + Volume( + left=bulge.left - 1.6, + right=bulge.right + 1.6, + back=bulge.back - 1.6, + front=bulge.front + 1.6, + bottom=bulge.bottom + E, + top=bulge.top + 2, + ) + .fillet_height(13) + .chamfer_depth(2, top=True) + .chamfer_width(2, top=True) + ) + + +HandleCap().render_to_file() diff --git a/examples/migo_link.py b/examples/migo_link.py new file mode 100644 index 0000000..209539c --- /dev/null +++ b/examples/migo_link.py @@ -0,0 +1,78 @@ +from muscad import EE, TT, E, Part, Volume + + +class MigoLinkSupportSide(Part): + body = Volume(width=48, depth=40, height=2).fillet_height() + clips = ( + Volume(width=body.width - 4, depth=4, height=4, center_x=body.center_x, bottom=body.top, back=body.back + 2) + .fillet_width(2, top=True) + .fillet_height() + .y_symmetry(body.center_y) + ) + cutout = ~Volume( + width=10 + TT, + center_x=body.center_x - 8, + depth=2 + TT, + center_y=body.center_y, + center_z=body.center_z, + height=body.height + EE, + ).x_symmetry(body.center_x) + + +left_side = MigoLinkSupportSide() +right_side = MigoLinkSupportSide().bottom_to_top().align(top=left_side.bottom + 147) + + +class MigoLinkSupportJunction(Part): + body = Volume(width=38, depth=2, bottom=left_side.body.top, top=right_side.body.bottom) + arms = ( + Volume( + width=10 - TT, + center_x=left_side.center_x - 8, + depth=2 - TT, + center_y=body.center_y, + top=body.bottom, + height=5, + ) + .fillet_depth(bottom=True) + .x_symmetry(body.center_x) + .z_symmetry(body.center_z) + ) + gap = ~Volume( + top=left_side.bottom - TT, height=1, center_x=body.center_x, width=10, center_y=body.center_y, depth=3 + ).z_mirror(body.center_z) + holes1 = ~Volume( + width=26, depth=3, height=50, center_x=body.center_x, center_y=body.center_y, top=body.center_z - 10 + ).fillet_depth(4).z_symmetry(body.center_z) + holes2 = ~Volume( + width=10, depth=3, height=10, left=body.left - E, center_y=body.center_y, center_z=body.center_z + ).fillet_depth(right=True).reverse_fillet_left(4, top=True, bottom=True).x_symmetry(body.center_x) + + +junction = MigoLinkSupportJunction() + + +class Stop(Part): + handle = Volume( + center_x=junction.gap.center_x, + width=junction.gap.width + 4, + front=junction.back, + depth=4, + center_z=junction.gap.center_z, + height=1.2, + ).fillet_height(back=True) + stop = Volume( + center_x=junction.gap.center_x, + width=junction.gap.width, + back=handle.front, + depth=5, + center_z=junction.gap.center_z, + height=junction.gap.height - TT, + ).fillet_height(front=True) + + +stop = Stop() + +(left_side + right_side + junction + stop).render_to_file("migo_link") +left_side.render_to_file("migo_side") +junction.front_to_bottom().render_to_file("migo_junction") diff --git a/examples/muscad_printer.py b/examples/muscad_printer.py index 042465f..9178678 100644 --- a/examples/muscad_printer.py +++ b/examples/muscad_printer.py @@ -82,7 +82,6 @@ BED_CENTER_Y = 0 BED_CENTER_Z = 0 - EXTRUSION_SIZE = 30 X_EXTRUSION_LENGTH = BED_WIDTH + 140 @@ -714,6 +713,174 @@ def __stl__(self) -> Object: x_carriage = XCarriage() +class XCarriageDirectDrive(Part): + x_bearing_top_left = ~gantry.x_bearing_top_left + x_bearing_top_right = ~gantry.x_bearing_top_right + x_bearing_bottom = ~gantry.x_bearing_bottom + + body = Volume( + left=x_bearing_top_left.left + E, + right=x_bearing_top_right.right - E, + front=x_bearing_bottom.back - E, + depth=8, + bottom=gantry.x_bearing_bottom.top + 1, + top=x_bearing_top_left.top - E, + ).fillet_depth() + Volume( + left=x_bearing_bottom.left, + right=x_bearing_bottom.right, + front=x_bearing_bottom.back - E, + depth=8, + bottom=x_bearing_bottom.bottom + E, + top=x_bearing_bottom.top + 1, + ).fillet_depth(bottom=True, left=True).reverse_fillet_top(left=True, right=True) + + center_pulleys_bolt = ~Bolt.M3(20, head_clearance=100).add_nut( + -1, inline_clearance_size=10 + ).bottom_to_front().align(center_x=body.center_x, back=body.back, center_z=body.center_z) + + extruder = ( + ~E3Dv6Extruder() + .z_rotate(-90) + .align( + center_x=body.center_x, + center_y=body.back - 13, + top=body.center_z + 10, + ) + .debug() + ) + + nema = ( + ~StepperMotor.nema17() + .top_to_front() + .align(front=extruder.center_y - 8, bottom=extruder.top - 3, center_x=extruder.center_x + 7) + .debug() + ) + blower = ( + ~Blower.blower50x50x15(bolt=Bolt.M3(20).add_nut(-E, inline_clearance_size=20)) + .x_rotate(-90) + .z_rotate(-90) + .align( + left=body.left, + front=body.back - 2, + bottom=extruder.top - 3, + ) + .debug() + ) + + fan = ( + ~Fan.fan40x40x20(bolt=None) + .add_bolts( + bolt=Bolt.M3(30).add_nut(-8.5, side_clearance_size=10, angle=180, T=0.1), + spacing=32, + ) + .x_rotate(90) + .z_rotate(180) + .align( + center_x=nema.center_x, + back=nema.back, + top=nema.bottom - 1, + ) + .debug() + ) + + extruder_holder = Volume( + left=fan.left, + right=fan.right, + front=body.back, + back=extruder.center_y - 1, + center_z=body.center_z, + bottom=x_bearing_bottom.top + 1, + ).fillet_depth() + + cable_guide_top = ( + Volume( + center_x=body.center_x, + width=24, + back=body.back, + front=body.front, + bottom=body.top - E, + top=frame.y_extrusion_right_top.top - 1, + ) + .reverse_fillet_bottom(left=True, right=True) + .fillet_depth(r=6, top=True) + ) + + cable_guide_bolts = ( + ~Bolt.M3(10) + .add_nut(-T, inline_clearance_size=10) + .bottom_to_back() + .align( + center_x=cable_guide_top.center_x, + back=cable_guide_top.back - E, + center_z=cable_guide_top.top - 6, + ) + .z_mirror(cable_guide_top.top - 10) + .debug() + ) + + cable_guide_side = ( + Volume( + left=gantry.x_bearing_bottom.right, + right=body.right, + back=body.back, + front=body.front, + bottom=body.bottom, + height=12, + ) + .fillet_depth(right=True) + .reverse_fillet_left(top=True) + ) + cable_hole = ~Volume( + right=body.right + E, + width=10, + front=x_bearing_bottom.front, + back=body.back - 1, + bottom=body.bottom + 3, + height=6, + ).fillet_depth(2) + + anti_warp = ~( + Volume( + center_x=body.center_x, + width=1, + front=body.front + E, + depth=1, + top=cable_guide_top.top - 1, + bottom=body.bottom + 1, + ).fillet_height(0.5, front=True) + + Volume( + left=body.left + 1, + right=body.right - 1, + front=body.front + E, + depth=1, + top=x_bearing_top_left.bottom, + height=1, + ).fillet_width(0.5, front=True) + + Volume( + left=body.left + 1, + right=body.right - 1, + front=body.front + E, + depth=1, + bottom=x_bearing_bottom.top, + height=1, + ).fillet_width(0.5, front=True) + + Volume( + left=body.left + 1, + right=body.right - 1, + front=body.front + E, + depth=1, + bottom=x_bearing_top_left.top, + height=1, + ).fillet_width(0.5, front=True) + ) + + def __stl__(self) -> Object: + return self.back_to_top() + + +x_carriage_direct_drive = XCarriageDirectDrive() + + class XAxisPulleys(Part): x_rod_bottom = ~gantry.x_rod_bottom center_pulleys_bolt = x_carriage.center_pulleys_bolt @@ -1007,6 +1174,191 @@ def __stl__(self) -> Object: tunnel = Tunnel() +class ExtruderClampDirectDrive(Part): + _extruder_holder = x_carriage_direct_drive.extruder_holder + + extruder = x_carriage_direct_drive.extruder + + fan = x_carriage_direct_drive.fan + # sensor = ( + # ~InductionSensor.LJ12A3() + # .align( + # center_x=extruder.center_x - 35, + # front=x_carriage.body.back - 3, + # bottom=extruder.bottom + 4, + # ) + # .debug() + # ) + + clamp = Volume( + left=_extruder_holder.left, + right=_extruder_holder.right, + front=_extruder_holder.back - 0.5, + back=fan.front + T, + bottom=_extruder_holder.bottom - 29, + top=_extruder_holder.top, + ).fillet_depth() + + tunnel = ~Hull( + Volume( + center_x=clamp.center_x, + width=36, + front=clamp.back, + back=clamp.back - 1, + top=clamp.top - 7, + bottom=clamp.bottom + 5, + ).fillet_depth(7), + Volume( + center_x=extruder.center_x, + width=20, + front=clamp.front + 1, + back=clamp.front, + top=clamp.top - 13, + bottom=clamp.bottom - 1, + ).fillet_depth(4), + ) + + cable_guide = Volume( + left=clamp.right, + width=3, + front=clamp.front, + back=clamp.back, + bottom=clamp.bottom, + height=6, + ).fillet_depth(1, right=True).reverse_fillet_left(top=True).reverse_fillet_bottom(left=True) - Volume( + left=clamp.right, + width=4, + front=clamp.front + E, + back=clamp.back - E, + center_z=clamp.bottom + 3, + height=3, + ).fillet_depth(1, left=True) + + # sensor_holder_up = ( + # Volume( + # right=clamp.left - TT, + # width=18, + # depth=18, + # center_y=sensor.center_y, + # top=clamp.top - 2, + # height=9, + # ) + # .fillet_height(6, left=True) + # .fillet_height(6, front=True, right=True) + # ) + + # sensor_holder_down = sensor_holder_up.z_mirror(center=clamp.center_z) + # + # sensor_arm_up = Volume( + # right=clamp.left - TT, + # width=3, + # back=clamp.back, + # front=sensor_holder_up.back, + # top=sensor_holder_up.top, + # bottom=sensor_holder_up.bottom, + # ).fillet_height(back=True, left=True).reverse_fillet_front(left=True) + Volume( + # right=clamp.left, + # width=1, + # front=clamp.front, + # back=clamp.back, + # top=sensor_holder_up.top, + # bottom=sensor_holder_up.bottom, + # ) + # + # sensor_arm_down = sensor_arm_up.z_mirror(center=clamp.center_z) + + def __stl__(self) -> Object: + return self.back_to_bottom() + + +extruder_clamp_direct_drive = ExtruderClampDirectDrive() + + +class TunnelDirectDrive(Part): + NOZZLE_SIZE = 0.4 + + extruder = x_carriage_direct_drive.extruder + + tunnel = Volume( + left=x_carriage_direct_drive.blower.blower.left + T, + right=x_carriage_direct_drive.blower.blower.right - T, + front=x_carriage_direct_drive.body.back - T, + back=x_carriage_direct_drive.blower.blower.back + T, + top=x_carriage_direct_drive.extruder_holder.center_z, + bottom=x_carriage_direct_drive.bottom, + ).fillet_height(NOZZLE_SIZE * 3) - Volume( + left=x_carriage_direct_drive.blower.blower.left + NOZZLE_SIZE * 3.1 + T, + right=x_carriage_direct_drive.blower.blower.right - NOZZLE_SIZE * 3.1 - T, + front=x_carriage_direct_drive.body.back - NOZZLE_SIZE * 3.1 - T, + back=x_carriage_direct_drive.blower.blower.back + NOZZLE_SIZE * 3.1 + T, + top=x_carriage_direct_drive.extruder_holder.center_z + E, + bottom=x_carriage_direct_drive.bottom - E, + ).fillet_height(NOZZLE_SIZE * 2) + + blower = Hull( + Volume( + center_x=extruder.center_x - 6, + width=5, + center_y=extruder.center_y, + depth=30, + bottom=extruder.bottom + 1, + height=E, + ).fillet_height(NOZZLE_SIZE * 3), + Volume( + left=tunnel.left, + right=tunnel.right, + back=tunnel.back, + front=tunnel.front, + top=tunnel.bottom, + height=E, + ).fillet_height(NOZZLE_SIZE * 2), + ) - Hull( + Volume( + center_x=extruder.center_x - 6, + width=3, + center_y=extruder.center_y, + depth=28, + bottom=extruder.bottom + 1 - E, + height=E, + ).fillet_height(NOZZLE_SIZE * 3), + Volume( + left=tunnel.left + NOZZLE_SIZE * 3.1, + right=tunnel.right - NOZZLE_SIZE * 3.1, + back=tunnel.back + NOZZLE_SIZE * 3.1, + front=tunnel.front - NOZZLE_SIZE * 3.1, + top=tunnel.bottom + E, + height=E, + ).fillet_height(NOZZLE_SIZE * 3), + ) + + bolt_left = ~Bolt.M2(10).bottom_to_back().align( + center_x=tunnel.left - 3, + center_y=x_carriage_direct_drive.body.back, + center_z=x_carriage_direct_drive.bottom + 20, + ) + bolt_holder_left = ( + Volume( + center_x=bolt_left.center_x, + right=tunnel.left + 0.5, + front=x_carriage_direct_drive.body.back - T, + depth=3, + center_z=bolt_left.center_z + 2, + height=15, + ) + .chamfer_depth(8, top=True, left=True) + .fillet_depth(bottom=True, left=True) + ) + + bolt_right = ~bolt_left.x_mirror(center=tunnel.center_x) + bolt_holder_right = bolt_holder_left.x_mirror(center=tunnel.center_x) + + def __stl__(self) -> Object: + return self.upside_down() + + +tunnel_direct_drive = TunnelDirectDrive() + + class CableChainCarriageAttachment(Part): bolts = x_carriage.cable_guide_bolts body = Volume( @@ -1016,7 +1368,9 @@ class CableChainCarriageAttachment(Part): depth=3, bottom=x_carriage.x_bearing_top_right.top + 1, top=x_carriage.cable_guide_top.top, - ).fillet_depth(6) + ).fillet_depth( + 6, + ) chain_attach = ( Tube( @@ -3142,6 +3496,16 @@ class MotorCableGuide(Part): + tunnel ).render_to_file("x_carriage_assembled") + ( + x_carriage_direct_drive + + x_axis_pulleys + + cable_clamp_back + + extruder_clamp_direct_drive + + cable_chain_carriage_attachment + + cable_clamp_top + + tunnel_direct_drive + ).render_to_file("x_carriage_direct_drive_assembled") + ( y_carriage_right + xy_stepper_mount_right diff --git a/examples/sd_usb_holder.py b/examples/sd_usb_holder.py new file mode 100644 index 0000000..3d4cdef --- /dev/null +++ b/examples/sd_usb_holder.py @@ -0,0 +1,42 @@ +from muscad import E, Part, Volume + + +class SdUsbHolder(Part): + sd_part = Volume(width=60, depth=30, height=16).fillet_depth(r=4, top=True) + sd_holes = ~Volume( + left=sd_part.center_x + 3, + width=25, + back=sd_part.back + 1.6, + depth=2.8, + bottom=sd_part.bottom + 2, + top=sd_part.top + E, + ).reverse_chamfer_top(left=True, right=True).array(6, y=4.8).x_symmetry(sd_part.center_x) + usb_part = Volume( + right=sd_part.right, width=40, back=sd_part.front, depth=80, bottom=sd_part.bottom, height=16 + ).fillet_depth(r=4, top=True) + usb_holes = ~Volume( + left=usb_part.center_x + 4, width=13, back=usb_part.back + 1, depth=6, top=usb_part.top + E, height=13 + ).reverse_chamfer_top(left=True, right=True).array(8, y=10).x_symmetry(usb_part.center_x) + microsd_part = ( + Volume( + left=sd_part.left, + right=usb_part.left, + back=sd_part.front, + front=usb_part.front, + bottom=sd_part.bottom, + height=10, + ) + .fillet_depth(4, top=True, left=True) + .reverse_fillet_right(top=True) + ) + microsd_holes = ~Volume( + center_x=microsd_part.center_x, + width=12, + back=microsd_part.back + 3, + depth=1.4, + bottom=usb_part.bottom + 2, + top=microsd_part.top + E, + ).reverse_chamfer_top(left=True, right=True).array(10, y=8) + + +SdUsbHolder().render_to_file(openscad=False) diff --git a/src/muscad/utils/volume.py b/src/muscad/utils/volume.py index b73d869..d7766e6 100644 --- a/src/muscad/utils/volume.py +++ b/src/muscad/utils/volume.py @@ -456,7 +456,7 @@ def _add_front_edges( self.front_left_edge = ( part.z_linear_extrude(self.height) .align( - right=self.left - E, + right=self.left + E, front=self.front, center_z=self.center_z, ) @@ -508,7 +508,7 @@ def _add_top_edges( part.y_linear_extrude(self.depth) .left_to_right() .align( - right=self.left - E, + right=self.left + E, center_y=self.center_y, top=self.top, ) From 5230a6857f1e23a060821bde6dfe75d05a24ad0a Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 1 Feb 2024 20:48:00 +0100 Subject: [PATCH 10/13] add Object.array() --- src/muscad/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/muscad/base.py b/src/muscad/base.py index eada666..5465aec 100644 --- a/src/muscad/base.py +++ b/src/muscad/base.py @@ -1052,6 +1052,10 @@ def divide( msg = "You must provide one plane to divide the Object, defined by one X, Y, Z coordinate" raise ValueError(msg) + def array(self, n: int, x: float = 0, y: float = 0, z: float = 0) -> Object: + """Multiply this object n times, translating each one by (x,y,z).""" + return Union(self.translate(x=x * i, y=y * i, z=z * i) for i in range(n)) + def __stl__(self) -> Object: return self # pragma: no cover From b9463de44f11612ac0e16fb3276d76cd8f1729fa Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 1 Feb 2024 20:48:07 +0100 Subject: [PATCH 11/13] update ruff --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 91442ac..c61d7b6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: hooks: - id: blacken-docs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.11 + rev: v0.1.15 hooks: - id: ruff-format - id: ruff From da5b44ae35f2f0b90b621eb55de359e059352646 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 12 Feb 2024 19:04:41 +0100 Subject: [PATCH 12/13] update deps --- .pre-commit-config.yaml | 2 +- poetry.lock | 270 +++++++++++++++++----------------------- pyproject.toml | 16 +-- 3 files changed, 124 insertions(+), 164 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c61d7b6..766ff60 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: hooks: - id: blacken-docs - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.15 + rev: v0.2.1 hooks: - id: ruff-format - id: ruff diff --git a/poetry.lock b/poetry.lock index c8b8a98..e89fdd6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -21,56 +21,56 @@ tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "p [[package]] name = "bandit" -version = "1.7.6" +version = "1.7.7" description = "Security oriented static analyser for python code." optional = false python-versions = ">=3.8" files = [ - {file = "bandit-1.7.6-py3-none-any.whl", hash = "sha256:36da17c67fc87579a5d20c323c8d0b1643a890a2b93f00b3d1229966624694ff"}, - {file = "bandit-1.7.6.tar.gz", hash = "sha256:72ce7bc9741374d96fb2f1c9a8960829885f1243ffde743de70a19cee353e8f3"}, + {file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"}, + {file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=3.1.30" PyYAML = ">=5.3.1" rich = "*" stevedore = ">=1.20.0" [package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +baseline = ["GitPython (>=3.1.30)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] toml = ["tomli (>=1.1.0)"] yaml = ["PyYAML"] [[package]] name = "black" -version = "23.12.1" +version = "24.1.1" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, + {file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"}, + {file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"}, + {file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"}, + {file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"}, + {file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"}, + {file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"}, + {file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"}, + {file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"}, + {file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"}, + {file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"}, + {file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"}, + {file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"}, + {file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"}, + {file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"}, + {file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"}, + {file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"}, + {file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"}, + {file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"}, + {file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"}, + {file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"}, + {file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"}, + {file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"}, ] [package.dependencies] @@ -126,63 +126,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.0" +version = "7.4.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36b0ea8ab20d6a7564e89cb6135920bc9188fb5f1f7152e94e8300b7b189441a"}, - {file = "coverage-7.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0676cd0ba581e514b7f726495ea75aba3eb20899d824636c6f59b0ed2f88c471"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0ca5c71a5a1765a0f8f88022c52b6b8be740e512980362f7fdbb03725a0d6b9"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7c97726520f784239f6c62506bc70e48d01ae71e9da128259d61ca5e9788516"}, - {file = "coverage-7.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:815ac2d0f3398a14286dc2cea223a6f338109f9ecf39a71160cd1628786bc6f5"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:80b5ee39b7f0131ebec7968baa9b2309eddb35b8403d1869e08f024efd883566"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5b2ccb7548a0b65974860a78c9ffe1173cfb5877460e5a229238d985565574ae"}, - {file = "coverage-7.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:995ea5c48c4ebfd898eacb098164b3cc826ba273b3049e4a889658548e321b43"}, - {file = "coverage-7.4.0-cp310-cp310-win32.whl", hash = "sha256:79287fd95585ed36e83182794a57a46aeae0b64ca53929d1176db56aacc83451"}, - {file = "coverage-7.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5b14b4f8760006bfdb6e08667af7bc2d8d9bfdb648351915315ea17645347137"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04387a4a6ecb330c1878907ce0dc04078ea72a869263e53c72a1ba5bbdf380ca"}, - {file = "coverage-7.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea81d8f9691bb53f4fb4db603203029643caffc82bf998ab5b59ca05560f4c06"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74775198b702868ec2d058cb92720a3c5a9177296f75bd97317c787daf711505"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76f03940f9973bfaee8cfba70ac991825611b9aac047e5c80d499a44079ec0bc"}, - {file = "coverage-7.4.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:485e9f897cf4856a65a57c7f6ea3dc0d4e6c076c87311d4bc003f82cfe199d25"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6ae8c9d301207e6856865867d762a4b6fd379c714fcc0607a84b92ee63feff70"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bf477c355274a72435ceb140dc42de0dc1e1e0bf6e97195be30487d8eaaf1a09"}, - {file = "coverage-7.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:83c2dda2666fe32332f8e87481eed056c8b4d163fe18ecc690b02802d36a4d26"}, - {file = "coverage-7.4.0-cp311-cp311-win32.whl", hash = "sha256:697d1317e5290a313ef0d369650cfee1a114abb6021fa239ca12b4849ebbd614"}, - {file = "coverage-7.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:26776ff6c711d9d835557ee453082025d871e30b3fd6c27fcef14733f67f0590"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:13eaf476ec3e883fe3e5fe3707caeb88268a06284484a3daf8250259ef1ba143"}, - {file = "coverage-7.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846f52f46e212affb5bcf131c952fb4075b55aae6b61adc9856222df89cbe3e2"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26f66da8695719ccf90e794ed567a1549bb2644a706b41e9f6eae6816b398c4a"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:164fdcc3246c69a6526a59b744b62e303039a81e42cfbbdc171c91a8cc2f9446"}, - {file = "coverage-7.4.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:316543f71025a6565677d84bc4df2114e9b6a615aa39fb165d697dba06a54af9"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bb1de682da0b824411e00a0d4da5a784ec6496b6850fdf8c865c1d68c0e318dd"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e8d06778e8fbffccfe96331a3946237f87b1e1d359d7fbe8b06b96c95a5407a"}, - {file = "coverage-7.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a56de34db7b7ff77056a37aedded01b2b98b508227d2d0979d373a9b5d353daa"}, - {file = "coverage-7.4.0-cp312-cp312-win32.whl", hash = "sha256:51456e6fa099a8d9d91497202d9563a320513fcf59f33991b0661a4a6f2ad450"}, - {file = "coverage-7.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd3c1e4cb2ff0083758f09be0f77402e1bdf704adb7f89108007300a6da587d0"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d1bf53c4c8de58d22e0e956a79a5b37f754ed1ffdbf1a260d9dcfa2d8a325e"}, - {file = "coverage-7.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:109f5985182b6b81fe33323ab4707011875198c41964f014579cf82cebf2bb85"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc9d4bc55de8003663ec94c2f215d12d42ceea128da8f0f4036235a119c88ac"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc6d65b21c219ec2072c1293c505cf36e4e913a3f936d80028993dd73c7906b1"}, - {file = "coverage-7.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a10a4920def78bbfff4eff8a05c51be03e42f1c3735be42d851f199144897ba"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b8e99f06160602bc64da35158bb76c73522a4010f0649be44a4e167ff8555952"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7d360587e64d006402b7116623cebf9d48893329ef035278969fa3bbf75b697e"}, - {file = "coverage-7.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29f3abe810930311c0b5d1a7140f6395369c3db1be68345638c33eec07535105"}, - {file = "coverage-7.4.0-cp38-cp38-win32.whl", hash = "sha256:5040148f4ec43644702e7b16ca864c5314ccb8ee0751ef617d49aa0e2d6bf4f2"}, - {file = "coverage-7.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9864463c1c2f9cb3b5db2cf1ff475eed2f0b4285c2aaf4d357b69959941aa555"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:936d38794044b26c99d3dd004d8af0035ac535b92090f7f2bb5aa9c8e2f5cd42"}, - {file = "coverage-7.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:799c8f873794a08cdf216aa5d0531c6a3747793b70c53f70e98259720a6fe2d7"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7defbb9737274023e2d7af02cac77043c86ce88a907c58f42b580a97d5bcca9"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1526d265743fb49363974b7aa8d5899ff64ee07df47dd8d3e37dcc0818f09ed"}, - {file = "coverage-7.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf635a52fc1ea401baf88843ae8708591aa4adff875e5c23220de43b1ccf575c"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:756ded44f47f330666843b5781be126ab57bb57c22adbb07d83f6b519783b870"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0eb3c2f32dabe3a4aaf6441dde94f35687224dfd7eb2a7f47f3fd9428e421058"}, - {file = "coverage-7.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bfd5db349d15c08311702611f3dccbef4b4e2ec148fcc636cf8739519b4a5c0f"}, - {file = "coverage-7.4.0-cp39-cp39-win32.whl", hash = "sha256:53d7d9158ee03956e0eadac38dfa1ec8068431ef8058fe6447043db1fb40d932"}, - {file = "coverage-7.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfd2a8b6b0d8e66e944d47cdec2f47c48fef2ba2f2dff5a9a75757f64172857e"}, - {file = "coverage-7.4.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:c530833afc4707fe48524a44844493f36d8727f04dcce91fb978c414a8556cc6"}, - {file = "coverage-7.4.0.tar.gz", hash = "sha256:707c0f58cb1712b8809ece32b68996ee1e609f71bd14615bd8f87a1293cb610e"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [package.dependencies] @@ -245,19 +245,19 @@ typing = ["typing-extensions (>=4.8)"] [[package]] name = "flake8" -version = "6.1.0" +version = "7.0.0" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"}, - {file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"}, + {file = "flake8-7.0.0-py2.py3-none-any.whl", hash = "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"}, + {file = "flake8-7.0.0.tar.gz", hash = "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" -pyflakes = ">=3.1.0,<3.2.0" +pyflakes = ">=3.2.0,<3.3.0" [[package]] name = "flake8-bandit" @@ -276,13 +276,13 @@ flake8 = ">=5.0.0" [[package]] name = "flake8-bugbear" -version = "23.12.2" +version = "24.2.6" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false python-versions = ">=3.8.1" files = [ - {file = "flake8-bugbear-23.12.2.tar.gz", hash = "sha256:32b2903e22331ae04885dae25756a32a8c666c85142e933f43512a70f342052a"}, - {file = "flake8_bugbear-23.12.2-py3-none-any.whl", hash = "sha256:83324bad4d90fee4bf64dd69c61aff94debf8073fbd807c8b6a36eec7a2f0719"}, + {file = "flake8-bugbear-24.2.6.tar.gz", hash = "sha256:f9cb5f2a9e792dd80ff68e89a14c12eed8620af8b41a49d823b7a33064ac9658"}, + {file = "flake8_bugbear-24.2.6-py3-none-any.whl", hash = "sha256:663ef5de80cd32aacd39d362212983bc4636435a6f83700b4ed35acbd0b7d1b8"}, ] [package.dependencies] @@ -326,46 +326,15 @@ restructuredtext-lint = "*" [package.extras] develop = ["build", "twine"] -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.40" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] - [[package]] name = "identify" -version = "2.5.33" +version = "2.5.34" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"}, - {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"}, + {file = "identify-2.5.34-py2.py3-none-any.whl", hash = "sha256:a4316013779e433d08b96e5eabb7f641e6c7942e4ab5d4c509ebd2e7a8994aed"}, + {file = "identify-2.5.34.tar.gz", hash = "sha256:ee17bc9d499899bc9eaec1ac7bf2dc9eedd480db9d88b96d123d3b64a9d34f5d"}, ] [package.extras] @@ -549,28 +518,28 @@ flake8 = ">=5.0.0" [[package]] name = "platformdirs" -version = "4.1.0" +version = "4.2.0" 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.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"}, - {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -640,13 +609,13 @@ toml = ["tomli (>=1.2.3)"] [[package]] name = "pyflakes" -version = "3.1.0" +version = "3.2.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" files = [ - {file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"}, - {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] [[package]] @@ -666,13 +635,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "7.4.4" +version = "8.0.0" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, + {file = "pytest-8.0.0-py3-none-any.whl", hash = "sha256:50fb9cbe836c3f20f0dfa99c565201fb75dc54c8d76373cd1bde06b06657bdb6"}, + {file = "pytest-8.0.0.tar.gz", hash = "sha256:249b1b0864530ba251b7438274c4d251c58d868edaaec8762893ad4a0d71c36c"}, ] [package.dependencies] @@ -680,7 +649,7 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" +pluggy = ">=1.3.0,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] @@ -787,13 +756,13 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruamel-yaml" -version = "0.18.5" +version = "0.18.6" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" optional = false python-versions = ">=3.7" files = [ - {file = "ruamel.yaml-0.18.5-py3-none-any.whl", hash = "sha256:a013ac02f99a69cdd6277d9664689eb1acba07069f912823177c5eced21a6ada"}, - {file = "ruamel.yaml-0.18.5.tar.gz", hash = "sha256:61917e3a35a569c1133a8f772e1226961bf5a1198bea7e23f06a0841dea1ab0e"}, + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, ] [package.dependencies] @@ -850,31 +819,20 @@ files = [ [[package]] name = "setuptools" -version = "69.0.3" +version = "69.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, - {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, + {file = "setuptools-69.1.0-py3-none-any.whl", hash = "sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6"}, + {file = "setuptools-69.1.0.tar.gz", hash = "sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - [[package]] name = "snowballstemmer" version = "2.2.0" diff --git a/pyproject.toml b/pyproject.toml index 657669f..d50df1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,12 @@ blank = true [tool.ruff] target-version = "py38" line-length = 120 +exclude = [ + "tests" +] + + +[tool.ruff.lint] select = [ "A", "C4", @@ -90,19 +96,15 @@ select = [ "W", "YTT", ] -exclude = [ - "tests" -] extend-ignore = [ - "D100", "D101", "D102", "D103", "D104", "D105", "D107", "E402", "F403", "N802", "N803" + "D100", "D101", "D102", "D103", "D104", "D105", "D107", "E402", "F403", "N802", "N803", "ERA001" ] - -[tool.ruff.pydocstyle] +[tool.ruff.lint.pydocstyle] convention = "google" ignore-decorators = ['override'] -[tool.ruff.pylint] +[tool.ruff.lint.pylint] max-args = 10 [tool.coverage.report] From dccc9df95307b51fb378424df7e4673b3b2e9e63 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Mon, 12 Feb 2024 19:05:04 +0100 Subject: [PATCH 13/13] merge Barrel and Tube, fix Threads --- src/muscad/utils/__init__.py | 2 +- src/muscad/utils/barrel.py | 124 --------------------------------- src/muscad/utils/tube.py | 94 ++++++++++++++++++++++--- src/muscad/vitamins/threads.py | 50 ++++++++----- 4 files changed, 115 insertions(+), 155 deletions(-) delete mode 100644 src/muscad/utils/barrel.py diff --git a/src/muscad/utils/__init__.py b/src/muscad/utils/__init__.py index 7c207ea..e2600d3 100644 --- a/src/muscad/utils/__init__.py +++ b/src/muscad/utils/__init__.py @@ -1,7 +1,7 @@ -from .barrel import * from .fillet import * from .shapes import * from .stack import * from .star import * from .surface import * +from .tube import * from .volume import * diff --git a/src/muscad/utils/barrel.py b/src/muscad/utils/barrel.py deleted file mode 100644 index 673710e..0000000 --- a/src/muscad/utils/barrel.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import annotations - -from contextlib import suppress - -from muscad import Cylinder, Part, calc - - -class Barrel(Part): - def init( # type: ignore[override] - self, - d: float | None = None, - *, - r: float | None = None, - d2: float | None = None, - r2: float | None = None, - left: float | None = None, - center_x: float | None = None, - right: float | None = None, - width: float | None = None, - back: float | None = None, - center_y: float | None = None, - front: float | None = None, - depth: float | None = None, - bottom: float | None = None, - center_z: float | None = None, - top: float | None = None, - height: float | None = None, - segments: int | None = None, - ) -> None: - match d, r: - case None, None: - # no explicit diameter or radius, try to deduce it from the other params - with suppress(ValueError): - left, center_x, right, width = calc(left, center_x, right, width) - with suppress(ValueError): - back, center_y, front, depth = calc(back, center_y, front) - match width, depth: - case None, None: - msg = ( - "No sufficient parameter to calculate the barrel diameter." - " Please provide either a diameter `d`, a radius `r`," - "or at least 2 two parameters on axis X or Y." - ) - raise ValueError(msg) - case float(), None: - d = width - case None, float(): - d = depth - case float(), float() if width != depth: - msg = ( - f"width ({width}mm) is different from depth ({depth}mm)." - " I don't know which one to use as diameter." - ) - raise ValueError(msg) - case float(), None: - pass - case None, float() if r is not None: - d = r * 2 - case float(), float() if r is not None and d is not None and r * 2 != d: - msg = ( - "diameter `d` and radius `r` must be consistent." "Please fix, or provide either one or the other." - ) - raise ValueError(msg) - - assert d is not None - - match r2, d2: - case None, float(): - pass - case float(), None if r2 is not None: - d2 = r2 * 2 - case float(), float() if d2 is not None and r2 is not None and d2 != r2 * 2: - msg = ( - f"inconsistent top diameter `d2` ({d2}mm) and top radius ({r2}mm)." - "Please fix, or provide either one or the other." - ) - raise ValueError(msg) - - match width, d: - case None, float(): - width = d - case float(), float() if width != d: - msg = f"diameter `d` ({d}mm) is different from width ({width}mm)." - raise ValueError(msg) - - match depth, d: - case None, float(): - depth = d - case float(), None: - assert False - case float(), float() if depth != d: - msg = f"diameter `d` ({d}mm) is different from depth ({depth}mm)." - raise ValueError(msg) - - self._left, self._center_x, self._right, self._width = calc(left, center_x, right, width) - self._back, self._center_y, self._front, self._depth = calc(back, center_y, front, depth) - self._bottom, self._center_z, self._top, self._height = calc(bottom, center_z, top, height) - self.cylinder = Cylinder(d=d, d2=d2, h=self._height, segments=segments).align( - center_x=self._center_x, center_y=self._center_y, center_z=self._center_z - ) - - @property - def left(self) -> float: - return self._left - - @property - def right(self) -> float: - return self._right - - @property - def back(self) -> float: - return self._back - - @property - def front(self) -> float: - return self._front - - @property - def bottom(self) -> float: - return self._bottom - - @property - def top(self) -> float: - return self._top diff --git a/src/muscad/utils/tube.py b/src/muscad/utils/tube.py index 4c1d18c..d7a6e25 100644 --- a/src/muscad/utils/tube.py +++ b/src/muscad/utils/tube.py @@ -8,34 +8,106 @@ class Tube(Part): def init( # type: ignore[override] self, - *args: Misc | Hole | Object, diameter: float | None = None, - top_diameter: float | None = None, + *args: Misc | Hole | Object, radius: float | None = None, + top_diameter: float | None = None, + top_radius: float | None = None, left: float | None = None, center_x: float | None = None, right: float | None = None, + width: float | None = None, back: float | None = None, center_y: float | None = None, front: float | None = None, + depth: float | None = None, bottom: float | None = None, center_z: float | None = None, top: float | None = None, height: float | None = None, + segments: int | None = None, **kwargs: Misc | Hole | Object, ) -> None: - if diameter is None and radius is not None: - diameter = radius * 2 - with contextlib.suppress(ValueError): - # first try to get the cylinder diameter from the x coordinates - left, center_x, right, diameter = calc(left, center_x, right, diameter) + match diameter, radius: + case None, None: + # no explicit diameter or radius, try to deduce it from the other params + with contextlib.suppress(ValueError): + left, center_x, right, width = calc(left, center_x, right, width) + with contextlib.suppress(ValueError): + back, center_y, front, depth = calc(back, center_y, front) + match width, depth: + case None, None: + msg = ( + "No sufficient parameter to calculate the barrel diameter." + " Please provide either a diameter `d`, a radius `r`," + "or at least 2 two parameters on axis X or Y." + ) + raise ValueError(msg) + case float() | int(), None: + diameter = width + case None, float() | int(): + diameter = depth + case float() | int(), float() | int() if width != depth: + msg = ( + f"width ({width}mm) is different from depth ({depth}mm)." + " I don't know which one to use as diameter." + ) + raise ValueError(msg) + case float() | int(), None if diameter is not None: + pass + case None, float() if radius is not None: + diameter = radius * 2 + case ( + float() + | int(), + float() + | int(), + ) if radius is not None and diameter is not None and radius * 2 != diameter: + msg = ( + "diameter `d` and radius `r` must be consistent." "Please fix, or provide either one or the other." + ) + raise ValueError(msg) - # fallback to the y coordinates - back, center_y, front, diameter = calc(back, center_y, front, diameter) + assert diameter is not None + + match top_radius, top_diameter: + case None, float() | int(): + pass + case float() | int(), None if top_radius is not None: + top_diameter = top_radius * 2 + case ( + float() + | int(), + float() + | int(), + ) if top_diameter is not None and top_radius is not None and top_diameter != top_radius * 2: + msg = ( + f"inconsistent top diameter `d2` ({top_diameter}mm) and top radius ({top_radius}mm)." + "Please fix, or provide either one or the other." + ) + raise ValueError(msg) + + match width, diameter: + case None, float() | int(): + width = diameter + case float() | int(), float() | int() if width != diameter: + msg = f"diameter `d` ({diameter}mm) is different from width ({width}mm)." + raise ValueError(msg) + + match depth, diameter: + case None, float() | int(): + depth = diameter + case float() | int(), None: + assert False + case float() | int(), float() | int() if depth != diameter: + msg = f"diameter `d` ({diameter}mm) is different from depth ({depth}mm)." + raise ValueError(msg) + left, center_x, right, width = calc(left, center_x, right, width) + back, center_y, front, depth = calc(back, center_y, front, depth) bottom, center_z, top, height = calc(bottom, center_z, top, height) - self.cylinder = Cylinder(d=diameter, d2=top_diameter, h=height).align( - center_x=center_x, center_y=center_y, bottom=bottom + self.cylinder = Cylinder(d=diameter, d2=top_diameter, h=height, segments=segments).align( + center_x=center_x, center_y=center_y, center_z=center_z ) super().init(*args, **kwargs) diff --git a/src/muscad/vitamins/threads.py b/src/muscad/vitamins/threads.py index a38a51c..c75e5fe 100644 --- a/src/muscad/vitamins/threads.py +++ b/src/muscad/vitamins/threads.py @@ -4,12 +4,12 @@ from math import floor, pi from muscad import ( - Barrel, Cylinder, Object, Part, Polygon, Polyhedron, + Tube, Union, cos, sin, @@ -29,18 +29,30 @@ def init( # type: ignore[override] bottom_countersink: bool = False, ) -> None: if not top_countersink and not bottom_countersink: - self.shape = Barrel(height=length, d=outer_diameter, segments=segments) + self.shape = Tube(height=length, diameter=outer_diameter, bottom=0, segments=segments) else: - self.shape = Barrel(bottom=step / 2, height=length - step + 0.005, d=outer_diameter, segments=segments) + self.shape = Tube(bottom=step / 2, height=length - step + 0.005, diameter=outer_diameter, segments=segments) self.top_countersink = ( - Barrel(bottom=self.shape.top, height=step / 2, d=outer_diameter, d2=inner_diameter, segments=segments) + Tube( + bottom=self.shape.top, + height=step / 2, + diameter=outer_diameter, + top_diameter=inner_diameter, + segments=segments, + ) if top_countersink - else Barrel(bottom=self.shape.top, height=step / 2, d=outer_diameter, segments=segments) + else Tube(bottom=self.shape.top, height=step / 2, diameter=outer_diameter, segments=segments) ) self.bottom_countersink = ( - Barrel(height=step / 2, top=self.shape.bottom, d=outer_diameter, d2=inner_diameter, segments=segments) + Tube( + height=step / 2, + top=self.shape.bottom, + diameter=inner_diameter, + top_diameter=outer_diameter, + segments=segments, + ) if bottom_countersink - else Barrel(height=step / 2, top=self.shape.bottom, d=outer_diameter, segments=segments) + else Tube(height=step / 2, top=self.shape.bottom, diameter=outer_diameter, segments=segments) ) @@ -48,7 +60,7 @@ class ScrewThread(Part): def init( # type: ignore[override] self, *, - outer_diameter: float, + diameter: float, length: float, step: int, top_countersink: bool = False, @@ -56,14 +68,14 @@ def init( # type: ignore[override] shape_degrees: float = 45, resolution: float = 0.5, ) -> None: - inner_diameter = outer_diameter - step * cos(shape_degrees) / sin(shape_degrees) - segments = floor(pi * outer_diameter / resolution) + inner_diameter = diameter - step * cos(shape_degrees) / sin(shape_degrees) + segments = floor(pi * diameter / resolution) ttn = round(length / step + 1) lfxy = 360 / segments zt = step / segments self.thread = ThreadShape( length=length, - outer_diameter=outer_diameter, + outer_diameter=diameter, inner_diameter=inner_diameter, segments=segments, step=step, @@ -85,13 +97,13 @@ def init( # type: ignore[override] ], [0, 0, i * step], [ - outer_diameter / 2 * cos(j * lfxy), - outer_diameter / 2 * sin(j * lfxy), + diameter / 2 * cos(j * lfxy), + diameter / 2 * sin(j * lfxy), i * step + j * zt - step / 2, ], [ - outer_diameter / 2 * cos((j + 1) * lfxy), - outer_diameter / 2 * sin((j + 1) * lfxy), + diameter / 2 * cos((j + 1) * lfxy), + diameter / 2 * sin((j + 1) * lfxy), i * step + (j + 1) * zt - step / 2, ], [ @@ -182,7 +194,7 @@ def init(self, height: float, diameter: float) -> None: # type: ignore[override y1 = height / 2 y2 = height - self.head = Barrel(bottom=0, height=height, d=d0, segments=6) & Polygon( + self.head = Tube(bottom=0, height=height, d=d0, segments=6) & Polygon( (x0, y0), (x1, y0), (x2, y1), (x1, y2), (x0, y2) ).z_rotational_extrude(bottom=0) @@ -199,15 +211,15 @@ def init( # type: ignore[override] resolution: float = 0.5, ) -> None: self.nut = HexHead(height, diameter) - self.countersinks = ~Barrel( + self.countersinks = ~Tube( bottom=self.nut.bottom - 0.1, height=thread_step / 2, d=thread_outer_diameter, d2=thread_outer_diameter - (diameter / 2 + 0.1) * cos(step_shape_degrees) / sin(step_shape_degrees), ).z_mirror(center=self.nut.center_z) self.bore = ~ScrewThread( - outer_diameter=thread_outer_diameter, length=height, step=thread_step, shape_degrees=step_shape_degrees + diameter=thread_outer_diameter, length=height, step=thread_step, shape_degrees=step_shape_degrees ).align(center_z=self.nut.center_z) -HexNut(diameter=9, height=4, thread_outer_diameter=6.2).render_to_file() +# HexNut(diameter=9, height=4, thread_outer_diameter=6.2).render_to_file()