diff --git a/.flake8 b/.flake8 index 4f0551e3f8..34c4ca7131 100644 --- a/.flake8 +++ b/.flake8 @@ -3,5 +3,5 @@ exclude = venv, __init__.py, doc/_build select = W191, W291, W293, W391, E115, E117, E122, E124, E125, E225, E231, E301, E303, E501, F401, F403 count = True max-complexity = 10 -max-line-length = 110 +max-line-length = 115 statistics = True diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..3e60285a43 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Dropping Python 3.9 support (pyupgrade) +d099b3b4bb80a3461f7f174a6c2944aa13b10a05 diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 274d8946f4..a196d1b860 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -1,4 +1,3 @@ -# check spelling, codestyle name: GitHub CI # run only on main branch. This avoids duplicated actions on PRs @@ -56,7 +55,7 @@ jobs: run: | pip install -U pip pip install 'poetry!=1.7.0' - poetry install --with dev,test + poetry install --with dev,test --all-extras - name: Build API package from custom branch if: "${{ env.API_BRANCH != '' }}" @@ -83,7 +82,7 @@ jobs: name: "Documentation style" runs-on: ubuntu-latest steps: - - uses: ansys/actions/doc-style@v7 + - uses: ansys/actions/doc-style@v8 with: token: ${{ secrets.GITHUB_TOKEN }} @@ -93,7 +92,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.9', '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12'] should-release: - ${{ github.event_name == 'push' && contains(github.ref, 'refs/tags') }} exclude: @@ -101,19 +100,153 @@ jobs: os: macos-latest steps: - name: "Build wheelhouse and perform smoke test" - uses: ansys/actions/build-wheelhouse@v7 + uses: ansys/actions/build-wheelhouse@v8 with: library-name: ${{ env.PACKAGE_NAME }} operating-system: ${{ matrix.os }} python-version: ${{ matrix.python-version }} + check-vulnerabilities: + name: "Check library vulnerabilities" + runs-on: ubuntu-latest + steps: + - uses: ansys/actions/check-vulnerabilities@v8.1 + with: + python-version: ${{ env.MAIN_PYTHON_VERSION }} + token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} + python-package-name: 'ansys-acp-core' + dev-mode: ${{ github.ref != 'refs/heads/main' }} + extra-targets: "plotting" + + testing-direct-launch: + name: Testing with direct launch mode + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - uses: actions/checkout@v4 + - name: Login in Github Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # Python version is determined by the base image + - name: Build docker image + run: docker build -t test_runner -f dockerfiles/DirectLaunchTest.Dockerfile . + - name: Run tests + run: | + docker run \ + --mount type=bind,source="$(pwd)",target=/home/container/pyacp \ + -u $(id -u):$(id -g) \ + -e ANSYSLMD_LICENSE_FILE=$ANSYSLMD_LICENSE_FILE \ + test_runner + env: + ANSYSLMD_LICENSE_FILE: "1055@${{ secrets.LICENSE_SERVER }}" + - name: "Upload coverage to Codecov" + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + files: coverage.xml + flags: 'direct-launch,server-latest' + + testing-minimum-deps: + name: Testing with minimum dependencies + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + server-version: ["latest"] + steps: + - uses: actions/checkout@v4 + + - name: Pip cache + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('poetry.lock') }}-testing-minimum-deps + restore-keys: | + pip-${{ runner.os }}-${{ matrix.python-version }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: | + 3.10 + ${{ matrix.python-version }} + + - name: Install library, with only the 'main' group + run: | + pip install -U pip + pip install 'poetry!=1.7.0' + poetry install --only main + + - name: Check that PyACP can be imported + run: | + poetry run python -c "import ansys.acp.core" + + - name: Install the 'test' group + run: | + poetry install --with test + + - name: Build API package from custom branch + if: "${{ env.API_BRANCH != '' }}" + run: | + python3.10 -m venv .api_builder_venv + . .api_builder_venv/bin/activate + python -m pip install --upgrade pip wheel + mkdir .api_package + python -m pip wheel --no-deps --wheel-dir .api_package git+https://github.com/ansys/ansys-api-acp.git@${{ env.API_BRANCH }} + + - name: Install custom API branch package + if: "${{ env.API_BRANCH != '' }}" + # The --no-deps flag is added since this may cause dependency conflicts with + # other transitive dependencies. For example, when a newer version of protobuf + # is installed. + run: | + poetry run pip install --no-deps --force-reinstall .api_package/*.whl + + - name: Login in Github Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Unit testing + working-directory: tests/unittests + run: | + docker pull $IMAGE_NAME + poetry run pytest -v --license-server=1055@$LICENSE_SERVER --no-server-log-files --docker-image=$IMAGE_NAME --cov=ansys.acp.core --cov-report=term --cov-report=xml --cov-report=html -m "not plotting" + env: + LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} + IMAGE_NAME: ghcr.io/ansys/acp:${{ matrix.server-version }} + + - name: "Upload coverage to Codecov" + uses: codecov/codecov-action@v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + files: coverage.xml + flags: 'server-${{ matrix.server-version }},python-${{ matrix.python-version }},minimum-deps' + testing: name: Testing runs-on: ubuntu-latest timeout-minutes: 30 strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] + server-version: ["latest"] + include: + - python-version: "3.12" + server-version: "2024R2" + - python-version: "3.12" + server-version: "2025R1" + - python-version: "3.12" + server-version: "2025R2" steps: - uses: actions/checkout@v4 @@ -137,7 +270,7 @@ jobs: run: | pip install -U pip pip install 'poetry!=1.7.0' - poetry install --with test + poetry install --with test --extras plotting - name: Build API package from custom branch if: "${{ env.API_BRANCH != '' }}" @@ -170,31 +303,23 @@ jobs: poetry run pytest -v --license-server=1055@$LICENSE_SERVER --no-server-log-files --docker-image=$IMAGE_NAME --cov=ansys.acp.core --cov-report=term --cov-report=xml --cov-report=html env: LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} - IMAGE_NAME: "ghcr.io/ansys/acp${{ github.event.inputs.docker_image_suffix || ':latest' }}" - - - name: "Upload coverage report (HTML)" - uses: actions/upload-artifact@v4 - if: matrix.python-version == env.MAIN_PYTHON_VERSION - with: - name: coverage-report-html - path: htmlcov - retention-days: 7 + IMAGE_NAME: ghcr.io/ansys/acp:${{ matrix.server-version }} - name: "Upload coverage to Codecov" - uses: codecov/codecov-action@v4 - if: matrix.python-version == env.MAIN_PYTHON_VERSION + uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: files: coverage.xml + flags: 'server-${{ matrix.server-version }},python-${{ matrix.python-version }}' - name: Benchmarks working-directory: tests/benchmarks run: | - poetry run pytest -v --license-server=1055@$LICENSE_SERVER --no-server-log-files --docker-image=$IMAGE_NAME --build-benchmark-image --benchmark-json benchmark_output.json --benchmark-group-by=fullname ${{ (matrix.python-version == '3.9' && github.ref == 'refs/heads/main') && ' ' || '--validate-benchmarks-only' }} + poetry run pytest -v --license-server=1055@$LICENSE_SERVER --no-server-log-files --docker-image=$IMAGE_NAME --build-benchmark-image --benchmark-json benchmark_output.json --benchmark-group-by=fullname ${{ (matrix.python-version == env.MAIN_PYTHON_VERSION && matrix.server-version == 'latest') && ' ' || '--validate-benchmarks-only' }} env: LICENSE_SERVER: ${{ secrets.LICENSE_SERVER }} - IMAGE_NAME: ${{ env.DOCKER_IMAGE_NAME }} + IMAGE_NAME: ghcr.io/ansys/acp:${{ matrix.server-version }} - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 @@ -205,7 +330,7 @@ jobs: benchmark-data-dir-path: benchmarks auto-push: true github-token: ${{ secrets.GITHUB_TOKEN }} - if: matrix.python-version == env.MAIN_PYTHON_VERSION && github.ref == 'refs/heads/main' + if: matrix.python-version == env.MAIN_PYTHON_VERSION && matrix.server-version == 'latest' && github.ref == 'refs/heads/main' doctest: name: Test documentation snippets @@ -232,7 +357,7 @@ jobs: run: | pip install -U pip pip install 'poetry!=1.7.0' - poetry install --with test,dev + poetry install --with test,dev --all-extras - name: Build API package from custom branch if: "${{ env.API_BRANCH != '' }}" @@ -268,7 +393,7 @@ jobs: run: > poetry run ansys-launcher configure ACP docker_compose - --image_name_pyacp=ghcr.io/ansys/acp${{ github.event.inputs.docker_image_suffix || ':latest' }} + --image_name_acp=${{ env.DOCKER_IMAGE_NAME }} --image_name_filetransfer=ghcr.io/ansys/tools-filetransfer:latest --license_server=1055@$LICENSE_SERVER --keep_volume=False @@ -328,7 +453,7 @@ jobs: run: | pip install -U pip pip install 'poetry!=1.7.0' - poetry install --with dev + poetry install --with dev --all-extras - name: Build API package from custom branch if: "${{ env.API_BRANCH != '' }}" @@ -351,7 +476,7 @@ jobs: run: > poetry run ansys-launcher configure ACP docker_compose - --image_name_pyacp=${{ env.DOCKER_IMAGE_NAME }} + --image_name_acp=${{ env.DOCKER_IMAGE_NAME }} --image_name_filetransfer=ghcr.io/ansys/tools-filetransfer:latest --license_server=1055@$LICENSE_SERVER --keep_volume=False @@ -421,11 +546,11 @@ jobs: build: name: Build library runs-on: ubuntu-latest - needs: [code-style, testing, doc-style, docs, build-wheelhouse, doctest] + needs: [code-style, testing, testing-minimum-deps, testing-direct-launch, doc-style, docs, build-wheelhouse, doctest] # TODO: add check-vulnerabilities once we know it works on main timeout-minutes: 30 steps: - name: Build library source and wheel artifacts - uses: ansys/actions/build-library@v7 + uses: ansys/actions/build-library@v8 with: library-name: ${{ env.PACKAGE_NAME }} python-version: ${{ env.MAIN_PYTHON_VERSION }} @@ -437,14 +562,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Release to the public PyPI repository - uses: ansys/actions/release-pypi-public@v7 + uses: ansys/actions/release-pypi-public@v8 with: library-name: ${{ env.PACKAGE_NAME }} twine-username: "__token__" twine-token: ${{ secrets.PYPI_TOKEN }} - name: Release to GitHub - uses: ansys/actions/release-github@v7 + uses: ansys/actions/release-github@v8 with: library-name: ${{ env.PACKAGE_NAME }} @@ -455,11 +580,13 @@ jobs: needs: [build] steps: - name: Deploy the latest documentation - uses: ansys/actions/doc-deploy-dev@v7 + uses: ansys/actions/doc-deploy-dev@v8 with: cname: ${{ env.DOCUMENTATION_CNAME }} - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} force-orphan: false + bot-user: ${{ secrets.PYANSYS_CI_BOT_USERNAME }} + bot-email: ${{ secrets.PYANSYS_CI_BOT_EMAIL }} upload_docs_release: name: Upload release documentation @@ -468,7 +595,9 @@ jobs: needs: [release] steps: - name: Deploy the stable documentation - uses: ansys/actions/doc-deploy-stable@v7 + uses: ansys/actions/doc-deploy-stable@v8 with: cname: ${{ env.DOCUMENTATION_CNAME }} - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.PYANSYS_CI_BOT_TOKEN }} + bot-user: ${{ secrets.PYANSYS_CI_BOT_USERNAME }} + bot-email: ${{ secrets.PYANSYS_CI_BOT_EMAIL }} diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 9ca0cfc66d..cf2e43f215 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.9', '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12'] timeout-minutes: 30 steps: @@ -32,10 +32,10 @@ jobs: python -m venv test_env . test_env/bin/activate - pip install $(echo dist/*.whl) + pip install $(echo dist/*.whl)[plotting] poetry config virtualenvs.create false --local - poetry install --only test + poetry install --no-root --only test --extras plotting - name: Login in Github Container registry uses: docker/login-action@v3 diff --git a/.github/workflows/package_cleanup.yml b/.github/workflows/package_cleanup.yml index 7e95459378..b3ab76f83b 100644 --- a/.github/workflows/package_cleanup.yml +++ b/.github/workflows/package_cleanup.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: "Delete untagged package versions" - uses: ansys/actions/hk-package-clean-untagged@v7 + uses: ansys/actions/hk-package-clean-untagged@v8 with: package-org: 'ansys' package-name: 'acp' diff --git a/.gitignore b/.gitignore index 02b322703b..aaabf9661b 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,7 @@ dist/ # autogenerated docs _autosummary - +_gallery_backreferences # Testing .coverage @@ -83,3 +83,6 @@ examples/pymechanical/output/* /examples/workbench_project/ +# Vulnerability scanning +info_bandit.json +info_safety.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index acdf818a3c..fc6a80a6f4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,20 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 + rev: v3.19.0 hooks: - id: pyupgrade - args: [--py39-plus] + args: [--py310-plus] - repo: https://github.com/psf/black - rev: "24.4.2" # when changed, also update the version in blacken-docs + rev: "24.10.0" # when changed, also update the version in blacken-docs hooks: - id: black - repo: https://github.com/adamchainz/blacken-docs - rev: 1.18.0 + rev: 1.19.1 hooks: - id: blacken-docs - additional_dependencies: [black==24.4.2] + additional_dependencies: [black==24.10.0] - repo: https://github.com/pycqa/isort rev: "5.13.2" @@ -22,7 +22,7 @@ repos: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: "7.1.0" + rev: "7.1.1" hooks: - id: flake8 @@ -51,7 +51,7 @@ repos: exclude: "^(tests/)|(type_checks/)|(examples/)" - repo: https://github.com/kynan/nbstripout - rev: 0.7.1 + rev: 0.8.0 hooks: - id: nbstripout args: @@ -61,7 +61,7 @@ repos: ] - repo: https://github.com/ansys/pre-commit-hooks - rev: v0.3.1 + rev: v0.4.3 hooks: - id: add-license-headers args: ["--start_year", "2022"] diff --git a/AUTHORS b/AUTHORS index 4e1f434d47..f0532d79ee 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,8 +5,8 @@ # For contributions made under a Corporate CLA, the organization is # added to this file. # -# If you have contributed to the repository and wish to be added to this file -# please submit a request. +# If you have contributed to the repository and want to be added to this file, +# submit a request. # # ANSYS, Inc. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b77db6d65e..007f97120e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,17 +2,16 @@ -We absolutely welcome any code contributions and we hope that this -guide will facilitate an understanding of the PyACP code +We absolutely welcome all code contributions and hope that this +guide facilitates an understanding of the PyACP code repository. It is important to note that while the PyACP software -package is maintained by ANSYS and any submissions will be reviewed +package is maintained by Ansys and any submissions are reviewed thoroughly before merging, we still seek to foster a community that can support user questions and develop new features to make this software -a useful tool for all users. As such, we welcome and encourage any +a useful tool for all users. As such, we welcome and encourage any questions or submissions to this repository. -Please reference the [PyAnsys Developer's -Guide](https://dev.docs.pyansys.com) for the full documentation -regarding contributing to the PyACP project. +For comprehensive documentation on contributing to the PyACP project, +refer to the [PyAnsys developer's guide](https://dev.docs.pyansys.com). diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index fc53569ac3..fefd506689 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,6 +1,6 @@ # Contributors -## Project Lead or Owner +## Project Lead * [Dominik Gresch](https://github.com/greschd) * [Jan von Rickenbach](https://github.com/janvonrickenbach) diff --git a/README.rst b/README.rst index 0eba319ae2..8d6a9761d8 100644 --- a/README.rst +++ b/README.rst @@ -43,7 +43,7 @@ Install PyACP with: .. code-block:: - pip install ansys-acp-core + pip install ansys-acp-core[all] For installing PyACP in development mode, see the `Development Setup`_ instructions below. @@ -78,8 +78,8 @@ Model from an existing file: .. code-block:: pycon - >>> remote_filename = acp.upload_file(local_path="") - >>> model = acp.import_model(path=remote_filename) + >>> remote_path = acp.upload_file(local_path="") + >>> model = acp.import_model(path=remote_path) >>> model.name 'ACP Model' @@ -120,7 +120,7 @@ You will need to follow these steps: .. code-block:: bash - poetry install --with dev,test + poetry install --with dev,test --all-extras This step installs PyACP in an editable mode (no build step is needed, no re-install when changing the code). @@ -208,6 +208,11 @@ valid license server (e.g ``1055@mylicenseserver.com``). Then start the docker c Then build the documentation with the `Sphinx`_ commands mentioned above. +On Windows, you can use the shipped shell script: + +.. code-block:: batch + + .\doc\create_doc_windows.ps1 Distribution ^^^^^^^^^^^^ diff --git a/doc/Makefile b/doc/Makefile index 852468f0e3..efb413bfe0 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -28,5 +28,9 @@ pdf: # customized clean due to examples gallery clean: rm -rf $(BUILDDIR) - rm -rf $(SOURCEDIR)/examples/gallery_examples + rm -rf $(SOURCEDIR)/examples/images + rm -rf $(SOURCEDIR)/examples/modeling_features + rm -rf $(SOURCEDIR)/examples/use_cases + rm -rf $(SOURCEDIR)/examples/workflows + rm -rf $(SOURCEDIR)/examples/sg_execution_times.rst find . -type d -name "_autosummary" -exec rm -rf {} + diff --git a/doc/create_doc_windows.ps1 b/doc/create_doc_windows.ps1 new file mode 100644 index 0000000000..086264b279 --- /dev/null +++ b/doc/create_doc_windows.ps1 @@ -0,0 +1,32 @@ +if ($Env:ANSYSLMD_LICENSE_FILE -ne $null) +{ + "ANSYSLMD_LICENSE_FILE=" + $Env:ANSYSLMD_LICENSE_FILE +} +else +{ + "Env variable 'ANSYSLMD_LICENSE_FILE' is required for the license checks." + "Example: ANSYSLMD_LICENSE_FILE='1055@my_license_server'" + exit 1 +} + +docker pull ghcr.io/ansys/pydpf-composites:latest +docker pull ghcr.io/ansys/mapdl:latest + +$Env:ANSYS_DPF_ACCEPT_LA="Y" +$Env:PYMAPDL_PORT=59991 +$Env:PYMAPDL_START_INSTANCE="FALSE" +$Env:PYDPF_COMPOSITES_DOCKER_CONTAINER_PORT=59992 +$Env:SPHINXOPT_NITPICKY=0 +# whether to skip the gallery (examples) +$Env:PYACP_DOC_SKIP_GALLERY="0" +# whether to skip the API documentation +$Env:PYACP_DOC_SKIP_API="0" + +$ParentDir = Split-Path -Parent $PSScriptRoot +$DockerComposeFile = Join-Path -Path $ParentDir -ChildPath "docker-compose/docker-compose-extras.yaml" +docker compose -f $DockerComposeFile up -d + +$MakeFile = Join-Path -Path $PSScriptRoot -ChildPath "make.bat" +& $MakeFile html + +docker compose -f $DockerComposeFile down diff --git a/doc/make.bat b/doc/make.bat index 294ec1d12c..d3cbb56b3e 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -10,6 +10,7 @@ if "%SPHINXBUILD%" == "" ( set SOURCEDIR=source set BUILDDIR=build set REPO_ROOT=%~dp0\..\ +set SPHINXOPTS=-n if "%1" == "" goto help if "%1" == "clean" goto clean @@ -33,8 +34,12 @@ goto end :clean rmdir /s /q %BUILDDIR% > /NUL 2>&1 -for /d /r %SOURCEDIR% %%d in (_autosummary) do @if exist "%%d" rmdir /s /q "%%d" -rmdir /s /q %SOURCEDIR%\examples\gallery_examples +for /d /r %SOURCEDIR% %%d in (_autosummary,_gallery_backreferences) do @if exist "%%d" rmdir /s /q "%%d" +rmdir /s /q %SOURCEDIR%\examples\images +rmdir /s /q %SOURCEDIR%\examples\modeling_features +rmdir /s /q %SOURCEDIR%\examples\use_cases +rmdir /s /q %SOURCEDIR%\examples\workflows +del %SOURCEDIR%\examples\sg_execution_times.rst goto end :pdf diff --git a/doc/source/_static/gallery_thumbnails/README.txt b/doc/source/_static/gallery_thumbnails/README.txt new file mode 100644 index 0000000000..ef35fe7775 --- /dev/null +++ b/doc/source/_static/gallery_thumbnails/README.txt @@ -0,0 +1,19 @@ +This directory contains thumbnails for the gallery examples which are +not built in CI. The thumbnails are used in the gallery index page. + +To update a thumbnail: +- Remove the ``# sphinx_gallery_thumbnail_path`` configuration in the + example file. +- Build the documentation, running the example that you want to update. + You may need to add a configuration option like + ``# sphinx_gallery_thumbnail_number = -1`` to select the image which should + be used as thumbnail. [1] +- Copy the generated thumbnail from the example directory to this one. +- Re-add the ``# sphinx_gallery_thumbnail_path`` configuration in the + example file. + +NOTE: If the example file has been renamed since the last time the thumbnail +was generated, the thumbnail filename will also be different. + +[1] These options conflict with ``# sphinx_gallery_thumbnail_path``, so they +cannot be added permanently to the example file. diff --git a/doc/source/_static/gallery_thumbnails/sphx_glr_001-materials_thumb.png b/doc/source/_static/gallery_thumbnails/sphx_glr_001-materials_thumb.png new file mode 100644 index 0000000000..6c2fa014dc Binary files /dev/null and b/doc/source/_static/gallery_thumbnails/sphx_glr_001-materials_thumb.png differ diff --git a/doc/source/_static/gallery_thumbnails/sphx_glr_03-pymechanical-shell-workflow_thumb.png b/doc/source/_static/gallery_thumbnails/sphx_glr_03-pymechanical-shell-workflow_thumb.png new file mode 100644 index 0000000000..6ff574b0c6 Binary files /dev/null and b/doc/source/_static/gallery_thumbnails/sphx_glr_03-pymechanical-shell-workflow_thumb.png differ diff --git a/doc/source/_static/gallery_thumbnails/sphx_glr_04-pymechanical-solid-workflow_thumb.png b/doc/source/_static/gallery_thumbnails/sphx_glr_04-pymechanical-solid-workflow_thumb.png new file mode 100644 index 0000000000..418dab84b6 Binary files /dev/null and b/doc/source/_static/gallery_thumbnails/sphx_glr_04-pymechanical-solid-workflow_thumb.png differ diff --git a/doc/source/_static/gallery_thumbnails/sphx_glr_05-pymechanical-to-cdb-workflow_thumb.png b/doc/source/_static/gallery_thumbnails/sphx_glr_05-pymechanical-to-cdb-workflow_thumb.png new file mode 100644 index 0000000000..9816156d7b Binary files /dev/null and b/doc/source/_static/gallery_thumbnails/sphx_glr_05-pymechanical-to-cdb-workflow_thumb.png differ diff --git a/doc/source/_static/gallery_thumbnails/sphx_glr_06-cdb-to-pymechanical-workflow_thumb.png b/doc/source/_static/gallery_thumbnails/sphx_glr_06-cdb-to-pymechanical-workflow_thumb.png new file mode 100644 index 0000000000..128e7340fe Binary files /dev/null and b/doc/source/_static/gallery_thumbnails/sphx_glr_06-cdb-to-pymechanical-workflow_thumb.png differ diff --git a/doc/source/_templates/autosummary/base.rst b/doc/source/_templates/autosummary/base.rst new file mode 100644 index 0000000000..80e4862b95 --- /dev/null +++ b/doc/source/_templates/autosummary/base.rst @@ -0,0 +1,12 @@ +.. vale off + +{{ name | escape | underline}} + +.. currentmodule:: {{ module }} + +.. auto{{ objtype }}:: {{ objname }} + +.. minigallery:: + :add-heading: Examples using {{ objname }} + + {{ module }}.{{ objname }} diff --git a/doc/source/_templates/autosummary/class.rst b/doc/source/_templates/autosummary/class.rst new file mode 100644 index 0000000000..bb5752170c --- /dev/null +++ b/doc/source/_templates/autosummary/class.rst @@ -0,0 +1,39 @@ +.. vale off + +{{ objname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + + {% block methods %} + + {% if methods %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + :toctree: + {% for item in methods %} + {% if item != "__init__" %} + {{ name }}.{{ item }} + {% endif %} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Attributes') }} + + .. autosummary:: + :toctree: + {% for item in attributes %} + {{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + +.. minigallery:: + :add-heading: Examples using {{ objname }} + + {{ module }}.{{ objname }} diff --git a/doc/source/_templates/autosummary/experimental/base.rst.jinja2 b/doc/source/_templates/autosummary/experimental/base.rst.jinja2 new file mode 100644 index 0000000000..31470b81d1 --- /dev/null +++ b/doc/source/_templates/autosummary/experimental/base.rst.jinja2 @@ -0,0 +1,14 @@ +{{ name | escape | underline}} + +.. currentmodule:: {{ module }} + +.. warning:: + + This is an experimental feature. The API is subject to change without notice. + +.. auto{{ objtype }}:: {{ objname }} + +.. minigallery:: + :add-heading: Examples using {{ objname }} + + {{ module }}.{{ objname }} diff --git a/doc/source/_templates/autosummary/no_methods_doc/class.rst.jinja2 b/doc/source/_templates/autosummary/no_methods_doc/class.rst.jinja2 index ed6ca024ed..610ab08085 100644 --- a/doc/source/_templates/autosummary/no_methods_doc/class.rst.jinja2 +++ b/doc/source/_templates/autosummary/no_methods_doc/class.rst.jinja2 @@ -17,3 +17,9 @@ {%- endfor %} {% endif %} {% endblock %} + + +.. minigallery:: + :add-heading: Examples using {{ objname }} + + {{ module }}.{{ objname }} diff --git a/doc/source/api/dpf_integration_helpers.rst b/doc/source/api/dpf_integration_helpers.rst new file mode 100644 index 0000000000..f31e1b4072 --- /dev/null +++ b/doc/source/api/dpf_integration_helpers.rst @@ -0,0 +1,9 @@ +DPF integration helpers +----------------------- + +.. currentmodule:: ansys.acp.core.dpf_integration_helpers + +.. autosummary:: + :toctree: _autosummary + + get_dpf_unit_system diff --git a/doc/source/api/enum_types.rst b/doc/source/api/enum_types.rst index e609b287cc..4bcdf2c004 100644 --- a/doc/source/api/enum_types.rst +++ b/doc/source/api/enum_types.rst @@ -8,30 +8,53 @@ Enumeration data types :template: autosummary/no_methods_doc/class.rst.jinja2 ArrowType + BaseElementMaterialHandling BooleanOperationType - CutoffMaterialType - CutoffRuleType - DimensionType - DrapingMaterialType + CutOffGeometryOrientationType + CutOffMaterialHandling + CutOffRuleType + DrapingMaterialModel DrapingType - DropoffMaterialType - EdgeSetType + DropOffMaterialHandling + DropOffType EdgeSetType ElementalDataType + ElementTechnology + ExtrusionGuideType + ExtrusionMethod + ExtrusionType FeFormat GeometricalRuleType + HDF5CompositeCAEImportMode + HDF5CompositeCAEProjectionMode IgnorableEntity + ImportedPlyDrapingType + ImportedPlyOffsetType + ImportedPlyThicknessType + IntersectionType + LayupMappingRosetteSelectionMethod + LinkedObjectHandling LookUpTable3DInterpolationAlgorithm LookUpTableColumnValueType + MeshImportType NodalDataType OffsetType - PlyCutoffType + PhysicalDimension + PlyCutOffType PlyGeometryExportFormat PlyType + ReinforcingBehavior RosetteSelectionMethod RosetteType + SectionCutType SensorType - StatusType + SnapToGeometryOrientationType + SolidModelExportFormat + SolidModelImportFormat + SolidModelOffsetDirectionType + SolidModelSkinExportFormat + Status + StressStateType SymmetryType ThicknessFieldType ThicknessType diff --git a/doc/source/api/example_helpers.rst b/doc/source/api/example_helpers.rst index 7ddab31ee5..fdd6df5bbd 100644 --- a/doc/source/api/example_helpers.rst +++ b/doc/source/api/example_helpers.rst @@ -1,7 +1,7 @@ Example helpers --------------- -.. currentmodule:: ansys.acp.core.example_helpers +.. currentmodule:: ansys.acp.core.extras.example_helpers .. autosummary:: :toctree: _autosummary diff --git a/doc/source/api/index.rst b/doc/source/api/index.rst index b02f968c6d..bdfc694178 100644 --- a/doc/source/api/index.rst +++ b/doc/source/api/index.rst @@ -1,21 +1,32 @@ +.. _api_reference: + API reference ============= -This section describes the API of the public PyACP classes, functions, -and attributes. +.. jinja:: conditional_skip + + {% if not skip_api %} + This section describes the API of the public PyACP classes, functions, + and attributes. -.. currentmodule:: ansys.acp.core + .. currentmodule:: ansys.acp.core -.. toctree:: - :maxdepth: 2 + .. toctree:: + :maxdepth: 2 - server - tree_objects - mesh_data - linked_object_definitions - material_property_sets - enum_types - plot_utils - workflow - example_helpers - internal + server + tree_objects + mesh_data + linked_object_definitions + material_property_sets + enum_types + other_types + plot_utils + other_utils + dpf_integration_helpers + mechanical_integration_helpers + example_helpers + internal + {% else %} + The API reference is not available in this documentation build. + {% endif %} diff --git a/doc/source/api/internal.rst b/doc/source/api/internal.rst index 100101d121..a35d04a211 100644 --- a/doc/source/api/internal.rst +++ b/doc/source/api/internal.rst @@ -15,6 +15,8 @@ Internal objects _model_printer.Node _server.common.ControllableServerProtocol _server.common.ServerProtocol + _tree_objects._elemental_or_nodal_data.MeshDataT + _tree_objects._elemental_or_nodal_data.ScalarDataT _tree_objects._grpc_helpers.edge_property_list.EdgePropertyList _tree_objects._grpc_helpers.edge_property_list.GenericEdgePropertyType _tree_objects._grpc_helpers.linked_object_list.ChildT @@ -26,12 +28,10 @@ Internal objects _tree_objects._grpc_helpers.polymorphic_from_pb.CreatableFromResourcePath _tree_objects._grpc_helpers.protocols.CreateRequest _tree_objects._grpc_helpers.protocols.ObjectInfo - _tree_objects._mesh_data.MeshDataT - _tree_objects._mesh_data.ScalarDataT + _tree_objects._solid_model_export.SolidModelExportMixin _tree_objects.base.CreatableTreeObject + _tree_objects.base.ServerWrapper _tree_objects.base.TreeObject _tree_objects.base.TreeObjectBase - _tree_objects.base.ServerWrapper _tree_objects.material.property_sets.wrapper.TC _tree_objects.material.property_sets.wrapper.TV - _workflow._LocalWorkingDir diff --git a/doc/source/api/linked_object_definitions.rst b/doc/source/api/linked_object_definitions.rst index 6f2168eca1..1383ae6641 100644 --- a/doc/source/api/linked_object_definitions.rst +++ b/doc/source/api/linked_object_definitions.rst @@ -7,7 +7,8 @@ Linked object definitions :toctree: _autosummary FabricWithAngle + Lamina LinkedSelectionRule - TaperEdge + PrimaryPly SubShape - Lamina + TaperEdge diff --git a/doc/source/api/mechanical_integration_helpers.rst b/doc/source/api/mechanical_integration_helpers.rst new file mode 100644 index 0000000000..784725952e --- /dev/null +++ b/doc/source/api/mechanical_integration_helpers.rst @@ -0,0 +1,25 @@ +Mechanical integration helpers +------------------------------ + +.. warning:: + + The PyACP / PyMechanical integration is still experimental and will change + in future releases. + + It is also limited in the following ways: + + - Only remote Mechanical sessions on Windows are supported. + - Only one ACP shell or solid model can be imported into Mechanical. + + PyACP currently provides helper functions for integrating with PyMechanical. + These functions will be replaced with native PyMechanical functions in the future. + +.. currentmodule:: ansys.acp.core.mechanical_integration_helpers + +.. autosummary:: + :toctree: _autosummary + :template: autosummary/internal/base.rst.jinja2 + + export_mesh_for_acp + import_acp_composite_definitions + import_acp_mesh_from_cdb diff --git a/doc/source/api/mesh_data.rst b/doc/source/api/mesh_data.rst index 18fd954363..3246a99aa5 100644 --- a/doc/source/api/mesh_data.rst +++ b/doc/source/api/mesh_data.rst @@ -1,7 +1,7 @@ Mesh data objects ----------------- -.. currentmodule:: ansys.acp.core +.. currentmodule:: ansys.acp.core.mesh_data .. autosummary:: :toctree: _autosummary @@ -10,14 +10,16 @@ Mesh data objects AnalysisPlyNodalData BooleanSelectionRuleElementalData BooleanSelectionRuleNodalData - CutoffSelectionRuleElementalData - CutoffSelectionRuleNodalData + CutOffSelectionRuleElementalData + CutOffSelectionRuleNodalData CylindricalSelectionRuleElementalData CylindricalSelectionRuleNodalData ElementSetElementalData ElementSetNodalData GeometricalSelectionRuleElementalData GeometricalSelectionRuleNodalData + ImportedSolidModelElementalData + ImportedSolidModelNodalData MeshData ModelElementalData ModelingPlyElementalData @@ -30,6 +32,10 @@ Mesh data objects ProductionPlyElementalData ProductionPlyNodalData ScalarData + SolidElementSetElementalData + SolidElementSetNodalData + SolidModelElementalData + SolidModelNodalData SphericalSelectionRuleElementalData SphericalSelectionRuleNodalData TriangleMesh diff --git a/doc/source/api/other_types.rst b/doc/source/api/other_types.rst new file mode 100644 index 0000000000..b6cc687cef --- /dev/null +++ b/doc/source/api/other_types.rst @@ -0,0 +1,14 @@ +Other types +----------- + +.. currentmodule:: ansys.acp.core + +.. autosummary:: + :toctree: _autosummary + + CoordinateTransformation + ImportedSolidModelExportSettings + ShellMappingProperties + SolidMappingProperties + SolidModelExportSettings + DropOffSettings diff --git a/doc/source/api/workflow.rst b/doc/source/api/other_utils.rst similarity index 54% rename from doc/source/api/workflow.rst rename to doc/source/api/other_utils.rst index 4ed3357967..ea0ca088aa 100644 --- a/doc/source/api/workflow.rst +++ b/doc/source/api/other_utils.rst @@ -1,13 +1,11 @@ -Workflow --------- +Other utilities +--------------- .. currentmodule:: ansys.acp.core .. autosummary:: :toctree: _autosummary - ACPWorkflow - get_composite_post_processing_files get_model_tree - get_dpf_unit_system print_model + recursive_copy diff --git a/doc/source/api/server.rst b/doc/source/api/server.rst index d9107d0285..f6903c7291 100644 --- a/doc/source/api/server.rst +++ b/doc/source/api/server.rst @@ -6,7 +6,7 @@ Server management .. autosummary:: :toctree: _autosummary - ACP + ACPInstance ConnectLaunchConfig DirectLaunchConfig DockerComposeLaunchConfig diff --git a/doc/source/api/tree_objects.rst b/doc/source/api/tree_objects.rst index 64c0660fbb..9905adf6da 100644 --- a/doc/source/api/tree_objects.rst +++ b/doc/source/api/tree_objects.rst @@ -8,14 +8,25 @@ ACP objects AnalysisPly BooleanSelectionRule + ButtJointSequence CADComponent CADGeometry - CutoffSelectionRule + CutOffGeometry + CutOffSelectionRule CylindricalSelectionRule EdgeSet ElementSet + ExtrusionGuide Fabric + FieldDefinition GeometricalSelectionRule + ImportedAnalysisPly + ImportedModelingGroup + ImportedModelingPly + ImportedProductionPly + ImportedSolidModel + InterfaceLayer + LayupMappingObject LookUpTable1D LookUpTable1DColumn LookUpTable3D @@ -28,7 +39,12 @@ ACP objects ParallelSelectionRule ProductionPly Rosette + SamplingPoint + SectionCut Sensor + SnapToGeometry + SolidElementSet + SolidModel SphericalSelectionRule Stackup SubLaminate diff --git a/doc/source/conf.py b/doc/source/conf.py index 656ac6f26c..ef8f70dc0c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,12 +1,101 @@ """Sphinx documentation configuration file.""" from datetime import datetime +import inspect import os +import pathlib +import sys import warnings import pyvista from pyvista.plotting.utilities.sphinx_gallery import DynamicScraper from sphinx.builders.latex import LaTeXBuilder +import sphinx.util.inspect + + +def _signature( + subject, + bound_method: bool = False, + type_aliases=None, +): + """Monkeypatch for 'sphinx.util.inspect.signature'. + + This function defines a custom signature function which is used by the 'sphinx.ext.autodoc'. + The main purpose is to force / fix using the class parameter type hints instead of the class + attribute type hints. + See https://github.com/sphinx-doc/sphinx/issues/11207 for context. + """ + + # Import classes which were guarded with a 'typing.TYPE_CHECKING' explicitly here, otherwise + # the 'eval' in 'inspect.signature' will fail. + # Some imports are needed because these types occur in a dataclass base class, which is + # not in the same module as the documented class. + from collections.abc import Sequence # noqa: F401 + + import numpy as np # noqa: F401 + import pyvista # noqa: F401 + from pyvista.core.pointset import PolyData, UnstructuredGrid # noqa: F401 + + from ansys.acp.core import ( # noqa: F401 + BooleanSelectionRule, + CADComponent, + GeometricalSelectionRule, + Model, + ModelingGroup, + ) + + # Import type aliases so that they can be resolved correctly. + from ansys.acp.core._tree_objects.field_definition import ( # noqa: F401 + _SCOPE_ENTITIES_LINKABLE_TO_FIELD_DEFINITION, + ) + from ansys.acp.core._tree_objects.linked_selection_rule import ( # noqa: F401 + _LINKABLE_SELECTION_RULE_TYPES, + ) + from ansys.acp.core._tree_objects.oriented_selection_set import ( # noqa: F401 + _SELECTION_RULES_LINKABLE_TO_OSS, + ) + from ansys.acp.core._tree_objects.sensor import _LINKABLE_ENTITY_TYPES # noqa: F401 + from ansys.acp.core._tree_objects.sublaminate import _LINKABLE_MATERIAL_TYPES # noqa: F401 + from ansys.acp.core._utils.typing_helper import StrEnum + from ansys.acp.core.mesh_data import MeshData, ScalarData, VectorData # noqa: F401 + from ansys.dpf.composites.data_sources import ContinuousFiberCompositesFiles # noqa: F401 + from ansys.dpf.core import UnitSystem # noqa: F401 + import ansys.mechanical.core as pymechanical # noqa: F401 + + signature = inspect.signature(subject, locals=locals(), eval_str=True) + + if signature.parameters: + parameters = list(signature.parameters.values()) + if parameters[0].name == "self": + parameters.pop(0) + # dgresch Oct'24: + # Hack to fix the remaining issues with the signature. This is simpler than + # trying to get 'inspect.signature' to fully work, which would need to be done + # inside the 'define_create_method' function. + # I believe (speculation) the reason for this is that the 'create_' and 'add_' + # methods have an explicit __signature__ attribute, which stops the + # 'inspect.signature' from performing the 'eval'. + for i, param in enumerate(parameters): + if param.annotation in [ + "Sequence[_SCOPE_ENTITIES_LINKABLE_TO_FIELD_DEFINITION]", + "Sequence[_SELECTION_RULES_LINKABLE_TO_OSS]", + "Sequence[_LINKABLE_ENTITY_TYPES]", + "_LINKABLE_MATERIAL_TYPES", + ]: + param = param.replace(annotation=eval(param.annotation)) + # Represent StrEnum defaults as their string value, since this is + # easier to understand for library users. The enum type is still + # available in the type annotations. + if isinstance(param.default, StrEnum): + param = param.replace(default=param.default.value) + parameters[i] = param + signature = signature.replace(parameters=parameters) + return signature + + +sphinx.util.inspect.signature = _signature +napoleon_attr_annotations = False + LaTeXBuilder.supported_image_types = ["image/png", "image/pdf", "image/svg+xml"] from ansys_sphinx_theme import ( @@ -22,10 +111,19 @@ SKIP_GALLERY = os.environ.get("PYACP_DOC_SKIP_GALLERY", "0").lower() in ("1", "true") SKIP_API = os.environ.get("PYACP_DOC_SKIP_API", "0").lower() in ("1", "true") +SOURCE_DIR = pathlib.Path(__file__).parent -exclude_patterns = [] +# nested example index files are directly included in the parent index file +exclude_patterns = ["examples/*/index.rst"] if SKIP_API: - exclude_patterns.append("api/*") + # Exclude all API documentation except the top-level index file: + # Exclude files on the top level explicitly, except 'index.rst' + for file_path in (SOURCE_DIR / "api").iterdir(): + if not file_path.name == "index.rst": + pattern = str(file_path.relative_to(SOURCE_DIR).as_posix()) + exclude_patterns.append(pattern) + # Exclude all files in nested directories + exclude_patterns.append("api/**/*.rst") jinja_contexts = { @@ -70,7 +168,7 @@ html_theme_options = { "logo": "pyansys", "github_url": "https://github.com/ansys/pyacp", - "show_prev_next": False, + "show_prev_next": True, "show_breadcrumbs": True, "additional_breadcrumbs": [("PyAnsys", "https://docs.pyansys.com/")], "switcher": { @@ -88,13 +186,9 @@ "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", - "sphinx_autodoc_typehints", "numpydoc", "sphinx_copybutton", -] -if not SKIP_GALLERY: - extensions += ["sphinx_gallery.gen_gallery"] -extensions += [ + "sphinx_gallery.gen_gallery", "sphinx_design", # needed for pyvista offlineviewer directive "sphinx_jinja", "pyvista.ext.plot_directive", @@ -111,21 +205,25 @@ # "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None), "grpc": ("https://grpc.github.io/grpc/python/", None), "protobuf": ("https://googleapis.dev/python/protobuf/latest/", None), - "pyvista": ("https://docs.pyvista.org/version/stable", None), + "pyvista": ("https://docs.pyvista.org/", None), "ansys-dpf-core": ("https://dpf.docs.pyansys.com/version/stable/", None), "ansys-dpf-composites": ("https://composites.dpf.docs.pyansys.com/version/stable/", None), + "ansys-mechanical-core": ("https://mechanical.docs.pyansys.com/version/stable/", None), } nitpick_ignore = [ - ("py:class", "typing.Self"), - ("py:class", "numpy.float64"), - ("py:class", "numpy.int32"), - ("py:class", "numpy.int64"), # Ignore TypeVar / TypeAlias defined within PyACP: they are either not recognized correctly, # or misidentified as a class. ("py:class", "_PATH"), + ("py:class", "ChildT"), + ("py:class", "CreatableValueT"), ] nitpick_ignore_regex = [ + ("py:class", r"(typing\.|typing_extensions\.)?Self"), + ("py:class", r"(numpy\.typing|npt)\.NDArray"), + ("py:class", r"(numpy|np)\.float64"), + ("py:class", r"(numpy|np)\.int32"), + ("py:class", r"(numpy|np)\.int64"), ("py:class", r"ansys\.api\.acp\..*"), ("py:class", "None -- .*"), # from collections.abc # Ignore TypeVars defined within PyACP: they are either not recognized correctly, @@ -133,15 +231,16 @@ ("py:class", r"^(.*\.)?ValueT$"), ("py:class", r"^(.*\.)?TC$"), ("py:class", r"^(.*\.)?TV$"), + ("py:class", r"ansys\.acp.core\..*\.AttribT"), ("py:class", r"ansys\.acp.core\..*\.ChildT"), ("py:class", r"ansys\.acp.core\..*\.CreatableValueT"), - ("py:class", r"ansys\.acp.core\..*\.MeshDataT"), ("py:class", r"ansys\.acp.core\..*\.ScalarDataT"), + ("py:class", r"ansys\.acp.core\..*\.MeshDataT"), ] -# sphinx_autodoc_typehints configuration -typehints_defaults = "comma" -simplify_optional_unions = True +# sphinx.ext.autodoc configuration +autodoc_typehints = "description" +autodoc_typehints_description_target = "documented_params" # numpydoc configuration numpydoc_show_class_members = False @@ -171,24 +270,42 @@ r".*\.EdgePropertyList\.clear", } +if SKIP_GALLERY: + # Generate the gallery without executing the code. The gallery will not + # contain the output of the code cells. + # This is useful for more quickly building the documentation. + gallery_filename_pattern = "" +else: + if sys.platform == "win32": + gallery_filename_pattern = r".*\.py" + else: + gallery_filename_pattern = ( + r"^(?!.*pymechanical.*\.py).*\.py" # skip pymechanical examples on non-Windows + ) + +examples_dirs_base = pathlib.Path("../../examples/") +gallery_dirs_base = pathlib.Path("examples/") +example_subdir_names = ["modeling_features", "workflows", "use_cases"] + # sphinx gallery options sphinx_gallery_conf = { # convert rst to md for ipynb "pypandoc": True, # path to your examples scripts - "examples_dirs": ["../../examples/"], + "examples_dirs": [str(examples_dirs_base / subdir) for subdir in example_subdir_names], # path where to save gallery generated examples - "gallery_dirs": ["examples/gallery_examples"], + "gallery_dirs": [str(gallery_dirs_base / subdir) for subdir in example_subdir_names], # Pattern to search for example files - "filename_pattern": r"\.py", + "filename_pattern": gallery_filename_pattern, # Remove the "Download all examples" button from the top level gallery "download_all_examples": False, # Sort gallery example by filename instead of number of lines (default) "within_subsection_order": "FileNameSortKey", # directory where function granular galleries are stored - "backreferences_dir": None, - # Modules for which function level galleries are created. In - "doc_module": "ansys-acp-core", + "backreferences_dir": "api/_gallery_backreferences", + # Modules for which function level galleries are created. + "doc_module": ("ansys.acp.core"), + "exclude_implicit_doc": {"ansys\\.acp\\.core\\._.*"}, # ignore private submodules "image_scrapers": (DynamicScraper(), "matplotlib"), "ignore_pattern": r"__init__\.py", "thumbnail_size": (350, 350), @@ -205,7 +322,7 @@ templates_path = ["_templates"] # The suffix(es) of source filenames. -source_suffix = ".rst" +source_suffix = {".rst": "restructuredtext"} # The master toctree document. master_doc = "index" diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst index 9cd1fde812..eebfc14130 100644 --- a/doc/source/contributing.rst +++ b/doc/source/contributing.rst @@ -1,21 +1,23 @@ -Contributing -============ +.. _contributing: + +Contribute +========== .. include:: ../../CONTRIBUTING.md :start-after: :end-before: -For example, you could contribute by: +For example, you can make contributions as follows: -- Reporting a bug or suggesting a feature -- Giving feedback or making suggestions for improving the documentation -- Submitting a pull request to fix a bug or add a feature -- Reporting your use case, and how PyACP helped you or what is still missing +- Report a bug or suggest a feature. +- Give feedback or make suggestions for improving the documentation. +- Submit a pull request to fix a bug or add a feature. +- Report your use case, explain how PyACP has helped you, or indicate what features are still missing. -For feedback, suggestions, or bug reports, please open an `issue +For feedback, suggestions, or bug reports, open an `issue `_ on the PyACP GitHub repository. -For code changes or documentation improvements, please open a `pull request +For code changes or documentation improvements, open a `pull request `_. diff --git a/doc/source/examples/.gitignore b/doc/source/examples/.gitignore index 718bd55d42..86693017f6 100644 --- a/doc/source/examples/.gitignore +++ b/doc/source/examples/.gitignore @@ -1 +1,3 @@ -gallery_examples +* +!.gitignore +!./index.rst diff --git a/doc/source/examples/index.rst b/doc/source/examples/index.rst index d6623ccdb5..5cc4bd8c3e 100644 --- a/doc/source/examples/index.rst +++ b/doc/source/examples/index.rst @@ -1,25 +1,34 @@ .. _ref_examples: -.. - Add links to the gallery examples which would otherwise cause a warning due - to missing references +======== +Examples +======== -.. jinja:: conditional_skip +ACP modeling features +===================== - {% if skip_gallery %} - .. _sphx_glr_examples_gallery_examples_001_basic_flat_plate.py: +These examples show how to use PyACP for defining composite layups. - {% endif %} +.. include:: modeling_features/index.rst + :start-line: 2 - ======== - Examples - ======== - {% if not skip_gallery %} - .. include:: gallery_examples/index.rst - :start-line: 2 - {% else %} - .. note:: +.. _workflow_examples: - The gallery examples are not included in this build of the documentation. - {% endif %} +Workflow examples +================= + +These examples show how to combine PyACP with other tools to create a full +simulation workflow. + +.. include:: workflows/index.rst + :start-line: 2 + +Use case examples +================= + +These examples can serve as an inspiration for how you can tackle your own +use cases with PyACP. + +.. include:: use_cases/index.rst + :start-line: 2 diff --git a/doc/source/index.rst b/doc/source/index.rst index 2ed66f2fad..59f61b5a02 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,27 +1,20 @@ -.. jinja:: conditional_skip - .. toctree:: - :hidden: - :maxdepth: 3 - intro - user_guide/index - examples/index - {% if not skip_api %} - api/index - {% endif %} - contributing +.. toctree:: + :hidden: + :maxdepth: 3 + + PyACP home + intro + user_guide/index + examples/index + api/index + contributing PyACP ----- -.. note:: - - PyACP is currently released as a beta version. This means that the API may - change in future releases. We encourage you to try PyACP and provide us with - feedback. - PyACP enables modelling continuous-fiber composite structures from within your Python environment. It provides access to the features of Ansys Composite PrepPost (ACP) through a Python interface. @@ -35,48 +28,70 @@ optimization of composite structures. :gutter: 2 .. grid-item-card:: :octicon:`rocket` Getting started - :link: intro - :link-type: doc + :link: getting_started + :link-type: ref Contains installation instructions and a simple example to get you started with PyACP. .. grid-item-card:: :octicon:`tools` How-to guides - :link: user_guide/howto/index - :link-type: doc + :link: howto + :link-type: ref Guides on how to achieve specific tasks with PyACP. .. grid-item-card:: :octicon:`light-bulb` Concepts - :link: user_guide/concepts/index - :link-type: doc + :link: concepts + :link-type: ref Explains the concepts and terminology used in PyACP. .. grid-item-card:: :octicon:`play` Examples - :link: examples/index - :link-type: doc + :link: ref_examples + :link-type: ref A collection of examples demonstrating the capabilities of PyACP. .. grid-item-card:: :octicon:`file-code` API reference - {% if not skip_api %}:link: api/index - :link-type: doc - {% endif %} + :link: api_reference + :link-type: ref Describes the public Python classes, methods, and functions. .. grid-item-card:: :octicon:`code` Contributing :link: contributing - :link-type: doc + :link-type: ref Information on how to contribute to PyACP. + .. grid-item-card:: :octicon:`mortar-board` ACP documentation + :link: https://ansyshelp.ansys.com/public/?returnurl=/Views/Secured/prod_page.html?pn=Composites&pid=CompositePrepPost + :columns: 12 + + Describes the modeling features and techniques of Ansys Composite PrepPost. + + +.. _limitations: Limitations ^^^^^^^^^^^ -* Only shell workflows are supported, solid models can not yet be defined using PyACP -* FieldDefinitions for variable material properties are not supported -* Butt joint sequences and interface layers are not supported -* Section cuts are not supported +* Field definitions are currently only supported through (Py)Mechanical. + The workflow from PyACP to (Py)MADL ignores field definitions. +* The PyACP to PyMADL workflow does not fully support variable materials. +* The PyACP to PyMechanical workflow is experimental and has the following limitations: + + * It only works on Windows, with a remote (not embedded) PyMechanical session. + * Only one ACP shell or solid model is supported at a time. + * Named selections defined in ACP are not transferred to PyMechanical. + * The ``ansys.acp.core.mechanical_integration_helpers`` module may be + changed or removed in future versions, when the corresponding features + are available in PyMechanical directly. + * Post-processing with the Composite Failure Tool inside Mechanical is not + yet supported. + +* Visualization and mesh data of imported plies are not supported yet. +* Section cuts cannot be visualized. +* Sampling point analysis data is not available. +* Imported solid model mapping statistics are not available. +* Sensor by solid model is not yet supported. diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 64b29c95cf..e06d2026fc 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -1,3 +1,5 @@ +.. _getting_started: + Getting started --------------- @@ -8,7 +10,7 @@ PyACP supports Ansys 2024 R2 and later. To install PyACP, run the following comm .. code-block:: bash - pip install ansys-acp-core[examples] + pip install ansys-acp-core[all] You should use a `virtual environment `_, because it keeps Python packages isolated from your system Python. @@ -55,27 +57,23 @@ Get a model You can resume a model from an existing ACP DB (ACPH5) or built it from scratch by importing an FE model (mesh). -To load an existing model with PyACP, use the :meth:`.ACPWorkflow.from_acph5_file` method: +To load an existing model with PyACP, use the :meth:`.import_model` method: .. testcode:: - workflow = pyacp.ACPWorkflow.from_acph5_file( - acp=acp, - acph5_file_path="model.acph5", - ) - model = workflow.model + model = acp.import_model("model.acph5") -To import an FE model, use the :meth:`.ACPWorkflow.from_cdb_or_dat_file` method. +To import an FE model, use the ``format="ansys:cdb"`` or ``format="ansys:dat"`` +parameter, respectively. The following example imports a CDB file. .. testcode:: - workflow = pyacp.ACPWorkflow.from_cdb_or_dat_file( - acp=acp, - cdb_or_dat_file_path="model.cdb", + model = acp.import_model( + "model.cdb", + format="ansys:cdb", unit_system=pyacp.UnitSystemType.MPA, ) - model = workflow.model .. testcode:: :hide: @@ -153,9 +151,9 @@ Continue exploring This is just a brief introduction to PyACP. To learn more: -- Check out the `examples `_ to see complete examples of how to use PyACP. -- The `how-to guides `_ provide instructions on how to perform specific tasks. -- The `API reference `_ provides detailed information on all available classes and methods. +- Check out the :ref:`examples ` to see complete examples of how to use PyACP. +- The :ref:`how-to guides ` provide instructions on how to perform specific tasks. +- The :ref:`API reference ` provides detailed information on all available classes and methods. .. testcode:: :hide: diff --git a/doc/source/user_guide/compatibility.rst b/doc/source/user_guide/compatibility.rst new file mode 100644 index 0000000000..f9558757a0 --- /dev/null +++ b/doc/source/user_guide/compatibility.rst @@ -0,0 +1,118 @@ +Compatibility +============= + +Server version compatibility +---------------------------- + +PyACP is compatible with all versions of the ACP gRPC server since version 2024R2. + +However, some features are not available when using older versions of the server. +Version 2025R1 is the first full release of the ACP gRPC server, which makes +almost all features of ACP available through PyACP. + +Added in 2025R1 +~~~~~~~~~~~~~~~ + +The following features were added in version 2025R1 of the ACP gRPC server. + +Tree objects +'''''''''''' + +- :class:`.ButtJointSequence` +- :class:`.CutOffGeometry` +- :class:`.ExtrusionGuide` +- :class:`.FieldDefinition` +- :class:`.ImportedAnalysisPly` +- :class:`.ImportedModelingPly` +- :class:`.ImportedProductionPly` +- :class:`.ImportedSolidModel` +- :class:`.InterfaceLayer` +- :class:`.LayupMappingObject` +- :class:`.SamplingPoint` +- :class:`.SectionCut` +- :class:`.SnapToGeometry` +- :class:`.SolidElementSet` +- :class:`.SolidModel` + +Methods +''''''' + +- :meth:`.Model.import_hdf5_composite_cae` +- :meth:`.Model.export_hdf5_composite_cae` +- :meth:`.Model.import_materials` +- :meth:`.Model.export_modeling_ply_geometries` + +Other features +'''''''''''''' + +- Mesh attributes for classes other than the :class:`.Model` class. +- The ``.shell_mesh`` and ``.solid_mesh`` attributes. + + +Upgrading PyACP +--------------- + +The following section describes how to upgrade to newer versions of PyACP. + +Upgrading from the beta version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The beta version of PyACP did not yet provide a stable API. Consequently, some +backwards-incompatible changes were made in the first stable release to improve +the API. + +If you encounter any difficulties upgrading from the beta version, feel free to +open an `issue `_ on the PyACP GitHub +repository. + +Removed features +'''''''''''''''' + +- The ``ACPWorkflow`` class for managing file up- and download was removed. Instead, + file up- and download is now managed automatically by default. You can directly + use the :meth:`.ACPInstance.import_model` method for importing models, and methods + such as :meth:`.Model.save`, :meth:`.Model.export_analysis_model`, or + :meth:`.Model.export_hdf5_composite_cae` for saving / exporting data. + See the :ref:`file management section ` for more information. +- The ``get_composite_post_processing_files`` function was removed, since it only + covered the shell workflow. Instead, you can directly use the ``ansys.dpf.composites`` + API, as shown in the :ref:`workflow examples `. + +New submodules +'''''''''''''' + +Some features were moved into submodules instead of being exposed at the top level +``ansys.acp.core`` module: + +- Elemental, nodal, and mesh data types were moved to the ``ansys.acp.core.mesh_data`` submodule. +- The ``example_helpers`` submodule was moved to the ``ansys.acp.core.extras`` submodule. +- The ``get_dpf_unit_system`` function was moved to the ``ansys.acp.core.dpf_integration_helpers`` submodule. + + +Renamed classes +''''''''''''''' + +The following classes were renamed: + +- ``ACP`` renamed to ``ACPInstance``. +- ``DrapingMaterialType`` renamed to ``DrapingMaterialModel``. +- ``StatusType`` renamed to ``Status``. +- ``DimensionType`` renamed to ``PhysicalDimension``. +- ``CutoffMaterialType`` renamed to ``CutOffMaterialType``. +- ``CutoffRuleType`` renamed to ``CutOffRuleType``. +- ``CutoffSelectionRule`` renamed to ``CutOffSelectionRule``. +- ``CutoffSelectionRuleElementalData`` renamed to ``CutOffSelectionRuleElementalData`` and moved to ``ansys.acp.core.mesh_data``. +- ``CutoffSelectionRuleNodalData`` renamed to ``CutOffSelectionRuleNodalData`` and moved to ``ansys.acp.core.mesh_data``. +- ``PlyCutoffType`` renamed to ``PlyCutOffType``. +- ``DropoffMaterialType`` renamed to ``DropOffMaterialType``. + + +Renamed attributes +'''''''''''''''''' + +The following attributes were renamed: + +- ``dimension_type`` renamed to ``physical_dimension`` on the ``LookUpTable1DColumn`` and ``LookUpTable3DColumn`` classes. +- ``draping_type`` renamed to ``draping`` on the ``ModelingPly`` class. +- ``include_rule_type`` renamed to ``include_rule`` on all selection rule classes. +- ``relative_rule_type`` renamed to ``relative_rule`` on all selection rule classes. diff --git a/doc/source/user_guide/concepts/index.rst b/doc/source/user_guide/concepts/index.rst index 3b96c1a561..7f4f4d87e6 100644 --- a/doc/source/user_guide/concepts/index.rst +++ b/doc/source/user_guide/concepts/index.rst @@ -1,3 +1,5 @@ +.. _concepts: + Concepts -------- diff --git a/doc/source/user_guide/concepts/material_property_sets.rst b/doc/source/user_guide/concepts/material_property_sets.rst index 0985ce43c3..69b4287d9d 100644 --- a/doc/source/user_guide/concepts/material_property_sets.rst +++ b/doc/source/user_guide/concepts/material_property_sets.rst @@ -70,7 +70,7 @@ you must redefine the stress and strain limits. For engineering constants, however, the orthotropic and isotropic definitions are interlinked. Therefore, when you change the ply type of a material, the engineering constants are automatically converted. -To avoid accidental use of incorrect engineering constants, PyACP enforces +To avoid accidental use of incorrect engineering constants, PyACP enforces conversion and assignment rules, as described later on this page. Conversion rules @@ -94,8 +94,7 @@ The following rules apply when changing the ply type of a material: >>> import ansys.acp.core as pyacp >>> acp = pyacp.launch_acp() - >>> path = acp.upload_file("../tests/data/minimal_complete_model_no_matml_link.acph5") - >>> model = acp.import_model(path=path) + >>> model = acp.import_model("../tests/data/minimal_complete_model_no_matml_link.acph5") Consider the following example: diff --git a/doc/source/user_guide/concepts/store.rst b/doc/source/user_guide/concepts/store.rst index 95aa389b70..878d276b10 100644 --- a/doc/source/user_guide/concepts/store.rst +++ b/doc/source/user_guide/concepts/store.rst @@ -29,7 +29,7 @@ Consider the following example. First, launch an ACP instance and import a model .. testcode:: :hide: - path = acp.upload_file("../tests/data/minimal_complete_model_no_matml_link.acph5") + path = "../tests/data/minimal_complete_model_no_matml_link.acph5" .. doctest:: @@ -92,11 +92,6 @@ You may also use the :meth:`clone <.Material.clone>` method to copy an object be >>> acp2 = pyacp.launch_acp() -.. testcode:: - :hide: - - path = acp2.upload_file("../tests/data/minimal_complete_model_no_matml_link.acph5") - .. doctest:: >>> # path = ... # path to another model file diff --git a/doc/source/user_guide/howto/create_input_file.rst b/doc/source/user_guide/howto/create_input_file.rst index 283c7683f2..758775e499 100644 --- a/doc/source/user_guide/howto/create_input_file.rst +++ b/doc/source/user_guide/howto/create_input_file.rst @@ -1,20 +1,39 @@ .. _input_file_for_pyacp: Create input file for PyACP ---------------------------- +=========================== To start working with PyACP, an input file that contains the mesh is required. PyACP supports reading -the mesh from CDB and DAT files with the :meth:`.ACPWorkflow.from_cdb_or_dat_file()` method. PyACP reads the mesh, including any coordinate systems, element sets, -edge sets, and materials from the input file. Once the layup has been created with PyACP, you can export its CDB file with the :meth:`.ACPWorkflow.get_local_cdb_file` method. This file -contains all the data from the initial input file along with the layup information and -materials added by PyACP. An attempt is made to preserve the original input file as much as possible. -This includes the original mesh, materials, and boundary conditions. Therefore, you may directly use the exported CDB file -for an analysis through PyMAPDL. For more information, see :ref:`sphx_glr_examples_gallery_examples_001_basic_flat_plate.py`. +the mesh from: -.. _input_file_from_mechanical: +#. CDB or DAT files, using the :meth:`.ACPInstance.import_model()` method with the ``"ansys::cdb"`` or ``"ansys:dat"`` format. +#. HDF5 transfer files generated by Mechanical, using the :meth:`.ACPInstance.import_model()` method with the ``"ansys::h5"`` format. -Create an input file with Ansys Mechanical -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +PyACP reads the mesh, including any coordinate systems, element sets, and edge sets +from the input file. In the case of CDB or DAT files, PyACP also reads the materials +from the input file. +Once the layup has been created with PyACP, you can export the model for downstream analysis. + +.. important:: + + The :meth:`.Model.export_analysis_model` method to *export* a CDB file from PyACP is only + available if the input was read from a CDB or DAT file. + If the input was read from a Mechanical HDF5 transfer file, you can export the model + either to the HDF5 Composite CAE format, or to transfer formats to PyMechanical. + See the :ref:`workflow examples ` for more information. + +The following sections describe how to create the input file in CDB or Mechanical HDF5 format. + +Create a CDB input file +----------------------- + +.. _cdb_file_from_mechanical: + +Create a CDB file with Ansys Mechanical +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the Mechanical GUI +''''''''''''''''''''' One way to create an input file for PyACP is to create a static structural setup and export the solver input file. To do this, follow these steps: @@ -28,13 +47,17 @@ One way to create an input file for PyACP is to create a static structural setup * Save the input file to a DAT file in the desired location. -The created input file can be read with the :meth:`.ACPWorkflow.from_cdb_or_dat_file` method. -For a complete example, see :ref:`sphx_glr_examples_gallery_examples_001_basic_flat_plate.py`. +For a complete example, see :ref:`pymapdl_workflow_example`. .. note:: The imported model always contains the dummy material named ``1`` that was assigned to the geometry. +With scripting +'''''''''''''' + +You can also create a CDB file from Mechanical or PyMechanical with scripting, using the ``WriteInputFile`` method of the analysis object. See :ref:`pymechanical_to_cdb_example` for a complete example. + Create an input file with Ansys Mechanical APDL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -49,13 +72,17 @@ You can also create an input file for PyACP by performing these steps: CDWRITE,ALL,FILE,cdbfile.cdb -The created input file can be read with :meth:`.ACPWorkflow.from_cdb_or_dat_file`. See -:ref:`sphx_glr_examples_gallery_examples_001_basic_flat_plate.py` for a complete example. +See :ref:`pymapdl_workflow_example` for a complete example. Notes on material handling ~~~~~~~~~~~~~~~~~~~~~~~~~~ Materials present in the input file (\*.cdb or \*.dat) are read into PyACP. The following rules apply: -* If the material has defined a UVID, then the material is imported as locked. This means the material cannot be edited in PyACP. If the input file was created with Ansys Mechanical (see :ref:`input_file_from_mechanical`), this is always the case. In Mechanical APDL, you can define a UVID with the ``MP,UVID`` or ``MPDATAT,UNBL,16,UVID`` command. +* If the material has defined a UVID, then the material is imported as locked. This means the material cannot be edited in PyACP. If the input file was created with Ansys Mechanical (see :ref:`cdb_file_from_mechanical`), this is always the case. In Mechanical APDL, you can define a UVID with the ``MP,UVID`` or ``MPDATAT,UNBL,16,UVID`` command. * If the material has no UVID, then the material is copied on import. Only the copied material appears in PyACP. The original material is not changed and appears unmodified in the output file. + +Create a Mechanical HDF5 transfer file (experimental) +----------------------------------------------------- + +The Mechanical to ACP HDF5 transfer file can be created using the :func:`.export_mesh_for_acp` helper function. See :ref:`pymechanical_shell_example` or :ref:`pymechanical_solid_example` for complete examples. diff --git a/doc/source/user_guide/howto/file_management.rst b/doc/source/user_guide/howto/file_management.rst index d79e11bf1c..c4c785e4da 100644 --- a/doc/source/user_guide/howto/file_management.rst +++ b/doc/source/user_guide/howto/file_management.rst @@ -1,13 +1,7 @@ -Manage input and output files ------------------------------ - -When defining your workflow using PyACP and other tools, you may need control -over where the input and output files are stored. This guide shows two -ways to manage them. +.. _file_management: -In the following examples, the ACP instance is launched on a remote server. The -differences between local and remote ACP instances, in terms of file management, -are explained afterwards in :ref:`local_vs_remote`. +Manage input and output files +============================= .. doctest:: :hide: @@ -25,94 +19,82 @@ are explained afterwards in :ref:`local_vs_remote`. >>> doctest_tempdir = tempfile.TemporaryDirectory() >>> os.chdir(doctest_tempdir.name) +When defining your workflow using PyACP and other tools, you may need control +over where the input and output files are stored. There are two main ways to +manage files: auto-transfer mode and manual file management. -.. doctest:: +.. note:: - >>> import ansys.acp.core as pyacp - >>> acp = pyacp.launch_acp() + When using a local ACP instance (``"direct"`` launch mode), the auto-transfer + and manual modes are identical, as long as the current working directory is + not changed after launching the ACP instance. +Auto-transfer mode +------------------ -Using a predefined workflow -''''''''''''''''''''''''''' +When passing the ``auto_transfer_files=True`` parameter to :func:`.launch_acp` +(the default behavior), PyACP automatically uploads files to the ACP instance +and downloads output files to the local machine. -The simplest way to manage files is by using the :class:`.ACPWorkflow` class. This class -uses predetermined filenames and automatically handles uploading and downloading files. +Paths passed to the PyACP functions are all paths on the local machine. They +can be either absolute paths, or relative to the current working directory of +the Python instance. -Loading input files -~~~~~~~~~~~~~~~~~~~ +The only exception is the ``external_path`` attribute of the :class:`.CADGeometry` +and :class:`.ImportedSolidModel` classes. This attribute refers to a path on the +server side. It can again be an absolute path, or relative to the ACP instance's +working directory. +You can instead use the :meth:`.CADGeometry.refresh` and +:meth:`.ImportedSolidModel.refresh` methods to define the input file, which also +handles the upload automatically. -To get started with loading input files, you must define a workflow using either an -FE model (CDB or DAT) file or an ACP model (ACPH5) file. +.. note:: -The following example assumes that you have a directory, ``DATA_DIRECTORY``, that contains an ``input_file.cdb`` file. + On local ACP instances, the up- and download methods simply convert the + paths to be relative to the ACP instance's working directory if needed. -.. doctest:: - >>> DATA_DIRECTORY - PosixPath('...') - >>> list(DATA_DIRECTORY.iterdir()) - [PosixPath('.../input_file.cdb')] +Loading input files +~~~~~~~~~~~~~~~~~~~ -Create an :class:`.ACPWorkflow` instance that works with this file using -the :meth:`.ACPWorkflow.from_cdb_or_dat_file` method: +To load an input file, pass its path on your local machine to the +:meth:`.import_model` method: .. doctest:: - >>> workflow = pyacp.ACPWorkflow.from_cdb_or_dat_file( - ... acp=acp, - ... cdb_or_dat_file_path=DATA_DIRECTORY / "input_file.cdb", + >>> import ansys.acp.core as pyacp + >>> acp = pyacp.launch_acp() + >>> # DATA_DIRECTORY is a directory containing the input file + >>> model = acp.import_model( + ... DATA_DIRECTORY / "input_file.cdb", + ... format="ansys:cdb", ... unit_system=pyacp.UnitSystemType.MPA, ... ) - -That uploads the file to the ACP instance and creates a model from it. You -can access the newly created model using the ``workflow.model`` command: - -.. doctest:: - - >>> workflow.model + >>> model Getting output files ~~~~~~~~~~~~~~~~~~~~ -Use the workflow's ``get_local_*()`` methods to create and download -output files. For example, to get the ACPH5 file of the model, use the -:meth:`.get_local_acph5_file` method: +When getting output files, pass the desired path on your local machine to the +export / save method. .. doctest:: - >>> model = workflow.model - >>> model.name = "My model" - >>> workflow.get_local_acph5_file() - PosixPath('/tmp/.../My model.acph5') - -Note that the filename is based on the model name. - -Using a custom working directory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, the output files are stored in a temporary directory. You can -specify a custom working directory using the ``local_working_directory`` argument of -the :class:`.ACPWorkflow` constructor: - -.. doctest:: - - >>> workflow = pyacp.ACPWorkflow.from_cdb_or_dat_file( - ... acp=acp, - ... cdb_or_dat_file_path=DATA_DIRECTORY / "input_file.cdb", - ... unit_system=pyacp.UnitSystemType.MPA, - ... local_working_directory=pathlib.Path("."), - ... ) - -Any produced output files are now stored in the custom working directory. Input files -are also copied to this directory before being uploaded to the ACP instance. + >>> import os + >>> model.save("output_file.acph5") + >>> "output_file.acph5" in os.listdir() + True Manual file management -'''''''''''''''''''''' +---------------------- -To get more control over where files are stored, you can manually upload and -download them to the server and specify their names. +When passing ``auto_transfer_files=False`` to :func:`.launch_acp`, PyACP does not +automatically upload or download files. + +In this case, you need to manually manage the up- and download of files, as +described in the following sections. Loading input files ~~~~~~~~~~~~~~~~~~~ @@ -122,6 +104,7 @@ using the :meth:`.upload_file` method: .. doctest:: + >>> acp = pyacp.launch_acp(auto_transfer_files=False) >>> uploaded_path = acp.upload_file(DATA_DIRECTORY / "input_file.cdb") >>> uploaded_path PurePosixPath('input_file.cdb') @@ -145,9 +128,17 @@ Getting output files To get the ACPH5 file, it must be stored on the server. You can manually do that using the model's :meth:`.save` method: +.. doctest:: + :hide: + + >>> # need to delete the file since it was created in the previous example + >>> pathlib.Path("output_file.acph5").unlink(missing_ok=True) + .. doctest:: >>> model.save("output_file.acph5") + >>> "output_file.acph5" in os.listdir() + False Then, you can download the file using the :meth:`.download_file` method of the ACP instance: @@ -155,42 +146,11 @@ instance: .. doctest:: >>> acp.download_file( - ... remote_filename="output_file.acph5", local_path="output_file_downloaded.acph5" + ... remote_path="output_file.acph5", + ... local_path="output_file_downloaded.acph5", ... ) - - -.. _local_vs_remote: - -Local versus remote ACP instance -'''''''''''''''''''''''''''''''' - -In the preceding examples, ACP ran on a remote server. However, -you can also launch ACP as a process on your local machine. For information on launching -ACP locally, see :ref:`launch_configuration`. - -When the ACP instance is local, you can use the same code described previously. However, -the effects are slightly different: - -When using a workflow -~~~~~~~~~~~~~~~~~~~~~ - -- The input file is still copied to the ``local_working_directory`` argument, but then it is loaded - directly into the ACP instance. There is no separate upload step. -- The output files are stored in the ``local_working_directory`` argument by default. - - -When using manual upload and download -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- The :meth:`.upload_file` method has no effect and simply returns the input file path. -- The :meth:`.download_file` method copies the file to the specified ``local_path`` argument if - the ``local_path`` and ``remote_filename`` arguments are not the same. - -.. hint:: - - Even when they have no effect, it is good practice to include the upload and download - steps in your code. That way, both local and remote ACP instances can use it. - + >>> "output_file_downloaded.acph5" in os.listdir() + True .. doctest:: :hide: diff --git a/doc/source/user_guide/howto/index.rst b/doc/source/user_guide/howto/index.rst index 3b00320ff9..01e827b639 100644 --- a/doc/source/user_guide/howto/index.rst +++ b/doc/source/user_guide/howto/index.rst @@ -1,3 +1,5 @@ +.. _howto: + How-to guides ------------- diff --git a/doc/source/user_guide/howto/launch_configuration.rst b/doc/source/user_guide/howto/launch_configuration.rst index 060f82c233..3f2e82e4eb 100644 --- a/doc/source/user_guide/howto/launch_configuration.rst +++ b/doc/source/user_guide/howto/launch_configuration.rst @@ -94,7 +94,7 @@ This parameter expects a configuration object matching the selected ``launch_mod acp = pyacp.launch_acp( config=pyacp.DockerComposeLaunchConfig( - image_name_pyacp="ghcr.io/ansys/acp:latest", + image_name_acp="ghcr.io/ansys/acp:latest", image_name_filetransfer="ghcr.io/ansys/tools-filetransfer:latest", keep_volume=True, license_server=f"1055@{os.environ['LICENSE_SERVER']}", diff --git a/doc/source/user_guide/howto/print_model.rst b/doc/source/user_guide/howto/print_model.rst index 70ec3c084f..3930f7f901 100644 --- a/doc/source/user_guide/howto/print_model.rst +++ b/doc/source/user_guide/howto/print_model.rst @@ -8,8 +8,7 @@ A tree structure gives an overview of an ACP model. To print a model's tree stru import ansys.acp.core as pyacp acp = pyacp.launch_acp() - path = acp.upload_file("../tests/data/minimal_complete_model_no_matml_link.acph5") - model = acp.import_model(path=path) + model = acp.import_model("../tests/data/minimal_complete_model_no_matml_link.acph5") .. doctest:: @@ -22,28 +21,27 @@ You can print the tree structure using the :func:`.print_model` function: .. doctest:: >>> pyacp.print_model(model) - Model - Material Data - Materials - Structural Steel - Fabrics - Fabric.1 + 'ACP Model' + Materials + 'Structural Steel' + Fabrics + 'Fabric.1' Element Sets - All_Elements + 'All_Elements' Edge Sets - ns_edge - Geometry + 'ns_edge' Rosettes - Global Coordinate System - Lookup Tables - Selection Rules + 'Global Coordinate System' Oriented Selection Sets - OrientedSelectionSet.1 + 'OrientedSelectionSet.1' Modeling Groups - ModelingGroup.1 - ModelingPly.1 - ProductionPly - P1L1__ModelingPly.1 + 'ModelingGroup.1' + Modeling Plies + 'ModelingPly.1' + Production Plies + 'P1__ModelingPly.1' + Analysis Plies + 'P1L1__ModelingPly.1' @@ -53,16 +51,66 @@ Alternatively, you can use :func:`.get_model_tree` to get a tree representation. >>> tree_root = pyacp.get_model_tree(model) >>> tree_root.label - 'Model' + "'ACP Model'" >>> for child in tree_root.children: ... print(child.label) ... - Material Data + Materials + Fabrics Element Sets Edge Sets - Geometry Rosettes - Lookup Tables - Selection Rules Oriented Selection Sets Modeling Groups + + +The ``hide_empty`` label can be set to ``False`` to also show empty groups: + +.. doctest:: + + >>> pyacp.print_model(model, hide_empty=False) + 'ACP Model' + Materials + 'Structural Steel' + Fabrics + 'Fabric.1' + Stackups + Sublaminates + Element Sets + 'All_Elements' + Edge Sets + 'ns_edge' + Cad Geometries + Virtual Geometries + Rosettes + 'Global Coordinate System' + Lookup Tables 1d + Lookup Tables 3d + Parallel Selection Rules + Cylindrical Selection Rules + Spherical Selection Rules + Tube Selection Rules + Cut Off Selection Rules + Geometrical Selection Rules + Variable Offset Selection Rules + Boolean Selection Rules + Oriented Selection Sets + 'OrientedSelectionSet.1' + Modeling Groups + 'ModelingGroup.1' + Modeling Plies + 'ModelingPly.1' + Production Plies + 'P1__ModelingPly.1' + Analysis Plies + 'P1L1__ModelingPly.1' + Interface Layers + Butt Joint Sequences + Imported Modeling Groups + Sampling Points + Section Cuts + Solid Models + Imported Solid Models + Sensors + Field Definitions + diff --git a/doc/source/user_guide/howto/view_model_in_acp_gui.rst b/doc/source/user_guide/howto/view_model_in_acp_gui.rst index 0c262960c6..b082d77c40 100644 --- a/doc/source/user_guide/howto/view_model_in_acp_gui.rst +++ b/doc/source/user_guide/howto/view_model_in_acp_gui.rst @@ -3,7 +3,6 @@ View model in ACP GUI --------------------- -To view the PyACP model in the ACP GUI, save the model to a file using the :meth:`.ACPWorkflow.get_local_acph5_file` method and then open the saved file in ACP by selecting **File > Open**. +To view the PyACP model in the ACP GUI, save the model to a file using the :meth:`.Model.save` method and then open the saved file in ACP by selecting **File > Open**. In the ACP GUI, reload the model from its context menu by right-clicking the model's name in the tree and selecting **Reload Model**. A common workflow is to save the model to a file at the end of the script and reload it in ACP after each script run. - diff --git a/doc/source/user_guide/howto/visualize_model.rst b/doc/source/user_guide/howto/visualize_model.rst index dd357c9640..b86c14ee04 100644 --- a/doc/source/user_guide/howto/visualize_model.rst +++ b/doc/source/user_guide/howto/visualize_model.rst @@ -13,21 +13,20 @@ Visualize model >>> import tempfile >>> import pathlib >>> import ansys.acp.core as pyacp - >>> from ansys.acp.core import example_helpers + >>> from ansys.acp.core.extras import ExampleKeys, get_example_file >>> acp = pyacp.launch_acp() >>> tempdir = tempfile.TemporaryDirectory() - >>> input_file = example_helpers.get_example_file( - ... example_helpers.ExampleKeys.RACE_CAR_NOSE_ACPH5, pathlib.Path(tempdir.name) + >>> input_file = get_example_file( + ... ExampleKeys.RACE_CAR_NOSE_ACPH5, pathlib.Path(tempdir.name) ... ) - >>> path = acp.upload_file(input_file) - >>> model = acp.import_model(path=path) + >>> model = acp.import_model(input_file) - >>> input_file_geometry = example_helpers.get_example_file( - ... example_helpers.ExampleKeys.RACE_CAR_NOSE_STEP, pathlib.Path(tempdir.name) + >>> input_file_geometry = get_example_file( + ... ExampleKeys.RACE_CAR_NOSE_STEP, pathlib.Path(tempdir.name) ... ) - >>> path_geometry = acp.upload_file(input_file_geometry) - >>> model.create_cad_geometry(name="nose_geometry", external_path=path_geometry) + >>> cad_geometry = model.create_cad_geometry(name="nose_geometry") + >>> cad_geometry.refresh(input_file_geometry) >>> model.update() @@ -41,6 +40,14 @@ Visualize model >>> model.mesh.to_pyvista().plot() + You can also access and plot the mesh data for specific tree objects. For example, the following code plots the mesh for a modeling ply. + + .. pyvista-plot:: + :context: + + >>> modeling_ply = model.modeling_groups['nose'].modeling_plies['mp.nose.4'] + >>> modeling_ply.mesh.to_pyvista().plot() + .. _directions_plotter: @@ -54,7 +61,6 @@ Visualize model .. pyvista-plot:: :context: - >>> modeling_ply = model.modeling_groups['nose'].modeling_plies['mp.nose.4'] >>> elemental_data = modeling_ply.elemental_data >>> directions_plotter = pyacp.get_directions_plotter( ... model=model, @@ -69,6 +75,23 @@ Visualize model The color scheme used in this plot for the various components matches the ACP GUI. + The directions plot can be scoped to a specific region of the model by using the ``mesh`` parameter. For example, the following code only plots the part covered by the modeling ply. + + .. pyvista-plot:: + :context: + + >>> directions_plotter = pyacp.get_directions_plotter( + ... model=model, + ... mesh=modeling_ply.mesh, + ... components=[ + ... elemental_data.orientation, + ... elemental_data.fiber_direction + ... ], + ... length_factor=10., + ... culling_factor=10, + ... ) + >>> directions_plotter.show() + Showing the mesh data ~~~~~~~~~~~~~~~~~~~~~ @@ -89,6 +112,14 @@ Visualize model >>> pyvista_mesh = thickness_data.get_pyvista_mesh(mesh=model.mesh) >>> pyvista_mesh.plot() + Again, the ``mesh`` parameter can be used to limit the scope of the plot. + + .. pyvista-plot:: + :context: + + >>> pyvista_mesh = thickness_data.get_pyvista_mesh(mesh=model.element_sets["els_wing_assembly"].mesh) + >>> pyvista_mesh.plot() + Vector data ''''''''''' @@ -144,6 +175,12 @@ Visualize model >>> acp.stop(timeout=0) + Showing solid model + ~~~~~~~~~~~~~~~~~~~ + + The visualization of a solid mesh and its elemental data is shown in the example :ref:`solid_model_example`. + + {% else %} .. note:: diff --git a/doc/source/user_guide/index.rst b/doc/source/user_guide/index.rst index d710537dc0..66b4701e6c 100644 --- a/doc/source/user_guide/index.rst +++ b/doc/source/user_guide/index.rst @@ -11,3 +11,5 @@ section provides in-depth explanations. howto/index concepts/index + compatibility + security_considerations diff --git a/doc/source/user_guide/security_considerations.rst b/doc/source/user_guide/security_considerations.rst new file mode 100644 index 0000000000..220b32ab3d --- /dev/null +++ b/doc/source/user_guide/security_considerations.rst @@ -0,0 +1,67 @@ +Security considerations +======================= + +This section provides information on security considerations for the use +of PyACP. It is important to understand the capabilities which PyACP +provides, especially when using it to build apps or scripts that accept +untrusted input. + +.. _security_launch_acp: + +Launching ACP +------------- + +The :py:func:`.launch_acp` function has different security implications depending +on the launch mode used: + +Direct launch +^^^^^^^^^^^^^ + +When using the ``"direct"`` launch mode: + +- The executable which is launched is configurable either in the function + parameters, or in the ``ansys-tools-local-product-launcher`` configuration + file. This may allow an attacker to launch arbitrary executables on the system. +- The standard output and standard error file paths are configurable. This may + be used to overwrite arbitrary files on the system. + +When exposing the ``"direct"`` launch mode to untrusted users, it is important +to validate that the executable path and file paths are safe, or hard-code +them in the app. + +Docker compose launch +^^^^^^^^^^^^^^^^^^^^^ + +The ``"docker_compose"`` launch mode executes the ``docker`` or ``docker-compose`` +commands on the system. + +This may pose the following risks: + +- If the user can override which container is launched, they may be able to + launch arbitrary containers on the system. This is especially problematic + if ``docker`` is configured to run with elevated privileges. +- If the user can override the ``docker`` or ``docker-compose`` executable + in the environment, they may be able to execute arbitrary commands on the + system. + +When exposing the ``"docker_compose"`` launch mode to untrusted users, it is important +to validate that the container being launched, and control the environment the +command is executed in. + +Connect launch +^^^^^^^^^^^^^^ + +The ``"connect"`` launch mode connects to an existing ACP server. This mode does +not pose any particular security risks, besides allowing access to a port on the +system. + +.. _security_file_upload_download: + +File up- and downloads +---------------------- + +The :py:meth:`.ACPInstance.upload_file` and :py:meth:`.ACPInstance.download_file` methods create files +on the local or remote machine, without any validation of the file content or path. + +When exposing these methods to untrusted users, it is important to validate that +only files that are safe to be uploaded or downloaded are processed. diff --git a/doc/styles/config/vocabularies/ANSYS/accept.txt b/doc/styles/config/vocabularies/ANSYS/accept.txt index 019c433cb3..291d7aeeed 100644 --- a/doc/styles/config/vocabularies/ANSYS/accept.txt +++ b/doc/styles/config/vocabularies/ANSYS/accept.txt @@ -8,3 +8,6 @@ GUI Mechanical APDL API +untrusted +DPF +2025R1 diff --git a/docker-compose/docker-compose-benchmark.yaml b/docker-compose/docker-compose-benchmark.yaml index 5d499d1630..5f3ad26450 100644 --- a/docker-compose/docker-compose-benchmark.yaml +++ b/docker-compose/docker-compose-benchmark.yaml @@ -1,11 +1,10 @@ -version: '3.8' services: acp-grpc-server: restart: unless-stopped # This docker-compose file should be used only with the container generated from # 'ConnectionTest.Dockerfile', since that image switches from the 'root' user # to the 'container' user in the ENTRYPOINT script. - image: ${IMAGE_NAME_PYACP:-pyacp-benchmark-runner} + image: ${IMAGE_NAME_ACP:-pyacp-benchmark-runner} command: [ "${PYACP_DELAY:-100ms}", @@ -15,7 +14,7 @@ services: environment: - ANSYSLMD_LICENSE_FILE=${ANSYSLMD_LICENSE_FILE} ports: - - "${PORT_PYACP:-50555}:50051" + - "${PORT_ACP:-50555}:50051" working_dir: /home/container/workdir volumes: - "acp_data:/home/container/workdir/" diff --git a/docker-compose/docker-compose-extras.yaml b/docker-compose/docker-compose-extras.yaml index 5aad5fd323..ea943f925b 100644 --- a/docker-compose/docker-compose-extras.yaml +++ b/docker-compose/docker-compose-extras.yaml @@ -1,4 +1,3 @@ -version: '3.8' services: mapdl: restart: unless-stopped diff --git a/dockerfiles/DirectLaunchTest.Dockerfile b/dockerfiles/DirectLaunchTest.Dockerfile new file mode 100644 index 0000000000..fd24ca3093 --- /dev/null +++ b/dockerfiles/DirectLaunchTest.Dockerfile @@ -0,0 +1,41 @@ +# syntax=docker/dockerfile:1 + +ARG BASE_IMAGE=ghcr.io/ansys/acp:latest + +FROM $BASE_IMAGE + +USER root + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + python3 \ + python3-pip \ + python3-venv \ + pipx \ + libxrender1 \ + && \ + rm -rf /var/lib/apt/lists/* + + +USER container + +RUN pipx install poetry + +ENV PATH="$PATH:/home/container/.local/bin" + +# COPY --chown=container:container . /home/container/pyacp + +WORKDIR /home/container/pyacp + +# Make /home/container writable to any user +RUN chmod -R 777 /home/container/ + +COPY --chmod=755 < tuple[CADGeometry, VirtualGeometry]: + """Create a CAD geometry and virtual geometry.""" + geometry_file = get_example_file(example_key, WORKING_DIR) + geometry_obj = model.create_cad_geometry() + geometry_obj.refresh(geometry_file) # upload and load the geometry file + model.update() + virtual_geometry = model.create_virtual_geometry( + name="thickness_virtual_geometry", cad_components=geometry_obj.root_shapes.values() + ) + return geometry_obj, virtual_geometry + + +# %% +# Snap the top to a geometry +# -------------------------- +# +# The :class:`.SnapToGeometry` allows to shape the bottom or top of the solid model. +# First, import the geometry and then add the snap-to feature to the solid model. +snap_to_geom, snap_to_virtual_geom = create_virtual_geometry_from_file(ExampleKeys.SNAP_TO_GEOMETRY) +solid_model.create_snap_to_geometry( + name="Snap-to Geometry", + cad_geometry=snap_to_virtual_geom, + orientation_type=SnapToGeometryOrientationType.TOP, + oriented_selection_set=model.oriented_selection_sets["oss"], +) + +model.update() +plot_model_with_geometry(snap_to_geom, 0.5) + +# %% +# Shape the walls +# --------------- +# +# The :class:`.ExtrusionGuide` is used to shape the side walls of the solid model. +# The feature can be defined by a direction as shown here or through a geometry. +edge_set = model.create_edge_set( + name="Edge Set", + edge_set_type=EdgeSetType.BY_REFERENCE, + element_set=model.element_sets["All_Elements"], + limit_angle=30, + origin=(0.05, 0, 0), +) +solid_model.create_extrusion_guide( + name="Extrusion Guide", + edge_set=edge_set, + extrusion_guide_type=ExtrusionGuideType.BY_DIRECTION, + direction=(-0.5, 1, 0), + radius=0.005, + depth=0.6, +) +model.update() +plot_model_with_geometry(None) + +# %% +# Cut-off an edge +# --------------- +# +# The :class:`.CutOffGeometry` is used to crop elements from the solid model. +cut_off_cad_geom, cut_off_virtual_geom = create_virtual_geometry_from_file( + ExampleKeys.CUT_OFF_GEOMETRY_SOLID_MODEL +) +solid_model.create_cut_off_geometry( + name="Cut-off Geometry", + cad_geometry=cut_off_virtual_geom, + orientation_type=CutOffGeometryOrientationType.UP, +) + +model.update() +plot_model_with_geometry(cut_off_cad_geom) + + +# %% +# Plot results on the solid mesh +# ------------------------------ +# +# The plotting capabilities also support the visualization of ply-wise results, +# such as directions or thicknesses as shown here. + +# %% +# Get the analysis ply of interest +ap = solid_model.analysis_plies["P2L1__ply"] + +# %% +# Plot fiber directions +# ~~~~~~~~~~~~~~~~~~~~~ +direction_plotter = get_directions_plotter( + model=model, + mesh=ap.solid_mesh, + components=[ + ap.elemental_data.fiber_direction, + ], + length_factor=10.0, + culling_factor=10, +) +direction_plotter.add_mesh(model.solid_mesh.to_pyvista(), opacity=0.2, show_edges=False) +direction_plotter.camera_position = FLAT_PLATE_SOLID_CAMERA +direction_plotter.show() + +# %% +# Plot thicknesses +# ~~~~~~~~~~~~~~~~ +thickness_data = ap.elemental_data.thickness +thickness_pyvista_mesh = thickness_data.get_pyvista_mesh(mesh=ap.solid_mesh) # type: ignore +plotter = pyvista.Plotter() +plotter.add_mesh(thickness_pyvista_mesh) +plotter.add_mesh(model.solid_mesh.to_pyvista(), opacity=0.2, show_edges=False) +plotter.camera_position = FLAT_PLATE_SOLID_CAMERA +plotter.show() diff --git a/examples/modeling_features/030-imported-plies.py b/examples/modeling_features/030-imported-plies.py new file mode 100644 index 0000000000..fe9cbf9d8d --- /dev/null +++ b/examples/modeling_features/030-imported-plies.py @@ -0,0 +1,245 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _imported_plies_example: + +Imported ply +============ + +The definition and use of imported plies is demonstrated in this example. +In a nutshell, the difference between :class:`.ImportedModelingPly` and +:class:`.ModelingPly` is that the surface mesh of an :class:`.ImportedModelingPly` +is defined by an external source, such as a CAD surface, where a :class:`.ModelingPly` +is always defined on the initial loaded shell mesh. +Therefore, an imported ply can only be used in combination with an +:class:`.ImportedSolidModel`. + +This examples shows hot to: + +- Load an initial mesh +- Add a :class:`.Material` and :class:`.Fabric` +- Import geometries which will be used to define the surface of the :class:`.ImportedModelingPly` +- Add two imported modeling plies +- Create an :class:`.ImportedSolidModel` +- Map the imported plies to the solid model +- Visualized the mapped plies. +""" + +# %% +# Import modules +# -------------- +# +# Import the standard library and third-party dependencies. +import os +import pathlib +import tempfile + +import pyvista + +# %% +# Import the PyACP dependencies. +from ansys.acp.core import CADGeometry, ImportedPlyOffsetType, PlyType, VirtualGeometry, launch_acp +from ansys.acp.core.extras import ExampleKeys, get_example_file +from ansys.acp.core.material_property_sets import ConstantDensity, ConstantEngineeringConstants + +# sphinx_gallery_thumbnail_number = 3 + + +CAMERA_POSITION = [(0.0436, 0.0102, 0.0193), (0.0111, 0.0035, 0.0046), (-0.1685, 0.9827, -0.0773)] + +# %% +# Start ACP and load the model +# ---------------------------- + +# %% +# Get the example file from the server. +tempdir = tempfile.TemporaryDirectory() +WORKING_DIR = pathlib.Path(tempdir.name) +input_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_DAT, WORKING_DIR) + +# %% +# Launch the PyACP server and connect to it. +acp = launch_acp() + +# %% +# Create a model by loading a shell mesh +model = acp.import_model(path=input_file, format="ansys:dat") + +# %% +# Add a material and a fabric with 1mm thickness. +# The fabric is used for the imported modeling ply. +engineering_constants_ud = ConstantEngineeringConstants.from_orthotropic_constants( + E1=5e10, E2=1e10, E3=1e10, nu12=0.28, nu13=0.28, nu23=0.3, G12=5e9, G23=4e9, G31=4e9 +) +density_ud = ConstantDensity(rho=2700) + +ud_material = model.create_material( + name="E-Glass UD", + ply_type=PlyType.REGULAR, + engineering_constants=engineering_constants_ud, + density=density_ud, +) + +engineering_resin = ConstantEngineeringConstants.from_isotropic_constants(E=5e9, nu=0.3) +density_resin = ConstantDensity(rho=1200) + +void_material = model.create_material( + name="Void material", + ply_type=PlyType.ISOTROPIC, + engineering_constants=engineering_resin, + density=density_resin, +) +filler_material = model.create_material( + name="Filler material", + ply_type=PlyType.ISOTROPIC, + engineering_constants=engineering_resin, + density=density_resin, +) + +fabric = model.create_fabric(name="E-Glass Fabric", material=ud_material, thickness=0.001) + + +# %% +# Import CAD geometries +# --------------------- +# +# Import two cad surfaces to define the surface of the imported modeling plies. +def create_virtual_geometry_from_file( + example_key: ExampleKeys, +) -> tuple[CADGeometry, VirtualGeometry]: + """Create a CAD geometry and virtual geometry.""" + geometry_file = get_example_file(example_key, WORKING_DIR) + geometry_obj = model.create_cad_geometry() + geometry_obj.refresh(geometry_file) # upload and load the geometry file + model.update() + virtual_geometry = model.create_virtual_geometry( + name=os.path.basename(geometry_file), cad_components=geometry_obj.root_shapes.values() + ) + return geometry_obj, virtual_geometry + + +triangle_surf_cad, triangle_surf_vcad = create_virtual_geometry_from_file( + ExampleKeys.RULE_GEOMETRY_TRIANGLE +) +top_surf_cad, top_surf_vcad = create_virtual_geometry_from_file(ExampleKeys.SNAP_TO_GEOMETRY) + +# %% +# Definition of Imported Plies +# ---------------------------- +imported_ply_group = model.create_imported_modeling_group(name="Imported Ply Group") +imported_ply_triangle = imported_ply_group.create_imported_modeling_ply( + name="Triangle Ply", + offset_type=ImportedPlyOffsetType.BOTTOM_OFFSET, + ply_material=fabric, + mesh_geometry=triangle_surf_vcad, + ply_angle=0, + rosettes=[model.rosettes["12"]], +) + +imported_ply_top = imported_ply_group.create_imported_modeling_ply( + name="Triangle Ply", + offset_type=ImportedPlyOffsetType.MIDDLE_OFFSET, + ply_material=fabric, + mesh_geometry=top_surf_vcad, + ply_angle=45, + rosettes=[model.rosettes["12"]], +) +model.update() + + +# %% +# Imported plies cannot be visualized directly yet but the cad geometries are +# shown here instead. +# To visualize the imported plies, you can save the model and load it in ACP +# standalone. +def plotter_with_all_geometries(cad_geometries): + colors = ["green", "yellow", "blue", "red"] + plotter = pyvista.Plotter() + for index, cad in enumerate(cad_geometries): + geom_mesh = cad.visualization_mesh.to_pyvista() + plotter.add_mesh(geom_mesh, color=colors[index], opacity=0.1) + edges = geom_mesh.extract_feature_edges() + plotter.add_mesh(edges, color="white", line_width=4) + plotter.add_mesh(edges, color="black", line_width=2) + plotter.camera_position = CAMERA_POSITION + return plotter + + +plotter = plotter_with_all_geometries([triangle_surf_cad, top_surf_cad]) +plotter.show() + +# %% +# Map Imported Plies onto a solid mesh +# ------------------------------------ +# +# An external solid mesh is loaded now to map the imported plies +# onto the solid model. The next figure shows the imported solid mesh +# and the imported plies. +local_solid_mesh_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_SOLID_MESH_CDB, WORKING_DIR) +remote_solid_mesh_file = acp.upload_file(local_solid_mesh_file) +imported_solid_model = model.create_imported_solid_model( + name="Imported Solid Model", + external_path=remote_solid_mesh_file, + format="ansys:cdb", +) +imported_solid_model.import_initial_mesh() +plotter = plotter_with_all_geometries([triangle_surf_cad, top_surf_cad]) +plotter.add_mesh(imported_solid_model.solid_mesh.to_pyvista(), show_edges=True, opacity=0.5) +plotter.show() + +# %% +# Add a mapping object to link the imported plies with the solid model. +# In this example, all imported plies are mapped in one go. +# The remaining elemental volume and elements which do not intersect +# with the imported plies are filled with a void and filler material, +# respectively. +imported_solid_model.create_layup_mapping_object( + name="Map imported plies", + use_imported_plies=True, # enable imported plies + select_all_plies=True, # select all plies + entire_solid_mesh=True, + scale_ply_thicknesses=False, + void_material=void_material, + delete_lost_elements=False, + filler_material=filler_material, + rosettes=[model.rosettes["12"]], +) +model.update() + +# %% +# Show the imported ply geometries and mapped plies on the solid model. +# Note that the analysis plies are not yet directly accessible via +# the API of the imported solid model. Also, elemental data such as +# thicknesses are not yet implemented for imported plies. +plotter = plotter_with_all_geometries([triangle_surf_cad, top_surf_cad]) +for imported_ply in [imported_ply_triangle, imported_ply_top]: + for pp in imported_ply.imported_production_plies.values(): + for ap in pp.imported_analysis_plies.values(): + plotter.add_mesh(ap.solid_mesh.to_pyvista(), show_edges=True, opacity=1) +plotter.add_mesh(mesh=imported_solid_model.solid_mesh.to_pyvista(), show_edges=False, opacity=0.2) +plotter.show() + +# %% +# The imported solid model can be passed to Mechanical or MAPDL to run an analysis +# as shown in the examples :ref:`pymechanical_solid_example` and +# :ref:`pymapdl_workflow_example`. diff --git a/examples/modeling_features/031-imported-solid-model.py b/examples/modeling_features/031-imported-solid-model.py new file mode 100644 index 0000000000..5b4ac76d17 --- /dev/null +++ b/examples/modeling_features/031-imported-solid-model.py @@ -0,0 +1,276 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _imported_solid_model_example: + +Imported solid model +==================== + +This example guides you through the definition of an :class:`.ImportedSolidModel` +which allows to map the layup onto an external solid mesh. +In contrast to the :class:`.SolidModel`, the raw solid mesh of +:class:`.ImportedSolidModel` is loaded from an external source, such as a CDB file. +In this example, the layup is applied onto a t-joint which consists of different +parts such as shell, stringer, and bonding skins. +The example only shows the PyACP part of the setup. For a complete composite analysis, +see :ref:`pymapdl_workflow_example`. + +This example starts from an ACP model with layup. It shows how to: + +- Create an :class:`.ImportedSolidModel` from an external mesh. +- Define the :class:`.LayupMappingObject` to apply the layup onto the solid mesh. +- Scope plies to specific parts of the solid mesh. +- Visualize the mapped layup. + +It is recommended to look at the Ansys help for all the details. This example shows the +basic setup only. +""" + +# %% +# Import the standard library and third-party dependencies. +import pathlib +import tempfile + +import pyvista + +# %% +# Import the PyACP dependencies. +from ansys.acp.core import ElementTechnology, LayupMappingRosetteSelectionMethod, launch_acp +from ansys.acp.core.extras import ExampleKeys, get_example_file + +# sphinx_gallery_thumbnail_number = 5 + + +# %% +# Start ACP and load the model +# ---------------------------- +# %% +# Get the example file from the server. +tempdir = tempfile.TemporaryDirectory() +WORKING_DIR = pathlib.Path(tempdir.name) +input_file = get_example_file(ExampleKeys.IMPORTED_SOLID_MODEL_ACPH5, WORKING_DIR) + +# %% +# Launch the PyACP server and connect to it. +acp = launch_acp() + +# %% +# Load the model from an acph5 file +model = acp.import_model(input_file) + +# %% +# Import external solid model +# --------------------------- +# +# Get the solid mesh file and create an ImportedSolidModel, +# load the initial mesh and show the raw mesh without any mapping. +solid_mesh_file = get_example_file(ExampleKeys.IMPORTED_SOLID_MODEL_SOLID_MESH, WORKING_DIR) +imported_solid_model = model.create_imported_solid_model( + name="Imported Solid Model", +) +imported_solid_model.refresh(path=solid_mesh_file, format="ansys:h5") +imported_solid_model.import_initial_mesh() +model.solid_mesh.to_pyvista().plot(show_edges=True) + +# %% +# The solid element sets are used as target for the mapping later. +# Here is the full list and one is visualized. +imported_solid_model.solid_element_sets.keys() + +solid_eset_mesh = imported_solid_model.solid_element_sets[ + "mapping_target bonding skin right" +].solid_mesh +plotter = pyvista.Plotter() +plotter.add_mesh(solid_eset_mesh.to_pyvista()) +plotter.add_mesh(model.solid_mesh.to_pyvista(), opacity=0.2, show_edges=False) +plotter.show() + +# %% +# Add mapping objects +# ------------------- +# +# Link the layup (plies) of the top skin of the sandwich +# with the corresponding named selections of the solid mesh +# and show the updated solid model. +solid_esets = imported_solid_model.solid_element_sets + +imported_solid_model.create_layup_mapping_object( + name="sandwich skin top", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[model.element_sets["els_sandwich_skin_top"]], + entire_solid_mesh=False, + solid_element_sets=[solid_esets["mapping_target sandwich skin top"]], +) + +model.update() +model.solid_mesh.to_pyvista().plot(show_edges=True) + +# %% +# Show the mass of the solid model elements +mass_data = model.elemental_data.mass +assert mass_data is not None +mass_data.get_pyvista_mesh(mesh=model.solid_mesh).plot(show_edges=True) + +# %% +# Add other mapping objects +imported_solid_model.create_layup_mapping_object( + name="sandwich skin bottom", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[model.element_sets["els_sandwich_skin_bottom"]], + entire_solid_mesh=False, + solid_element_sets=[ + imported_solid_model.solid_element_sets["mapping_target sandwich skin bottom"] + ], +) + +imported_solid_model.create_layup_mapping_object( + name="stringer", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[model.element_sets["els_stringer_skin_left"]], + entire_solid_mesh=False, + solid_element_sets=[ + solid_esets[v] + for v in [ + "mapping_target stringer honeycomb", + "mapping_target stringer skin left", + "mapping_target stringer skin right", + ] + ], +) + +imported_solid_model.create_layup_mapping_object( + name="bonding skin", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[ + model.element_sets[v] for v in ["els_bonding_skin_left", "els_bonding_skin_right"] + ], + entire_solid_mesh=False, + solid_element_sets=[ + solid_esets[v] + for v in ["mapping_target bonding skin left", "mapping_target bonding skin right"] + ], +) + +# %% +# Show intermediate result +model.update() +model.solid_mesh.to_pyvista().plot(show_edges=True) + +# %% +# The mapping can also be done for specific plies +# as shown for the core materials. +imported_solid_model.create_layup_mapping_object( + name="foam", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[ + model.element_sets[v] for v in ["els_foam_core_left", "els_foam_core_right"] + ], + select_all_plies=False, + sequences=[model.modeling_groups["MG foam_core"]], + entire_solid_mesh=False, + solid_element_sets=[solid_esets["mapping_target foam core"]], + delete_lost_elements=False, + filler_material=model.materials["SAN Foam (81 kg m^-3)"], + rosettes=[model.rosettes["Global Coordinate System"]], + rosette_selection_method=LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, +) + +imported_solid_model.create_layup_mapping_object( + name="honeycomb", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[ + model.element_sets[v] for v in ["els_honeycomb_left", "els_honeycomb_right"] + ], + select_all_plies=False, + sequences=[model.modeling_groups["MG honeycomb_core"]], + entire_solid_mesh=False, + solid_element_sets=[solid_esets["mapping_target sandwich honeycomb"]], + delete_lost_elements=False, + filler_material=model.materials["Honeycomb"], + rosettes=[model.rosettes["Global Coordinate System"]], + rosette_selection_method=LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, +) +model.update() +model.solid_mesh.to_pyvista().plot(show_edges=True) + +# %% +# Add filler mapping objects where the solid mesh is "filled" +# with a single material. No plies from the layup are used here. + +imported_solid_model.create_layup_mapping_object( + name="resin", + element_technology=ElementTechnology.LAYERED_ELEMENT, + shell_element_sets=[], + entire_solid_mesh=False, + solid_element_sets=[ + solid_esets[v] for v in ["mapping_target adhesive", "mapping_target adhesive stringer root"] + ], + delete_lost_elements=False, + filler_material=model.materials["Resin Epoxy"], + rosettes=[model.rosettes["Global Coordinate System"]], + rosette_selection_method=LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, +) + +# %% +# Show final solid mesh with mapped layup +model.update() +model.solid_mesh.to_pyvista().plot(show_edges=True) + + +# %% +# Show extent and thickness of mapped plies +# ----------------------------------------- +# +# Use :func:`.print_model` to get the list of plies. After identifying the ply +# of interest, for example the thickness can be visualized. Note that +# only ply-wise data of :class:`.AnalysisPly` can be visualized on the +# solid mesh. :class:`.ProductionPly` and :class:`.ModelingPly` cannot +# be visualized on the solid mesh. +ap = ( + model.modeling_groups["MG bonding_skin_right"] + .modeling_plies["ModelingPly.26"] + .production_plies["ProductionPly.33"] + .analysis_plies["P1L1__ModelingPly.26"] +) +thickness_data = ap.elemental_data.thickness +thickness_pyvista_mesh = thickness_data.get_pyvista_mesh(mesh=ap.solid_mesh) # type: ignore +plotter = pyvista.Plotter() +plotter.add_mesh(thickness_pyvista_mesh) +plotter.add_mesh(model.solid_mesh.to_pyvista(), opacity=0.2, show_edges=False) +plotter.show() + + +# %% +# Other features +# -------------- +# +# The :class:`.CutOffGeometry` can be used in combination witt the :class:`.ImportedSolidModel` +# as well. See example :ref:`solid_model_example` for more details. +# More plotting capabilities are shown in the example :ref:`solid_model_example` as well. +# +# An example of an :class:`.ImportedSolidModel` in combination with :class:`.ImportedModelingPly` +# is shown in :ref:`imported_plies_example`. +# +# The solid mesh can be exported as CDB for MAPDL or to PyMechanical for further analysis. +# These workflows are shown in :ref:`pymapdl_workflow_example` and +# :ref:`pymechanical_solid_example`. diff --git a/examples/modeling_features/050-composite_cae_h5.py b/examples/modeling_features/050-composite_cae_h5.py new file mode 100644 index 0000000000..525c9ef21f --- /dev/null +++ b/examples/modeling_features/050-composite_cae_h5.py @@ -0,0 +1,238 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +.. _composite_cae_h5_example: + +HDF5 Composite CAE +================== + +The HDF5 Composite CAE interface of PyACP is demonstrated in +this example. It shows how to write (export) and read (import) +layup data to and from a HDF5 Composite CAE file, respectively. +The HDF5 Composite CAE format is a vendor independent format +to exchange composite layup information between CAE tools. + +This examples demonstrates how to: +- Load and manipulate a model +- Export data to a HDF5 Composite CAE file +- Import and map layup from HDF5 Composite CAE onto a different model (mesh) +- Export data with ply offsets (3D plies) +- Import a layup as :class:`.ImportedModelingPly` +- Import HDF5 Composite CAE with 3D plies and map the layup onto an :class:`.ImportedSolidModel` + +""" + +# %% +# Import the standard library and third-party dependencies. +import pathlib +import tempfile + +import pyvista + +# %% +# Import the PyACP dependencies. +from ansys.acp.core import ( + HDF5CompositeCAEProjectionMode, + LinkedSelectionRule, + OffsetType, + launch_acp, +) +from ansys.acp.core.extras import ( + FLAT_PLATE_SHELL_CAMERA, + FLAT_PLATE_SOLID_CAMERA, + ExampleKeys, + get_example_file, +) + +# sphinx_gallery_thumbnail_number = 2 + + +# %% +# Start ACP and load the model +# ---------------------------- +# %% +# Get the example file from the server. +tempdir = tempfile.TemporaryDirectory() +WORKING_DIR = pathlib.Path(tempdir.name) +acph5_input_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_ACPH5, WORKING_DIR) + +# %% +# Launch the PyACP server and connect to it. +acp = launch_acp() + +# %% +# Load the model from an acph5 file +model = acp.import_model(acph5_input_file) + +# %% +# Crop some plies in order to generate a variable laminate +pr_x = model.create_parallel_selection_rule( + name="x axis", + direction=(1, 0, 0), + lower_limit=0.0025, + upper_limit=0.0075, +) +pr_z = model.create_parallel_selection_rule( + name="z axis", + direction=(0, 0, 1), + lower_limit=0.0015, + upper_limit=0.0085, +) +boolean_rule = model.create_boolean_selection_rule( + name="boolean rule", + selection_rules=[LinkedSelectionRule(pr_x), LinkedSelectionRule(pr_z)], +) + +for ply_name in ["ply_1_45_UD", "ply_2_-45_UD", "ply_3_45_UD", "ply_4_-45_UD"]: + ply = model.modeling_groups["modeling_group"].modeling_plies[ply_name] + ply.selection_rules = [LinkedSelectionRule(boolean_rule)] + +model.update() + +# %% +# Plot the thickness distribution +thickness = model.elemental_data.thickness +assert thickness is not None +thickness.get_pyvista_mesh(mesh=model.mesh).plot(show_edges=True) + +# %% +# Write HDF5 Composite CAE file +# ----------------------------- +# +# Export the entire layup to a HDF5 Composite CAE file. +h5_output_file = WORKING_DIR / "hdf5_composite_cae.h5" +model.export_hdf5_composite_cae( + path=h5_output_file, +) + +# %% +# Load HDF5 Composite CAE file into a different model +# --------------------------------------------------- +# +# A new acp model is created by importing a refined mesh of the same geometry. +# Both meshes (initial mesh in blue, refined one in red) are shown below. +dat_input_file_refined = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_REFINED_DAT, WORKING_DIR) +refined_model = acp.import_model(path=dat_input_file_refined, format="ansys:dat") + +plotter = pyvista.Plotter() +plotter.add_mesh( + model.shell_mesh.to_pyvista(), + color="blue", + edge_color="blue", + show_edges=True, + style="wireframe", + line_width=4, +) +plotter.add_mesh( + refined_model.shell_mesh.to_pyvista(), + color="red", + edge_color="red", + show_edges=True, + style="wireframe", + line_width=2, +) +plotter.camera_position = FLAT_PLATE_SHELL_CAMERA +plotter.show() + +# %% +# Import the HDF5 Composite CAE file which is then automatically mapped +# onto the refined mesh. In this example, the default settings +# (tolerances, etc.) are used. +refined_model.import_hdf5_composite_cae( + path=h5_output_file, +) +refined_model.update() + +# %% +# Plot the thickness distribution on the refined model +thickness = refined_model.elemental_data.thickness +assert thickness is not None +thickness.get_pyvista_mesh(mesh=refined_model.mesh).plot(show_edges=True) + +# %% +# 3D plies with ply-offsets +# ------------------------- +# +# The HDF5 Composite CAE interface also allows to export the 3D plies +# (plies with offsets) which can then be used to create +# imported modeling plies. The initial model is used to +# write a new HDF5 with ``layup_representation_3d`` enabled. +h5_output_file_3D = WORKING_DIR / "hdf5_composite_cae_3D.h5" +model.export_hdf5_composite_cae( + path=h5_output_file_3D, + layup_representation_3d=True, + offset_type=OffsetType.BOTTOM_OFFSET, +) + +# %% +# A new acp model is created to properly separate the different workflows. +refined_model_3D = acp.import_model(path=dat_input_file_refined, format="ansys:dat") +refined_model_3D.import_hdf5_composite_cae( + path=h5_output_file_3D, projection_mode=HDF5CompositeCAEProjectionMode.SOLID +) + +# %% +# An imported solid model is required for the 3D workflow (with imported modeling plies). +# Details about :class:`.ImportedSolidModel` and :class:`.ImportedModelingPly` can be found +# in the examples :ref:`imported_solid_model_example` and :ref:`imported_plies_example`. +local_solid_mesh_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_SOLID_MESH_CDB, WORKING_DIR) +remote_solid_mesh_file = acp.upload_file(local_solid_mesh_file) +imported_solid_model = refined_model_3D.create_imported_solid_model( + name="Imported Solid Model", + external_path=remote_solid_mesh_file, + format="ansys:cdb", +) + +# %% +# The :class:`.LayupMappingObject` is used to configure the mapping of the imported plies +# onto the imported solid model. +imported_solid_model.create_layup_mapping_object( + name="Map imported plies", + use_imported_plies=True, # enable imported plies + select_all_plies=True, # select all plies + scale_ply_thicknesses=True, + entire_solid_mesh=True, + delete_lost_elements=True, # elements without plies are deleted +) +refined_model_3D.update() + +# %% +# The mapped top layer of the imported laminate is shown below. +# Note that the solid elements which do not intersect with the +# layup are deleted in this example. +imported_analysis_ply = ( + refined_model_3D.imported_modeling_groups["modeling_group"] + .imported_modeling_plies["ply_5_0_UD"] + .imported_production_plies["ImportedProductionPly.6"] + .imported_analysis_plies["P1L1__ply_5_0_UD"] +) +plotter = pyvista.Plotter() +plotter.add_mesh(imported_analysis_ply.solid_mesh.to_pyvista(), show_edges=True) +plotter.add_mesh(refined_model_3D.solid_mesh.to_pyvista(), opacity=0.2, show_edges=False) +plotter.camera_position = FLAT_PLATE_SOLID_CAMERA +plotter.show() + +# %% +# Note that the visualization of imported plies and imported solid model +# is limited. As an alternative, you can save the model and review it +# in ACP standalone. diff --git a/examples/README.rst b/examples/modeling_features/README.rst similarity index 100% rename from examples/README.rst rename to examples/modeling_features/README.rst diff --git a/examples/pymechanical/Readme.md b/examples/pymechanical/Readme.md deleted file mode 100644 index ca3df560ba..0000000000 --- a/examples/pymechanical/Readme.md +++ /dev/null @@ -1,6 +0,0 @@ -This is an example of a full composite workflow using PyMechanical (without Workbench). It is currently not part of the PyACP example suite. - -The example works with both "remote" (remote_workflow.py) and "embedded" (embedded_workflow) PyMechanical. -For the example's documentation, see "embedded_workflow.py". - -This example works on Windows and could not be tested on Linux due to https://github.com/ansys/pymechanical/issues/352. \ No newline at end of file diff --git a/examples/pymechanical_with_shim/Readme.md b/examples/pymechanical_with_shim/Readme.md new file mode 100644 index 0000000000..688c4b2a24 --- /dev/null +++ b/examples/pymechanical_with_shim/Readme.md @@ -0,0 +1,10 @@ +This is an example of a shell workflow using PyMechanical, using the 'shim' defined +in the ``acp_future`` sub-directory. The shim enables running the workflow on both +remote and embedded PyMechanical. + +Variants of this example _not_ using the shim have been integrated into the main +examples directory. However, these work only on Windows, and only with the remote +PyMechanical. + +This example works on Windows, but could not be tested on Linux due to +https://github.com/ansys/pymechanical/issues/352. diff --git a/examples/pymechanical/acp_future/ACPFuture.csproj b/examples/pymechanical_with_shim/acp_future/ACPFuture.csproj similarity index 100% rename from examples/pymechanical/acp_future/ACPFuture.csproj rename to examples/pymechanical_with_shim/acp_future/ACPFuture.csproj diff --git a/examples/pymechanical/acp_future/ACPFuture.sln b/examples/pymechanical_with_shim/acp_future/ACPFuture.sln similarity index 100% rename from examples/pymechanical/acp_future/ACPFuture.sln rename to examples/pymechanical_with_shim/acp_future/ACPFuture.sln diff --git a/examples/pymechanical/acp_future/Properties/AssemblyInfo.cs b/examples/pymechanical_with_shim/acp_future/Properties/AssemblyInfo.cs similarity index 100% rename from examples/pymechanical/acp_future/Properties/AssemblyInfo.cs rename to examples/pymechanical_with_shim/acp_future/Properties/AssemblyInfo.cs diff --git a/examples/pymechanical/acp_future/Shims.cs b/examples/pymechanical_with_shim/acp_future/Shims.cs similarity index 100% rename from examples/pymechanical/acp_future/Shims.cs rename to examples/pymechanical_with_shim/acp_future/Shims.cs diff --git a/examples/pymechanical/acp_future/readme.md b/examples/pymechanical_with_shim/acp_future/readme.md similarity index 100% rename from examples/pymechanical/acp_future/readme.md rename to examples/pymechanical_with_shim/acp_future/readme.md diff --git a/examples/pymechanical/constants.py b/examples/pymechanical_with_shim/constants.py similarity index 100% rename from examples/pymechanical/constants.py rename to examples/pymechanical_with_shim/constants.py diff --git a/examples/pymechanical/embedded_workflow.py b/examples/pymechanical_with_shim/embedded_workflow.py similarity index 100% rename from examples/pymechanical/embedded_workflow.py rename to examples/pymechanical_with_shim/embedded_workflow.py diff --git a/examples/pymechanical/generate_mesh.py b/examples/pymechanical_with_shim/generate_mesh.py similarity index 100% rename from examples/pymechanical/generate_mesh.py rename to examples/pymechanical_with_shim/generate_mesh.py diff --git a/examples/pymechanical/geometry/flat_plate.agdb b/examples/pymechanical_with_shim/geometry/flat_plate.agdb similarity index 100% rename from examples/pymechanical/geometry/flat_plate.agdb rename to examples/pymechanical_with_shim/geometry/flat_plate.agdb diff --git a/examples/pymechanical/output/.gitkeep b/examples/pymechanical_with_shim/output/.gitkeep similarity index 100% rename from examples/pymechanical/output/.gitkeep rename to examples/pymechanical_with_shim/output/.gitkeep diff --git a/examples/pymechanical/postprocess_results.py b/examples/pymechanical_with_shim/postprocess_results.py similarity index 100% rename from examples/pymechanical/postprocess_results.py rename to examples/pymechanical_with_shim/postprocess_results.py diff --git a/examples/pymechanical/remote_workflow.py b/examples/pymechanical_with_shim/remote_workflow.py similarity index 100% rename from examples/pymechanical/remote_workflow.py rename to examples/pymechanical_with_shim/remote_workflow.py diff --git a/examples/pymechanical/set_bc.py b/examples/pymechanical_with_shim/set_bc.py similarity index 100% rename from examples/pymechanical/set_bc.py rename to examples/pymechanical_with_shim/set_bc.py diff --git a/examples/pymechanical/setup_acp_model.py b/examples/pymechanical_with_shim/setup_acp_model.py similarity index 100% rename from examples/pymechanical/setup_acp_model.py rename to examples/pymechanical_with_shim/setup_acp_model.py diff --git a/examples/009_optimization.py b/examples/use_cases/01-optimizing-ply-angles.py similarity index 89% rename from examples/009_optimization.py rename to examples/use_cases/01-optimizing-ply-angles.py index d4d2251d75..eb4b704adf 100644 --- a/examples/009_optimization.py +++ b/examples/use_cases/01-optimizing-ply-angles.py @@ -23,8 +23,8 @@ """ .. _optimization_example: -Optimization example -==================== +Optimizing ply angles +===================== This example demonstrates how to use the ACP, MAPDL, and DPF servers to optimize the ply angles in a composite lay-up. The optimization aims to minimize the maximum inverse @@ -61,6 +61,7 @@ # %% # Import Ansys libraries import ansys.acp.core as pyacp +from ansys.acp.core.extras import ExampleKeys, get_example_file import ansys.dpf.composites as pydpf_composites import ansys.mapdl.core as pymapdl @@ -94,23 +95,20 @@ # # The ``prepare_acp_model`` function imports the ``optimization_model.dat`` file into a new # ACP model and creates a lay-up with six plies. -# It returns a :class:`.ACPWorkflow` object that can be used to access the model and -# generate the output files. +# It returns the :class:`.Model` instance. -input_file = pyacp.example_helpers.get_example_file( - example_key=pyacp.example_helpers.ExampleKeys.OPTIMIZATION_EXAMPLE_DAT, +input_file = get_example_file( + example_key=ExampleKeys.OPTIMIZATION_EXAMPLE_DAT, working_directory=workdir, ) -def prepare_acp_model(*, acp, workdir, input_file): +def prepare_acp_model(*, acp, input_file): # Import the DAT input file into a new ACP model - acp_workflow = pyacp.ACPWorkflow.from_cdb_or_dat_file( - acp=acp, - cdb_or_dat_file_path=input_file, - local_working_directory=workdir, + model = acp.import_model( + input_file, + format="ansys:dat", ) - model = acp_workflow.model model.name = "optimization_example" element_set = model.element_sets["All_Elements"] @@ -170,16 +168,16 @@ def prepare_acp_model(*, acp, workdir, input_file): oriented_selection_sets=[oss], number_of_layers=1, ) - acp_workflow.model.update() - return acp_workflow + model.update() + return model # %% # Create the ACP model and visualize the first ply's fiber direction. -acp_workflow = prepare_acp_model(acp=acp_instance, workdir=workdir, input_file=input_file) -ply = list(acp_workflow.model.modeling_groups["Modeling_Group"].modeling_plies.values())[0] +model = prepare_acp_model(acp=acp_instance, input_file=input_file) +ply = list(model.modeling_groups["Modeling_Group"].modeling_plies.values())[0] pyacp.get_directions_plotter( - model=acp_workflow.model, + model=model, components=[ply.elemental_data.fiber_direction], length_factor=5.0, culling_factor=5, @@ -196,8 +194,7 @@ def prepare_acp_model(*, acp, workdir, input_file): # updates the model. -def update_ply_angles(*, acp_workflow, parameters): - model = acp_workflow.model +def update_ply_angles(*, model, parameters): modeling_plies = list(model.modeling_groups["Modeling_Group"].modeling_plies.values()) assert len(modeling_plies) == len(parameters) for angle, modeling_ply in zip(parameters, modeling_plies): @@ -206,7 +203,7 @@ def update_ply_angles(*, acp_workflow, parameters): model.update() -update_ply_angles(acp_workflow=acp_workflow, parameters=[0, 45, 90, 135, 180, 225]) +update_ply_angles(model=model, parameters=[0, 45, 90, 135, 180, 225]) # %% @@ -215,7 +212,7 @@ def update_ply_angles(*, acp_workflow, parameters): def solve_cdb(*, mapdl, cdb_file, workdir): mapdl.clear() - mapdl.input(cdb_file) + mapdl.input(str(cdb_file)) # Solve the model. Note that the model contains two timesteps. mapdl.allsel() mapdl.slashsolu() @@ -231,8 +228,9 @@ def solve_cdb(*, mapdl, cdb_file, workdir): return rst_file_local_path -cdb_file = acp_workflow.get_local_cdb_file() -rst_file = solve_cdb(mapdl=mapdl, cdb_file=cdb_file, workdir=workdir) +cdb_file_path = workdir / "optimization_example.cdb" +model.export_analysis_model(cdb_file_path) +rst_file = solve_cdb(mapdl=mapdl, cdb_file=cdb_file_path, workdir=workdir) # %% # The ``get_max_irf()`` function uses PyDPF Composites to calculate the maximum @@ -246,20 +244,29 @@ def solve_cdb(*, mapdl, cdb_file, workdir): name="Combined Failure Criterion", failure_criteria=[max_stress_criterion], ) +materials_file_path = workdir / "materials.xml" +model.export_materials(materials_file_path) def get_max_irf( *, - acp_workflow, + model, dpf_server, rst_file, failure_criterion, ): + composite_definitions_file = workdir / "ACPCompositeDefinitions.h5" + model.export_shell_composite_definitions(composite_definitions_file) # Create the composite model and configure its input composite_model = pydpf_composites.composite_model.CompositeModel( - composite_files=pyacp.get_composite_post_processing_files( - acp_workflow=acp_workflow, - local_rst_file_path=rst_file, + composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles( + rst=rst_file, + composite={ + "shell": pydpf_composites.data_sources.CompositeDefinitionFiles( + composite_definitions_file + ) + }, + engineering_data=materials_file_path, ), server=dpf_server, ) @@ -281,7 +288,7 @@ def get_max_irf_for_time(time): get_max_irf( - acp_workflow=acp_workflow, + model=model, dpf_server=dpf_server, rst_file=rst_file, failure_criterion=combined_failure_criterion, @@ -298,13 +305,14 @@ def get_max_irf_for_time(time): def get_max_irf_for_parameters( - parameters, *, acp_workflow, mapdl, dpf_server, failure_criterion, workdir, results + parameters, *, model, mapdl, dpf_server, failure_criterion, workdir, results ): - update_ply_angles(acp_workflow=acp_workflow, parameters=parameters) - cdb_file = acp_workflow.get_local_cdb_file() - rst_file = solve_cdb(mapdl=mapdl, cdb_file=cdb_file, workdir=workdir) + update_ply_angles(model=model, parameters=parameters) + cdb_file_path = workdir / "optimization_example.cdb" + model.export_analysis_model(cdb_file_path) + rst_file = solve_cdb(mapdl=mapdl, cdb_file=cdb_file_path, workdir=workdir) res = get_max_irf( - acp_workflow=acp_workflow, + model=model, dpf_server=dpf_server, rst_file=rst_file, failure_criterion=failure_criterion, @@ -321,7 +329,7 @@ def get_max_irf_for_parameters( results: list[float] = [] optimization_function = partial( get_max_irf_for_parameters, - acp_workflow=acp_workflow, + model=model, mapdl=mapdl, dpf_server=dpf_server, failure_criterion=combined_failure_criterion, diff --git a/examples/use_cases/README.rst b/examples/use_cases/README.rst new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/examples/use_cases/README.rst @@ -0,0 +1 @@ + diff --git a/examples/001_basic_flat_plate.py b/examples/workflows/01-pymapdl-workflow.py similarity index 84% rename from examples/001_basic_flat_plate.py rename to examples/workflows/01-pymapdl-workflow.py index f1adb50b8a..10df7f2274 100644 --- a/examples/001_basic_flat_plate.py +++ b/examples/workflows/01-pymapdl-workflow.py @@ -21,10 +21,10 @@ # SOFTWARE. """ -.. _basic_flat_plate: +.. _pymapdl_workflow_example: -Basic PyACP workflow example -============================ +PyMAPDL workflow +================ This example shows how to define a composite lay-up with PyACP, solve the resulting model with PyMAPDL, and run a failure analysis with PyDPF Composites. @@ -52,16 +52,14 @@ # %% # Import the PyACP dependencies. from ansys.acp.core import ( - ACPWorkflow, PlyType, - example_helpers, - get_composite_post_processing_files, + dpf_integration_helpers, get_directions_plotter, - get_dpf_unit_system, launch_acp, material_property_sets, print_model, ) +from ansys.acp.core.extras import ExampleKeys, get_example_file # sphinx_gallery_thumbnail_number = 3 @@ -70,12 +68,10 @@ # Launch PyACP # ------------ # -# Get the example file from the server. +# Download the example input file. tempdir = tempfile.TemporaryDirectory() WORKING_DIR = pathlib.Path(tempdir.name) -input_file = example_helpers.get_example_file( - example_helpers.ExampleKeys.BASIC_FLAT_PLATE_DAT, WORKING_DIR -) +input_file = get_example_file(ExampleKeys.BASIC_FLAT_PLATE_DAT, WORKING_DIR) # %% # Launch the PyACP server and connect to it. @@ -85,18 +81,9 @@ # Create an ACP workflow instance and load the model # -------------------------------------------------- # -# Define the input file and instantiate an ``ACPWorkflow`` instance. -# The ``ACPWorkflow`` class provides convenience methods that simplify the file handling. -# It automatically creates a model based on the input file. - -workflow = ACPWorkflow.from_cdb_or_dat_file( - acp=acp, - cdb_or_dat_file_path=input_file, - local_working_directory=WORKING_DIR, -) +# Import the model from the input file. -model = workflow.model -print(workflow.working_directory.path) +model = acp.import_model(input_file, format="ansys:dat") print(model.unit_system) # %% @@ -206,7 +193,9 @@ # %% # Load the CDB file into PyMAPDL. -mapdl.input(str(workflow.get_local_cdb_file())) +analysis_model_path = WORKING_DIR / "analysis_model.cdb" +model.export_analysis_model(analysis_model_path) +mapdl.input(str(analysis_model_path)) # %% # Solve the model. @@ -223,8 +212,8 @@ # %% # Download the RST file for composite-specific postprocessing. rstfile_name = f"{mapdl.jobname}.rst" -rst_file_local_path = workflow.working_directory.path / rstfile_name -mapdl.download(rstfile_name, str(workflow.working_directory.path)) +rst_file_local_path = WORKING_DIR / rstfile_name +mapdl.download(rstfile_name, str(WORKING_DIR)) # %% # Postprocessing with PyDPF Composites @@ -235,6 +224,10 @@ from ansys.dpf.composites.composite_model import CompositeModel from ansys.dpf.composites.constants import FailureOutput +from ansys.dpf.composites.data_sources import ( + CompositeDefinitionFiles, + ContinuousFiberCompositesFiles, +) from ansys.dpf.composites.failure_criteria import CombinedFailureCriterion, MaxStrainCriterion from ansys.dpf.composites.server_helpers import connect_to_or_start_server @@ -254,9 +247,17 @@ # %% # Create the composite model and configure its input. +composite_definitions_file = WORKING_DIR / "ACPCompositeDefinitions.h5" +model.export_shell_composite_definitions(composite_definitions_file) +materials_file = WORKING_DIR / "materials.xml" +model.export_materials(materials_file) composite_model = CompositeModel( - get_composite_post_processing_files(workflow, rst_file_local_path), - default_unit_system=get_dpf_unit_system(model.unit_system), + composite_files=ContinuousFiberCompositesFiles( + rst=rst_file_local_path, + composite={"shell": CompositeDefinitionFiles(composite_definitions_file)}, + engineering_data=materials_file, + ), + default_unit_system=dpf_integration_helpers.get_dpf_unit_system(model.unit_system), server=dpf_server, ) diff --git a/examples/008_solve_class40.py b/examples/workflows/02-advanced-pymapdl-workflow.py similarity index 83% rename from examples/008_solve_class40.py rename to examples/workflows/02-advanced-pymapdl-workflow.py index 2f609ab02b..f328c0d317 100644 --- a/examples/008_solve_class40.py +++ b/examples/workflows/02-advanced-pymapdl-workflow.py @@ -21,10 +21,10 @@ # SOFTWARE. """ -.. _solve_class40_example: +.. _advanced_pymapdl_workflow_example: -Class 40 example -================ +Advanced PyMAPDL workflow +========================= This example shows how to define a composite lay-up with PyACP, solve the resulting model with PyMAPDL, and run a failure analysis with PyDPF Composites. @@ -35,8 +35,6 @@ PyDPF Composites for postprocessing. The additional input files (``material.xml`` and ``ACPCompositeDefinitions.h5``) can also be stored with PyACP and passed to PyDPF Composites. -The MAPDL and DPF services are run in Docker containers that share a volume (working -directory). """ # %% @@ -45,7 +43,6 @@ # %% # Import the standard library and third-party dependencies. -import os import pathlib import tempfile @@ -63,30 +60,26 @@ acp = pyacp.launch_acp() # %% +# Get example input files +# ----------------------- # -# Load mesh and materials from CDB file -# ------------------------------------- +# Create a temporary working directory, and download the example input files +# to this directory. -# %% -# Define the directory in which the input files are stored. -try: - EXAMPLES_DIR = pathlib.Path(os.environ["REPO_ROOT"]) / "examples" -except KeyError: - EXAMPLES_DIR = pathlib.Path(__file__).parent -EXAMPLE_DATA_DIR = EXAMPLES_DIR / "data" / "class40" +working_dir = tempfile.TemporaryDirectory() +working_dir_path = pathlib.Path(working_dir.name) +input_file = pyacp.extras.example_helpers.get_example_file( + pyacp.extras.example_helpers.ExampleKeys.CLASS40_CDB, working_dir_path +) # %% -# Send ``class40.cdb`` to the server. -CDB_FILENAME = "class40.cdb" -local_file_path = str(EXAMPLE_DATA_DIR / CDB_FILENAME) -print(local_file_path) -cdb_file_path = acp.upload_file(local_path=local_file_path) +# +# Load mesh and materials from CDB file +# ------------------------------------- # %% # Load the CDB file into PyACP and set the unit system. -model = acp.import_model( - path=cdb_file_path, format="ansys:cdb", unit_system=pyacp.UnitSystemType.MPA -) +model = acp.import_model(path=input_file, format="ansys:cdb", unit_system=pyacp.UnitSystemType.MPA) model @@ -286,35 +279,23 @@ def add_ply(mg, name, ply_material, angle, oss): # Write out ACP Model # ------------------- -ACPH5_FILE = "class40.acph5" -CDB_FILENAME_OUT = "class40_analysis_model.cdb" -COMPOSITE_DEFINITIONS_H5 = "ACPCompositeDefinitions.h5" -MATML_FILE = "materials.xml" +acph5_filename = "class40.acph5" +cdb_filename_out = "class40_analysis_model.cdb" +composite_definition_h5_filename = "ACPCompositeDefinitions.h5" +matml_filename = "materials.xml" # %% # Update and save the ACP model. model.update() -model.save(ACPH5_FILE, save_cache=True) +model.save(working_dir_path / acph5_filename, save_cache=True) # %% # Save the model as a CDB file for solving with PyMAPDL. -model.export_analysis_model(CDB_FILENAME_OUT) +model.export_analysis_model(working_dir_path / cdb_filename_out) # Export the shell lay-up and material file for PyDPF Composites. -model.export_shell_composite_definitions(COMPOSITE_DEFINITIONS_H5) -model.export_materials(MATML_FILE) - -# %% -# Download files from the ACP server to a local directory. -tmp_dir = tempfile.TemporaryDirectory() -WORKING_DIR = pathlib.Path(tmp_dir.name) -cdb_file_local_path = pathlib.Path(WORKING_DIR) / CDB_FILENAME_OUT -matml_file_local_path = pathlib.Path(WORKING_DIR) / MATML_FILE -composite_definitions_local_path = pathlib.Path(WORKING_DIR) / COMPOSITE_DEFINITIONS_H5 -acp.download_file(remote_filename=CDB_FILENAME_OUT, local_path=str(cdb_file_local_path)) -acp.download_file(remote_filename=MATML_FILE, local_path=str(matml_file_local_path)) -acp.download_file( - remote_filename=COMPOSITE_DEFINITIONS_H5, local_path=str(composite_definitions_local_path) -) +model.export_shell_composite_definitions(working_dir_path / composite_definition_h5_filename) +model.export_materials(working_dir_path / matml_filename) + # %% # Solve with PyMAPDL @@ -326,9 +307,10 @@ def add_ply(mg, name, ply_material, angle, oss): mapdl = launch_mapdl() mapdl.clear() + # %% # Load the CDB file into PyMAPDL. -mapdl.input(str(cdb_file_local_path)) +mapdl.input(str(working_dir_path / cdb_filename_out)) # %% # Solve the model. @@ -344,8 +326,7 @@ def add_ply(mg, name, ply_material, angle, oss): # Download the RST file for further postprocessing. rstfile_name = f"{mapdl.jobname}.rst" -rst_file_local_path = pathlib.Path(tmp_dir.name) / rstfile_name -mapdl.download(rstfile_name, tmp_dir.name) +mapdl.download(rstfile_name, working_dir_path) # %% # Postprocessing with PyDPF Composites @@ -389,11 +370,13 @@ def add_ply(mg, name, ply_material, angle, oss): # Create the composite model and configure its input. composite_model = CompositeModel( composite_files=ContinuousFiberCompositesFiles( - rst=rst_file_local_path, + rst=working_dir_path / rstfile_name, composite={ - "shell": CompositeDefinitionFiles(definition=composite_definitions_local_path), + "shell": CompositeDefinitionFiles( + definition=working_dir_path / composite_definition_h5_filename + ), }, - engineering_data=matml_file_local_path, + engineering_data=working_dir_path / matml_filename, ), default_unit_system=unit_systems.solver_nmm, server=dpf_server, diff --git a/examples/workflows/03-pymechanical-shell-workflow.py b/examples/workflows/03-pymechanical-shell-workflow.py new file mode 100644 index 0000000000..677e1a95fa --- /dev/null +++ b/examples/workflows/03-pymechanical-shell-workflow.py @@ -0,0 +1,311 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +""" + +.. _pymechanical_shell_example: + +PyMechanical shell workflow +=========================== + +This example shows how to set up a simple shell model with PyACP and +PyMechanical: + +- The geometry is imported into Mechanical and meshed. +- The mesh is exported to ACP. +- A simple lay-up is defined in ACP. +- Plies and materials are exported from ACP, and imported into Mechanical. +- Boundary conditions are set in Mechanical. +- The model is solved. +- The results are post-processed in PyDPF Composites. + +.. warning:: + + The PyACP / PyMechanical integration is still experimental. Refer to the + :ref:`limitations section ` for more information. + +""" + +# %% +# Import modules and start the Ansys products +# ------------------------------------------- + + +# %% +# Import the standard library and third-party dependencies. + +from concurrent.futures import ThreadPoolExecutor +import pathlib +import tempfile +import textwrap + +# %% +# Import PyACP, PyMechanical, and PyDPF Composites. + +# isort: off +import ansys.acp.core as pyacp +import ansys.dpf.composites as pydpf_composites +import ansys.mechanical.core as pymechanical + +# sphinx_gallery_thumbnail_path = '_static/gallery_thumbnails/sphx_glr_03-pymechanical-shell-workflow_thumb.png' + +# %% +# Start the ACP, Mechanical, and DPF servers. We use a ``ThreadPoolExecutor`` +# to start them in parallel. +with ThreadPoolExecutor() as executor: + futures = [ + executor.submit(pymechanical.launch_mechanical, batch=True), + executor.submit(pyacp.launch_acp), + executor.submit(pydpf_composites.server_helpers.connect_to_or_start_server), + ] + mechanical, acp, dpf = (fut.result() for fut in futures) + +# %% +# Get example input files +# ----------------------- +# +# Create a temporary working directory, and download the example input files +# to this directory. + +working_dir = tempfile.TemporaryDirectory() +working_dir_path = pathlib.Path(working_dir.name) +input_geometry = pyacp.extras.example_helpers.get_example_file( + pyacp.extras.example_helpers.ExampleKeys.CLASS40_AGDB, working_dir_path +) + +# %% +# Generate the mesh in PyMechanical +# --------------------------------- +# +# Load the geometry into Mechanical, generate the mesh, and export it to the +# appropriate transfer format for ACP. + +mesh_path = working_dir_path / "mesh.h5" +mechanical.run_python_script( + # This script runs in the Mechanical Python environment, which uses IronPython 2.7. + textwrap.dedent( + f"""\ + # Import the geometry + geometry_import = Model.GeometryImportGroup.AddGeometryImport() + + import_format = Ansys.Mechanical.DataModel.Enums.GeometryImportPreference.Format.Automatic + import_preferences = Ansys.ACT.Mechanical.Utilities.GeometryImportPreferences() + import_preferences.ProcessNamedSelections = True + import_preferences.ProcessCoordinateSystems = True + + geometry_file = {str(input_geometry)!r} + geometry_import.Import( + geometry_file, + import_format, + import_preferences + ) + + # The thickness will be overridden by the ACP model, but is required + # for the model to be valid. + for body in Model.Geometry.GetChildren( + Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body, True + ): + body.Thickness = Quantity(1e-6, "m") + + Model.Mesh.GenerateMesh() + """ + ) +) +pyacp.mechanical_integration_helpers.export_mesh_for_acp(mechanical=mechanical, path=mesh_path) + +# %% +# Set up the ACP model +# -------------------- +# +# Setup basic ACP lay-up based on the mesh in ``mesh_path``, and export material and composite +# definition file to output_path. + +composite_definitions_h5 = "ACPCompositeDefinitions.h5" +matml_file = "materials.xml" + + +model = acp.import_model(mesh_path, format="ansys:h5") + +mat = model.create_material(name="mat") + +mat.ply_type = "regular" +mat.engineering_constants.E1 = 1e12 +mat.engineering_constants.E2 = 1e11 +mat.engineering_constants.E3 = 1e11 +mat.engineering_constants.G12 = 1e10 +mat.engineering_constants.G23 = 1e10 +mat.engineering_constants.G31 = 1e10 +mat.engineering_constants.nu12 = 0.3 +mat.engineering_constants.nu13 = 0.3 +mat.engineering_constants.nu23 = 0.3 + +mat.strain_limits = pyacp.material_property_sets.ConstantStrainLimits.from_orthotropic_constants( + eXc=-0.01, + eYc=-0.01, + eZc=-0.01, + eXt=0.01, + eYt=0.01, + eZt=0.01, + eSxy=0.01, + eSyz=0.01, + eSxz=0.01, +) + +corecell_81kg_5mm = model.create_fabric(name="Corecell 81kg", thickness=0.005, material=mat) + +ros = model.create_rosette(name="ros", origin=(0, 0, 0)) + +oss = model.create_oriented_selection_set( + name="oss", + orientation_point=(-0, 0, 0), + orientation_direction=(0.0, 1, 0.0), + element_sets=[model.element_sets["All_Elements"]], + rosettes=[ros], +) + +mg = model.create_modeling_group(name="group") +mg.create_modeling_ply( + name="ply", + ply_material=corecell_81kg_5mm, + oriented_selection_sets=[oss], + ply_angle=45, + number_of_layers=1, + global_ply_nr=0, # add at the end +) +mg.create_modeling_ply( + name="ply2", + ply_material=corecell_81kg_5mm, + oriented_selection_sets=[oss], + ply_angle=0, + number_of_layers=2, + global_ply_nr=0, # add at the end +) + +# %% +# Update and Save the ACP model +# ----------------------------- + +model.update() + +model.export_shell_composite_definitions(working_dir_path / composite_definitions_h5) +model.export_materials(working_dir_path / matml_file) + +# %% +# Import materials and plies into Mechanical +# ------------------------------------------ +# +# Import materials into Mechanical + +mechanical.run_python_script(f"Model.Materials.Import({str(working_dir_path / matml_file)!r})") + +# %% +# Import plies into Mechanical + +pyacp.mechanical_integration_helpers.import_acp_composite_definitions( + mechanical=mechanical, + path=working_dir_path / composite_definitions_h5, +) + +# %% +# Set boundary condition and solve +# --------------------------------- +# + +mechanical.run_python_script( + textwrap.dedent( + """\ + front_edge = Model.AddNamedSelection() + front_edge.Name = "Front Edge" + front_edge.ScopingMethod = GeometryDefineByType.Worksheet + + front_edge.GenerationCriteria.Add(None) + front_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge + front_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + front_edge.GenerationCriteria[0].Operator = SelectionOperatorType.GreaterThan + front_edge.GenerationCriteria[0].Value = Quantity('-4.6 [m]') + front_edge.Generate() + + back_edge = Model.AddNamedSelection() + back_edge.Name = "Back Edge" + back_edge.ScopingMethod = GeometryDefineByType.Worksheet + + back_edge.GenerationCriteria.Add(None) + back_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge + back_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + back_edge.GenerationCriteria[0].Operator = SelectionOperatorType.LessThan + back_edge.GenerationCriteria[0].Value = Quantity('-7.8 [m]') + back_edge.Generate() + + analysis = Model.AddStaticStructuralAnalysis() + + fixed_support = analysis.AddFixedSupport() + fixed_support.Location = back_edge + + force = analysis.AddForce() + force.DefineBy = LoadDefineBy.Components + force.XComponent.Output.SetDiscreteValue(0, Quantity(1e6, "N")) + force.Location = front_edge + + analysis.Solution.Solve(True) + """ + ) +) + +rst_file = [filename for filename in mechanical.list_files() if filename.endswith(".rst")][0] +matml_out = [filename for filename in mechanical.list_files() if filename.endswith("MatML.xml")][0] + +# %% +# Postprocess results +# ------------------- +# +# Evaluate the failure criteria using the PyDPF Composites. + + +max_strain = pydpf_composites.failure_criteria.MaxStrainCriterion() +cfc = pydpf_composites.failure_criteria.CombinedFailureCriterion( + name="Combined Failure Criterion", + failure_criteria=[max_strain], +) + +composite_model = pydpf_composites.composite_model.CompositeModel( + composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles( + rst=rst_file, + composite={ + "shell": pydpf_composites.data_sources.CompositeDefinitionFiles( + definition=working_dir_path / composite_definitions_h5 + ), + }, + engineering_data=working_dir_path / matml_out, + ), + server=dpf, +) + +# Evaluate the failure criteria +output_all_elements = composite_model.evaluate_failure_criteria(cfc) + +# Query and plot the results +irf_field = output_all_elements.get_field( + {"failure_label": pydpf_composites.constants.FailureOutput.FAILURE_VALUE} +) + +irf_field.plot() diff --git a/examples/workflows/04-pymechanical-solid-workflow.py b/examples/workflows/04-pymechanical-solid-workflow.py new file mode 100644 index 0000000000..fe142634c1 --- /dev/null +++ b/examples/workflows/04-pymechanical-solid-workflow.py @@ -0,0 +1,367 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +""" + +.. _pymechanical_solid_example: + +PyMechanical solid workflow +=========================== + +This example shows how to set up a simple solid model with PyACP and +PyMechanical: + +- The geometry is imported into Mechanical and meshed. +- The mesh is exported to ACP. +- A simple lay-up and solid model is defined in ACP. +- The solid model is exported, to a CDB file and a composite definition file. +- In a separate Mechanical instance, the solid model is imported. +- Materials and plies are imported. +- Boundary conditions are set. +- The model is solved. +- The results are post-processed in PyDPF Composites. + +.. warning:: + + The PyACP / PyMechanical integration is still experimental. Refer to the + :ref:`limitations section ` for more information. + +""" + +# %% +# Import modules and start the Ansys products +# ------------------------------------------- + + +# %% +# Import the standard library and third-party dependencies. + +from concurrent.futures import ThreadPoolExecutor +import pathlib +import tempfile +import textwrap + +# %% +# Import PyACP, PyMechanical, and PyDPF Composites. + +# isort: off + +import ansys.acp.core as pyacp +import ansys.dpf.composites as pydpf_composites +import ansys.mechanical.core as pymechanical + +# sphinx_gallery_thumbnail_path = '_static/gallery_thumbnails/sphx_glr_04-pymechanical-solid-workflow_thumb.png' + +# %% +# Start the ACP, Mechanical, and DPF servers. We use a ``ThreadPoolExecutor`` +# to start them in parallel. +with ThreadPoolExecutor() as executor: + futures = [ + executor.submit(pymechanical.launch_mechanical, batch=True), + executor.submit(pymechanical.launch_mechanical, batch=True), + executor.submit(pyacp.launch_acp), + executor.submit(pydpf_composites.server_helpers.connect_to_or_start_server), + ] + mechanical_shell_geometry, mechanical_solid_model, acp, dpf = (fut.result() for fut in futures) + +# %% +# Get example input files +# ----------------------- +# +# Create a temporary working directory, and download the example input files +# to this directory. + +working_dir = tempfile.TemporaryDirectory() +working_dir_path = pathlib.Path(working_dir.name) +input_geometry = pyacp.extras.example_helpers.get_example_file( + pyacp.extras.example_helpers.ExampleKeys.CLASS40_AGDB, working_dir_path +) + + +# %% +# Generate the mesh in PyMechanical +# --------------------------------- +# +# Load the geometry into Mechanical, generate the mesh, and export it to the +# appropriate transfer format for ACP. + +mesh_path = working_dir_path / "mesh.h5" +mechanical_shell_geometry.run_python_script( + # This script runs in the Mechanical Python environment, which uses IronPython 2.7. + textwrap.dedent( + f"""\ + geometry_import = Model.GeometryImportGroup.AddGeometryImport() + + import_format = Ansys.Mechanical.DataModel.Enums.GeometryImportPreference.Format.Automatic + import_preferences = Ansys.ACT.Mechanical.Utilities.GeometryImportPreferences() + import_preferences.ProcessNamedSelections = True + import_preferences.ProcessCoordinateSystems = True + + geometry_file = {str(input_geometry)!r} + geometry_import.Import( + geometry_file, + import_format, + import_preferences + ) + + for body in Model.Geometry.GetChildren( + Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body, True + ): + body.Thickness = Quantity(1e-6, "m") + + hull = Model.AddNamedSelection() + hull.Name = "hull" + hull.ScopingMethod = GeometryDefineByType.Worksheet + # Add all faces with Z location < 0.9 m + hull.GenerationCriteria.Add(None) + hull.GenerationCriteria[0].EntityType = SelectionType.GeoFace + hull.GenerationCriteria[0].Operator = SelectionOperatorType.LessThan + hull.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationZ + hull.GenerationCriteria[0].Value = Quantity('0.9 [m]') + # Remove keeltower + hull.GenerationCriteria.Add(None) + hull.GenerationCriteria[1].Action = SelectionActionType.Remove + hull.GenerationCriteria[1].Criterion = SelectionCriterionType.LocationX + hull.GenerationCriteria[1].Operator = SelectionOperatorType.RangeInclude + hull.GenerationCriteria[1].LowerBound = Quantity('-6.7 [m]') + hull.GenerationCriteria[1].UpperBound = Quantity('-5.9 [m]') + # Add back keeltower bottom + hull.GenerationCriteria.Add(None) + hull.GenerationCriteria[2].Criterion = SelectionCriterionType.LocationZ + hull.GenerationCriteria[2].Operator = SelectionOperatorType.LessThan + hull.GenerationCriteria[2].Value = Quantity('-0.25 [m]') + # Remove bulkhead + hull.GenerationCriteria.Add(None) + hull.GenerationCriteria[3].Action = SelectionActionType.Remove + hull.GenerationCriteria[3].Criterion = SelectionCriterionType.LocationX + hull.GenerationCriteria[3].Operator = SelectionOperatorType.RangeInclude + hull.GenerationCriteria[3].LowerBound = Quantity('-5.7 [m]') + hull.GenerationCriteria[3].UpperBound = Quantity('-5.6 [m]') + hull.Generate() + + Model.Mesh.GenerateMesh() + """ + ) +) +pyacp.mechanical_integration_helpers.export_mesh_for_acp( + mechanical=mechanical_shell_geometry, path=mesh_path +) + + +# %% +# Set up the ACP model +# -------------------- +# +# Setup basic ACP lay-up based on the mesh in ``mesh_path``, and export the following +# files to ``output_path``: +# +# - Materials XML file +# - Composite definitions HDF5 file +# - Solid model composite definitions HDF5 file +# - Solid model CDB file + +matml_file = "materials.xml" +solid_model_cdb_file = "SolidModel.cdb" +solid_model_composite_definitions_h5 = "SolidModel.h5" + +model = acp.import_model(path=mesh_path, format="ansys:h5") + +mat = model.create_material(name="mat") + +mat.ply_type = "regular" +mat.engineering_constants.E1 = 1e12 +mat.engineering_constants.E2 = 1e11 +mat.engineering_constants.E3 = 1e11 +mat.engineering_constants.G12 = 1e10 +mat.engineering_constants.G23 = 1e10 +mat.engineering_constants.G31 = 1e10 +mat.engineering_constants.nu12 = 0.3 +mat.engineering_constants.nu13 = 0.3 +mat.engineering_constants.nu23 = 0.3 + +mat.strain_limits = pyacp.material_property_sets.ConstantStrainLimits.from_orthotropic_constants( + eXc=-0.01, + eYc=-0.01, + eZc=-0.01, + eXt=0.01, + eYt=0.01, + eZt=0.01, + eSxy=0.01, + eSyz=0.01, + eSxz=0.01, +) + +corecell_81kg_5mm = model.create_fabric(name="Corecell 81kg", thickness=0.005, material=mat) + +ros = model.create_rosette(name="ros", origin=(0, 0, 0)) + +oss = model.create_oriented_selection_set( + name="oss", + orientation_point=(-0, 0, 0), + orientation_direction=(0.0, 1, 0.0), + element_sets=[model.element_sets["All_Elements"]], + rosettes=[ros], +) + +mg = model.create_modeling_group(name="group") +mg.create_modeling_ply( + name="ply", + ply_material=corecell_81kg_5mm, + oriented_selection_sets=[oss], + ply_angle=45, + number_of_layers=1, + global_ply_nr=0, # add at the end +) +mg.create_modeling_ply( + name="ply2", + ply_material=corecell_81kg_5mm, + oriented_selection_sets=[oss], + ply_angle=0, + number_of_layers=2, + global_ply_nr=0, # add at the end +) + +solid_model = model.create_solid_model( + element_sets=[model.element_sets["hull"]], +) + +# %% +# Update and Save the ACP model +# ----------------------------- + +model.update() + +model.export_materials(working_dir_path / matml_file) +solid_model.export(working_dir_path / solid_model_cdb_file, format="ansys:cdb") +solid_model.export(working_dir_path / solid_model_composite_definitions_h5, format="ansys:h5") + + +# %% +# Import mesh, materials and plies into Mechanical +# ------------------------------------------------ +# +# Import geometry, mesh, and named selections into Mechanical + +pyacp.mechanical_integration_helpers.import_acp_mesh_from_cdb( + mechanical=mechanical_solid_model, cdb_path=working_dir_path / solid_model_cdb_file +) + + +# %% +# Import materials into Mechanical + +mechanical_solid_model.run_python_script( + f"Model.Materials.Import({str(working_dir_path / matml_file)!r})" +) + +# %% +# Import plies into Mechanical + +pyacp.mechanical_integration_helpers.import_acp_composite_definitions( + mechanical=mechanical_solid_model, path=working_dir_path / solid_model_composite_definitions_h5 +) + + +# %% +# Set boundary condition and solve +# --------------------------------- +# +# Set boundary condition and solve + +mechanical_solid_model.run_python_script( + textwrap.dedent( + """\ + analysis = Model.AddStaticStructuralAnalysis() + + front_face = Model.AddNamedSelection() + front_face.Name = "front_face" + front_face.ScopingMethod = GeometryDefineByType.Worksheet + front_face.GenerationCriteria.Add(None) + front_face.GenerationCriteria[0].EntityType = SelectionType.GeoFace + front_face.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + front_face.GenerationCriteria[0].Operator = SelectionOperatorType.Largest + front_face.Generate() + + back_face = Model.AddNamedSelection() + back_face.Name = "back_face" + back_face.ScopingMethod = GeometryDefineByType.Worksheet + back_face.GenerationCriteria.Add(None) + back_face.GenerationCriteria[0].EntityType = SelectionType.GeoFace + back_face.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + back_face.GenerationCriteria[0].Operator = SelectionOperatorType.Smallest + back_face.Generate() + + fixed_support = analysis.AddFixedSupport() + fixed_support.Location = back_face + + force = analysis.AddForce() + force.DefineBy = LoadDefineBy.Components + force.XComponent.Output.SetDiscreteValue(0, Quantity(1e5, "N")) + force.Location = front_face + + analysis.Solve(True) + """ + ) +) + +rst_file = [ + filename for filename in mechanical_solid_model.list_files() if filename.endswith(".rst") +][0] +matml_out = [ + filename for filename in mechanical_solid_model.list_files() if filename.endswith("MatML.xml") +][0] + +# %% +# Postprocess results +# ------------------- +# +# Evaluate the failure criteria using the PyDPF Composites. + +max_strain = pydpf_composites.failure_criteria.MaxStrainCriterion() +cfc = pydpf_composites.failure_criteria.CombinedFailureCriterion( + name="Combined Failure Criterion", + failure_criteria=[max_strain], +) + +composite_model = pydpf_composites.composite_model.CompositeModel( + composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles( + rst=rst_file, + composite={ + "solid": pydpf_composites.data_sources.CompositeDefinitionFiles( + definition=working_dir_path / solid_model_composite_definitions_h5 + ), + }, + engineering_data=working_dir_path / matml_out, + ), + server=dpf, +) + +# Evaluate the failure criteria +output_all_elements = composite_model.evaluate_failure_criteria(cfc) + +# Query and plot the results +irf_field = output_all_elements.get_field( + {"failure_label": pydpf_composites.constants.FailureOutput.FAILURE_VALUE} +) + +irf_field.plot() diff --git a/examples/workflows/05-pymechanical-to-cdb-workflow.py b/examples/workflows/05-pymechanical-to-cdb-workflow.py new file mode 100644 index 0000000000..2089016be5 --- /dev/null +++ b/examples/workflows/05-pymechanical-to-cdb-workflow.py @@ -0,0 +1,316 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +""" + +.. _pymechanical_to_cdb_example: + +PyMechanical to CDB shell workflow +================================== + +This example shows how to set up a workflow that uses PyMechanical to mesh the +geometry and define the load case, PyACP to define a layup, PyMAPDL to solve the +model, and PyDPF Composites to post-process the results. + +This workflow does *not* suffer from the limitations of the PyACP to +PyMechanical integration. + +""" + + +# %% +# Import modules and start the Ansys products +# ------------------------------------------- + + +# %% +# Import the standard library and third-party dependencies. + +from concurrent.futures import ThreadPoolExecutor +import pathlib +import tempfile +import textwrap + +# %% +# Import PyACP, PyMechanical, and PyDPF Composites. + +# isort: off +import ansys.acp.core as pyacp +import ansys.dpf.core as pydpf_core +import ansys.dpf.composites as pydpf_composites +import ansys.mapdl.core as pymapdl +import ansys.mechanical.core as pymechanical + +# sphinx_gallery_thumbnail_path = '_static/gallery_thumbnails/sphx_glr_05-pymechanical-to-cdb-workflow_thumb.png' + +# %% +# Start the ACP, Mechanical, and DPF servers. We use a ``ThreadPoolExecutor`` +# to start them in parallel. +with ThreadPoolExecutor() as executor: + futures = [ + executor.submit(pymechanical.launch_mechanical, batch=True), + executor.submit(pyacp.launch_acp), + executor.submit(pymapdl.launch_mapdl), + executor.submit(pydpf_composites.server_helpers.connect_to_or_start_server), + ] + mechanical, acp, mapdl, dpf = (fut.result() for fut in futures) + mapdl.clear() + +# %% +# Get example input files +# ----------------------- +# +# Create a temporary working directory, and download the example input files +# to this directory. + +working_dir = tempfile.TemporaryDirectory() +working_dir_path = pathlib.Path(working_dir.name) +input_geometry = pyacp.extras.example_helpers.get_example_file( + pyacp.extras.example_helpers.ExampleKeys.CLASS40_AGDB, working_dir_path +) + +# %% +# Generate the mesh in PyMechanical +# --------------------------------- +# +# Load the geometry into Mechanical, generate the mesh, and define the +# load case. + +cdb_path_initial = working_dir_path / "model_from_mechanical.cdb" +mechanical.run_python_script( + # This script runs in the Mechanical Python environment, which uses IronPython 2.7. + textwrap.dedent( + f"""\ + # Import the geometry + geometry_import = Model.GeometryImportGroup.AddGeometryImport() + + import_format = Ansys.Mechanical.DataModel.Enums.GeometryImportPreference.Format.Automatic + import_preferences = Ansys.ACT.Mechanical.Utilities.GeometryImportPreferences() + import_preferences.ProcessNamedSelections = True + import_preferences.ProcessCoordinateSystems = True + + geometry_file = {str(input_geometry)!r} + geometry_import.Import( + geometry_file, + import_format, + import_preferences + ) + + # The thickness will be overridden by the ACP model, but is required + # for the model to be valid. + for body in Model.Geometry.GetChildren( + Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body, True + ): + body.Thickness = Quantity(1e-6, "m") + + Model.Mesh.GenerateMesh() + + # Define named selections at the front and back edges + front_edge = Model.AddNamedSelection() + front_edge.Name = "Front Edge" + front_edge.ScopingMethod = GeometryDefineByType.Worksheet + + front_edge.GenerationCriteria.Add(None) + front_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge + front_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + front_edge.GenerationCriteria[0].Operator = SelectionOperatorType.GreaterThan + front_edge.GenerationCriteria[0].Value = Quantity('-4.6 [m]') + front_edge.Generate() + + back_edge = Model.AddNamedSelection() + back_edge.Name = "Back Edge" + back_edge.ScopingMethod = GeometryDefineByType.Worksheet + + back_edge.GenerationCriteria.Add(None) + back_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge + back_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + back_edge.GenerationCriteria[0].Operator = SelectionOperatorType.LessThan + back_edge.GenerationCriteria[0].Value = Quantity('-7.8 [m]') + back_edge.Generate() + + # Create a static structural analysis, and define the boundary + # conditions (fixed support at the back edge, force at the front edge). + analysis = Model.AddStaticStructuralAnalysis() + + fixed_support = analysis.AddFixedSupport() + fixed_support.Location = back_edge + + force = analysis.AddForce() + force.DefineBy = LoadDefineBy.Components + force.XComponent.Output.SetDiscreteValue(0, Quantity(1e6, "N")) + force.Location = front_edge + + # Export the model to a CDB file + analysis.WriteInputFile({str(cdb_path_initial)!r}) + """ + ) +) + +# %% +# Set up the ACP model +# -------------------- +# +# Setup basic ACP lay-up based on the CDB file. + + +model = acp.import_model(path=cdb_path_initial, format="ansys:cdb") + +mat = model.create_material(name="mat") + +mat.ply_type = "regular" +mat.engineering_constants.E1 = 1e12 +mat.engineering_constants.E2 = 1e11 +mat.engineering_constants.E3 = 1e11 +mat.engineering_constants.G12 = 1e10 +mat.engineering_constants.G23 = 1e10 +mat.engineering_constants.G31 = 1e10 +mat.engineering_constants.nu12 = 0.3 +mat.engineering_constants.nu13 = 0.3 +mat.engineering_constants.nu23 = 0.3 + +mat.strain_limits = pyacp.material_property_sets.ConstantStrainLimits.from_orthotropic_constants( + eXc=-0.01, + eYc=-0.01, + eZc=-0.01, + eXt=0.01, + eYt=0.01, + eZt=0.01, + eSxy=0.01, + eSyz=0.01, + eSxz=0.01, +) + +corecell_81kg_5mm = model.create_fabric(name="Corecell 81kg", thickness=0.005, material=mat) + +ros = model.create_rosette(name="ros", origin=(0, 0, 0)) + +oss = model.create_oriented_selection_set( + name="oss", + orientation_point=(-0, 0, 0), + orientation_direction=(0.0, 1, 0.0), + element_sets=[model.element_sets["All_Elements"]], + rosettes=[ros], +) + +mg = model.create_modeling_group(name="group") +mg.create_modeling_ply( + name="ply", + ply_material=corecell_81kg_5mm, + oriented_selection_sets=[oss], + ply_angle=45, + number_of_layers=1, + global_ply_nr=0, # add at the end +) +mg.create_modeling_ply( + name="ply2", + ply_material=corecell_81kg_5mm, + oriented_selection_sets=[oss], + ply_angle=0, + number_of_layers=2, + global_ply_nr=0, # add at the end +) + +# %% +# Update and Save the ACP model +# ----------------------------- + +model.update() + +cdb_filename_out = "model_from_acp.cdb" +composite_definitions_h5_filename = "ACPCompositeDefinitions.h5" +matml_filename = "materials.xml" + +model.export_analysis_model(working_dir_path / cdb_filename_out) +model.export_shell_composite_definitions(working_dir_path / composite_definitions_h5_filename) +model.export_materials(working_dir_path / matml_filename) + +# %% +# Solve with PyMAPDL +# ------------------ + +mapdl.clear() +# %% +# Load the CDB file into PyMAPDL. +mapdl.input(str(working_dir_path / cdb_filename_out)) + +# %% +# Solve the model. +mapdl.allsel() +mapdl.slashsolu() +mapdl.solve() + +# %% +# Show the displacements in postprocessing. +mapdl.post1() +mapdl.set("last") +mapdl.post_processing.plot_nodal_displacement(component="NORM") + +# %% +# Download the RST file for further postprocessing. +rstfile_name = f"{mapdl.jobname}.rst" +rst_file_local_path = working_dir_path / rstfile_name +mapdl.download(rstfile_name, working_dir_path) + +# %% +# Postprocessing with PyDPF Composites +# ------------------------------------ +# +# Specify the combined failure criterion. +max_strain = pydpf_composites.failure_criteria.MaxStrainCriterion() + +cfc = pydpf_composites.failure_criteria.CombinedFailureCriterion( + name="Combined Failure Criterion", + failure_criteria=[max_strain], +) + +# %% +# Create the composite model and configure its input. +composite_model = pydpf_composites.composite_model.CompositeModel( + composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles( + rst=rst_file_local_path, + composite={ + "shell": pydpf_composites.data_sources.CompositeDefinitionFiles( + definition=working_dir_path / composite_definitions_h5_filename + ), + }, + engineering_data=working_dir_path / matml_filename, + ), + default_unit_system=pydpf_core.unit_system.unit_systems.solver_nmm, + server=dpf, +) + +# %% +# Evaluate the failure criteria. +output_all_elements = composite_model.evaluate_failure_criteria(cfc) + +# %% +# Query and plot the results. +# +# Note that the maximum IRF is different when compared to :ref:`pymechanical_shell_example` +# because ACP sets the ``ERESX,NO`` option in the CDB file. This option disables interpolation +# of the results from the integration point to the nodes. + +irf_field = output_all_elements.get_field( + {"failure_label": pydpf_composites.constants.FailureOutput.FAILURE_VALUE} +) +irf_field.plot() diff --git a/examples/workflows/06-cdb-to-pymechanical-workflow.py b/examples/workflows/06-cdb-to-pymechanical-workflow.py new file mode 100644 index 0000000000..9beb625729 --- /dev/null +++ b/examples/workflows/06-cdb-to-pymechanical-workflow.py @@ -0,0 +1,309 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +""" + +.. _cdb_to_pymechanical_example: + +CDB to PyMechanical shell workflow +================================== + +This example shows how to define a composite lay-up in PyACP based on a mesh +from a CDB file, import the model into PyMechanical for defining the load and +boundary conditions, and run a failure analysis with PyDPF Composites. + +""" + + +# %% +# Import modules and start the Ansys products +# ------------------------------------------- + + +# %% +# Import the standard library and third-party dependencies. + +from concurrent.futures import ThreadPoolExecutor +import pathlib +import tempfile +import textwrap + +# %% +# Import PyACP, PyMechanical, and PyDPF Composites. + +# isort: off +import ansys.acp.core as pyacp +from ansys.acp.core.extras import example_helpers +import ansys.dpf.composites as pydpf_composites +import ansys.mechanical.core as pymechanical + +# sphinx_gallery_thumbnail_path = '_static/gallery_thumbnails/sphx_glr_06-cdb-to-pymechanical-workflow_thumb.png' + +# %% +# Start the ACP, Mechanical, and DPF servers. We use a ``ThreadPoolExecutor`` +# to start them in parallel. +with ThreadPoolExecutor() as executor: + futures = [ + executor.submit(pyacp.launch_acp), + executor.submit(pymechanical.launch_mechanical, batch=True), + executor.submit(pydpf_composites.server_helpers.connect_to_or_start_server), + ] + acp, mechanical, dpf = (fut.result() for fut in futures) + +# %% +# Get example input files +# ----------------------- +# +# Create a temporary working directory, and download the example input files +# to this directory. + +working_dir = tempfile.TemporaryDirectory() +working_dir_path = pathlib.Path(working_dir.name) +input_file = example_helpers.get_example_file( + example_helpers.ExampleKeys.BASIC_FLAT_PLATE_DAT, working_dir_path +) + +# %% +# Set up the ACP model +# -------------------- +# +# Setup basic ACP lay-up based on the CDB file. + + +model = acp.import_model(path=input_file, format="ansys:cdb") +model.unit_system + +# %% +# Visualize the loaded mesh. +mesh = model.mesh.to_pyvista() +mesh.plot(show_edges=True) + + +# %% +# Define the composite lay-up +# --------------------------- +# +# Create an orthotropic material and fabric including strain limits, which are later +# used to postprocess the simulation. +engineering_constants = ( + pyacp.material_property_sets.ConstantEngineeringConstants.from_orthotropic_constants( + E1=5e10, E2=1e10, E3=1e10, nu12=0.28, nu13=0.28, nu23=0.3, G12=5e9, G23=4e9, G31=4e9 + ) +) + +strain_limit = 0.01 +strain_limits = pyacp.material_property_sets.ConstantStrainLimits.from_orthotropic_constants( + eXc=-strain_limit, + eYc=-strain_limit, + eZc=-strain_limit, + eXt=strain_limit, + eYt=strain_limit, + eZt=strain_limit, + eSxy=strain_limit, + eSyz=strain_limit, + eSxz=strain_limit, +) + +ud_material = model.create_material( + name="UD", + ply_type=pyacp.PlyType.REGULAR, + engineering_constants=engineering_constants, + strain_limits=strain_limits, +) + +fabric = model.create_fabric(name="UD", material=ud_material, thickness=1e-4) + + +# %% +# Define a rosette and oriented selection set. Plot the orientation. +rosette = model.create_rosette(origin=(0.0, 0.0, 0.0), dir1=(1.0, 0.0, 0.0), dir2=(0.0, 0.0, 1.0)) + +oss = model.create_oriented_selection_set( + name="oss", + orientation_point=(0.0, 0.0, 0.0), + orientation_direction=(0.0, 1.0, 0), + element_sets=[model.element_sets["All_Elements"]], + rosettes=[rosette], +) + +model.update() + +plotter = pyacp.get_directions_plotter(model=model, components=[oss.elemental_data.orientation]) +plotter.show() + + +# %% +# Create various plies with different angles and add them to a modeling group. +modeling_group = model.create_modeling_group(name="modeling_group") +angles = [0, 45, -45, 45, -45, 0] +for idx, angle in enumerate(angles): + modeling_group.create_modeling_ply( + name=f"ply_{idx}_{angle}_{fabric.name}", + ply_angle=angle, + ply_material=fabric, + oriented_selection_sets=[oss], + ) + +model.update() + + +# %% +# Show the fiber directions of a specific ply. +modeling_ply = model.modeling_groups["modeling_group"].modeling_plies["ply_4_-45_UD"] + + +fiber_direction = modeling_ply.elemental_data.fiber_direction +assert fiber_direction is not None +plotter = pyacp.get_directions_plotter( + model=model, + components=[fiber_direction], +) + +plotter.show() + + +# %% +# For a quick overview, print the model tree. Note that +# the model can also be opened in the ACP GUI. For more +# information, see :ref:`view_the_model_in_the_acp_gui`. +pyacp.print_model(model) + +# %% +# Save the ACP model +# ------------------ + +cdb_filename = "model.cdb" +composite_definitions_h5_filename = "ACPCompositeDefinitions.h5" +matml_filename = "materials.xml" + +model.export_analysis_model(working_dir_path / cdb_filename) +model.export_shell_composite_definitions(working_dir_path / composite_definitions_h5_filename) +model.export_materials(working_dir_path / matml_filename) + + +# %% +# Import mesh, materials and plies into Mechanical +# ------------------------------------------------ +# +# Import geometry, mesh, and named selections into Mechanical + + +pyacp.mechanical_integration_helpers.import_acp_mesh_from_cdb( + mechanical=mechanical, cdb_path=working_dir_path / cdb_filename +) + + +# %% +# Import materials into Mechanical + +mechanical.run_python_script(f"Model.Materials.Import({str(working_dir_path / matml_filename)!r})") + +# %% +# Import plies into Mechanical + +pyacp.mechanical_integration_helpers.import_acp_composite_definitions( + mechanical=mechanical, path=working_dir_path / composite_definitions_h5_filename +) + +# %% +# Set boundary condition and solve +# --------------------------------- +# + +mechanical.run_python_script( + textwrap.dedent( + """\ + front_edge = Model.AddNamedSelection() + front_edge.Name = "Front Edge" + front_edge.ScopingMethod = GeometryDefineByType.Worksheet + + front_edge.GenerationCriteria.Add(None) + front_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge + front_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + front_edge.GenerationCriteria[0].Operator = SelectionOperatorType.Largest + front_edge.Generate() + + back_edge = Model.AddNamedSelection() + back_edge.Name = "Back Edge" + back_edge.ScopingMethod = GeometryDefineByType.Worksheet + + back_edge.GenerationCriteria.Add(None) + back_edge.GenerationCriteria[0].EntityType = SelectionType.GeoEdge + back_edge.GenerationCriteria[0].Criterion = SelectionCriterionType.LocationX + back_edge.GenerationCriteria[0].Operator = SelectionOperatorType.Smallest + back_edge.Generate() + + analysis = Model.AddStaticStructuralAnalysis() + + fixed_support = analysis.AddFixedSupport() + fixed_support.Location = back_edge + + force = analysis.AddForce() + force.DefineBy = LoadDefineBy.Components + force.XComponent.Output.SetDiscreteValue(0, Quantity(100, "N")) + force.Location = front_edge + + analysis.Solution.Solve(True) + """ + ) +) + + +rst_file = [filename for filename in mechanical.list_files() if filename.endswith(".rst")][0] +matml_out = [filename for filename in mechanical.list_files() if filename.endswith("MatML.xml")][0] + +# %% +# Postprocess results +# ------------------- +# +# Evaluate the failure criteria using the PyDPF Composites. + + +max_strain = pydpf_composites.failure_criteria.MaxStrainCriterion() +cfc = pydpf_composites.failure_criteria.CombinedFailureCriterion( + name="Combined Failure Criterion", + failure_criteria=[max_strain], +) + +composite_model = pydpf_composites.composite_model.CompositeModel( + composite_files=pydpf_composites.data_sources.ContinuousFiberCompositesFiles( + rst=rst_file, + composite={ + "shell": pydpf_composites.data_sources.CompositeDefinitionFiles( + definition=working_dir_path / composite_definitions_h5_filename + ), + }, + engineering_data=working_dir_path / matml_out, + ), + server=dpf, +) + +# Evaluate the failure criteria +output_all_elements = composite_model.evaluate_failure_criteria(cfc) + +# Query and plot the results +irf_field = output_all_elements.get_field( + {"failure_label": pydpf_composites.constants.FailureOutput.FAILURE_VALUE} +) + +irf_field.plot() diff --git a/examples/workflows/README.rst b/examples/workflows/README.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/poetry.lock b/poetry.lock index 50a52fa99a..b45a84b4cb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,108 +20,109 @@ tests = ["hypothesis", "pytest"] [[package]] name = "aiohappyeyeballs" -version = "2.3.6" +version = "2.4.3" description = "Happy Eyeballs for asyncio" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.3.6-py3-none-any.whl", hash = "sha256:15dca2611fa78442f1cb54cf07ffb998573f2b4fbeab45ca8554c045665c896b"}, - {file = "aiohappyeyeballs-2.3.6.tar.gz", hash = "sha256:88211068d2a40e0436033956d7de3926ff36d54776f8b1022d6b21320cadae79"}, + {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, + {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, ] [[package]] name = "aiohttp" -version = "3.10.3" +version = "3.11.6" description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db"}, - {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1"}, - {file = "aiohttp-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9"}, - {file = "aiohttp-3.10.3-cp310-cp310-win32.whl", hash = "sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9"}, - {file = "aiohttp-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, - {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, - {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, - {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, - {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d1100e68e70eb72eadba2b932b185ebf0f28fd2f0dbfe576cfa9d9894ef49752"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a541414578ff47c0a9b0b8b77381ea86b0c8531ab37fc587572cb662ccd80b88"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d5548444ef60bf4c7b19ace21f032fa42d822e516a6940d36579f7bfa8513f9c"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba2e838b5e6a8755ac8297275c9460e729dc1522b6454aee1766c6de6d56e5e"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48665433bb59144aaf502c324694bec25867eb6630fcd831f7a893ca473fcde4"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bac352fceed158620ce2d701ad39d4c1c76d114255a7c530e057e2b9f55bdf9f"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0f670502100cdc567188c49415bebba947eb3edaa2028e1a50dd81bd13363f"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b09f38a67679e32d380fe512189ccb0b25e15afc79b23fbd5b5e48e4fc8fd9"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:cd788602e239ace64f257d1c9d39898ca65525583f0fbf0988bcba19418fe93f"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:214277dcb07ab3875f17ee1c777d446dcce75bea85846849cc9d139ab8f5081f"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:32007fdcaab789689c2ecaaf4b71f8e37bf012a15cd02c0a9db8c4d0e7989fa8"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:123e5819bfe1b87204575515cf448ab3bf1489cdeb3b61012bde716cda5853e7"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:812121a201f0c02491a5db335a737b4113151926a79ae9ed1a9f41ea225c0e3f"}, - {file = "aiohttp-3.10.3-cp38-cp38-win32.whl", hash = "sha256:b97dc9a17a59f350c0caa453a3cb35671a2ffa3a29a6ef3568b523b9113d84e5"}, - {file = "aiohttp-3.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:3731a73ddc26969d65f90471c635abd4e1546a25299b687e654ea6d2fc052394"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21"}, - {file = "aiohttp-3.10.3-cp39-cp39-win32.whl", hash = "sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac"}, - {file = "aiohttp-3.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752"}, - {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, + {file = "aiohttp-3.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7510b3ca2275691875ddf072a5b6cd129278d11fe09301add7d292fc8d3432de"}, + {file = "aiohttp-3.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfab0d2c3380c588fc925168533edb21d3448ad76c3eadc360ff963019161724"}, + {file = "aiohttp-3.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf02dba0f342f3a8228f43fae256aafc21c4bc85bffcf537ce4582e2b1565188"}, + {file = "aiohttp-3.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92daedf7221392e7a7984915ca1b0481a94c71457c2f82548414a41d65555e70"}, + {file = "aiohttp-3.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2274a7876e03429e3218589a6d3611a194bdce08c3f1e19962e23370b47c0313"}, + {file = "aiohttp-3.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a2e1eae2d2f62f3660a1591e16e543b2498358593a73b193006fb89ee37abc6"}, + {file = "aiohttp-3.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:978ec3fb0a42efcd98aae608f58c6cfcececaf0a50b4e86ee3ea0d0a574ab73b"}, + {file = "aiohttp-3.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51f87b27d9219ed4e202ed8d6f1bb96f829e5eeff18db0d52f592af6de6bdbf"}, + {file = "aiohttp-3.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:04d1a02a669d26e833c8099992c17f557e3b2fdb7960a0c455d7b1cbcb05121d"}, + {file = "aiohttp-3.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3679d5fcbc7f1ab518ab4993f12f80afb63933f6afb21b9b272793d398303b98"}, + {file = "aiohttp-3.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a4b24e03d04893b5c8ec9cd5f2f11dc9c8695c4e2416d2ac2ce6c782e4e5ffa5"}, + {file = "aiohttp-3.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d9abdfd35ecff1c95f270b7606819a0e2de9e06fa86b15d9080de26594cf4c23"}, + {file = "aiohttp-3.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b5c3e7928a0ad80887a5eba1c1da1830512ddfe7394d805badda45c03db3109"}, + {file = "aiohttp-3.11.6-cp310-cp310-win32.whl", hash = "sha256:913dd9e9378f3c38aeb5c4fb2b8383d6490bc43f3b427ae79f2870651ae08f22"}, + {file = "aiohttp-3.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:4ac26d482c2000c3a59bf757a77adc972828c9d4177b4bd432a46ba682ca7271"}, + {file = "aiohttp-3.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26ac4c960ea8debf557357a172b3ef201f2236a462aefa1bc17683a75483e518"}, + {file = "aiohttp-3.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b1f13ebc99fb98c7c13057b748f05224ccc36d17dee18136c695ef23faaf4ff"}, + {file = "aiohttp-3.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4679f1a47516189fab1774f7e45a6c7cac916224c91f5f94676f18d0b64ab134"}, + {file = "aiohttp-3.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74491fdb3d140ff561ea2128cb7af9ba0a360067ee91074af899c9614f88a18f"}, + {file = "aiohttp-3.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f51e1a90412d387e62aa2d243998c5eddb71373b199d811e6ed862a9f34f9758"}, + {file = "aiohttp-3.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72ab89510511c3bb703d0bb5504787b11e0ed8be928ed2a7cf1cda9280628430"}, + {file = "aiohttp-3.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6681c9e046d99646e8059266688374a063da85b2e4c0ebfa078cda414905d080"}, + {file = "aiohttp-3.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a17f8a6d3ab72cbbd137e494d1a23fbd3ea973db39587941f32901bb3c5c350"}, + {file = "aiohttp-3.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:867affc7612a314b95f74d93aac550ce0909bc6f0b6c658cc856890f4d326542"}, + {file = "aiohttp-3.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:00d894ebd609d5a423acef885bd61e7f6a972153f99c5b3ea45fc01fe909196c"}, + {file = "aiohttp-3.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:614c87be9d0d64477d1e4b663bdc5d1534fc0a7ebd23fb08347ab9fd5fe20fd7"}, + {file = "aiohttp-3.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:533ed46cf772f28f3bffae81c0573d916a64dee590b5dfaa3f3d11491da05b95"}, + {file = "aiohttp-3.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:589884cfbc09813afb1454816b45677e983442e146183143f988f7f5a040791a"}, + {file = "aiohttp-3.11.6-cp311-cp311-win32.whl", hash = "sha256:1da63633ba921669eec3d7e080459d4ceb663752b3dafb2f31f18edd248d2170"}, + {file = "aiohttp-3.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:d778ddda09622e7d83095cc8051698a0084c155a1474bfee9bac27d8613dbc31"}, + {file = "aiohttp-3.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:943a952df105a5305257984e7a1f5c2d0fd8564ff33647693c4d07eb2315446d"}, + {file = "aiohttp-3.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d24ec28b7658970a1f1d98608d67f88376c7e503d9d45ff2ba1949c09f2b358c"}, + {file = "aiohttp-3.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6720e809a660fdb9bec7c168c582e11cfedce339af0a5ca847a5d5b588dce826"}, + {file = "aiohttp-3.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4252d30da0ada6e6841b325869c7ef5104b488e8dd57ec439892abbb8d7b3615"}, + {file = "aiohttp-3.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f65f43ff01b238aa0b5c47962c83830a49577efe31bd37c1400c3d11d8a32835"}, + {file = "aiohttp-3.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dc5933f6c9b26404444d36babb650664f984b8e5fa0694540e7b7315d11a4ff"}, + {file = "aiohttp-3.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5bf546ba0c029dfffc718c4b67748687fd4f341b07b7c8f1719d6a3a46164798"}, + {file = "aiohttp-3.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c351d05bbeae30c088009c0bb3b17dda04fd854f91cc6196c448349cc98f71c3"}, + {file = "aiohttp-3.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:10499079b063576fad1597898de3f9c0a2ce617c19cc7cd6b62fdcff6b408bf7"}, + {file = "aiohttp-3.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:442ee82eda47dd59798d6866ce020fb8d02ea31ac9ac82b3d719ed349e6a9d52"}, + {file = "aiohttp-3.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:86fce9127bc317119b34786d9e9ae8af4508a103158828a535f56d201da6ab19"}, + {file = "aiohttp-3.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:973d26a5537ce5d050302eb3cd876457451745b1da0624cbb483217970e12567"}, + {file = "aiohttp-3.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:532b8f038a4e001137d3600cea5d3439d1881df41bdf44d0f9651264d562fdf0"}, + {file = "aiohttp-3.11.6-cp312-cp312-win32.whl", hash = "sha256:4863c59f748dbe147da82b389931f2a676aebc9d3419813ed5ca32d057c9cb32"}, + {file = "aiohttp-3.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:5d7f481f82c18ac1f7986e31ba6eea9be8b2e2c86f1ef035b6866179b6c5dd68"}, + {file = "aiohttp-3.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:40f502350496ba4c6820816d3164f8a0297b9aa4e95d910da31beb189866a9df"}, + {file = "aiohttp-3.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9072669b0bffb40f1f6977d0b5e8a296edc964f9cefca3a18e68649c214d0ce3"}, + {file = "aiohttp-3.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:518160ecf4e6ffd61715bc9173da0925fcce44ae6c7ca3d3f098fe42585370fb"}, + {file = "aiohttp-3.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f69cc1b45115ac44795b63529aa5caa9674be057f11271f65474127b24fc1ce6"}, + {file = "aiohttp-3.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6be90a6beced41653bda34afc891617c6d9e8276eef9c183f029f851f0a3c3d"}, + {file = "aiohttp-3.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00c22fe2486308770d22ef86242101d7b0f1e1093ce178f2358f860e5149a551"}, + {file = "aiohttp-3.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2607ebb783e3aeefa017ec8f34b506a727e6b6ab2c4b037d65f0bc7151f4430a"}, + {file = "aiohttp-3.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f761d6819870c2a8537f75f3e2fc610b163150cefa01f9f623945840f601b2c"}, + {file = "aiohttp-3.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e44d1bc6c88f5234115011842219ba27698a5f2deee245c963b180080572aaa2"}, + {file = "aiohttp-3.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e0cb6a1b1f499cb2aa0bab1c9f2169ad6913c735b7447e058e0c29c9e51c0b5"}, + {file = "aiohttp-3.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a76b4d4ca34254dca066acff2120811e2a8183997c135fcafa558280f2cc53f3"}, + {file = "aiohttp-3.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:69051c1e45fb18c0ae4d39a075532ff0b015982e7997f19eb5932eb4a3e05c17"}, + {file = "aiohttp-3.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:aff2ed18274c0bfe0c1d772781c87d5ca97ae50f439729007cec9644ee9b15fe"}, + {file = "aiohttp-3.11.6-cp313-cp313-win32.whl", hash = "sha256:2fbea25f2d44df809a46414a8baafa5f179d9dda7e60717f07bded56300589b3"}, + {file = "aiohttp-3.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:f77bc29a465c0f9f6573d1abe656d385fa673e34efe615bd4acc50899280ee47"}, + {file = "aiohttp-3.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:de6123b298d17bca9e53581f50a275b36e10d98e8137eb743ce69ee766dbdfe9"}, + {file = "aiohttp-3.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a10200f705f4fff00e148b7f41e5d1d929c7cd4ac523c659171a0ea8284cd6fb"}, + {file = "aiohttp-3.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7776ef6901b54dd557128d96c71e412eec0c39ebc07567e405ac98737995aad"}, + {file = "aiohttp-3.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e5c2a55583cd91936baf73d223807bb93ace6eb1fe54424782690f2707162ab"}, + {file = "aiohttp-3.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b032bd6cf7422583bf44f233f4a1489fee53c6d35920123a208adc54e2aba41e"}, + {file = "aiohttp-3.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fe2d99acbc5cf606f75d7347bf3a027c24c27bc052d470fb156f4cfcea5739"}, + {file = "aiohttp-3.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84a79c366375c2250934d1238abe5d5ea7754c823a1c7df0c52bf0a2bfded6a9"}, + {file = "aiohttp-3.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c33cbbe97dc94a34d1295a7bb68f82727bcbff2b284f73ae7e58ecc05903da97"}, + {file = "aiohttp-3.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:19e4fb9ac727834b003338dcdd27dcfe0de4fb44082b01b34ed0ab67c3469fc9"}, + {file = "aiohttp-3.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a97f6b2afbe1d27220c0c14ea978e09fb4868f462ef3d56d810d206bd2e057a2"}, + {file = "aiohttp-3.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c3f7afeea03a9bc49be6053dfd30809cd442cc12627d6ca08babd1c1f9e04ccf"}, + {file = "aiohttp-3.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0d10967600ce5bb69ddcb3e18d84b278efb5199d8b24c3c71a4959c2f08acfd0"}, + {file = "aiohttp-3.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:60f2f631b9fe7aa321fa0f0ff3f5d8b9f7f9b72afd4eecef61c33cf1cfea5d58"}, + {file = "aiohttp-3.11.6-cp39-cp39-win32.whl", hash = "sha256:4d2b75333deb5c5f61bac5a48bba3dbc142eebbd3947d98788b6ef9cc48628ae"}, + {file = "aiohttp-3.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:8908c235421972a2e02abcef87d16084aabfe825d14cc9a1debd609b3cfffbea"}, + {file = "aiohttp-3.11.6.tar.gz", hash = "sha256:fd9f55c1b51ae1c20a1afe7216a64a88d38afee063baa23c7fce03757023c999"}, ] [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -130,7 +131,7 @@ speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, @@ -142,24 +143,24 @@ frozenlist = ">=1.1.0" [[package]] name = "alabaster" -version = "0.7.16" +version = "1.0.0" description = "A light, configurable Sphinx theme" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, - {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] [[package]] name = "ansys-api-acp" -version = "0.1.0.dev9" +version = "0.2.0" description = "Autogenerated Python package for the Ansys Composite PrepPost (ACP) gRPC API." optional = false python-versions = ">=3.7" files = [ - {file = "ansys_api_acp-0.1.0.dev9-py3-none-any.whl", hash = "sha256:003795e8a3f038edecf93b1031da3d6e809338ee5e42be40f0182d0bdf6782b1"}, - {file = "ansys_api_acp-0.1.0.dev9.tar.gz", hash = "sha256:5ade9e3c9d9da091202a5e55286f8afa5b0f30d2a58133414730bc879356d565"}, + {file = "ansys_api_acp-0.2.0-py3-none-any.whl", hash = "sha256:8aebea7150acf54b6cbbdc9bbac8bc7f7fdc71ea027a934d792c622926366764"}, + {file = "ansys_api_acp-0.2.0.tar.gz", hash = "sha256:177ad65573a844e7271fa9c1b692494f3b9a994e5eb9363ac7e7be645e7b73cd"}, ] [package.dependencies] @@ -170,7 +171,7 @@ protobuf = ">=3.19,<6" name = "ansys-api-mapdl" version = "0.5.2" description = "Autogenerated python gRPC interface package for ansys-api-mapdl, built on 10:27:13 on 09 July 2024" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "ansys-api-mapdl-0.5.2.tar.gz", hash = "sha256:0d1f043884ba052b3afcbcd4e359a2baa81d017cc386b857607c220c12d32da5"}, @@ -185,7 +186,7 @@ protobuf = ">=3.19,<5" name = "ansys-api-mechanical" version = "0.1.2" description = "Autogenerated python gRPC interface package for ansys-api-mechanical, built on 14:21:07 on 30 April 2024" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "ansys_api_mechanical-0.1.2-py3-none-any.whl", hash = "sha256:dfb344e4d32850570f7745061b73fbb7e6f69932e27452e68ee8bfde9c26ce99"}, @@ -200,7 +201,7 @@ protobuf = ">=3.19,<6" name = "ansys-api-platform-instancemanagement" version = "1.1.0" description = "Autogenerated python gRPC interface package for ansys-api-platform-instancemanagement, built on 09:44:35 on 24 April 2024" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "ansys_api_platform_instancemanagement-1.1.0-py3-none-any.whl", hash = "sha256:f7f5f310f600b9218976f363987681ea03f6d4de8958b7d144b3904c76bb8678"}, @@ -213,28 +214,28 @@ protobuf = ">=3.19,<6" [[package]] name = "ansys-api-tools-filetransfer" -version = "0.1.0" +version = "0.1.1" description = "Autogenerated python gRPC interface package for ansys-api-tools-filetransfer." optional = false python-versions = ">=3.7" files = [ - {file = "ansys-api-tools-filetransfer-0.1.0.tar.gz", hash = "sha256:f6e58af43016a0a4e0e7f3569cbface912d0a1004e858db90902c20371dd7345"}, - {file = "ansys_api_tools_filetransfer-0.1.0-py3-none-any.whl", hash = "sha256:0c888e3e2f76db82842d9437bf5521c38578feeb74ab56352702fbcf1cfec934"}, + {file = "ansys_api_tools_filetransfer-0.1.1-py3-none-any.whl", hash = "sha256:84baefa5e82106e74a0a49f01ba99283297d15094d47ea1a0490f7535e2f4a5b"}, + {file = "ansys_api_tools_filetransfer-0.1.1.tar.gz", hash = "sha256:180e41edc767d9ff4ce9980bb5b7002faa04d2a551281e39c01b5b8d5d6d6c92"}, ] [package.dependencies] grpcio = ">=1.17,<2.0" -protobuf = ">=3.19,<5" +protobuf = ">=3.19,<6" [[package]] name = "ansys-dpf-composites" -version = "0.6.0" +version = "0.6.1" description = "Post-processing of composite structures based on Ansys DPF" -optional = false +optional = true python-versions = "<3.13,>=3.9" files = [ - {file = "ansys_dpf_composites-0.6.0-py3-none-any.whl", hash = "sha256:f77c2f6b24884e717b3d3c7ae5eb0ddc0cc63a67a6d996599639f2c5aeacea86"}, - {file = "ansys_dpf_composites-0.6.0.tar.gz", hash = "sha256:5e0d79cf878f14716e6497fe8a65dcab8621e40fcd47808aee4ee9337996bb77"}, + {file = "ansys_dpf_composites-0.6.1-py3-none-any.whl", hash = "sha256:8d78dd9e1974c1b241c57a98c9a25ec138cf8ad5e997c803ab464ce38e118b64"}, + {file = "ansys_dpf_composites-0.6.1.tar.gz", hash = "sha256:a3feb7813ea7a2fd92c1a1bd1425b57de8d248e18f98b33d2abe883d1f95bbbe"}, ] [package.dependencies] @@ -252,22 +253,22 @@ test = ["pytest (>=7.1.2)", "pytest-cov (>=3.0.0)", "pytest-rerunfailures (>=11. [[package]] name = "ansys-dpf-core" -version = "0.13.0" +version = "0.13.3" description = "Data Processing Framework - Python Core" -optional = false +optional = true python-versions = "<4,>=3.9" files = [ - {file = "ansys_dpf_core-0.13.0-py3-none-any.whl", hash = "sha256:765195ee06db16a124568384caafb334a5d5b777639fd99564abc3f0c7872ea5"}, - {file = "ansys_dpf_core-0.13.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:1cf4838a6d0c0efd8cd649c311dd8fc80b404aeaacd0d20fc60041ee27904f27"}, - {file = "ansys_dpf_core-0.13.0-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:31c5d45800176db40142f9e9f0f2b797b5a02aba3323951194349b9e792a8472"}, - {file = "ansys_dpf_core-0.13.0-py3-none-win_amd64.whl", hash = "sha256:70ccecd50baf757f1abb88562298df2b66c78a08e0d4124d29bbf95af786372e"}, + {file = "ansys_dpf_core-0.13.3-py3-none-any.whl", hash = "sha256:09d1089005043728b8ac53dd0c88dee9c27b3a3dc051ab586bc658bf403ab0e2"}, + {file = "ansys_dpf_core-0.13.3-py3-none-manylinux1_x86_64.whl", hash = "sha256:ff76d86892eb34a6f8ac97b26e50aefc7456c0b9535a72b71a2d05d1897a4065"}, + {file = "ansys_dpf_core-0.13.3-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:bd1a6a80f8f56c8efb1bf67c3f621aaa5adaa45b427802fc2fbeb952c8ad4a47"}, + {file = "ansys_dpf_core-0.13.3-py3-none-win_amd64.whl", hash = "sha256:4daa55f8644e5745eff34eeecad23671957508bb5a2878b484b29ad0375677ce"}, ] [package.dependencies] google-api-python-client = "*" grpcio = ">=1.63.0" importlib-metadata = ">=4.0" -numpy = "<2" +numpy = "*" packaging = "*" protobuf = "*" psutil = "*" @@ -275,17 +276,17 @@ setuptools = "*" tqdm = "*" [package.extras] -plotting = ["imageio (<2.28.1)", "imageio-ffmpeg", "matplotlib (>=3.2)", "pyvista (>=0.32.0)"] +plotting = ["imageio (<2.28.1)", "imageio-ffmpeg", "matplotlib (>=3.2)", "pyvista (>=0.32.0)", "vtk (!=9.4.0)"] [[package]] name = "ansys-mapdl-core" -version = "0.68.4" +version = "0.68.6" description = "A Python wrapper for Ansys MAPDL." -optional = false -python-versions = "<3.13,>=3.9" +optional = true +python-versions = "<3.13,>=3.10" files = [ - {file = "ansys_mapdl_core-0.68.4-py3-none-any.whl", hash = "sha256:66c2e8421b52f9b85518795cfc5a6ee9379947c72555c8687ec70445c1a7d5eb"}, - {file = "ansys_mapdl_core-0.68.4.tar.gz", hash = "sha256:5d98199c3ab37316261c7ab60f335a46c7b569576eb439d76757007158b7627d"}, + {file = "ansys_mapdl_core-0.68.6-py3-none-any.whl", hash = "sha256:8a62178e7b0aeb71a40edd4b97cfc2fa7849df643ad574f41b52aebea552b2f8"}, + {file = "ansys_mapdl_core-0.68.6.tar.gz", hash = "sha256:cb387b4f6a251f8da1ce0f5a6cca73b2ad83cdd8574fdb1da102280cb95c9f44"}, ] [package.dependencies] @@ -294,74 +295,74 @@ ansys-mapdl-reader = ">=0.51.7" ansys-math-core = ">=0.1.2" ansys-platform-instancemanagement = ">=1.0,<2.0" ansys-tools-path = ">=0.3.1" +ansys-tools-visualization-interface = ">=0.2.6" click = ">=8.1.3" grpcio = ">=1.30.0" importlib-metadata = ">=4.0" matplotlib = ">=3.0.0" -numpy = {version = ">=1.14.0,<2.0.0", markers = "python_version >= \"3.9\""} +numpy = {version = ">=1.14.0,<3.0.0", markers = "python_version >= \"3.9\""} pexpect = {version = ">=4.8.0", markers = "platform_system == \"Linux\""} platformdirs = ">=3.6.0" protobuf = ">=3.12.2" psutil = ">=5.9.4" pyansys-tools-versioning = ">=0.3.3" pyiges = {version = ">=0.3.1", extras = ["full"]} -pyvista = ">=0.38.1" scipy = ">=1.3.0" tabulate = ">=0.8.0" tqdm = ">=4.45.0" vtk = ">=9.0.0" [package.extras] -doc = ["ansys-dpf-core (==0.10.1)", "ansys-mapdl-reader (==0.53.0)", "ansys-sphinx-theme (==0.16.6)", "grpcio (==1.65.0)", "imageio (==2.34.2)", "imageio-ffmpeg (==0.5.1)", "jupyter_sphinx (==0.5.3)", "jupyterlab (>=3.2.8)", "matplotlib (==3.9.1)", "numpydoc (==1.7.0)", "pandas (==2.2.2)", "plotly (==5.22.0)", "pyiges[full] (==0.3.1)", "pypandoc (==1.13)", "pytest-sphinx (==0.6.3)", "pythreejs (==2.4.2)", "pyvista[jupyter] (==0.43.10)", "sphinx (==7.3.7)", "sphinx-autobuild (==2024.4.16)", "sphinx-autodoc-typehints (==1.25.2)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.0)", "sphinx-gallery (==0.16.0)", "sphinx-notfound-page (==1.0.2)", "sphinxcontrib-websupport (==1.2.7)", "sphinxemoji (==0.3.1)", "vtk (==9.3.1)"] -jupyter = ["ipywidgets", "jupyterlab (>=3)", "pyvista[jupyter]"] -tests = ["ansys-dpf-core (==0.10.1)", "autopep8 (==2.3.1)", "matplotlib (==3.9.1)", "pandas (==2.2.2)", "pyansys-tools-report (==0.7.3)", "pyiges[full] (==0.3.1)", "pytest (==8.2.2)", "pytest-cov (==5.0.0)", "pytest-memprof (<0.3.0)", "pytest-pyvista (==0.1.9)", "pytest-rerunfailures (==14.0)", "pyvista (==0.43.10)", "scipy (==1.14.0)", "vtk (==9.3.1)"] +doc = ["ansys-dpf-core (==0.10.1)", "ansys-mapdl-reader (==0.54.1)", "ansys-sphinx-theme (==1.1.2)", "ansys-tools-visualization-interface (==0.4.5)", "grpcio (==1.66.2)", "imageio (==2.35.1)", "imageio-ffmpeg (==0.5.1)", "jupyter (==1.1.1)", "jupyter_sphinx (==0.5.3)", "jupyterlab (>=3.2.8)", "matplotlib (==3.9.2)", "nbformat (==5.10.4)", "numpydoc (==1.8.0)", "pandas (==2.2.3)", "plotly (==5.24.1)", "pyiges[full] (==0.3.1)", "pypandoc (==1.14)", "pytest-sphinx (==0.6.3)", "pythreejs (==2.4.2)", "sphinx (==8.1.0)", "sphinx-autobuild (==2024.10.3)", "sphinx-autodoc-typehints (==1.25.2)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-gallery (==0.18.0)", "sphinx-jinja (==2.0.2)", "sphinx-notfound-page (==1.0.4)", "sphinxcontrib-websupport (==2.0.0)", "sphinxemoji (==0.3.1)", "vtk (==9.3.1)"] +jupyter = ["ipywidgets", "jupyterlab (>=3)"] +tests = ["ansys-dpf-core (==0.10.1)", "ansys-tools-visualization-interface (==0.4.5)", "autopep8 (==2.3.1)", "matplotlib (==3.9.2)", "pandas (==2.2.3)", "pyansys-tools-report (==0.8.0)", "pyiges[full] (==0.3.1)", "pytest (==8.3.3)", "pytest-cov (==5.0.0)", "pytest-memprof (<0.3.0)", "pytest-pyvista (==0.1.9)", "pytest-rerunfailures (==14.0)", "scipy (==1.14.1)", "vtk (==9.3.1)"] [[package]] name = "ansys-mapdl-reader" -version = "0.53.0" +version = "0.54.1" description = "Pythonic interface to files generated by MAPDL" -optional = false -python-versions = ">=3.7,<4" -files = [ - {file = "ansys-mapdl-reader-0.53.0.tar.gz", hash = "sha256:c7cd8ae1f2a3d6b86a0e443fc02f9d5139a06f49968672e9e417b98a558b8f5d"}, - {file = "ansys_mapdl_reader-0.53.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a100b0fb0be0d64ad8286cda6a7e70e4fcf80d10424504565a08af22c2484616"}, - {file = "ansys_mapdl_reader-0.53.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29b362179a7ce4febb1a205068a5ce1852faae95e0bf05eef037a46eee3ec73f"}, - {file = "ansys_mapdl_reader-0.53.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80cd2eded333358bc2b0e4eb4590dc2fc17e81258f340830cfc901f5f2cd256b"}, - {file = "ansys_mapdl_reader-0.53.0-cp310-cp310-win_amd64.whl", hash = "sha256:95487623058ba1f287513dbd282b069df2c48e6dfa2adfbe94aec8e374b80266"}, - {file = "ansys_mapdl_reader-0.53.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2731c2b73b6d3aafe47343b4d5225ff04346dfca7291855c7f5c031804671d3c"}, - {file = "ansys_mapdl_reader-0.53.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:032712a9624974f390d5c2f0f8867e3b78015f7accbd898784cf7e3d992b80ff"}, - {file = "ansys_mapdl_reader-0.53.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ef0c57448f8beabc06145e2f9b7b59d3fb2f0ae9fb5cddfba67b7969b9249b6"}, - {file = "ansys_mapdl_reader-0.53.0-cp311-cp311-win_amd64.whl", hash = "sha256:40a418a7a7da89c1fbe46a022096d168096f79f80e538113cc5b43dc37d7bcb9"}, - {file = "ansys_mapdl_reader-0.53.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5e3eff1c9e9478f78fe2bb5516b0279587c8a4bbf5b4b5c064aba51d41affced"}, - {file = "ansys_mapdl_reader-0.53.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e328139934287fe6589cc4201a98a51b0f10e4876db2bb394cf063bf35828a9b"}, - {file = "ansys_mapdl_reader-0.53.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4945065db8bb36ba83341dcdd762c20e60f93fe1d6e917046908cae41261b8f"}, - {file = "ansys_mapdl_reader-0.53.0-cp312-cp312-win_amd64.whl", hash = "sha256:6ec0ba7da3463953ef8688b6639ce99e2866edf54792494c81228eccdf617bfa"}, - {file = "ansys_mapdl_reader-0.53.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c5e796b474b7968443f6304d00c93b9351cce8dd3aafbaf7643c60a90a3315be"}, - {file = "ansys_mapdl_reader-0.53.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0dacef48856116df3bf3e8079fad73e7ed95b6335346a1be098bcc81feeb576d"}, - {file = "ansys_mapdl_reader-0.53.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:15909da54a9a1a6d802fe9fcbaf64318a9312385a160ebd792a256bb3880e1c4"}, - {file = "ansys_mapdl_reader-0.53.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:dc7bd910388fe81f6e32cba868e653326758bd810844ae58ad11fdf995c51536"}, - {file = "ansys_mapdl_reader-0.53.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:31596066fdc635ed85e7f0cd61d8e8d8458839d561097fd201c78c0b249a04bf"}, - {file = "ansys_mapdl_reader-0.53.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e8f63cad6ea9d9646e4573f791c222305121fd6d71acf27ec254ee6cc8d5b62"}, - {file = "ansys_mapdl_reader-0.53.0-cp39-cp39-win_amd64.whl", hash = "sha256:4ebfb62bde448f280aaeed034336ab2641fa3dfbf9ab256f000d15bf43df3cf3"}, +optional = true +python-versions = "<4,>=3.7" +files = [ + {file = "ansys_mapdl_reader-0.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9739a53801c946069f9085a016255a4b1781e4eff50aaec250cadf9ea5af6cd8"}, + {file = "ansys_mapdl_reader-0.54.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1c27ad42915261b5f85344a4cbdec9f810badf6637c1d560626b90dd252a1c3"}, + {file = "ansys_mapdl_reader-0.54.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ef034967c669d6e86b038915e462d3429c27025140f3cfa3a1f4e3c9b809907"}, + {file = "ansys_mapdl_reader-0.54.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7f6c1d5ff8aa67838731f8d5b16b0eace355730c31152baf58e331abfb9abf6"}, + {file = "ansys_mapdl_reader-0.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fb381c1efab68df61b9d220b8bc05697576d9a2498bf5dceaf26e99616339aaa"}, + {file = "ansys_mapdl_reader-0.54.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54a12d3eac49d6416c07a40d9d22dea1c5ce61cd0d8bb5812c50517dc0ea38f6"}, + {file = "ansys_mapdl_reader-0.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f8f2548267e92ec8ca069136a05c1a2eb05db12a6f9d3ecb3b02b6f89520b1"}, + {file = "ansys_mapdl_reader-0.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:f44496cedde256eefa3cdfc56900e5eb18e5faff2e4591be584c34245f4ee67e"}, + {file = "ansys_mapdl_reader-0.54.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9568c12bb50efd1529720a3c45b59bf4a4ed64bb5e3aa180025751e625647752"}, + {file = "ansys_mapdl_reader-0.54.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ba6d811e854026130f618be038528578068a89bd533c66e470d297666be20c07"}, + {file = "ansys_mapdl_reader-0.54.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7766adaf69adbb1aa29dbdf81f27045b743cbd7f056552fb979bbaf8f9306b5e"}, + {file = "ansys_mapdl_reader-0.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb41ab0d904dbdf2c78abf67bc122586247f625e78ebc0bbd377983aceee25fb"}, + {file = "ansys_mapdl_reader-0.54.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6aa630bd7e5703146c78ce9824775b3f1990517c19484bc7fc8ee40f03043bdf"}, + {file = "ansys_mapdl_reader-0.54.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0e0a089ad81b68ee75c4e8f6c4a95b87200d9f942c3ae33046702e958fccb69b"}, + {file = "ansys_mapdl_reader-0.54.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6204dab63039c7f529c6979dce6a0e7ba83e04a5df65dfc68aee0bf5c4eec609"}, + {file = "ansys_mapdl_reader-0.54.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7c5fdec2ebc57c3a20c706fdaf8e3216d2f42e822bc3ed3dd05c63828f7dd5d"}, + {file = "ansys_mapdl_reader-0.54.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0b7b16d3d7d44d7bdf67d9da5eaaaa63c18cba0991bac55d23fe60d732522952"}, + {file = "ansys_mapdl_reader-0.54.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e49b9c84affbeec7c417d60739f5d487b39cca3eaf57eeeb321afa27fed17aab"}, + {file = "ansys_mapdl_reader-0.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:f7d8c13cf74f023bd331203b487291d759f853e9868686a443b4a22cb20d4bbe"}, + {file = "ansys_mapdl_reader-0.54.1.tar.gz", hash = "sha256:e9ae69f8c674e111ed46628f55296c25ef4ec1ec603eda9edb39bc49895dfe13"}, ] [package.dependencies] appdirs = ">=1.4.0" matplotlib = ">=3.0.0" -numpy = ">=1.16.0" +numpy = ">=1.16.0,<3" pyvista = ">=0.32.0" tqdm = ">=4.45.0" vtk = ">=9.0.0" [[package]] name = "ansys-math-core" -version = "0.1.5" +version = "0.2.0" description = "A Python wrapper for PyAnsys Math libraries." -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.10" files = [ - {file = "ansys_math_core-0.1.5-py3-none-any.whl", hash = "sha256:5316aa088f68e550c5c6d5c59f86ed8983fed5906d4d4447cbe48e159a6ab162"}, - {file = "ansys_math_core-0.1.5.tar.gz", hash = "sha256:f088a13c716b03f06e60594c68524695582ffe03fc7f58e7d508f86f592d5f44"}, + {file = "ansys_math_core-0.2.0-py3-none-any.whl", hash = "sha256:68e554e45d8ec532cb4b5e3c0f902a035a047441654c20d36b0d25c71a11cf70"}, + {file = "ansys_math_core-0.2.0.tar.gz", hash = "sha256:3fea76c47118404feae198279bc8dc32e5c72f1fb0030f3ea34ff4c317a6789f"}, ] [package.dependencies] @@ -372,23 +373,24 @@ pyansys-tools-versioning = ">=0.3.3" scipy = ">=1.3.0" [package.extras] -doc = ["Sphinx (==7.3.7)", "ansys-mapdl-core (==0.68.1)", "ansys-mapdl-reader (==0.53.0)", "ansys-sphinx-theme (==0.15.2)", "jupyter_sphinx (==0.5.3)", "jupyterlab (==4.1.6)", "numpydoc (==1.7.0)", "pypandoc (==1.13)", "pytest-sphinx (==0.6.3)", "pyvista[jupyter,trame] (==0.43.5)", "scipy (==1.13.0)", "sphinx-autobuild (==2024.4.16)", "sphinx-autodoc-typehints (==2.1.0)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.5.0)", "sphinx-gallery (==0.15.0)", "sphinx-notfound-page (==1.0.0)", "trame (==3.6.0)", "vtk (==9.3.0)"] -tests = ["ansys-mapdl-core (==0.68.1)", "numpy (==1.26.4)", "pyansys-tools-report (==0.7.0)", "pytest (==8.1.1)", "pytest-cov (==5.0.0)", "pytest-rerunfailures (==14.0)", "pyvista (==0.43.5)", "scipy (==1.13.0)", "vtk (==9.3.0)"] +doc = ["Sphinx (==8.0.2)", "ansys-mapdl-core (==0.68.5)", "ansys-mapdl-reader (==0.54.1)", "ansys-sphinx-theme (==1.1.2)", "jupyter_sphinx (==0.5.3)", "jupyterlab (==4.2.5)", "numpydoc (==1.8.0)", "pypandoc (==1.13)", "pytest-sphinx (==0.6.3)", "pyvista[jupyter,trame] (==0.44.1)", "scipy (==1.14.1)", "sphinx-autobuild (==2024.10.3)", "sphinx-autodoc-typehints (==2.4.4)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-gallery (==0.17.1)", "sphinx-notfound-page (==1.0.4)", "trame (==3.6.5)", "vtk (==9.3.1)"] +tests = ["ansys-mapdl-core (==0.68.5)", "numpy (==2.1.2)", "pyansys-tools-report (==0.8.0)", "pytest (==8.3.3)", "pytest-cov (==5.0.0)", "pytest-rerunfailures (==14.0)", "pyvista (==0.44.1)", "scipy (==1.14.1)", "vtk (==9.3.1)"] [[package]] name = "ansys-mechanical-core" -version = "0.11.5" +version = "0.11.10" description = "A python wrapper for Ansys Mechanical" -optional = false -python-versions = "<4.0,>=3.9" +optional = true +python-versions = "<4.0,>=3.10" files = [ - {file = "ansys_mechanical_core-0.11.5-py3-none-any.whl", hash = "sha256:28a3ef8777a7a650ab6b4a2f853aa2837a840f7c76ceb34b55edd3ef5a22091c"}, - {file = "ansys_mechanical_core-0.11.5.tar.gz", hash = "sha256:70f3dc062b64af8a3e35b5ea1e854862456dd9f4a2a9642e51017491f619f60e"}, + {file = "ansys_mechanical_core-0.11.10-py3-none-any.whl", hash = "sha256:793595261966207627937d191e822252b0d33939cd2a5ad8e8debbdab0167137"}, + {file = "ansys_mechanical_core-0.11.10.tar.gz", hash = "sha256:6f71f9625b2441816047dd97aefd711edb63591f1434285f091b3f9dcb62b851"}, ] [package.dependencies] ansys-api-mechanical = "0.1.2" -ansys-mechanical-env = "0.1.7" +ansys-mechanical-env = "0.1.8" +ansys-mechanical-stubs = "0.1.4" ansys-platform-instancemanagement = ">=1.0.1" ansys-pythonnet = ">=3.1.0rc2" ansys-tools-path = ">=0.3.1" @@ -397,22 +399,24 @@ click = ">=8.1.3" clr-loader = "0.2.6" grpcio = ">=1.30.0" protobuf = ">=3.12.2,<6" +psutil = "6.1.0" +requests = ">=2,<3" tqdm = ">=4.45.0" [package.extras] -doc = ["ansys-sphinx-theme[autoapi] (==1.0.3)", "grpcio (==1.65.4)", "imageio (==2.34.2)", "imageio-ffmpeg (==0.5.1)", "jupyter_sphinx (==0.5.3)", "jupyterlab (>=3.2.8)", "matplotlib (==3.9.1.post1)", "numpy (==2.0.1)", "numpydoc (==1.8.0)", "pandas (==2.2.2)", "panel (==1.4.5)", "plotly (==5.23.0)", "pypandoc (==1.13)", "pytest-sphinx (==0.6.3)", "pythreejs (==2.4.2)", "pyvista (>=0.39.1)", "sphinx (==8.0.2)", "sphinx-autobuild (==2024.4.16)", "sphinx-autodoc-typehints (==2.2.3)", "sphinx-copybutton (==0.5.2)", "sphinx-gallery (==0.17.1)", "sphinx-notfound-page (==1.0.4)", "sphinx_design (==0.6.1)", "sphinxcontrib-websupport (==2.0.0)", "sphinxemoji (==0.3.1)"] -tests = ["pytest (==8.3.2)", "pytest-cov (==5.0.0)", "pytest-print (==1.0.0)"] -viz = ["ansys-tools-visualization-interface (>=0.2.6)", "usd-core (==24.8)"] +doc = ["ansys-sphinx-theme[autoapi] (==1.2.1)", "grpcio (==1.68.0)", "imageio (==2.36.0)", "imageio-ffmpeg (==0.5.1)", "jupyter_sphinx (==0.5.3)", "jupyterlab (>=3.2.8)", "matplotlib (==3.9.2)", "numpy (==2.1.3)", "numpydoc (==1.8.0)", "pandas (==2.2.3)", "panel (==1.5.4)", "plotly (==5.24.1)", "pypandoc (==1.14)", "pytest-sphinx (==0.6.3)", "pythreejs (==2.4.2)", "pyvista (>=0.39.1)", "sphinx (==8.1.3)", "sphinx-autobuild (==2024.10.3)", "sphinx-autodoc-typehints (==2.5.0)", "sphinx-copybutton (==0.5.2)", "sphinx-gallery (==0.18.0)", "sphinx-notfound-page (==1.0.4)", "sphinx_design (==0.6.1)", "sphinxcontrib-websupport (==2.0.0)", "sphinxemoji (==0.3.1)"] +tests = ["psutil (==6.1.0)", "pytest (==8.3.3)", "pytest-cov (==6.0.0)", "pytest-print (==1.0.2)"] +viz = ["ansys-tools-visualization-interface (>=0.2.6)", "usd-core (==24.11)"] [[package]] name = "ansys-mechanical-env" -version = "0.1.7" +version = "0.1.8" description = "A python wrapper for loading environment variables when using PyMechanical embedded instances in Linux." -optional = false -python-versions = "<4,>=3.9" +optional = true +python-versions = "<4,>=3.10" files = [ - {file = "ansys_mechanical_env-0.1.7-py3-none-any.whl", hash = "sha256:03004cc200a9c8e43fdc04b8879436f8089d4391daf98d7fac429bc997b62d6d"}, - {file = "ansys_mechanical_env-0.1.7.tar.gz", hash = "sha256:29a4ce796cf3719828f6af4fa7d1097333cb12d1b10b6ab199efe0f7ec8fc346"}, + {file = "ansys_mechanical_env-0.1.8-py3-none-any.whl", hash = "sha256:4746c83924cba91137ffd367712a033abf2a8ccc66e7e02b76c1a3a6a80b4bf9"}, + {file = "ansys_mechanical_env-0.1.8.tar.gz", hash = "sha256:5286202292f6aeb3bf2278003059dbcd64774db2decb16a8cf68d15affef51d6"}, ] [package.dependencies] @@ -420,11 +424,27 @@ ansys-tools-path = ">=0.3.1" click = ">=8.1.3" importlib-metadata = ">=4.0" +[[package]] +name = "ansys-mechanical-stubs" +version = "0.1.4" +description = "PyMechanical scripting API stubs." +optional = true +python-versions = "<4,>=3.10" +files = [ + {file = "ansys_mechanical_stubs-0.1.4-py3-none-any.whl", hash = "sha256:8c62d88f4c4c10c42044781c46e9dd2c68c3ee9813eff33efdc9328ea4fb8648"}, + {file = "ansys_mechanical_stubs-0.1.4.tar.gz", hash = "sha256:c07b5f5421cf59b1ed1806713117180f6ad22e873354637646cdcd3aa06ce853"}, +] + +[package.extras] +build = ["ansys-pythonnet (==3.1.0rc3)"] +doc = ["Sphinx (==7.3.7)", "ansys-sphinx-theme[autoapi] (==1.0.11)", "autodoc_pydantic (==2.0.1)", "jupyter_sphinx (==0.5.3)", "numpydoc (==1.6.0)", "sphinx-autodoc-typehints (==1.25.3)", "sphinx-copybutton (==0.5.2)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-jinja (==2.0.2)", "sphinx-markdown-builder (==0.6.6)", "sphinx-notfound-page (==1.0.2)", "sphinx_design (==0.6.1)", "sphinxcontrib-globalsubs (==0.1.1)", "sphinxcontrib-httpdomain (==1.8.1)", "sphinxnotes-strike (==1.2.1)"] +tests = ["pytest (==8.3.3)", "pytest-cov (==5.0.0)"] + [[package]] name = "ansys-platform-instancemanagement" version = "1.1.2" description = "A Python wrapper for Ansys platform instancemanagement" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "ansys_platform_instancemanagement-1.1.2-py3-none-any.whl", hash = "sha256:52afe4755c4233985a1f5f33cdb2a163a53db5cee75f04c3a4e47fc81e1d0bb1"}, @@ -441,39 +461,39 @@ tests = ["ansys-api-platform-instancemanagement (==1.0.0)", "grpcio-health-check [[package]] name = "ansys-pythonnet" -version = "3.1.0rc3" +version = "3.1.0rc4" description = ".NET and Mono integration for Python (Ansys, Inc. fork)" -optional = false +optional = true python-versions = "<3.13,>=3.7" files = [ - {file = "ansys-pythonnet-3.1.0rc3.tar.gz", hash = "sha256:369a0a5a838a0991f755b6d63c319ab6997f9dc464d016187227be5cd860a9cb"}, - {file = "ansys_pythonnet-3.1.0rc3-py3-none-any.whl", hash = "sha256:5cc60b510384dd53b6d6aeb2612d1687a83059e3ea460d910b125be246b65c0f"}, + {file = "ansys_pythonnet-3.1.0rc4-py3-none-any.whl", hash = "sha256:ff25706acdaec6dec7b2e6b37488fa366945fa992086131916ba826145984a87"}, + {file = "ansys_pythonnet-3.1.0rc4.tar.gz", hash = "sha256:6b696529197aaaf0a0f0e31e0fea2a9c0fe53e33e6f73dfdedb940196f03abf5"}, ] [package.dependencies] -clr-loader = ">=0.2.6,<0.3.0" +clr-loader = ">=0.2.5,<0.3.0" [[package]] name = "ansys-sphinx-theme" -version = "1.0.7" +version = "1.2.2" description = "A theme devised by ANSYS, Inc. for Sphinx documentation." optional = false -python-versions = "<4,>=3.9" +python-versions = "<4,>=3.10" files = [ - {file = "ansys_sphinx_theme-1.0.7-py3-none-any.whl", hash = "sha256:8d26b975bf4e332cbc32753983824c0233d4d62920991a6914c4e24b126390d4"}, - {file = "ansys_sphinx_theme-1.0.7.tar.gz", hash = "sha256:c4b9b012f5455c3e94b123f553ead59bb97bf235ab565032e895c0088d08fb51"}, + {file = "ansys_sphinx_theme-1.2.2-py3-none-any.whl", hash = "sha256:c1ea0dc37ce4263946514b4d11d59bca1ae8ddf3a79081be1a415146a4df9f90"}, + {file = "ansys_sphinx_theme-1.2.2.tar.gz", hash = "sha256:2e640ee9a6466bd8de1de007037d29e3f0205c2e7ff98b1999fa2e228fa77983"}, ] [package.dependencies] importlib-metadata = ">=4.0" Jinja2 = ">=3.1.2" pdf2image = ">=1.17.0" -pydata-sphinx-theme = ">=0.15.4,<0.16" +pydata-sphinx-theme = ">=0.15.4,<0.17" Sphinx = ">=4.2.0" [package.extras] -autoapi = ["sphinx-autoapi (==3.2.1)", "sphinx-design (==0.6.1)", "sphinx-jinja (==2.0.2)"] -doc = ["Pillow (>=9.0)", "PyGitHub (==2.3.0)", "Sphinx (==8.0.2)", "jupytext (==1.16.4)", "nbsphinx (==0.9.5)", "notebook (==7.2.1)", "numpydoc (==1.8.0)", "pandas (==2.2.2)", "pyvista[jupyter] (==0.44.1)", "requests (==2.32.3)", "sphinx-autoapi (==3.2.1)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-gallery (==0.17.1)", "sphinx-jinja (==2.0.2)", "sphinx-notfound-page (==1.0.4)"] +autoapi = ["sphinx-autoapi (==3.3.3)", "sphinx-design (==0.6.1)", "sphinx-jinja (==2.0.2)"] +doc = ["Pillow (>=9.0)", "PyGitHub (==2.4.0)", "Sphinx (==8.1.3)", "jupytext (==1.16.4)", "nbsphinx (==0.9.5)", "notebook (==7.2.2)", "numpydoc (==1.8.0)", "pandas (==2.2.3)", "pyvista[jupyter] (==0.44.1)", "requests (==2.32.3)", "sphinx-autoapi (==3.3.3)", "sphinx-copybutton (==0.5.2)", "sphinx-design (==0.6.1)", "sphinx-gallery (==0.18.0)", "sphinx-jinja (==2.0.2)", "sphinx-notfound-page (==1.0.4)"] [[package]] name = "ansys-tools-filetransfer" @@ -510,13 +530,13 @@ grpcio-health-checking = ">=1.43,<2.0" [[package]] name = "ansys-tools-path" -version = "0.6.0" +version = "0.7.0" description = "Library to locate Ansys products in a local machine." optional = false -python-versions = "<4,>=3.8" +python-versions = "<4,>=3.10" files = [ - {file = "ansys_tools_path-0.6.0-py3-none-any.whl", hash = "sha256:b0ffe61fece59dbcae63023b8c39c4fc6bf722d993bb70626cb9fd087ff28266"}, - {file = "ansys_tools_path-0.6.0.tar.gz", hash = "sha256:a6a3a35b2700905319347c785b59a7ac12e6f60148b3b516ec09546463fe9c8f"}, + {file = "ansys_tools_path-0.7.0-py3-none-any.whl", hash = "sha256:970596c1d9375498b97fa602c3f7e56435ca84e147743809b6a597667c654c57"}, + {file = "ansys_tools_path-0.7.0.tar.gz", hash = "sha256:96663cd13436e20f2ab2347aa6c487890962319c80bebc181419c957c19a8de0"}, ] [package.dependencies] @@ -524,19 +544,41 @@ click = ">=8.1.3" platformdirs = ">=3.6.0" [package.extras] -build = ["build (==1.2.1)", "twine (==5.1.0)"] -doc = ["Sphinx (==7.3.7)", "ansys-sphinx-theme (==0.16.2)", "numpydoc (==1.7.0)", "sphinx-copybutton (==0.5.2)"] -tests = ["pyfakefs (==5.5.0)", "pytest (==8.2.1)", "pytest-cov (==5.0.0)"] +build = ["build (==1.2.2.post1)", "twine (==5.1.1)"] +doc = ["Sphinx (==8.1.3)", "ansys-sphinx-theme (==1.2.0)", "numpydoc (==1.8.0)", "sphinx-copybutton (==0.5.2)"] +tests = ["pyfakefs (==5.7.1)", "pytest (==8.3.3)", "pytest-cov (==6.0.0)"] + +[[package]] +name = "ansys-tools-visualization-interface" +version = "0.5.0" +description = "A Python visualization interface for PyAnsys libraries" +optional = true +python-versions = "<4,>=3.10" +files = [ + {file = "ansys_tools_visualization_interface-0.5.0-py3-none-any.whl", hash = "sha256:0f73d515e039b4335ab8b902e65fa6d673db809ba07957164adccc08d8906565"}, + {file = "ansys_tools_visualization_interface-0.5.0.tar.gz", hash = "sha256:a8410f9761f2e9be13961516f414acfd16d6b345015252afc129fc4cc43c38a1"}, +] + +[package.dependencies] +pyvista = ">=0.43.0,<1" +trame = ">=3.6.0,<4" +trame-vtk = ">=2.8.7,<3" +trame-vuetify = ">=2.4.3,<3" +websockets = ">=12.0,<14" + +[package.extras] +doc = ["ansys-fluent-core (==0.26.1)", "ansys-sphinx-theme (==1.1.7)", "jupyter_sphinx (==0.5.3)", "jupytext (==1.16.4)", "nbsphinx (==0.9.5)", "numpydoc (==1.8.0)", "sphinx (==8.1.3)", "sphinx-autoapi (==3.3.3)", "sphinx-copybutton (==0.5.2)", "sphinx-gallery (==0.18.0)", "sphinx-jinja (==2.0.2)", "sphinx_design (==0.6.1)"] +tests = ["pytest (==8.3.3)", "pytest-cov (==5.0.0)", "pytest-pyvista (==0.1.9)"] [[package]] name = "anyio" -version = "4.4.0" +version = "4.6.2.post1" description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, + {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, ] [package.dependencies] @@ -546,9 +588,9 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] [[package]] name = "appdirs" @@ -576,7 +618,7 @@ files = [ name = "argon2-cffi" version = "23.1.0" description = "Argon2 for Python" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, @@ -596,7 +638,7 @@ typing = ["mypy"] name = "argon2-cffi-bindings" version = "21.2.0" description = "Low-level CFFI bindings for Argon2" -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, @@ -633,7 +675,7 @@ tests = ["pytest"] name = "arrow" version = "1.3.0" description = "Better dates & times for Python" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, @@ -668,13 +710,13 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [[package]] name = "async-timeout" -version = "4.0.3" +version = "5.0.1" description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" +optional = true +python-versions = ">=3.8" files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, + {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, + {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, ] [[package]] @@ -748,33 +790,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.8.0" +version = "24.10.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, + {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, + {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, + {file = "black-24.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f"}, + {file = "black-24.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e"}, + {file = "black-24.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad"}, + {file = "black-24.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50"}, + {file = "black-24.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392"}, + {file = "black-24.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175"}, + {file = "black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3"}, + {file = "black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65"}, + {file = "black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f"}, + {file = "black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8"}, + {file = "black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981"}, + {file = "black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b"}, + {file = "black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2"}, + {file = "black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b"}, + {file = "black-24.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd"}, + {file = "black-24.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f"}, + {file = "black-24.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800"}, + {file = "black-24.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7"}, + {file = "black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d"}, + {file = "black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875"}, ] [package.dependencies] @@ -788,124 +830,123 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.10)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bleach" -version = "6.1.0" +version = "6.2.0" description = "An easy safelist-based HTML-sanitizing tool." -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, - {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, + {file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"}, + {file = "bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"}, ] [package.dependencies] -six = ">=1.9.0" webencodings = "*" [package.extras] -css = ["tinycss2 (>=1.1.0,<1.3)"] +css = ["tinycss2 (>=1.1.0,<1.5)"] [[package]] name = "cachetools" -version = "5.4.0" +version = "5.5.0" description = "Extensible memoizing collections and decorators" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, - {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] name = "cffi" -version = "1.17.0" +version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"}, - {file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"}, - {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"}, - {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"}, - {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"}, - {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"}, - {file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"}, - {file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"}, - {file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"}, - {file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"}, - {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"}, - {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"}, - {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"}, - {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"}, - {file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"}, - {file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"}, - {file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"}, - {file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"}, - {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"}, - {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"}, - {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"}, - {file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"}, - {file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"}, - {file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"}, - {file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"}, - {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"}, - {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"}, - {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"}, - {file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"}, - {file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"}, - {file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"}, - {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"}, - {file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"}, - {file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"}, - {file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"}, - {file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"}, - {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"}, - {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"}, - {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"}, - {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"}, - {file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"}, - {file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"}, - {file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, + {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, + {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, + {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] [package.dependencies] @@ -924,101 +965,116 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.3.2" +version = "3.4.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, + {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, + {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, + {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, + {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, + {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, + {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, + {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, + {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, + {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] [[package]] @@ -1039,7 +1095,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "clr-loader" version = "0.2.6" description = "Generic pure Python loader for .NET runtimes" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "clr_loader-0.2.6-py3-none-any.whl", hash = "sha256:79bbfee4bf6ac2f4836d89af2c39e0c32dce5d0c062596185aef380f317507a6"}, @@ -1079,146 +1135,146 @@ test = ["pytest"] [[package]] name = "contourpy" -version = "1.2.1" +version = "1.3.1" description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false -python-versions = ">=3.9" -files = [ - {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, - {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, - {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, - {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, - {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, - {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, - {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, - {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, - {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, - {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, - {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, +optional = true +python-versions = ">=3.10" +files = [ + {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"}, + {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"}, + {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, ] [package.dependencies] -numpy = ">=1.20" +numpy = ">=1.23" [package.extras] bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] +test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] [[package]] name = "coverage" -version = "7.6.1" +version = "7.6.7" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, - {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, - {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, - {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, - {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, - {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, - {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, - {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, - {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, - {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, - {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, - {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, - {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, - {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, - {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, - {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, - {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, - {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, - {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, - {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, - {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, - {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, - {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, - {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, - {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, - {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, - {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, - {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, - {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, - {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, - {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, - {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, - {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, - {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, - {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, - {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, - {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, - {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, + {file = "coverage-7.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e"}, + {file = "coverage-7.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314"}, + {file = "coverage-7.6.7-cp310-cp310-win32.whl", hash = "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a"}, + {file = "coverage-7.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163"}, + {file = "coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469"}, + {file = "coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4"}, + {file = "coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2"}, + {file = "coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f"}, + {file = "coverage-7.6.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9"}, + {file = "coverage-7.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb"}, + {file = "coverage-7.6.7-cp312-cp312-win32.whl", hash = "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76"}, + {file = "coverage-7.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c"}, + {file = "coverage-7.6.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46f21663e358beae6b368429ffadf14ed0a329996248a847a4322fb2e35d64d3"}, + {file = "coverage-7.6.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40cca284c7c310d622a1677f105e8507441d1bb7c226f41978ba7c86979609ab"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77256ad2345c29fe59ae861aa11cfc74579c88d4e8dbf121cbe46b8e32aec808"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87ea64b9fa52bf395272e54020537990a28078478167ade6c61da7ac04dc14bc"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d608a7808793e3615e54e9267519351c3ae204a6d85764d8337bd95993581a8"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdd94501d65adc5c24f8a1a0eda110452ba62b3f4aeaba01e021c1ed9cb8f34a"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82c809a62e953867cf57e0548c2b8464207f5f3a6ff0e1e961683e79b89f2c55"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb684694e99d0b791a43e9fc0fa58efc15ec357ac48d25b619f207c41f2fd384"}, + {file = "coverage-7.6.7-cp313-cp313-win32.whl", hash = "sha256:963e4a08cbb0af6623e61492c0ec4c0ec5c5cf74db5f6564f98248d27ee57d30"}, + {file = "coverage-7.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:14045b8bfd5909196a90da145a37f9d335a5d988a83db34e80f41e965fb7cb42"}, + {file = "coverage-7.6.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f2c7a045eef561e9544359a0bf5784b44e55cefc7261a20e730baa9220c83413"}, + {file = "coverage-7.6.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dd4e4a49d9c72a38d18d641135d2fb0bdf7b726ca60a103836b3d00a1182acd"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c95e0fa3d1547cb6f021ab72f5c23402da2358beec0a8e6d19a368bd7b0fb37"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f63e21ed474edd23f7501f89b53280014436e383a14b9bd77a648366c81dce7b"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead9b9605c54d15be228687552916c89c9683c215370c4a44f1f217d2adcc34d"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0573f5cbf39114270842d01872952d301027d2d6e2d84013f30966313cadb529"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e2c8e3384c12dfa19fa9a52f23eb091a8fad93b5b81a41b14c17c78e23dd1d8b"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:70a56a2ec1869e6e9fa69ef6b76b1a8a7ef709972b9cc473f9ce9d26b5997ce3"}, + {file = "coverage-7.6.7-cp313-cp313t-win32.whl", hash = "sha256:dbba8210f5067398b2c4d96b4e64d8fb943644d5eb70be0d989067c8ca40c0f8"}, + {file = "coverage-7.6.7-cp313-cp313t-win_amd64.whl", hash = "sha256:dfd14bcae0c94004baba5184d1c935ae0d1231b8409eb6c103a5fd75e8ecdc56"}, + {file = "coverage-7.6.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37a15573f988b67f7348916077c6d8ad43adb75e478d0910957394df397d2874"}, + {file = "coverage-7.6.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6cce5c76985f81da3769c52203ee94722cd5d5889731cd70d31fee939b74bf0"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ab9763d291a17b527ac6fd11d1a9a9c358280adb320e9c2672a97af346ac2c"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cf96ceaa275f071f1bea3067f8fd43bec184a25a962c754024c973af871e1b7"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee9cf6b0134d6f932d219ce253ef0e624f4fa588ee64830fcba193269e4daa3"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2bc3e45c16564cc72de09e37413262b9f99167803e5e48c6156bccdfb22c8327"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:623e6965dcf4e28a3debaa6fcf4b99ee06d27218f46d43befe4db1c70841551c"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850cfd2d6fc26f8346f422920ac204e1d28814e32e3a58c19c91980fa74d8289"}, + {file = "coverage-7.6.7-cp39-cp39-win32.whl", hash = "sha256:c296263093f099da4f51b3dff1eff5d4959b527d4f2f419e16508c5da9e15e8c"}, + {file = "coverage-7.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:90746521206c88bdb305a4bf3342b1b7316ab80f804d40c536fc7d329301ee13"}, + {file = "coverage-7.6.7-pp39.pp310-none-any.whl", hash = "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671"}, + {file = "coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24"}, ] [package.dependencies] @@ -1231,7 +1287,7 @@ toml = ["tomli"] name = "cycler" version = "0.12.1" description = "Composable style cycles" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, @@ -1244,33 +1300,37 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "debugpy" -version = "1.8.5" +version = "1.8.8" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.5-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7e4d594367d6407a120b76bdaa03886e9eb652c05ba7f87e37418426ad2079f7"}, - {file = "debugpy-1.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4413b7a3ede757dc33a273a17d685ea2b0c09dbd312cc03f5534a0fd4d40750a"}, - {file = "debugpy-1.8.5-cp310-cp310-win32.whl", hash = "sha256:dd3811bd63632bb25eda6bd73bea8e0521794cda02be41fa3160eb26fc29e7ed"}, - {file = "debugpy-1.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b78c1250441ce893cb5035dd6f5fc12db968cc07f91cc06996b2087f7cefdd8e"}, - {file = "debugpy-1.8.5-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:606bccba19f7188b6ea9579c8a4f5a5364ecd0bf5a0659c8a5d0e10dcee3032a"}, - {file = "debugpy-1.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9fb642938a7a609a6c865c32ecd0d795d56c1aaa7a7a5722d77855d5e77f2b"}, - {file = "debugpy-1.8.5-cp311-cp311-win32.whl", hash = "sha256:4fbb3b39ae1aa3e5ad578f37a48a7a303dad9a3d018d369bc9ec629c1cfa7408"}, - {file = "debugpy-1.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:345d6a0206e81eb68b1493ce2fbffd57c3088e2ce4b46592077a943d2b968ca3"}, - {file = "debugpy-1.8.5-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:5b5c770977c8ec6c40c60d6f58cacc7f7fe5a45960363d6974ddb9b62dbee156"}, - {file = "debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a65b00b7cdd2ee0c2cf4c7335fef31e15f1b7056c7fdbce9e90193e1a8c8cb"}, - {file = "debugpy-1.8.5-cp312-cp312-win32.whl", hash = "sha256:c9f7c15ea1da18d2fcc2709e9f3d6de98b69a5b0fff1807fb80bc55f906691f7"}, - {file = "debugpy-1.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:28ced650c974aaf179231668a293ecd5c63c0a671ae6d56b8795ecc5d2f48d3c"}, - {file = "debugpy-1.8.5-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:3df6692351172a42af7558daa5019651f898fc67450bf091335aa8a18fbf6f3a"}, - {file = "debugpy-1.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd04a73eb2769eb0bfe43f5bfde1215c5923d6924b9b90f94d15f207a402226"}, - {file = "debugpy-1.8.5-cp38-cp38-win32.whl", hash = "sha256:8f913ee8e9fcf9d38a751f56e6de12a297ae7832749d35de26d960f14280750a"}, - {file = "debugpy-1.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:a697beca97dad3780b89a7fb525d5e79f33821a8bc0c06faf1f1289e549743cf"}, - {file = "debugpy-1.8.5-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c"}, - {file = "debugpy-1.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84c276489e141ed0b93b0af648eef891546143d6a48f610945416453a8ad406"}, - {file = "debugpy-1.8.5-cp39-cp39-win32.whl", hash = "sha256:ad84b7cde7fd96cf6eea34ff6c4a1b7887e0fe2ea46e099e53234856f9d99a34"}, - {file = "debugpy-1.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:7b0fe36ed9d26cb6836b0a51453653f8f2e347ba7348f2bbfe76bfeb670bfb1c"}, - {file = "debugpy-1.8.5-py2.py3-none-any.whl", hash = "sha256:55919dce65b471eff25901acf82d328bbd5b833526b6c1364bd5133754777a44"}, - {file = "debugpy-1.8.5.zip", hash = "sha256:b2112cfeb34b4507399d298fe7023a16656fc553ed5246536060ca7bd0e668d0"}, + {file = "debugpy-1.8.8-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e59b1607c51b71545cb3496876544f7186a7a27c00b436a62f285603cc68d1c6"}, + {file = "debugpy-1.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d"}, + {file = "debugpy-1.8.8-cp310-cp310-win32.whl", hash = "sha256:b01f4a5e5c5fb1d34f4ccba99a20ed01eabc45a4684f4948b5db17a319dfb23f"}, + {file = "debugpy-1.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:535f4fb1c024ddca5913bb0eb17880c8f24ba28aa2c225059db145ee557035e9"}, + {file = "debugpy-1.8.8-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:c399023146e40ae373753a58d1be0a98bf6397fadc737b97ad612886b53df318"}, + {file = "debugpy-1.8.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09cc7b162586ea2171eea055985da2702b0723f6f907a423c9b2da5996ad67ba"}, + {file = "debugpy-1.8.8-cp311-cp311-win32.whl", hash = "sha256:eea8821d998ebeb02f0625dd0d76839ddde8cbf8152ebbe289dd7acf2cdc6b98"}, + {file = "debugpy-1.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:d4483836da2a533f4b1454dffc9f668096ac0433de855f0c22cdce8c9f7e10c4"}, + {file = "debugpy-1.8.8-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:0cc94186340be87b9ac5a707184ec8f36547fb66636d1029ff4f1cc020e53996"}, + {file = "debugpy-1.8.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64674e95916e53c2e9540a056e5f489e0ad4872645399d778f7c598eacb7b7f9"}, + {file = "debugpy-1.8.8-cp312-cp312-win32.whl", hash = "sha256:5c6e885dbf12015aed73770f29dec7023cb310d0dc2ba8bfbeb5c8e43f80edc9"}, + {file = "debugpy-1.8.8-cp312-cp312-win_amd64.whl", hash = "sha256:19ffbd84e757a6ca0113574d1bf5a2298b3947320a3e9d7d8dc3377f02d9f864"}, + {file = "debugpy-1.8.8-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:705cd123a773d184860ed8dae99becd879dfec361098edbefb5fc0d3683eb804"}, + {file = "debugpy-1.8.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890fd16803f50aa9cb1a9b9b25b5ec321656dd6b78157c74283de241993d086f"}, + {file = "debugpy-1.8.8-cp313-cp313-win32.whl", hash = "sha256:90244598214bbe704aa47556ec591d2f9869ff9e042e301a2859c57106649add"}, + {file = "debugpy-1.8.8-cp313-cp313-win_amd64.whl", hash = "sha256:4b93e4832fd4a759a0c465c967214ed0c8a6e8914bced63a28ddb0dd8c5f078b"}, + {file = "debugpy-1.8.8-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:143ef07940aeb8e7316de48f5ed9447644da5203726fca378f3a6952a50a9eae"}, + {file = "debugpy-1.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f95651bdcbfd3b27a408869a53fbefcc2bcae13b694daee5f1365b1b83a00113"}, + {file = "debugpy-1.8.8-cp38-cp38-win32.whl", hash = "sha256:26b461123a030e82602a750fb24d7801776aa81cd78404e54ab60e8b5fecdad5"}, + {file = "debugpy-1.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3cbf1833e644a3100eadb6120f25be8a532035e8245584c4f7532937edc652a"}, + {file = "debugpy-1.8.8-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:53709d4ec586b525724819dc6af1a7703502f7e06f34ded7157f7b1f963bb854"}, + {file = "debugpy-1.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a9c013077a3a0000e83d97cf9cc9328d2b0bbb31f56b0e99ea3662d29d7a6a2"}, + {file = "debugpy-1.8.8-cp39-cp39-win32.whl", hash = "sha256:ffe94dd5e9a6739a75f0b85316dc185560db3e97afa6b215628d1b6a17561cb2"}, + {file = "debugpy-1.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5c0e5a38c7f9b481bf31277d2f74d2109292179081f11108e668195ef926c0f9"}, + {file = "debugpy-1.8.8-py2.py3-none-any.whl", hash = "sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"}, + {file = "debugpy-1.8.8.zip", hash = "sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091"}, ] [[package]] @@ -1302,7 +1362,7 @@ files = [ name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, @@ -1311,13 +1371,13 @@ files = [ [[package]] name = "distlib" -version = "0.3.8" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] @@ -1369,13 +1429,13 @@ test = ["pytest (>=6)"] [[package]] name = "executing" -version = "2.0.1" +version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, ] [package.extras] @@ -1385,7 +1445,7 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth name = "fastjsonschema" version = "2.20.0" description = "Fastest Python implementation of JSON schema" -optional = false +optional = true python-versions = "*" files = [ {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, @@ -1397,69 +1457,77 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc [[package]] name = "filelock" -version = "3.15.4" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "fonttools" -version = "4.53.1" +version = "4.55.0" description = "Tools to manipulate font files" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, - {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, - {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, - {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, - {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, - {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, - {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, - {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, - {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, - {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, - {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, - {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, - {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, - {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, + {file = "fonttools-4.55.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:51c029d4c0608a21a3d3d169dfc3fb776fde38f00b35ca11fdab63ba10a16f61"}, + {file = "fonttools-4.55.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bca35b4e411362feab28e576ea10f11268b1aeed883b9f22ed05675b1e06ac69"}, + {file = "fonttools-4.55.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ce4ba6981e10f7e0ccff6348e9775ce25ffadbee70c9fd1a3737e3e9f5fa74f"}, + {file = "fonttools-4.55.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31d00f9852a6051dac23294a4cf2df80ced85d1d173a61ba90a3d8f5abc63c60"}, + {file = "fonttools-4.55.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e198e494ca6e11f254bac37a680473a311a88cd40e58f9cc4dc4911dfb686ec6"}, + {file = "fonttools-4.55.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7208856f61770895e79732e1dcbe49d77bd5783adf73ae35f87fcc267df9db81"}, + {file = "fonttools-4.55.0-cp310-cp310-win32.whl", hash = "sha256:e7e6a352ff9e46e8ef8a3b1fe2c4478f8a553e1b5a479f2e899f9dc5f2055880"}, + {file = "fonttools-4.55.0-cp310-cp310-win_amd64.whl", hash = "sha256:636caaeefe586d7c84b5ee0734c1a5ab2dae619dc21c5cf336f304ddb8f6001b"}, + {file = "fonttools-4.55.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fa34aa175c91477485c44ddfbb51827d470011e558dfd5c7309eb31bef19ec51"}, + {file = "fonttools-4.55.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:37dbb3fdc2ef7302d3199fb12468481cbebaee849e4b04bc55b77c24e3c49189"}, + {file = "fonttools-4.55.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5263d8e7ef3c0ae87fbce7f3ec2f546dc898d44a337e95695af2cd5ea21a967"}, + {file = "fonttools-4.55.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f307f6b5bf9e86891213b293e538d292cd1677e06d9faaa4bf9c086ad5f132f6"}, + {file = "fonttools-4.55.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f0a4b52238e7b54f998d6a56b46a2c56b59c74d4f8a6747fb9d4042190f37cd3"}, + {file = "fonttools-4.55.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3e569711464f777a5d4ef522e781dc33f8095ab5efd7548958b36079a9f2f88c"}, + {file = "fonttools-4.55.0-cp311-cp311-win32.whl", hash = "sha256:2b3ab90ec0f7b76c983950ac601b58949f47aca14c3f21eed858b38d7ec42b05"}, + {file = "fonttools-4.55.0-cp311-cp311-win_amd64.whl", hash = "sha256:aa046f6a63bb2ad521004b2769095d4c9480c02c1efa7d7796b37826508980b6"}, + {file = "fonttools-4.55.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:838d2d8870f84fc785528a692e724f2379d5abd3fc9dad4d32f91cf99b41e4a7"}, + {file = "fonttools-4.55.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f46b863d74bab7bb0d395f3b68d3f52a03444964e67ce5c43ce43a75efce9246"}, + {file = "fonttools-4.55.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33b52a9cfe4e658e21b1f669f7309b4067910321757fec53802ca8f6eae96a5a"}, + {file = "fonttools-4.55.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:732a9a63d6ea4a81b1b25a1f2e5e143761b40c2e1b79bb2b68e4893f45139a40"}, + {file = "fonttools-4.55.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7dd91ac3fcb4c491bb4763b820bcab6c41c784111c24172616f02f4bc227c17d"}, + {file = "fonttools-4.55.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f0e115281a32ff532118aa851ef497a1b7cda617f4621c1cdf81ace3e36fb0c"}, + {file = "fonttools-4.55.0-cp312-cp312-win32.whl", hash = "sha256:6c99b5205844f48a05cb58d4a8110a44d3038c67ed1d79eb733c4953c628b0f6"}, + {file = "fonttools-4.55.0-cp312-cp312-win_amd64.whl", hash = "sha256:f8c8c76037d05652510ae45be1cd8fb5dd2fd9afec92a25374ac82255993d57c"}, + {file = "fonttools-4.55.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8118dc571921dc9e4b288d9cb423ceaf886d195a2e5329cc427df82bba872cd9"}, + {file = "fonttools-4.55.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01124f2ca6c29fad4132d930da69158d3f49b2350e4a779e1efbe0e82bd63f6c"}, + {file = "fonttools-4.55.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ffd58d2691f11f7c8438796e9f21c374828805d33e83ff4b76e4635633674c"}, + {file = "fonttools-4.55.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5435e5f1eb893c35c2bc2b9cd3c9596b0fcb0a59e7a14121562986dd4c47b8dd"}, + {file = "fonttools-4.55.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d12081729280c39d001edd0f4f06d696014c26e6e9a0a55488fabc37c28945e4"}, + {file = "fonttools-4.55.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7ad1f1b98ab6cb927ab924a38a8649f1ffd7525c75fe5b594f5dab17af70e18"}, + {file = "fonttools-4.55.0-cp313-cp313-win32.whl", hash = "sha256:abe62987c37630dca69a104266277216de1023cf570c1643bb3a19a9509e7a1b"}, + {file = "fonttools-4.55.0-cp313-cp313-win_amd64.whl", hash = "sha256:2863555ba90b573e4201feaf87a7e71ca3b97c05aa4d63548a4b69ea16c9e998"}, + {file = "fonttools-4.55.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:00f7cf55ad58a57ba421b6a40945b85ac7cc73094fb4949c41171d3619a3a47e"}, + {file = "fonttools-4.55.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f27526042efd6f67bfb0cc2f1610fa20364396f8b1fc5edb9f45bb815fb090b2"}, + {file = "fonttools-4.55.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e67974326af6a8879dc2a4ec63ab2910a1c1a9680ccd63e4a690950fceddbe"}, + {file = "fonttools-4.55.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61dc0a13451143c5e987dec5254d9d428f3c2789a549a7cf4f815b63b310c1cc"}, + {file = "fonttools-4.55.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b2e526b325a903868c62155a6a7e24df53f6ce4c5c3160214d8fe1be2c41b478"}, + {file = "fonttools-4.55.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b7ef9068a1297714e6fefe5932c33b058aa1d45a2b8be32a4c6dee602ae22b5c"}, + {file = "fonttools-4.55.0-cp38-cp38-win32.whl", hash = "sha256:55718e8071be35dff098976bc249fc243b58efa263768c611be17fe55975d40a"}, + {file = "fonttools-4.55.0-cp38-cp38-win_amd64.whl", hash = "sha256:553bd4f8cc327f310c20158e345e8174c8eed49937fb047a8bda51daf2c353c8"}, + {file = "fonttools-4.55.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f901cef813f7c318b77d1c5c14cf7403bae5cb977cede023e22ba4316f0a8f6"}, + {file = "fonttools-4.55.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c9679fc0dd7e8a5351d321d8d29a498255e69387590a86b596a45659a39eb0d"}, + {file = "fonttools-4.55.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd2820a8b632f3307ebb0bf57948511c2208e34a4939cf978333bc0a3f11f838"}, + {file = "fonttools-4.55.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23bbbb49bec613a32ed1b43df0f2b172313cee690c2509f1af8fdedcf0a17438"}, + {file = "fonttools-4.55.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a656652e1f5d55b9728937a7e7d509b73d23109cddd4e89ee4f49bde03b736c6"}, + {file = "fonttools-4.55.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f50a1f455902208486fbca47ce33054208a4e437b38da49d6721ce2fef732fcf"}, + {file = "fonttools-4.55.0-cp39-cp39-win32.whl", hash = "sha256:161d1ac54c73d82a3cded44202d0218ab007fde8cf194a23d3dd83f7177a2f03"}, + {file = "fonttools-4.55.0-cp39-cp39-win_amd64.whl", hash = "sha256:ca7fd6987c68414fece41c96836e945e1f320cda56fc96ffdc16e54a44ec57a2"}, + {file = "fonttools-4.55.0-py3-none-any.whl", hash = "sha256:12db5888cd4dd3fcc9f0ee60c6edd3c7e1fd44b7dd0f31381ea03df68f8a153f"}, + {file = "fonttools-4.55.0.tar.gz", hash = "sha256:7636acc6ab733572d5e7eec922b254ead611f1cdad17be3f0be7418e8bfaca71"}, ] [package.extras] @@ -1480,7 +1548,7 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] name = "fqdn" version = "1.5.1" description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" -optional = false +optional = true python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" files = [ {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, @@ -1489,95 +1557,110 @@ files = [ [[package]] name = "frozenlist" -version = "1.4.1" +version = "1.5.0" description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, ] [[package]] name = "geomdl" version = "5.3.1" description = "Object-oriented B-Spline and NURBS evaluation library" -optional = false +optional = true python-versions = "*" files = [ {file = "geomdl-5.3.1-py2.py3-none-any.whl", hash = "sha256:0f36a4bacde5b218c73aadc69ff152e7f7fb3aa7260df0e6647a701a5351d76a"}, @@ -1586,13 +1669,13 @@ files = [ [[package]] name = "google-api-core" -version = "2.19.1" +version = "2.23.0" description = "Google API client core library" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd"}, - {file = "google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125"}, + {file = "google_api_core-2.23.0-py3-none-any.whl", hash = "sha256:c20100d4c4c41070cf365f1d8ddf5365915291b5eb11b83829fbd1c999b5122f"}, + {file = "google_api_core-2.23.0.tar.gz", hash = "sha256:2ceb087315e6af43f256704b871d99326b1f12a9d6ce99beaedec99ba26a0ace"}, ] [package.dependencies] @@ -1603,19 +1686,20 @@ protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4 requests = ">=2.18.0,<3.0.0.dev0" [package.extras] +async-rest = ["google-auth[aiohttp] (>=2.35.0,<3.0.dev0)"] grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.141.0" +version = "2.153.0" description = "Google API Client Library for Python" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "google_api_python_client-2.141.0-py2.py3-none-any.whl", hash = "sha256:43c05322b91791204465291b3852718fae38d4f84b411d8be847c4f86882652a"}, - {file = "google_api_python_client-2.141.0.tar.gz", hash = "sha256:0f225b1f45d5a6f8c2a400f48729f5d6da9a81138e81e0478d61fdd8edf6563a"}, + {file = "google_api_python_client-2.153.0-py2.py3-none-any.whl", hash = "sha256:6ff13bbfa92a57972e33ec3808e18309e5981b8ca1300e5da23bf2b4d6947384"}, + {file = "google_api_python_client-2.153.0.tar.gz", hash = "sha256:35cce8647f9c163fc04fb4d811fc91aae51954a2bdd74918decbe0e65d791dd2"}, ] [package.dependencies] @@ -1627,13 +1711,13 @@ uritemplate = ">=3.0.1,<5" [[package]] name = "google-auth" -version = "2.33.0" +version = "2.36.0" description = "Google Authentication Library" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "google_auth-2.33.0-py2.py3-none-any.whl", hash = "sha256:8eff47d0d4a34ab6265c50a106a3362de6a9975bb08998700e389f857e4d39df"}, - {file = "google_auth-2.33.0.tar.gz", hash = "sha256:d6a52342160d7290e334b4d47ba390767e4438ad0d45b7630774533e82655b95"}, + {file = "google_auth-2.36.0-py2.py3-none-any.whl", hash = "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb"}, + {file = "google_auth-2.36.0.tar.gz", hash = "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1"}, ] [package.dependencies] @@ -1643,7 +1727,7 @@ rsa = ">=3.1.4,<5" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] -enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +enterprise-cert = ["cryptography", "pyopenssl"] pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] @@ -1652,7 +1736,7 @@ requests = ["requests (>=2.20.0,<3.0.0.dev0)"] name = "google-auth-httplib2" version = "0.2.0" description = "Google Authentication Library: httplib2 transport" -optional = false +optional = true python-versions = "*" files = [ {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"}, @@ -1665,13 +1749,13 @@ httplib2 = ">=0.19.0" [[package]] name = "googleapis-common-protos" -version = "1.63.2" +version = "1.66.0" description = "Common protobufs used in Google APIs" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, - {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, + {file = "googleapis_common_protos-1.66.0-py2.py3-none-any.whl", hash = "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed"}, + {file = "googleapis_common_protos-1.66.0.tar.gz", hash = "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c"}, ] [package.dependencies] @@ -1682,61 +1766,70 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "grpcio" -version = "1.65.4" +version = "1.68.0" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.65.4-cp310-cp310-linux_armv7l.whl", hash = "sha256:0e85c8766cf7f004ab01aff6a0393935a30d84388fa3c58d77849fcf27f3e98c"}, - {file = "grpcio-1.65.4-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:e4a795c02405c7dfa8affd98c14d980f4acea16ea3b539e7404c645329460e5a"}, - {file = "grpcio-1.65.4-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d7b984a8dd975d949c2042b9b5ebcf297d6d5af57dcd47f946849ee15d3c2fb8"}, - {file = "grpcio-1.65.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644a783ce604a7d7c91412bd51cf9418b942cf71896344b6dc8d55713c71ce82"}, - {file = "grpcio-1.65.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5764237d751d3031a36fafd57eb7d36fd2c10c658d2b4057c516ccf114849a3e"}, - {file = "grpcio-1.65.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ee40d058cf20e1dd4cacec9c39e9bce13fedd38ce32f9ba00f639464fcb757de"}, - {file = "grpcio-1.65.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4482a44ce7cf577a1f8082e807a5b909236bce35b3e3897f839f2fbd9ae6982d"}, - {file = "grpcio-1.65.4-cp310-cp310-win32.whl", hash = "sha256:66bb051881c84aa82e4f22d8ebc9d1704b2e35d7867757f0740c6ef7b902f9b1"}, - {file = "grpcio-1.65.4-cp310-cp310-win_amd64.whl", hash = "sha256:870370524eff3144304da4d1bbe901d39bdd24f858ce849b7197e530c8c8f2ec"}, - {file = "grpcio-1.65.4-cp311-cp311-linux_armv7l.whl", hash = "sha256:85e9c69378af02e483bc626fc19a218451b24a402bdf44c7531e4c9253fb49ef"}, - {file = "grpcio-1.65.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2bd672e005afab8bf0d6aad5ad659e72a06dd713020554182a66d7c0c8f47e18"}, - {file = "grpcio-1.65.4-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:abccc5d73f5988e8f512eb29341ed9ced923b586bb72e785f265131c160231d8"}, - {file = "grpcio-1.65.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:886b45b29f3793b0c2576201947258782d7e54a218fe15d4a0468d9a6e00ce17"}, - {file = "grpcio-1.65.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be952436571dacc93ccc7796db06b7daf37b3b56bb97e3420e6503dccfe2f1b4"}, - {file = "grpcio-1.65.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8dc9ddc4603ec43f6238a5c95400c9a901b6d079feb824e890623da7194ff11e"}, - {file = "grpcio-1.65.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ade1256c98cba5a333ef54636095f2c09e6882c35f76acb04412f3b1aa3c29a5"}, - {file = "grpcio-1.65.4-cp311-cp311-win32.whl", hash = "sha256:280e93356fba6058cbbfc6f91a18e958062ef1bdaf5b1caf46c615ba1ae71b5b"}, - {file = "grpcio-1.65.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2b819f9ee27ed4e3e737a4f3920e337e00bc53f9e254377dd26fc7027c4d558"}, - {file = "grpcio-1.65.4-cp312-cp312-linux_armv7l.whl", hash = "sha256:926a0750a5e6fb002542e80f7fa6cab8b1a2ce5513a1c24641da33e088ca4c56"}, - {file = "grpcio-1.65.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2a1d4c84d9e657f72bfbab8bedf31bdfc6bfc4a1efb10b8f2d28241efabfaaf2"}, - {file = "grpcio-1.65.4-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:17de4fda50967679677712eec0a5c13e8904b76ec90ac845d83386b65da0ae1e"}, - {file = "grpcio-1.65.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dee50c1b69754a4228e933696408ea87f7e896e8d9797a3ed2aeed8dbd04b74"}, - {file = "grpcio-1.65.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74c34fc7562bdd169b77966068434a93040bfca990e235f7a67cdf26e1bd5c63"}, - {file = "grpcio-1.65.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:24a2246e80a059b9eb981e4c2a6d8111b1b5e03a44421adbf2736cc1d4988a8a"}, - {file = "grpcio-1.65.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18c10f0d054d2dce34dd15855fcca7cc44ec3b811139437543226776730c0f28"}, - {file = "grpcio-1.65.4-cp312-cp312-win32.whl", hash = "sha256:d72962788b6c22ddbcdb70b10c11fbb37d60ae598c51eb47ec019db66ccfdff0"}, - {file = "grpcio-1.65.4-cp312-cp312-win_amd64.whl", hash = "sha256:7656376821fed8c89e68206a522522317787a3d9ed66fb5110b1dff736a5e416"}, - {file = "grpcio-1.65.4-cp38-cp38-linux_armv7l.whl", hash = "sha256:4934077b33aa6fe0b451de8b71dabde96bf2d9b4cb2b3187be86e5adebcba021"}, - {file = "grpcio-1.65.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0cef8c919a3359847c357cb4314e50ed1f0cca070f828ee8f878d362fd744d52"}, - {file = "grpcio-1.65.4-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a925446e6aa12ca37114840d8550f308e29026cdc423a73da3043fd1603a6385"}, - {file = "grpcio-1.65.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf53e6247f1e2af93657e62e240e4f12e11ee0b9cef4ddcb37eab03d501ca864"}, - {file = "grpcio-1.65.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdb34278e4ceb224c89704cd23db0d902e5e3c1c9687ec9d7c5bb4c150f86816"}, - {file = "grpcio-1.65.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e6cbdd107e56bde55c565da5fd16f08e1b4e9b0674851d7749e7f32d8645f524"}, - {file = "grpcio-1.65.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:626319a156b1f19513156a3b0dbfe977f5f93db63ca673a0703238ebd40670d7"}, - {file = "grpcio-1.65.4-cp38-cp38-win32.whl", hash = "sha256:3d1bbf7e1dd1096378bd83c83f554d3b93819b91161deaf63e03b7022a85224a"}, - {file = "grpcio-1.65.4-cp38-cp38-win_amd64.whl", hash = "sha256:a99e6dffefd3027b438116f33ed1261c8d360f0dd4f943cb44541a2782eba72f"}, - {file = "grpcio-1.65.4-cp39-cp39-linux_armv7l.whl", hash = "sha256:874acd010e60a2ec1e30d5e505b0651ab12eb968157cd244f852b27c6dbed733"}, - {file = "grpcio-1.65.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b07f36faf01fca5427d4aa23645e2d492157d56c91fab7e06fe5697d7e171ad4"}, - {file = "grpcio-1.65.4-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:b81711bf4ec08a3710b534e8054c7dcf90f2edc22bebe11c1775a23f145595fe"}, - {file = "grpcio-1.65.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88fcabc332a4aef8bcefadc34a02e9ab9407ab975d2c7d981a8e12c1aed92aa1"}, - {file = "grpcio-1.65.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ba3e63108a8749994f02c7c0e156afb39ba5bdf755337de8e75eb685be244b"}, - {file = "grpcio-1.65.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8eb485801957a486bf5de15f2c792d9f9c897a86f2f18db8f3f6795a094b4bb2"}, - {file = "grpcio-1.65.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075f3903bc1749ace93f2b0664f72964ee5f2da5c15d4b47e0ab68e4f442c257"}, - {file = "grpcio-1.65.4-cp39-cp39-win32.whl", hash = "sha256:0a0720299bdb2cc7306737295d56e41ce8827d5669d4a3cd870af832e3b17c4d"}, - {file = "grpcio-1.65.4-cp39-cp39-win_amd64.whl", hash = "sha256:a146bc40fa78769f22e1e9ff4f110ef36ad271b79707577bf2a31e3e931141b9"}, - {file = "grpcio-1.65.4.tar.gz", hash = "sha256:2a4f476209acffec056360d3e647ae0e14ae13dcf3dfb130c227ae1c594cbe39"}, + {file = "grpcio-1.68.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:619b5d0f29f4f5351440e9343224c3e19912c21aeda44e0c49d0d147a8d01544"}, + {file = "grpcio-1.68.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a59f5822f9459bed098ffbceb2713abbf7c6fd13f2b9243461da5c338d0cd6c3"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c03d89df516128febc5a7e760d675b478ba25802447624edf7aa13b1e7b11e2a"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44bcbebb24363d587472089b89e2ea0ab2e2b4df0e4856ba4c0b087c82412121"}, + {file = "grpcio-1.68.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79f81b7fbfb136247b70465bd836fa1733043fdee539cd6031cb499e9608a110"}, + {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88fb2925789cfe6daa20900260ef0a1d0a61283dfb2d2fffe6194396a354c618"}, + {file = "grpcio-1.68.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:99f06232b5c9138593ae6f2e355054318717d32a9c09cdc5a2885540835067a1"}, + {file = "grpcio-1.68.0-cp310-cp310-win32.whl", hash = "sha256:a6213d2f7a22c3c30a479fb5e249b6b7e648e17f364598ff64d08a5136fe488b"}, + {file = "grpcio-1.68.0-cp310-cp310-win_amd64.whl", hash = "sha256:15327ab81131ef9b94cb9f45b5bd98803a179c7c61205c8c0ac9aff9d6c4e82a"}, + {file = "grpcio-1.68.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:3b2b559beb2d433129441783e5f42e3be40a9e1a89ec906efabf26591c5cd415"}, + {file = "grpcio-1.68.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46541de8425a4d6829ac6c5d9b16c03c292105fe9ebf78cb1c31e8d242f9155"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c1245651f3c9ea92a2db4f95d37b7597db6b246d5892bca6ee8c0e90d76fb73c"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1931c7aa85be0fa6cea6af388e576f3bf6baee9e5d481c586980c774debcb4"}, + {file = "grpcio-1.68.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b0ff09c81e3aded7a183bc6473639b46b6caa9c1901d6f5e2cba24b95e59e30"}, + {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8c73f9fbbaee1a132487e31585aa83987ddf626426d703ebcb9a528cf231c9b1"}, + {file = "grpcio-1.68.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6b2f98165ea2790ea159393a2246b56f580d24d7da0d0342c18a085299c40a75"}, + {file = "grpcio-1.68.0-cp311-cp311-win32.whl", hash = "sha256:e1e7ed311afb351ff0d0e583a66fcb39675be112d61e7cfd6c8269884a98afbc"}, + {file = "grpcio-1.68.0-cp311-cp311-win_amd64.whl", hash = "sha256:e0d2f68eaa0a755edd9a47d40e50dba6df2bceda66960dee1218da81a2834d27"}, + {file = "grpcio-1.68.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8af6137cc4ae8e421690d276e7627cfc726d4293f6607acf9ea7260bd8fc3d7d"}, + {file = "grpcio-1.68.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4028b8e9a3bff6f377698587d642e24bd221810c06579a18420a17688e421af7"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f60fa2adf281fd73ae3a50677572521edca34ba373a45b457b5ebe87c2d01e1d"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e18589e747c1e70b60fab6767ff99b2d0c359ea1db8a2cb524477f93cdbedf5b"}, + {file = "grpcio-1.68.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d30f3fee9372796f54d3100b31ee70972eaadcc87314be369360248a3dcffe"}, + {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7e0a3e72c0e9a1acab77bef14a73a416630b7fd2cbd893c0a873edc47c42c8cd"}, + {file = "grpcio-1.68.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a831dcc343440969aaa812004685ed322cdb526cd197112d0db303b0da1e8659"}, + {file = "grpcio-1.68.0-cp312-cp312-win32.whl", hash = "sha256:5a180328e92b9a0050958ced34dddcb86fec5a8b332f5a229e353dafc16cd332"}, + {file = "grpcio-1.68.0-cp312-cp312-win_amd64.whl", hash = "sha256:2bddd04a790b69f7a7385f6a112f46ea0b34c4746f361ebafe9ca0be567c78e9"}, + {file = "grpcio-1.68.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:fc05759ffbd7875e0ff2bd877be1438dfe97c9312bbc558c8284a9afa1d0f40e"}, + {file = "grpcio-1.68.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:15fa1fe25d365a13bc6d52fcac0e3ee1f9baebdde2c9b3b2425f8a4979fccea1"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:32a9cb4686eb2e89d97022ecb9e1606d132f85c444354c17a7dbde4a455e4a3b"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dba037ff8d284c8e7ea9a510c8ae0f5b016004f13c3648f72411c464b67ff2fb"}, + {file = "grpcio-1.68.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0efbbd849867e0e569af09e165363ade75cf84f5229b2698d53cf22c7a4f9e21"}, + {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:4e300e6978df0b65cc2d100c54e097c10dfc7018b9bd890bbbf08022d47f766d"}, + {file = "grpcio-1.68.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:6f9c7ad1a23e1047f827385f4713b5b8c6c7d325705be1dd3e31fb00dcb2f665"}, + {file = "grpcio-1.68.0-cp313-cp313-win32.whl", hash = "sha256:3ac7f10850fd0487fcce169c3c55509101c3bde2a3b454869639df2176b60a03"}, + {file = "grpcio-1.68.0-cp313-cp313-win_amd64.whl", hash = "sha256:afbf45a62ba85a720491bfe9b2642f8761ff348006f5ef67e4622621f116b04a"}, + {file = "grpcio-1.68.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:f8f695d9576ce836eab27ba7401c60acaf9ef6cf2f70dfe5462055ba3df02cc3"}, + {file = "grpcio-1.68.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9fe1b141cda52f2ca73e17d2d3c6a9f3f3a0c255c216b50ce616e9dca7e3441d"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:4df81d78fd1646bf94ced4fb4cd0a7fe2e91608089c522ef17bc7db26e64effd"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46a2d74d4dd8993151c6cd585594c082abe74112c8e4175ddda4106f2ceb022f"}, + {file = "grpcio-1.68.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a17278d977746472698460c63abf333e1d806bd41f2224f90dbe9460101c9796"}, + {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:15377bce516b1c861c35e18eaa1c280692bf563264836cece693c0f169b48829"}, + {file = "grpcio-1.68.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc5f0a4f5904b8c25729a0498886b797feb817d1fd3812554ffa39551112c161"}, + {file = "grpcio-1.68.0-cp38-cp38-win32.whl", hash = "sha256:def1a60a111d24376e4b753db39705adbe9483ef4ca4761f825639d884d5da78"}, + {file = "grpcio-1.68.0-cp38-cp38-win_amd64.whl", hash = "sha256:55d3b52fd41ec5772a953612db4e70ae741a6d6ed640c4c89a64f017a1ac02b5"}, + {file = "grpcio-1.68.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:0d230852ba97654453d290e98d6aa61cb48fa5fafb474fb4c4298d8721809354"}, + {file = "grpcio-1.68.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:50992f214264e207e07222703c17d9cfdcc2c46ed5a1ea86843d440148ebbe10"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:14331e5c27ed3545360464a139ed279aa09db088f6e9502e95ad4bfa852bb116"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f84890b205692ea813653ece4ac9afa2139eae136e419231b0eec7c39fdbe4c2"}, + {file = "grpcio-1.68.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0cf343c6f4f6aa44863e13ec9ddfe299e0be68f87d68e777328bff785897b05"}, + {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fd2c2d47969daa0e27eadaf15c13b5e92605c5e5953d23c06d0b5239a2f176d3"}, + {file = "grpcio-1.68.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:18668e36e7f4045820f069997834e94e8275910b1f03e078a6020bd464cb2363"}, + {file = "grpcio-1.68.0-cp39-cp39-win32.whl", hash = "sha256:2af76ab7c427aaa26aa9187c3e3c42f38d3771f91a20f99657d992afada2294a"}, + {file = "grpcio-1.68.0-cp39-cp39-win_amd64.whl", hash = "sha256:e694b5928b7b33ca2d3b4d5f9bf8b5888906f181daff6b406f4938f3a997a490"}, + {file = "grpcio-1.68.0.tar.gz", hash = "sha256:7e7483d39b4a4fddb9906671e9ea21aaad4f031cdfc349fec76bdfa1e404543a"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.65.4)"] +protobuf = ["grpcio-tools (>=1.68.0)"] [[package]] name = "grpcio-health-checking" @@ -1757,7 +1850,7 @@ protobuf = ">=4.21.6" name = "httplib2" version = "0.22.0" description = "A comprehensive HTTP client library." -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, @@ -1769,13 +1862,13 @@ pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0 [[package]] name = "hypothesis" -version = "6.111.2" +version = "6.122.1" description = "A library for property-based testing" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "hypothesis-6.111.2-py3-none-any.whl", hash = "sha256:055e8228958e22178d6077e455fd86a72044d02dac130dbf9c8b31e161b9809c"}, - {file = "hypothesis-6.111.2.tar.gz", hash = "sha256:0496ad28c7240ee9ba89fcc7fb1dc74e89f3e40fbcbbb5f73c0091558dec8e6e"}, + {file = "hypothesis-6.122.1-py3-none-any.whl", hash = "sha256:59e52da0f2529b40f0b7bd0c3c61d8b3fe3337102800bf3534c53d4a8bdf8a6d"}, + {file = "hypothesis-6.122.1.tar.gz", hash = "sha256:23280e802eef88316b02cb32205d74b5bf2e3de4a378e2579a8974117c512b83"}, ] [package.dependencies] @@ -1784,31 +1877,31 @@ exceptiongroup = {version = ">=1.0.0", markers = "python_version < \"3.11\""} sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["backports.zoneinfo (>=0.2.1)", "black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.70)", "django (>=3.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.13)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.17.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.1)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "crosshair-tool (>=0.0.78)", "django (>=4.2)", "dpcontracts (>=0.4)", "hypothesis-crosshair (>=0.0.18)", "lark (>=0.10.1)", "libcst (>=0.3.16)", "numpy (>=1.19.3)", "pandas (>=1.1)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "tzdata (>=2024.2)"] cli = ["black (>=19.10b0)", "click (>=7.0)", "rich (>=9.0.0)"] codemods = ["libcst (>=0.3.16)"] -crosshair = ["crosshair-tool (>=0.0.70)", "hypothesis-crosshair (>=0.0.13)"] +crosshair = ["crosshair-tool (>=0.0.78)", "hypothesis-crosshair (>=0.0.18)"] dateutil = ["python-dateutil (>=1.4)"] -django = ["django (>=3.2)"] +django = ["django (>=4.2)"] dpcontracts = ["dpcontracts (>=0.4)"] ghostwriter = ["black (>=19.10b0)"] lark = ["lark (>=0.10.1)"] -numpy = ["numpy (>=1.17.3)"] +numpy = ["numpy (>=1.19.3)"] pandas = ["pandas (>=1.1)"] pytest = ["pytest (>=4.6)"] pytz = ["pytz (>=2014.1)"] redis = ["redis (>=3.0.0)"] -zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2024.1)"] +zoneinfo = ["tzdata (>=2024.2)"] [[package]] name = "identify" -version = "2.6.0" +version = "2.6.2" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"}, + {file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"}, ] [package.extras] @@ -1816,15 +1909,18 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "imagesize" version = "1.4.1" @@ -1838,40 +1934,26 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] - -[[package]] -name = "importlib-resources" -version = "6.4.2" -description = "Read resources from Python packages" -optional = false -python-versions = ">=3.8" -files = [ - {file = "importlib_resources-6.4.2-py3-none-any.whl", hash = "sha256:8bba8c54a8a3afaa1419910845fa26ebd706dc716dd208d9b158b4b6966f5c5c"}, - {file = "importlib_resources-6.4.2.tar.gz", hash = "sha256:6cbfbefc449cc6e2095dd184691b7a12a04f40bc75dd4c55d31c34f174cdf57a"}, -] - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] [[package]] name = "iniconfig" @@ -1919,13 +2001,13 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio [[package]] name = "ipython" -version = "8.18.1" +version = "8.29.0" description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, - {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, + {file = "ipython-8.29.0-py3-none-any.whl", hash = "sha256:0188a1bd83267192123ccea7f4a8ed0a78910535dbaa3f37671dca76ebd429c8"}, + {file = "ipython-8.29.0.tar.gz", hash = "sha256:40b60e15b22591450eef73e40a027cf77bd652e757523eebc5bd7c7c498290eb"}, ] [package.dependencies] @@ -1934,43 +2016,44 @@ decorator = "*" exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} prompt-toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} +traitlets = ">=5.13.0" +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "intersphinx-registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli", "typing-extensions"] kernel = ["ipykernel"] +matplotlib = ["matplotlib"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] name = "ipywidgets" -version = "8.1.3" +version = "8.1.5" description = "Jupyter interactive widgets" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "ipywidgets-8.1.3-py3-none-any.whl", hash = "sha256:efafd18f7a142248f7cb0ba890a68b96abd4d6e88ddbda483c9130d12667eaf2"}, - {file = "ipywidgets-8.1.3.tar.gz", hash = "sha256:f5f9eeaae082b1823ce9eac2575272952f40d748893972956dc09700a6392d9c"}, + {file = "ipywidgets-8.1.5-py3-none-any.whl", hash = "sha256:3290f526f87ae6e77655555baba4f36681c555b8bdbbff430b70e52c34c86245"}, + {file = "ipywidgets-8.1.5.tar.gz", hash = "sha256:870e43b1a35656a80c18c9503bbf2d16802db1cb487eec6fab27d683381dde17"}, ] [package.dependencies] comm = ">=0.1.3" ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0.11,<3.1.0" +jupyterlab-widgets = ">=3.0.12,<3.1.0" traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0.11,<4.1.0" +widgetsnbextension = ">=4.0.12,<4.1.0" [package.extras] test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] @@ -1979,7 +2062,7 @@ test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] name = "isoduration" version = "20.11.0" description = "Operations with ISO 8601 durations" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, @@ -1991,22 +2074,22 @@ arrow = ">=0.15.0" [[package]] name = "jedi" -version = "0.19.1" +version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] [package.dependencies] -parso = ">=0.8.3,<0.9.0" +parso = ">=0.8.4,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jinja2" @@ -2029,7 +2112,7 @@ i18n = ["Babel (>=2.7)"] name = "jsonpointer" version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, @@ -2040,7 +2123,7 @@ files = [ name = "jsonschema" version = "4.23.0" description = "An implementation of JSON Schema validation for Python" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, @@ -2067,13 +2150,13 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "jsonschema-specifications" -version = "2023.12.1" +version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, + {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, + {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, ] [package.dependencies] @@ -2081,17 +2164,16 @@ referencing = ">=0.31.0" [[package]] name = "jupyter-client" -version = "8.6.2" +version = "8.6.3" description = "Jupyter protocol implementation and client libraries" optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, - {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, + {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"}, + {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"}, ] [package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" @@ -2126,7 +2208,7 @@ test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout" name = "jupyter-events" version = "0.10.0" description = "Jupyter Event System library" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "jupyter_events-0.10.0-py3-none-any.whl", hash = "sha256:4b72130875e59d57716d327ea70d3ebc3af1944d3717e5a498b8a06c6c159960"}, @@ -2151,7 +2233,7 @@ test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "p name = "jupyter-server" version = "2.14.2" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "jupyter_server-2.14.2-py3-none-any.whl", hash = "sha256:47ff506127c2f7851a17bf4713434208fc490955d0e8632e95014a9a9afbeefd"}, @@ -2185,18 +2267,17 @@ test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<9)", "pytest-console [[package]] name = "jupyter-server-proxy" -version = "4.3.0" +version = "4.4.0" description = "A Jupyter server extension to run additional processes and proxy to them that comes bundled JupyterLab extension to launch pre-defined processes." -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "jupyter_server_proxy-4.3.0-py3-none-any.whl", hash = "sha256:0e664cf46ff8acd4c66b947ef33eb6e8a1a7bc3896ba47517ab8f24da5d198d7"}, - {file = "jupyter_server_proxy-4.3.0.tar.gz", hash = "sha256:d14db5044dfc2e672f80b75b34df2c3439efd6fc90a7999aa37b0d592075ce70"}, + {file = "jupyter_server_proxy-4.4.0-py3-none-any.whl", hash = "sha256:707b5c84810bb8863d50f6c6d50a386fec216149e11802b7d4c451b54a63a9a6"}, + {file = "jupyter_server_proxy-4.4.0.tar.gz", hash = "sha256:e5732eb9c810c0caa997f90a2f15f7d09af638e7eea9c67eb5c43e9c1f0e1157"}, ] [package.dependencies] aiohttp = "*" -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} jupyter-server = ">=1.24.0" simpervisor = ">=1.0.0" tornado = ">=6.1.0" @@ -2212,7 +2293,7 @@ test = ["pytest", "pytest-asyncio", "pytest-cov", "pytest-html"] name = "jupyter-server-terminals" version = "0.5.3" description = "A Jupyter Server Extension Providing Terminals." -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa"}, @@ -2231,7 +2312,7 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> name = "jupyterlab-pygments" version = "0.3.0" description = "Pygments theme using JupyterLab CSS variables" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, @@ -2240,262 +2321,273 @@ files = [ [[package]] name = "jupyterlab-widgets" -version = "3.0.11" +version = "3.0.13" description = "Jupyter interactive widgets for JupyterLab" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "jupyterlab_widgets-3.0.11-py3-none-any.whl", hash = "sha256:78287fd86d20744ace330a61625024cf5521e1c012a352ddc0a3cdc2348becd0"}, - {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, + {file = "jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54"}, + {file = "jupyterlab_widgets-3.0.13.tar.gz", hash = "sha256:a2966d385328c1942b683a8cd96b89b8dd82c8b8f81dda902bb2bc06d46f5bed"}, ] [[package]] name = "kiwisolver" -version = "1.4.5" +version = "1.4.7" description = "A fast implementation of the Cassowary constraint solver" -optional = false -python-versions = ">=3.7" +optional = true +python-versions = ">=3.8" files = [ - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, - {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17"}, + {file = "kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05"}, + {file = "kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895"}, + {file = "kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c"}, + {file = "kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95"}, + {file = "kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052"}, + {file = "kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3"}, + {file = "kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b"}, + {file = "kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a"}, + {file = "kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258"}, + {file = "kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383"}, + {file = "kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb"}, + {file = "kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6"}, + {file = "kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34"}, + {file = "kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a"}, + {file = "kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76"}, + {file = "kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d5abf8f8ec1f4e22882273c423e16cae834c36856cac348cfbfa68e01c40f3a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:aeb3531b196ef6f11776c21674dba836aeea9d5bd1cf630f869e3d90b16cfade"}, + {file = "kiwisolver-1.4.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7d755065e4e866a8086c9bdada157133ff466476a2ad7861828e17b6026e22c"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08471d4d86cbaec61f86b217dd938a83d85e03785f51121e791a6e6689a3be95"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bbfcb7165ce3d54a3dfbe731e470f65739c4c1f85bb1018ee912bae139e263b"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d34eb8494bea691a1a450141ebb5385e4b69d38bb8403b5146ad279f4b30fa3"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9242795d174daa40105c1d86aba618e8eab7bf96ba8c3ee614da8302a9f95503"}, + {file = "kiwisolver-1.4.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0f64a48bb81af7450e641e3fe0b0394d7381e342805479178b3d335d60ca7cf"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8e045731a5416357638d1700927529e2b8ab304811671f665b225f8bf8d8f933"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4322872d5772cae7369f8351da1edf255a604ea7087fe295411397d0cfd9655e"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:e1631290ee9271dffe3062d2634c3ecac02c83890ada077d225e081aca8aab89"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:edcfc407e4eb17e037bca59be0e85a2031a2ac87e4fed26d3e9df88b4165f92d"}, + {file = "kiwisolver-1.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4d05d81ecb47d11e7f8932bd8b61b720bf0b41199358f3f5e36d38e28f0532c5"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win32.whl", hash = "sha256:b38ac83d5f04b15e515fd86f312479d950d05ce2368d5413d46c088dda7de90a"}, + {file = "kiwisolver-1.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:d83db7cde68459fc803052a55ace60bea2bae361fc3b7a6d5da07e11954e4b09"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583"}, + {file = "kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb"}, + {file = "kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4"}, + {file = "kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4"}, + {file = "kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bfa1acfa0c54932d5607e19a2c24646fb4c1ae2694437789129cf099789a3b00"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:eee3ea935c3d227d49b4eb85660ff631556841f6e567f0f7bda972df6c2c9935"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f3160309af4396e0ed04db259c3ccbfdc3621b5559b5453075e5de555e1f3a1b"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a17f6a29cf8935e587cc8a4dbfc8368c55edc645283db0ce9801016f83526c2d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10849fb2c1ecbfae45a693c070e0320a91b35dd4bcf58172c023b994283a124d"}, + {file = "kiwisolver-1.4.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ac542bf38a8a4be2dc6b15248d36315ccc65f0743f7b1a76688ffb6b5129a5c2"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225"}, + {file = "kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0"}, + {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, ] [[package]] name = "makefun" -version = "1.15.4" +version = "1.15.6" description = "Small library to dynamically create python functions." optional = false python-versions = "*" files = [ - {file = "makefun-1.15.4-py2.py3-none-any.whl", hash = "sha256:945d078a7e01a903f2cbef738b33e0ebc52b8d35fb7e20c528ed87b5c80db5b7"}, - {file = "makefun-1.15.4.tar.gz", hash = "sha256:9f9b9904e7c397759374a88f4c57781fbab2a458dec78df4b3ee6272cd9fb010"}, + {file = "makefun-1.15.6-py2.py3-none-any.whl", hash = "sha256:e69b870f0bb60304765b1e3db576aaecf2f9b3e5105afe8cfeff8f2afe6ad067"}, + {file = "makefun-1.15.6.tar.gz", hash = "sha256:26bc63442a6182fb75efed8b51741dd2d1db2f176bec8c64e20a586256b8f149"}, ] [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] name = "matplotlib" -version = "3.9.2" +version = "3.9.3" description = "Python plotting package" -optional = false +optional = true python-versions = ">=3.9" files = [ - {file = "matplotlib-3.9.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb"}, - {file = "matplotlib-3.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64"}, - {file = "matplotlib-3.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66"}, - {file = "matplotlib-3.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a"}, - {file = "matplotlib-3.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772"}, - {file = "matplotlib-3.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f"}, - {file = "matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447"}, - {file = "matplotlib-3.9.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e"}, - {file = "matplotlib-3.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9"}, - {file = "matplotlib-3.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7"}, - {file = "matplotlib-3.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c"}, - {file = "matplotlib-3.9.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e"}, - {file = "matplotlib-3.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9"}, - {file = "matplotlib-3.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b"}, - {file = "matplotlib-3.9.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413"}, - {file = "matplotlib-3.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b"}, - {file = "matplotlib-3.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03"}, - {file = "matplotlib-3.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51"}, - {file = "matplotlib-3.9.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c"}, - {file = "matplotlib-3.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2"}, - {file = "matplotlib-3.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5"}, - {file = "matplotlib-3.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca"}, - {file = "matplotlib-3.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea"}, - {file = "matplotlib-3.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc"}, - {file = "matplotlib-3.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697"}, - {file = "matplotlib-3.9.2.tar.gz", hash = "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92"}, + {file = "matplotlib-3.9.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:41b016e3be4e740b66c79a031a0a6e145728dbc248142e751e8dab4f3188ca1d"}, + {file = "matplotlib-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e0143975fc2a6d7136c97e19c637321288371e8f09cff2564ecd73e865ea0b9"}, + {file = "matplotlib-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f459c8ee2c086455744723628264e43c884be0c7d7b45d84b8cd981310b4815"}, + {file = "matplotlib-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687df7ceff57b8f070d02b4db66f75566370e7ae182a0782b6d3d21b0d6917dc"}, + {file = "matplotlib-3.9.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:edd14cf733fdc4f6e6fe3f705af97676a7e52859bf0044aa2c84e55be739241c"}, + {file = "matplotlib-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c40c244221a1adbb1256692b1133c6fb89418df27bf759a31a333e7912a4010"}, + {file = "matplotlib-3.9.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cf2a60daf6cecff6828bc608df00dbc794380e7234d2411c0ec612811f01969d"}, + {file = "matplotlib-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:213d6dc25ce686516208d8a3e91120c6a4fdae4a3e06b8505ced5b716b50cc04"}, + {file = "matplotlib-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c52f48eb75fcc119a4fdb68ba83eb5f71656999420375df7c94cc68e0e14686e"}, + {file = "matplotlib-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3c93796b44fa111049b88a24105e947f03c01966b5c0cc782e2ee3887b790a3"}, + {file = "matplotlib-3.9.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd1077b9a09b16d8c3c7075a8add5ffbfe6a69156a57e290c800ed4d435bef1d"}, + {file = "matplotlib-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:c96eeeb8c68b662c7747f91a385688d4b449687d29b691eff7068a4602fe6dc4"}, + {file = "matplotlib-3.9.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a361bd5583bf0bcc08841df3c10269617ee2a36b99ac39d455a767da908bbbc"}, + {file = "matplotlib-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e14485bb1b83eeb3d55b6878f9560240981e7bbc7a8d4e1e8c38b9bd6ec8d2de"}, + {file = "matplotlib-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a8d279f78844aad213c4935c18f8292a9432d51af2d88bca99072c903948045"}, + {file = "matplotlib-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6c12514329ac0d03128cf1dcceb335f4fbf7c11da98bca68dca8dcb983153a9"}, + {file = "matplotlib-3.9.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6e9de2b390d253a508dd497e9b5579f3a851f208763ed67fdca5dc0c3ea6849c"}, + {file = "matplotlib-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d796272408f8567ff7eaa00eb2856b3a00524490e47ad505b0b4ca6bb8a7411f"}, + {file = "matplotlib-3.9.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83"}, + {file = "matplotlib-3.9.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad"}, + {file = "matplotlib-3.9.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a"}, + {file = "matplotlib-3.9.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0"}, + {file = "matplotlib-3.9.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9"}, + {file = "matplotlib-3.9.3-cp313-cp313-win_amd64.whl", hash = "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70"}, + {file = "matplotlib-3.9.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639"}, + {file = "matplotlib-3.9.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6"}, + {file = "matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0"}, + {file = "matplotlib-3.9.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006"}, + {file = "matplotlib-3.9.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4"}, + {file = "matplotlib-3.9.3-cp313-cp313t-win_amd64.whl", hash = "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a"}, + {file = "matplotlib-3.9.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:031b7f5b8e595cc07def77ec5b58464e9bb67dc5760be5d6f26d9da24892481d"}, + {file = "matplotlib-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fa6e193c14d6944e0685cdb527cb6b38b0e4a518043e7212f214113af7391da"}, + {file = "matplotlib-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6eefae6effa0c35bbbc18c25ee6e0b1da44d2359c3cd526eb0c9e703cf055d"}, + {file = "matplotlib-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d3e5c7a99bd28afb957e1ae661323b0800d75b419f24d041ed1cc5d844a764"}, + {file = "matplotlib-3.9.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:816a966d5d376bf24c92af8f379e78e67278833e4c7cbc9fa41872eec629a060"}, + {file = "matplotlib-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fb0b37c896172899a4a93d9442ffdc6f870165f59e05ce2e07c6fded1c15749"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f2a4ea08e6876206d511365b0bc234edc813d90b930be72c3011bbd7898796f"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9b081dac96ab19c54fd8558fac17c9d2c9cb5cc4656e7ed3261ddc927ba3e2c5"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a0a63cb8404d1d1f94968ef35738900038137dab8af836b6c21bb6f03d75465"}, + {file = "matplotlib-3.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:896774766fd6be4571a43bc2fcbcb1dcca0807e53cab4a5bf88c4aa861a08e12"}, + {file = "matplotlib-3.9.3.tar.gz", hash = "sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5"}, ] [package.dependencies] contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" -importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} kiwisolver = ">=1.3.1" numpy = ">=1.23" packaging = ">=20.0" @@ -2504,7 +2596,7 @@ pyparsing = ">=2.3.1" python-dateutil = ">=2.7" [package.extras] -dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6)", "setuptools (>=64)", "setuptools_scm (>=7)"] +dev = ["meson-python (>=0.13.1)", "numpy (>=1.25)", "pybind11 (>=2.6,!=2.13.3)", "setuptools (>=64)", "setuptools_scm (>=7)"] [[package]] name = "matplotlib-inline" @@ -2524,7 +2616,7 @@ traitlets = "*" name = "mistune" version = "3.0.2" description = "A sane and fast Markdown parser with useful plugins and renderers" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, @@ -2533,213 +2625,231 @@ files = [ [[package]] name = "more-itertools" -version = "10.4.0" +version = "10.5.0" description = "More routines for operating on iterables, beyond itertools" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "more-itertools-10.4.0.tar.gz", hash = "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923"}, - {file = "more_itertools-10.4.0-py3-none-any.whl", hash = "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27"}, + {file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"}, + {file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"}, ] [[package]] name = "msgpack" -version = "1.0.8" +version = "1.1.0" description = "MessagePack serializer" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, - {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, - {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, - {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, - {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, - {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, - {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, - {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, - {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, - {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, - {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, + {file = "msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e"}, + {file = "msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68"}, + {file = "msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b"}, + {file = "msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044"}, + {file = "msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa"}, + {file = "msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59"}, + {file = "msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6"}, + {file = "msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5"}, + {file = "msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88"}, + {file = "msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2"}, + {file = "msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39"}, + {file = "msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c"}, + {file = "msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b"}, + {file = "msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b"}, + {file = "msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330"}, + {file = "msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca"}, + {file = "msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434"}, + {file = "msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c"}, + {file = "msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc"}, + {file = "msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96"}, + {file = "msgpack-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb"}, + {file = "msgpack-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f"}, + {file = "msgpack-1.1.0-cp38-cp38-win32.whl", hash = "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b"}, + {file = "msgpack-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48"}, + {file = "msgpack-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74"}, + {file = "msgpack-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b"}, + {file = "msgpack-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8"}, + {file = "msgpack-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd"}, + {file = "msgpack-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325"}, + {file = "msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e"}, ] [[package]] name = "multidict" -version = "6.0.5" +version = "6.1.0" description = "multidict implementation" -optional = false -python-versions = ">=3.7" +optional = true +python-versions = ">=3.8" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + [[package]] name = "mypy" -version = "1.11.2" +version = "1.13.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, - {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, - {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, - {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, - {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, - {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, - {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, - {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, - {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, - {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, - {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, - {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, - {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, - {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, - {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, - {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, - {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, - {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, - {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, - {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, - {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, - {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, - {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, ] [package.dependencies] @@ -2749,6 +2859,7 @@ typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] reports = ["lxml"] @@ -2768,7 +2879,7 @@ files = [ name = "nbclient" version = "0.10.0" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -optional = false +optional = true python-versions = ">=3.8.0" files = [ {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, @@ -2790,7 +2901,7 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= name = "nbconvert" version = "7.16.4" description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"}, @@ -2801,7 +2912,6 @@ files = [ beautifulsoup4 = "*" bleach = "!=5.0.0" defusedxml = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} jinja2 = ">=3.0" jupyter-core = ">=4.7" jupyterlab-pygments = "*" @@ -2828,7 +2938,7 @@ webpdf = ["playwright"] name = "nbformat" version = "5.10.4" description = "The Jupyter Notebook format" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, @@ -2856,6 +2966,25 @@ files = [ {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] +[[package]] +name = "networkx" +version = "3.4.2" +description = "Python package for creating and manipulating graphs and networks" +optional = false +python-versions = ">=3.10" +files = [ + {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, + {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, +] + +[package.extras] +default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"] +developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"] +doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"] +example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"] +extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"] +test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] + [[package]] name = "nodeenv" version = "1.9.1" @@ -2937,7 +3066,7 @@ test = ["matplotlib", "pytest", "pytest-cov"] name = "overrides" version = "7.7.0" description = "A decorator to automatically detect mismatch when overriding a method." -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, @@ -2946,20 +3075,20 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] name = "pandocfilters" version = "1.5.1" description = "Utilities for writing pandoc filters in python" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, @@ -3022,95 +3151,90 @@ ptyprocess = ">=0.5" [[package]] name = "pillow" -version = "10.4.0" +version = "11.0.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, - {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, - {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, - {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, - {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, - {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, - {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, - {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, - {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, - {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, - {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, - {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, - {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947"}, + {file = "pillow-11.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488"}, + {file = "pillow-11.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb"}, + {file = "pillow-11.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97"}, + {file = "pillow-11.0.0-cp310-cp310-win32.whl", hash = "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50"}, + {file = "pillow-11.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c"}, + {file = "pillow-11.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc"}, + {file = "pillow-11.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b"}, + {file = "pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306"}, + {file = "pillow-11.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9"}, + {file = "pillow-11.0.0-cp311-cp311-win32.whl", hash = "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5"}, + {file = "pillow-11.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291"}, + {file = "pillow-11.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923"}, + {file = "pillow-11.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9"}, + {file = "pillow-11.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6"}, + {file = "pillow-11.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc"}, + {file = "pillow-11.0.0-cp312-cp312-win32.whl", hash = "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6"}, + {file = "pillow-11.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47"}, + {file = "pillow-11.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699"}, + {file = "pillow-11.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527"}, + {file = "pillow-11.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f"}, + {file = "pillow-11.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb"}, + {file = "pillow-11.0.0-cp313-cp313-win32.whl", hash = "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798"}, + {file = "pillow-11.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de"}, + {file = "pillow-11.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b"}, + {file = "pillow-11.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2"}, + {file = "pillow-11.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a"}, + {file = "pillow-11.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8"}, + {file = "pillow-11.0.0-cp313-cp313t-win32.whl", hash = "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8"}, + {file = "pillow-11.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904"}, + {file = "pillow-11.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba"}, + {file = "pillow-11.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7"}, + {file = "pillow-11.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f"}, + {file = "pillow-11.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae"}, + {file = "pillow-11.0.0-cp39-cp39-win32.whl", hash = "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4"}, + {file = "pillow-11.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd"}, + {file = "pillow-11.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734"}, + {file = "pillow-11.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790"}, + {file = "pillow-11.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944"}, + {file = "pillow-11.0.0.tar.gz", hash = "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739"}, ] [package.extras] -docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] fpx = ["olefile"] mic = ["olefile"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] @@ -3119,19 +3243,19 @@ xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=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)"] -type = ["mypy (>=1.8)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" @@ -3152,7 +3276,7 @@ testing = ["pytest", "pytest-benchmark"] name = "pooch" version = "1.8.2" description = "A friend to fetch your data files" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "pooch-1.8.2-py3-none-any.whl", hash = "sha256:3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47"}, @@ -3171,13 +3295,13 @@ xxhash = ["xxhash (>=1.4.3)"] [[package]] name = "pre-commit" -version = "3.8.0" +version = "4.0.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false python-versions = ">=3.9" files = [ - {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, - {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, + {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, + {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, ] [package.dependencies] @@ -3189,13 +3313,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prometheus-client" -version = "0.20.0" +version = "0.21.0" description = "Python client for the Prometheus monitoring system." -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, - {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, + {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, + {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, ] [package.extras] @@ -3203,27 +3327,134 @@ twisted = ["twisted"] [[package]] name = "prompt-toolkit" -version = "3.0.47" +version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, - {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] [package.dependencies] wcwidth = "*" +[[package]] +name = "propcache" +version = "0.2.0" +description = "Accelerated property cache" +optional = true +python-versions = ">=3.8" +files = [ + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, + {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, + {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, + {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, + {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, + {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, + {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, + {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, + {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, + {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, + {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, + {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, + {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, + {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, + {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, + {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, + {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, + {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, + {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, + {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, + {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, + {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, + {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, + {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, + {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, + {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, + {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, + {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, + {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, + {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, + {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, + {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, + {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, +] + [[package]] name = "proto-plus" -version = "1.24.0" +version = "1.25.0" description = "Beautiful, Pythonic protocol buffers." -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, - {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, + {file = "proto_plus-1.25.0-py3-none-any.whl", hash = "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961"}, + {file = "proto_plus-1.25.0.tar.gz", hash = "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91"}, ] [package.dependencies] @@ -3234,52 +3465,53 @@ testing = ["google-api-core (>=1.31.5)"] [[package]] name = "protobuf" -version = "4.25.4" +version = "4.25.5" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, + {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, + {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, + {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, + {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, + {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, + {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, + {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, + {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, + {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] [[package]] name = "psutil" -version = "6.0.0" +version = "6.1.0" description = "Cross-platform lib for process and system monitoring in Python." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, + {file = "psutil-6.1.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942"}, + {file = "psutil-6.1.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76"}, + {file = "psutil-6.1.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc"}, + {file = "psutil-6.1.0-cp27-none-win32.whl", hash = "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e"}, + {file = "psutil-6.1.0-cp27-none-win_amd64.whl", hash = "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688"}, + {file = "psutil-6.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b"}, + {file = "psutil-6.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a"}, + {file = "psutil-6.1.0-cp36-cp36m-win32.whl", hash = "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca"}, + {file = "psutil-6.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747"}, + {file = "psutil-6.1.0-cp37-abi3-win32.whl", hash = "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e"}, + {file = "psutil-6.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be"}, + {file = "psutil-6.1.0.tar.gz", hash = "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a"}, ] [package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] +dev = ["black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest-cov", "requests", "rstcheck", "ruff", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "wheel"] +test = ["pytest", "pytest-xdist", "setuptools"] [[package]] name = "ptyprocess" @@ -3319,39 +3551,39 @@ files = [ [[package]] name = "pyansys-tools-versioning" -version = "0.5.0" +version = "0.6.0" description = "PyAnsys Tools Versioning." -optional = false -python-versions = ">=3.9,<4" +optional = true +python-versions = "<4,>=3.10" files = [ - {file = "pyansys_tools_versioning-0.5.0-py3-none-any.whl", hash = "sha256:5dcac7272cd70a034ac100094535f1e420951d06f00b9dacf6597366a6f50825"}, - {file = "pyansys_tools_versioning-0.5.0.tar.gz", hash = "sha256:ed2266f2919a8022ddfd42e3c8d4ccfcaf575b730a09173d2b847eb919489d7f"}, + {file = "pyansys_tools_versioning-0.6.0-py3-none-any.whl", hash = "sha256:a7191203ccd89ce86a5413e268b3a51127a5b9f5117dba909422bcfdf6e7f81f"}, + {file = "pyansys_tools_versioning-0.6.0.tar.gz", hash = "sha256:582d430c2325aa5f9fea64abdfb77c14dc5153e814e813d4a37f0f88531e6e41"}, ] [package.extras] -doc = ["Sphinx (==7.2.6)", "Sphinx-copybutton (==0.5.2)", "ansys_sphinx_theme (==0.12.1)", "numpydoc (==1.6.0)", "sphinx-autoapi (==3.0.0)"] -tests = ["hypothesis (==6.87.1)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)"] +doc = ["Sphinx (==8.0.2)", "Sphinx-copybutton (==0.5.2)", "ansys_sphinx_theme[autoapi] (==1.0.3)", "numpydoc (==1.8.0)"] +tests = ["hypothesis (==6.111.0)", "pytest (==8.3.2)", "pytest-cov (==5.0.0)"] [[package]] name = "pyasn1" -version = "0.6.0" +version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, - {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, ] [[package]] name = "pyasn1-modules" -version = "0.4.0" +version = "0.4.1" description = "A collection of ASN.1-based protocols modules" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, - {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, + {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, + {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, ] [package.dependencies] @@ -3370,13 +3602,13 @@ files = [ [[package]] name = "pydata-sphinx-theme" -version = "0.15.4" +version = "0.16.0" description = "Bootstrap-based Sphinx theme from the PyData community" optional = false python-versions = ">=3.9" files = [ - {file = "pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6"}, - {file = "pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d"}, + {file = "pydata_sphinx_theme-0.16.0-py3-none-any.whl", hash = "sha256:18c810ee4e67e05281e371e156c1fb5bb0fa1f2747240461b225272f7d8d57d8"}, + {file = "pydata_sphinx_theme-0.16.0.tar.gz", hash = "sha256:721dd26e05fa8b992d66ef545536e6cbe0110afb9865820a08894af1ad6f7707"}, ] [package.dependencies] @@ -3384,9 +3616,8 @@ accessible-pygments = "*" Babel = "*" beautifulsoup4 = "*" docutils = "!=0.17.0" -packaging = "*" pygments = ">=2.7" -sphinx = ">=5" +sphinx = ">=6.1" typing-extensions = "*" [package.extras] @@ -3414,7 +3645,7 @@ windows-terminal = ["colorama (>=0.4.6)"] name = "pyiges" version = "0.3.1" description = "Pythonic IGES reader" -optional = false +optional = true python-versions = "*" files = [ {file = "pyiges-0.3.1-py3-none-any.whl", hash = "sha256:74a89874649bc7cab139e1d8198cb8cb895537ff8693702d6feb9fb49d402ab5"}, @@ -3432,24 +3663,24 @@ full = ["geomdl", "pyvista (>=0.28.0)"] [[package]] name = "pypandoc" -version = "1.13" +version = "1.14" description = "Thin wrapper for pandoc." optional = false python-versions = ">=3.6" files = [ - {file = "pypandoc-1.13-py3-none-any.whl", hash = "sha256:4c7d71bf2f1ed122aac287113b5c4d537a33bbc3c1df5aed11a7d4a7ac074681"}, - {file = "pypandoc-1.13.tar.gz", hash = "sha256:31652073c7960c2b03570bd1e94f602ca9bc3e70099df5ead4cea98ff5151c1e"}, + {file = "pypandoc-1.14-py3-none-any.whl", hash = "sha256:1315c7ad7fac7236dacf69a05b521ed2c3f1d0177f70e9b92bfffce6c023df22"}, + {file = "pypandoc-1.14.tar.gz", hash = "sha256:6b4c45f5f1b9fb5bb562079164806bdbbc3e837b5402bcf3f1139edc5730a197"}, ] [[package]] name = "pyparsing" -version = "3.1.2" +version = "3.2.0" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -optional = false -python-versions = ">=3.6.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, + {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"}, + {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"}, ] [package.extras] @@ -3457,13 +3688,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "8.3.2" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, - {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -3479,33 +3710,33 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-benchmark" -version = "4.0.0" +version = "5.1.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "pytest-benchmark-4.0.0.tar.gz", hash = "sha256:fb0785b83efe599a6a956361c0691ae1dbb5318018561af10f3e915caa0048d1"}, - {file = "pytest_benchmark-4.0.0-py3-none-any.whl", hash = "sha256:fdb7db64e31c8b277dff9850d2a2556d8b60bcb0ea6524e36e28ffd7c87f71d6"}, + {file = "pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105"}, + {file = "pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89"}, ] [package.dependencies] py-cpuinfo = "*" -pytest = ">=3.8" +pytest = ">=8.1" [package.extras] aspect = ["aspectlib"] elasticsearch = ["elasticsearch"] -histogram = ["pygal", "pygaljs"] +histogram = ["pygal", "pygaljs", "setuptools"] [[package]] name = "pytest-cases" -version = "3.8.5" +version = "3.8.6" description = "Separate test code from test cases in pytest." optional = false python-versions = "*" files = [ - {file = "pytest-cases-3.8.5.tar.gz", hash = "sha256:c92054187847a7d30d8ab6709fb1670a830e4e19234be700c85c96b6d7102c16"}, - {file = "pytest_cases-3.8.5-py2.py3-none-any.whl", hash = "sha256:0c8e3727f6494e65b47321adbd94c767277c7b5de1bd40dd518a13f7443b1fff"}, + {file = "pytest_cases-3.8.6-py2.py3-none-any.whl", hash = "sha256:564c722492ea7e7ec3ac433fd14070180e65866f49fa35bfe938c0d5d9afba67"}, + {file = "pytest_cases-3.8.6.tar.gz", hash = "sha256:5c24e0ab0cb6f8e802a469b7965906a333d3babb874586ebc56f7e2cbe1a7c44"}, ] [package.dependencies] @@ -3515,17 +3746,17 @@ packaging = "*" [[package]] name = "pytest-cov" -version = "5.0.0" +version = "6.0.0" description = "Pytest plugin for measuring coverage." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, - {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, + {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, + {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, ] [package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} +coverage = {version = ">=7.5", extras = ["toml"]} pytest = ">=4.6" [package.extras] @@ -3549,7 +3780,7 @@ six = ">=1.5" name = "python-json-logger" version = "2.0.7" description = "A python library adding a json log formatter" -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, @@ -3558,20 +3789,20 @@ files = [ [[package]] name = "pyvista" -version = "0.44.1" +version = "0.44.2" description = "Easier Pythonic interface to VTK" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "pyvista-0.44.1-py3-none-any.whl", hash = "sha256:7a80e8114220ca36d57a4def8e6a3067c908b53b62aa426ea76c76069bb6d1c0"}, - {file = "pyvista-0.44.1.tar.gz", hash = "sha256:63976f5d57d151b3f7e1616dde40dcf56a66d1f37f6db067087fa9cc9667f512"}, + {file = "pyvista-0.44.2-py3-none-any.whl", hash = "sha256:8530d35f57cdaa33507ac9aec19e3292be0bc9026b28e4f12df7e8054438d028"}, + {file = "pyvista-0.44.2.tar.gz", hash = "sha256:db65943c3c1c9ba49fe16f5a25a5bc23b74bf1ea7a38aae4ef9c4dc5838ccf5e"}, ] [package.dependencies] ipywidgets = {version = "*", optional = true, markers = "extra == \"jupyter\""} jupyter-server-proxy = {version = "*", optional = true, markers = "extra == \"jupyter\""} matplotlib = ">=3.0.1" -nest-asyncio = {version = "*", optional = true, markers = "extra == \"jupyter\""} +nest_asyncio = {version = "*", optional = true, markers = "extra == \"jupyter\""} numpy = ">=1.21.0" pillow = "*" pooch = "*" @@ -3582,50 +3813,54 @@ trame-server = {version = ">=2.11.7", optional = true, markers = "extra == \"jup trame-vtk = {version = ">=2.5.8", optional = true, markers = "extra == \"jupyter\""} trame-vuetify = {version = ">=2.3.1", optional = true, markers = "extra == \"jupyter\""} typing-extensions = "*" -vtk = "*" +vtk = "<9.4.0" [package.extras] all = ["pyvista[colormaps,io,jupyter]"] colormaps = ["cmocean", "colorcet"] io = ["imageio", "meshio (>=5.2)"] -jupyter = ["ipywidgets", "jupyter-server-proxy", "nest-asyncio", "trame (>=2.5.2)", "trame-client (>=2.12.7)", "trame-server (>=2.11.7)", "trame-vtk (>=2.5.8)", "trame-vuetify (>=2.3.1)"] +jupyter = ["ipywidgets", "jupyter-server-proxy", "nest_asyncio", "trame (>=2.5.2)", "trame-client (>=2.12.7)", "trame-server (>=2.11.7)", "trame-vtk (>=2.5.8)", "trame-vuetify (>=2.3.1)"] [[package]] name = "pywin32" -version = "306" +version = "308" description = "Python for Window Extensions" optional = false python-versions = "*" files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, - {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, - {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, - {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, + {file = "pywin32-308-cp310-cp310-win32.whl", hash = "sha256:796ff4426437896550d2981b9c2ac0ffd75238ad9ea2d3bfa67a1abd546d262e"}, + {file = "pywin32-308-cp310-cp310-win_amd64.whl", hash = "sha256:4fc888c59b3c0bef905ce7eb7e2106a07712015ea1c8234b703a088d46110e8e"}, + {file = "pywin32-308-cp310-cp310-win_arm64.whl", hash = "sha256:a5ab5381813b40f264fa3495b98af850098f814a25a63589a8e9eb12560f450c"}, + {file = "pywin32-308-cp311-cp311-win32.whl", hash = "sha256:5d8c8015b24a7d6855b1550d8e660d8daa09983c80e5daf89a273e5c6fb5095a"}, + {file = "pywin32-308-cp311-cp311-win_amd64.whl", hash = "sha256:575621b90f0dc2695fec346b2d6302faebd4f0f45c05ea29404cefe35d89442b"}, + {file = "pywin32-308-cp311-cp311-win_arm64.whl", hash = "sha256:100a5442b7332070983c4cd03f2e906a5648a5104b8a7f50175f7906efd16bb6"}, + {file = "pywin32-308-cp312-cp312-win32.whl", hash = "sha256:587f3e19696f4bf96fde9d8a57cec74a57021ad5f204c9e627e15c33ff568897"}, + {file = "pywin32-308-cp312-cp312-win_amd64.whl", hash = "sha256:00b3e11ef09ede56c6a43c71f2d31857cf7c54b0ab6e78ac659497abd2834f47"}, + {file = "pywin32-308-cp312-cp312-win_arm64.whl", hash = "sha256:9b4de86c8d909aed15b7011182c8cab38c8850de36e6afb1f0db22b8959e3091"}, + {file = "pywin32-308-cp313-cp313-win32.whl", hash = "sha256:1c44539a37a5b7b21d02ab34e6a4d314e0788f1690d65b48e9b0b89f31abbbed"}, + {file = "pywin32-308-cp313-cp313-win_amd64.whl", hash = "sha256:fd380990e792eaf6827fcb7e187b2b4b1cede0585e3d0c9e84201ec27b9905e4"}, + {file = "pywin32-308-cp313-cp313-win_arm64.whl", hash = "sha256:ef313c46d4c18dfb82a2431e3051ac8f112ccee1a34f29c263c583c568db63cd"}, + {file = "pywin32-308-cp37-cp37m-win32.whl", hash = "sha256:1f696ab352a2ddd63bd07430080dd598e6369152ea13a25ebcdd2f503a38f1ff"}, + {file = "pywin32-308-cp37-cp37m-win_amd64.whl", hash = "sha256:13dcb914ed4347019fbec6697a01a0aec61019c1046c2b905410d197856326a6"}, + {file = "pywin32-308-cp38-cp38-win32.whl", hash = "sha256:5794e764ebcabf4ff08c555b31bd348c9025929371763b2183172ff4708152f0"}, + {file = "pywin32-308-cp38-cp38-win_amd64.whl", hash = "sha256:3b92622e29d651c6b783e368ba7d6722b1634b8e70bd376fd7610fe1992e19de"}, + {file = "pywin32-308-cp39-cp39-win32.whl", hash = "sha256:7873ca4dc60ab3287919881a7d4f88baee4a6e639aa6962de25a98ba6b193341"}, + {file = "pywin32-308-cp39-cp39-win_amd64.whl", hash = "sha256:71b3322d949b4cc20776436a9c9ba0eeedcbc9c650daa536df63f0ff111bb920"}, ] [[package]] name = "pywinpty" -version = "2.0.13" +version = "2.0.14" description = "Pseudo terminal support for Windows from Python." -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "pywinpty-2.0.13-cp310-none-win_amd64.whl", hash = "sha256:697bff211fb5a6508fee2dc6ff174ce03f34a9a233df9d8b5fe9c8ce4d5eaf56"}, - {file = "pywinpty-2.0.13-cp311-none-win_amd64.whl", hash = "sha256:b96fb14698db1284db84ca38c79f15b4cfdc3172065b5137383910567591fa99"}, - {file = "pywinpty-2.0.13-cp312-none-win_amd64.whl", hash = "sha256:2fd876b82ca750bb1333236ce98488c1be96b08f4f7647cfdf4129dfad83c2d4"}, - {file = "pywinpty-2.0.13-cp38-none-win_amd64.whl", hash = "sha256:61d420c2116c0212808d31625611b51caf621fe67f8a6377e2e8b617ea1c1f7d"}, - {file = "pywinpty-2.0.13-cp39-none-win_amd64.whl", hash = "sha256:71cb613a9ee24174730ac7ae439fd179ca34ccb8c5349e8d7b72ab5dea2c6f4b"}, - {file = "pywinpty-2.0.13.tar.gz", hash = "sha256:c34e32351a3313ddd0d7da23d27f835c860d32fe4ac814d372a3ea9594f41dde"}, + {file = "pywinpty-2.0.14-cp310-none-win_amd64.whl", hash = "sha256:0b149c2918c7974f575ba79f5a4aad58bd859a52fa9eb1296cc22aa412aa411f"}, + {file = "pywinpty-2.0.14-cp311-none-win_amd64.whl", hash = "sha256:cf2a43ac7065b3e0dc8510f8c1f13a75fb8fde805efa3b8cff7599a1ef497bc7"}, + {file = "pywinpty-2.0.14-cp312-none-win_amd64.whl", hash = "sha256:55dad362ef3e9408ade68fd173e4f9032b3ce08f68cfe7eacb2c263ea1179737"}, + {file = "pywinpty-2.0.14-cp313-none-win_amd64.whl", hash = "sha256:074fb988a56ec79ca90ed03a896d40707131897cefb8f76f926e3834227f2819"}, + {file = "pywinpty-2.0.14-cp39-none-win_amd64.whl", hash = "sha256:5725fd56f73c0531ec218663bd8c8ff5acc43c78962fab28564871b5fce053fd"}, + {file = "pywinpty-2.0.14.tar.gz", hash = "sha256:18bd9529e4a5daf2d9719aa17788ba6013e594ae94c5a0c27e83df3278b0660e"}, ] [[package]] @@ -3692,120 +3927,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.1.0" +version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, - {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, - {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, - {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, - {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, - {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, - {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, - {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, - {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, + {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, + {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, ] [package.dependencies] @@ -3815,7 +4050,7 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} name = "referencing" version = "0.35.1" description = "JSON Referencing + Python" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, @@ -3851,7 +4086,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "rfc3339-validator" version = "0.1.4" description = "A pure python RFC3339 validator" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, @@ -3865,7 +4100,7 @@ six = "*" name = "rfc3986-validator" version = "0.1.1" description = "Pure python rfc3986 validator" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, @@ -3874,121 +4109,108 @@ files = [ [[package]] name = "rpds-py" -version = "0.20.0" +version = "0.21.0" description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, - {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, - {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, + {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, + {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, + {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, + {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, + {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, + {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, + {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, + {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, + {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, + {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, + {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, + {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, + {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, + {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, + {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, + {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, + {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, + {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, + {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, + {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, + {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, + {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, + {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, + {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, + {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, + {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, + {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, + {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, ] [[package]] name = "rsa" version = "4.9" description = "Pure-Python RSA implementation" -optional = false +optional = true python-versions = ">=3.6,<4" files = [ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, @@ -4000,51 +4222,59 @@ pyasn1 = ">=0.1.3" [[package]] name = "scipy" -version = "1.13.1" +version = "1.14.1" description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, - {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, - {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, - {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, - {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, - {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, - {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, - {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, - {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, - {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, - {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, +optional = true +python-versions = ">=3.10" +files = [ + {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, + {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, + {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, + {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, + {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, + {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, + {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, + {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, + {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, + {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, ] [package.dependencies] -numpy = ">=1.22.4,<2.3" +numpy = ">=1.23.5,<2.3" [package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "scooby" version = "0.10.0" description = "A Great Dane turned Python environment detective" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "scooby-0.10.0-py3-none-any.whl", hash = "sha256:0a3d7e304f8ebb16f69ff7f6360c345d7f50b45f2ddbf7c3d18a6a0dc2cb03a6"}, @@ -4058,7 +4288,7 @@ cpu = ["mkl", "psutil"] name = "send2trash" version = "1.8.3" description = "Send file to trash natively under Mac OS X, Windows and Linux" -optional = false +optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" files = [ {file = "Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9"}, @@ -4072,25 +4302,29 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "72.2.0" +version = "75.5.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, - {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, + {file = "setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829"}, + {file = "setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib-metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "simpervisor" version = "1.0.0" description = "Simple async process supervisor" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "simpervisor-1.0.0-py3-none-any.whl", hash = "sha256:3e313318264559beea3f475ead202bc1cd58a2f1288363abb5657d306c5b8388"}, @@ -4115,7 +4349,7 @@ files = [ name = "sniffio" version = "1.3.1" description = "Sniff out which async library your code is running under" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, @@ -4157,59 +4391,39 @@ files = [ [[package]] name = "sphinx" -version = "7.4.7" +version = "8.1.3" description = "Python documentation generator" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, - {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, + {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, + {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, ] [package.dependencies] -alabaster = ">=0.7.14,<0.8.0" +alabaster = ">=0.7.14" babel = ">=2.13" colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} docutils = ">=0.20,<0.22" imagesize = ">=1.3" -importlib-metadata = {version = ">=6.0", markers = "python_version < \"3.10\""} Jinja2 = ">=3.1" packaging = ">=23.0" Pygments = ">=2.17" requests = ">=2.30.0" snowballstemmer = ">=2.2" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" +sphinxcontrib-applehelp = ">=1.0.7" +sphinxcontrib-devhelp = ">=1.0.6" +sphinxcontrib-htmlhelp = ">=2.0.6" +sphinxcontrib-jsmath = ">=1.0.1" +sphinxcontrib-qthelp = ">=1.0.6" sphinxcontrib-serializinghtml = ">=1.1.9" tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=6.0)", "importlib-metadata (>=6.0)", "mypy (==1.10.1)", "pytest (>=6.0)", "ruff (==0.5.2)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-docutils (==0.21.0.20240711)", "types-requests (>=2.30.0)"] +lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] -[[package]] -name = "sphinx-autodoc-typehints" -version = "2.2.3" -description = "Type hints (PEP 484) support for the Sphinx autodoc extension" -optional = false -python-versions = ">=3.9" -files = [ - {file = "sphinx_autodoc_typehints-2.2.3-py3-none-any.whl", hash = "sha256:b7058e8c5831e5598afca1a78fda0695d3291388d954464a6e480c36198680c0"}, - {file = "sphinx_autodoc_typehints-2.2.3.tar.gz", hash = "sha256:fde3d888949bd0a91207cf1e54afda58121dbb4bf1f183d0cc78a0826654c974"}, -] - -[package.dependencies] -sphinx = ">=7.3.5" - -[package.extras] -docs = ["furo (>=2024.1.29)"] -numpy = ["nptyping (>=2.5)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.4.4)", "defusedxml (>=0.7.1)", "diff-cover (>=9)", "pytest (>=8.1.1)", "pytest-cov (>=5)", "sphobjinv (>=2.3.1)", "typing-extensions (>=4.11)"] - [[package]] name = "sphinx-copybutton" version = "0.5.2" @@ -4255,13 +4469,13 @@ theme-sbt = ["sphinx-book-theme (>=1.1,<2.0)"] [[package]] name = "sphinx-gallery" -version = "0.17.1" +version = "0.18.0" description = "A Sphinx extension that builds an HTML gallery of examples from any set of Python scripts." optional = false python-versions = ">=3.8" files = [ - {file = "sphinx_gallery-0.17.1-py3-none-any.whl", hash = "sha256:0a1142a15a9d63169fe7b12167dc028891fb8db31bfc6d7de03ba0d68d591830"}, - {file = "sphinx_gallery-0.17.1.tar.gz", hash = "sha256:c9969abcc5ca8c24496014da8260833b8c3ccdb32c17716b5ba66f2e0a3cc183"}, + {file = "sphinx_gallery-0.18.0-py3-none-any.whl", hash = "sha256:54317366e77b182672797e5b46ab13cca9a27eafc3142c59dc4c211d4afe3420"}, + {file = "sphinx_gallery-0.18.0.tar.gz", hash = "sha256:4b5b5bc305348c01d00cf66ad852cfd2dd8b67f7f32ae3e2820c01557b3f92f9"}, ] [package.dependencies] @@ -4424,7 +4638,7 @@ widechars = ["wcwidth"] name = "terminado" version = "0.18.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, @@ -4443,13 +4657,13 @@ typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] [[package]] name = "tinycss2" -version = "1.3.0" +version = "1.4.0" description = "A tiny CSS parser" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, - {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, + {file = "tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289"}, + {file = "tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7"}, ] [package.dependencies] @@ -4461,13 +4675,13 @@ test = ["pytest", "ruff"] [[package]] name = "tomli" -version = "2.0.1" +version = "2.1.0" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, ] [[package]] @@ -4492,13 +4706,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.5" +version = "4.67.0" description = "Fast, Extensible Progress Meter" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, + {file = "tqdm-4.67.0-py3-none-any.whl", hash = "sha256:0cd8af9d56911acab92182e88d763100d4788bdf421d251616040cc4d44863be"}, + {file = "tqdm-4.67.0.tar.gz", hash = "sha256:fe5a6f95e6fe0b9755e9469b77b9c3cf850048224ecaa8293d7d2d31f97d869a"}, ] [package.dependencies] @@ -4506,6 +4720,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +discord = ["requests"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] @@ -4527,28 +4742,29 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "trame" -version = "3.6.3" +version = "3.7.0" description = "Trame, a framework to build applications in plain Python" -optional = false +optional = true python-versions = "*" files = [ - {file = "trame-3.6.3-py3-none-any.whl", hash = "sha256:c99968dfc39ac898bf6213d558a245cf7020f7f8761c3a617c2e0af56a03c1ad"}, - {file = "trame-3.6.3.tar.gz", hash = "sha256:09c1193eee3a2b1ed5a3dc46db75d8ee1cd1f8da66e4614d583f0ecf02705fc0"}, + {file = "trame-3.7.0-py3-none-any.whl", hash = "sha256:8af7c0d91749a18e6a4098aced90ff9b7aec3a40fa089e85d6ea8b5623353349"}, + {file = "trame-3.7.0.tar.gz", hash = "sha256:c2cc3c81b6be2b480584ecf789397e0925896b0d94eb8e807ae847e91e3b8850"}, ] [package.dependencies] -trame-client = ">=3,<4" -trame-server = ">=3,<4" +trame-client = ">=3.4,<4" +trame-server = ">=3.2.3,<4" +wslink = ">=2.1.3" [[package]] name = "trame-client" -version = "3.2.5" +version = "3.5.0" description = "Internal client of trame" -optional = false +optional = true python-versions = "*" files = [ - {file = "trame-client-3.2.5.tar.gz", hash = "sha256:6f81c2a78389f1962bec38a318b0de47f241dae7778b25dba75fa3ed4d58bd33"}, - {file = "trame_client-3.2.5-py3-none-any.whl", hash = "sha256:023e062175fef67d45a65ac7a367b0e3468f6746c4f8c7a7aa3fe8e82273c62a"}, + {file = "trame-client-3.5.0.tar.gz", hash = "sha256:e472255608e00bbb3683a805b97825e819326abb01ae007c3121606355691c25"}, + {file = "trame_client-3.5.0-py3-none-any.whl", hash = "sha256:5eff22c68859f88fe0ff7ad3d814e95f81c105e1296f292a47006571d9f9659f"}, ] [package.extras] @@ -4556,28 +4772,28 @@ test = ["Pillow", "pixelmatch", "pytest", "pytest-xprocess", "seleniumbase"] [[package]] name = "trame-server" -version = "3.0.3" +version = "3.2.3" description = "Internal server side implementation of trame" -optional = false +optional = true python-versions = "*" files = [ - {file = "trame-server-3.0.3.tar.gz", hash = "sha256:0261bfca4e2807406ae57b8bc5ce200286606ffd8c57d7b866bc7ab45be2398b"}, - {file = "trame_server-3.0.3-py3-none-any.whl", hash = "sha256:6bbce8a48c3bf2ba38fb679596414c66034f0301315da7040b073fc487649c20"}, + {file = "trame-server-3.2.3.tar.gz", hash = "sha256:4b5d38c17f6c2e8a7bd4644a1d45e2bd79df9829c4ae24e987633754748311f2"}, + {file = "trame_server-3.2.3-py3-none-any.whl", hash = "sha256:40a8ca401893ec91e1ee09ccf674a75ce81a4695916e71412d74612ebd045d8f"}, ] [package.dependencies] more-itertools = "*" -wslink = ">=2,<3" +wslink = ">=2.2.1,<3" [[package]] name = "trame-vtk" -version = "2.8.10" +version = "2.8.12" description = "VTK widgets for trame" -optional = false +optional = true python-versions = "*" files = [ - {file = "trame-vtk-2.8.10.tar.gz", hash = "sha256:4d8e38f7c1d5be8eafc28254bd5bd5dc5d4b336a334b944fc9383c9624184b5e"}, - {file = "trame_vtk-2.8.10-py3-none-any.whl", hash = "sha256:0fe850d1358193c1d73c9af87715d7877210df53edf4e4d468ba396c0845765c"}, + {file = "trame-vtk-2.8.12.tar.gz", hash = "sha256:f894e5e48347076dee97bcd745da21a24b421d66486fcefebe4fe8eae76c80c4"}, + {file = "trame_vtk-2.8.12-py3-none-any.whl", hash = "sha256:f8fefd082cfd5c6cf3d7185c6d8b5482e21ecce6031971dbdd8935f1895abc4a"}, ] [package.dependencies] @@ -4585,13 +4801,13 @@ trame-client = "*" [[package]] name = "trame-vuetify" -version = "2.6.2" +version = "2.7.2" description = "Vuetify widgets for trame" -optional = false +optional = true python-versions = "*" files = [ - {file = "trame-vuetify-2.6.2.tar.gz", hash = "sha256:38b6703527dd94fce31b18baf49fc0f74522f615a41268e8c9f26a2d789e7d07"}, - {file = "trame_vuetify-2.6.2-py3-none-any.whl", hash = "sha256:60582e1decbde75a439af40744d16f1a726ad2669cf7ade38291c5e43909be19"}, + {file = "trame-vuetify-2.7.2.tar.gz", hash = "sha256:b1489207c072345250baaea3b9a4b22fdca480bb58b195110eb779b0f60167b3"}, + {file = "trame_vuetify-2.7.2-py3-none-any.whl", hash = "sha256:08d682d6da82b0325e267b0ad7636cf37f4563acf396aafd55a3a0e21b5213b2"}, ] [package.dependencies] @@ -4599,24 +4815,24 @@ trame-client = "*" [[package]] name = "types-protobuf" -version = "5.27.0.20240626" +version = "5.28.3.20241030" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-5.27.0.20240626.tar.gz", hash = "sha256:683ba14043bade6785e3f937a7498f243b37881a91ac8d81b9202ecf8b191e9c"}, - {file = "types_protobuf-5.27.0.20240626-py3-none-any.whl", hash = "sha256:688e8f7e8d9295db26bc560df01fb731b27a25b77cbe4c1ce945647f7024f5c1"}, + {file = "types-protobuf-5.28.3.20241030.tar.gz", hash = "sha256:f7e6b45845d75393fb41c0b3ce82c46d775f9771fae2097414a1dbfe5b51a988"}, + {file = "types_protobuf-5.28.3.20241030-py3-none-any.whl", hash = "sha256:f3dae16adf342d4fb5bb3673cabb22549a6252e5dd66fc52d8310b1a39c64ba9"}, ] [[package]] name = "types-python-dateutil" -version = "2.9.0.20240316" +version = "2.9.0.20241003" description = "Typing stubs for python-dateutil" -optional = false +optional = true python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, + {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, + {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, ] [[package]] @@ -4634,7 +4850,7 @@ files = [ name = "uri-template" version = "1.3.0" description = "RFC 6570 URI Template Processor" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, @@ -4648,7 +4864,7 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake name = "uritemplate" version = "4.1.1" description = "Implementation of RFC 6570 URI Templates" -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, @@ -4657,13 +4873,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -4674,13 +4890,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.3" +version = "20.27.1" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, + {file = "virtualenv-20.27.1-py3-none-any.whl", hash = "sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4"}, + {file = "virtualenv-20.27.1.tar.gz", hash = "sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba"}, ] [package.dependencies] @@ -4696,7 +4912,7 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "vtk" version = "9.3.1" description = "VTK is an open-source toolkit for 3D computer graphics, image processing, and visualization" -optional = false +optional = true python-versions = "*" files = [ {file = "vtk-9.3.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:c41ed344b9cc90ee9dcfc5967815de272985647d0c8e0a57f0e8b4229bc1b0b9"}, @@ -4746,24 +4962,20 @@ files = [ [[package]] name = "webcolors" -version = "24.8.0" +version = "24.11.1" description = "A library for working with the color formats defined by HTML and CSS." -optional = false -python-versions = ">=3.8" +optional = true +python-versions = ">=3.9" files = [ - {file = "webcolors-24.8.0-py3-none-any.whl", hash = "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a"}, - {file = "webcolors-24.8.0.tar.gz", hash = "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d"}, + {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, + {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, ] -[package.extras] -docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["coverage[toml]"] - [[package]] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" -optional = false +optional = true python-versions = "*" files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, @@ -4774,7 +4986,7 @@ files = [ name = "websocket-client" version = "1.8.0" description = "WebSocket client for Python with low level API options" -optional = false +optional = true python-versions = ">=3.8" files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, @@ -4786,26 +4998,121 @@ docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"] optional = ["python-socks", "wsaccel"] test = ["websockets"] +[[package]] +name = "websockets" +version = "13.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = true +python-versions = ">=3.8" +files = [ + {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, + {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, + {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, + {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, + {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, + {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, + {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, + {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, + {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, + {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, + {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, + {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, + {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, + {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, + {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, + {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, + {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, + {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, + {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, + {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, + {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, + {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, + {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, + {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, +] + [[package]] name = "widgetsnbextension" -version = "4.0.11" +version = "4.0.13" description = "Jupyter interactive widgets for Jupyter Notebook" -optional = false +optional = true python-versions = ">=3.7" files = [ - {file = "widgetsnbextension-4.0.11-py3-none-any.whl", hash = "sha256:55d4d6949d100e0d08b94948a42efc3ed6dfdc0e9468b2c4b128c9a2ce3a7a36"}, - {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, + {file = "widgetsnbextension-4.0.13-py3-none-any.whl", hash = "sha256:74b2692e8500525cc38c2b877236ba51d34541e6385eeed5aec15a70f88a6c71"}, + {file = "widgetsnbextension-4.0.13.tar.gz", hash = "sha256:ffcb67bc9febd10234a362795f643927f4e0c05d9342c727b65d2384f8feacb6"}, ] [[package]] name = "wslink" -version = "2.1.2" +version = "2.2.1" description = "Python/JavaScript library for communicating over WebSocket" -optional = false +optional = true python-versions = "*" files = [ - {file = "wslink-2.1.2-py3-none-any.whl", hash = "sha256:d733e2116905d3b68411385bf01901b38cbd69cc29e069f85fc0fbd9702e6d2c"}, - {file = "wslink-2.1.2.tar.gz", hash = "sha256:b03831fb8f98f4196435e3057e0bc1f441adbab62d80c70f85ab5b6f4969cd68"}, + {file = "wslink-2.2.1-py3-none-any.whl", hash = "sha256:bfa1ce14b576f4a4ac4478e74c92a1445000f4fbf33923c98ffd1dc922c993e2"}, + {file = "wslink-2.2.1.tar.gz", hash = "sha256:cf79c7e3abe47f19af641c81f051ef97a2a2cbb96359cf243517afde16764b82"}, ] [package.dependencies] @@ -4817,126 +5124,125 @@ ssl = ["cryptography"] [[package]] name = "yarl" -version = "1.9.4" +version = "1.17.2" description = "Yet another URL library" -optional = false -python-versions = ">=3.7" +optional = true +python-versions = ">=3.9" files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, + {file = "yarl-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:93771146ef048b34201bfa382c2bf74c524980870bb278e6df515efaf93699ff"}, + {file = "yarl-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8281db240a1616af2f9c5f71d355057e73a1409c4648c8949901396dc0a3c151"}, + {file = "yarl-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:170ed4971bf9058582b01a8338605f4d8c849bd88834061e60e83b52d0c76870"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc61b005f6521fcc00ca0d1243559a5850b9dd1e1fe07b891410ee8fe192d0c0"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871e1b47eec7b6df76b23c642a81db5dd6536cbef26b7e80e7c56c2fd371382e"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a58a2f2ca7aaf22b265388d40232f453f67a6def7355a840b98c2d547bd037f"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:736bb076f7299c5c55dfef3eb9e96071a795cb08052822c2bb349b06f4cb2e0a"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fd51299e21da709eabcd5b2dd60e39090804431292daacbee8d3dabe39a6bc0"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:358dc7ddf25e79e1cc8ee16d970c23faee84d532b873519c5036dbb858965795"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:50d866f7b1a3f16f98603e095f24c0eeba25eb508c85a2c5939c8b3870ba2df8"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8b9c4643e7d843a0dca9cd9d610a0876e90a1b2cbc4c5ba7930a0d90baf6903f"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d63123bfd0dce5f91101e77c8a5427c3872501acece8c90df457b486bc1acd47"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4e76381be3d8ff96a4e6c77815653063e87555981329cf8f85e5be5abf449021"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:734144cd2bd633a1516948e477ff6c835041c0536cef1d5b9a823ae29899665b"}, + {file = "yarl-1.17.2-cp310-cp310-win32.whl", hash = "sha256:26bfb6226e0c157af5da16d2d62258f1ac578d2899130a50433ffee4a5dfa673"}, + {file = "yarl-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:76499469dcc24759399accd85ec27f237d52dec300daaca46a5352fcbebb1071"}, + {file = "yarl-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:792155279dc093839e43f85ff7b9b6493a8eaa0af1f94f1f9c6e8f4de8c63500"}, + {file = "yarl-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38bc4ed5cae853409cb193c87c86cd0bc8d3a70fd2268a9807217b9176093ac6"}, + {file = "yarl-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4a8c83f6fcdc327783bdc737e8e45b2e909b7bd108c4da1892d3bc59c04a6d84"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6d5fed96f0646bfdf698b0a1cebf32b8aae6892d1bec0c5d2d6e2df44e1e2d"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:782ca9c58f5c491c7afa55518542b2b005caedaf4685ec814fadfcee51f02493"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff6af03cac0d1a4c3c19e5dcc4c05252411bf44ccaa2485e20d0a7c77892ab6e"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a3f47930fbbed0f6377639503848134c4aa25426b08778d641491131351c2c8"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1fa68a3c921365c5745b4bd3af6221ae1f0ea1bf04b69e94eda60e57958907f"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:187df91395c11e9f9dc69b38d12406df85aa5865f1766a47907b1cc9855b6303"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:93d1c8cc5bf5df401015c5e2a3ce75a5254a9839e5039c881365d2a9dcfc6dc2"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:11d86c6145ac5c706c53d484784cf504d7d10fa407cb73b9d20f09ff986059ef"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c42774d1d1508ec48c3ed29e7b110e33f5e74a20957ea16197dbcce8be6b52ba"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8e589379ef0407b10bed16cc26e7392ef8f86961a706ade0a22309a45414d7"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1056cadd5e850a1c026f28e0704ab0a94daaa8f887ece8dfed30f88befb87bb0"}, + {file = "yarl-1.17.2-cp311-cp311-win32.whl", hash = "sha256:be4c7b1c49d9917c6e95258d3d07f43cfba2c69a6929816e77daf322aaba6628"}, + {file = "yarl-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:ac8eda86cc75859093e9ce390d423aba968f50cf0e481e6c7d7d63f90bae5c9c"}, + {file = "yarl-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dd90238d3a77a0e07d4d6ffdebc0c21a9787c5953a508a2231b5f191455f31e9"}, + {file = "yarl-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c74f0b0472ac40b04e6d28532f55cac8090e34c3e81f118d12843e6df14d0909"}, + {file = "yarl-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d486ddcaca8c68455aa01cf53d28d413fb41a35afc9f6594a730c9779545876"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25b7e93f5414b9a983e1a6c1820142c13e1782cc9ed354c25e933aebe97fcf2"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a0baff7827a632204060f48dca9e63fbd6a5a0b8790c1a2adfb25dc2c9c0d50"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:460024cacfc3246cc4d9f47a7fc860e4fcea7d1dc651e1256510d8c3c9c7cde0"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5870d620b23b956f72bafed6a0ba9a62edb5f2ef78a8849b7615bd9433384171"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2941756754a10e799e5b87e2319bbec481ed0957421fba0e7b9fb1c11e40509f"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9611b83810a74a46be88847e0ea616794c406dbcb4e25405e52bff8f4bee2d0a"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cd7e35818d2328b679a13268d9ea505c85cd773572ebb7a0da7ccbca77b6a52e"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6b981316fcd940f085f646b822c2ff2b8b813cbd61281acad229ea3cbaabeb6b"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:688058e89f512fb7541cb85c2f149c292d3fa22f981d5a5453b40c5da49eb9e8"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56afb44a12b0864d17b597210d63a5b88915d680f6484d8d202ed68ade38673d"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:17931dfbb84ae18b287279c1f92b76a3abcd9a49cd69b92e946035cff06bcd20"}, + {file = "yarl-1.17.2-cp312-cp312-win32.whl", hash = "sha256:ff8d95e06546c3a8c188f68040e9d0360feb67ba8498baf018918f669f7bc39b"}, + {file = "yarl-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:4c840cc11163d3c01a9d8aad227683c48cd3e5be5a785921bcc2a8b4b758c4f3"}, + {file = "yarl-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3294f787a437cb5d81846de3a6697f0c35ecff37a932d73b1fe62490bef69211"}, + {file = "yarl-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1e7fedb09c059efee2533119666ca7e1a2610072076926fa028c2ba5dfeb78c"}, + {file = "yarl-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da9d3061e61e5ae3f753654813bc1cd1c70e02fb72cf871bd6daf78443e9e2b1"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91c012dceadc695ccf69301bfdccd1fc4472ad714fe2dd3c5ab4d2046afddf29"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f11fd61d72d93ac23718d393d2a64469af40be2116b24da0a4ca6922df26807e"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46c465ad06971abcf46dd532f77560181387b4eea59084434bdff97524444032"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef6eee1a61638d29cd7c85f7fd3ac7b22b4c0fabc8fd00a712b727a3e73b0685"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4434b739a8a101a837caeaa0137e0e38cb4ea561f39cb8960f3b1e7f4967a3fc"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:752485cbbb50c1e20908450ff4f94217acba9358ebdce0d8106510859d6eb19a"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:17791acaa0c0f89323c57da7b9a79f2174e26d5debbc8c02d84ebd80c2b7bff8"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5c6ea72fe619fee5e6b5d4040a451d45d8175f560b11b3d3e044cd24b2720526"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db5ac3871ed76340210fe028f535392f097fb31b875354bcb69162bba2632ef4"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7a1606ba68e311576bcb1672b2a1543417e7e0aa4c85e9e718ba6466952476c0"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9bc27dd5cfdbe3dc7f381b05e6260ca6da41931a6e582267d5ca540270afeeb2"}, + {file = "yarl-1.17.2-cp313-cp313-win32.whl", hash = "sha256:52492b87d5877ec405542f43cd3da80bdcb2d0c2fbc73236526e5f2c28e6db28"}, + {file = "yarl-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:8e1bf59e035534ba4077f5361d8d5d9194149f9ed4f823d1ee29ef3e8964ace3"}, + {file = "yarl-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c556fbc6820b6e2cda1ca675c5fa5589cf188f8da6b33e9fc05b002e603e44fa"}, + {file = "yarl-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f2f44a4247461965fed18b2573f3a9eb5e2c3cad225201ee858726cde610daca"}, + {file = "yarl-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a3ede8c248f36b60227eb777eac1dbc2f1022dc4d741b177c4379ca8e75571a"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2654caaf5584449d49c94a6b382b3cb4a246c090e72453493ea168b931206a4d"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d41c684f286ce41fa05ab6af70f32d6da1b6f0457459a56cf9e393c1c0b2217"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2270d590997445a0dc29afa92e5534bfea76ba3aea026289e811bf9ed4b65a7f"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18662443c6c3707e2fc7fad184b4dc32dd428710bbe72e1bce7fe1988d4aa654"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75ac158560dec3ed72f6d604c81090ec44529cfb8169b05ae6fcb3e986b325d9"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1fee66b32e79264f428dc8da18396ad59cc48eef3c9c13844adec890cd339db5"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:585ce7cd97be8f538345de47b279b879e091c8b86d9dbc6d98a96a7ad78876a3"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c019abc2eca67dfa4d8fb72ba924871d764ec3c92b86d5b53b405ad3d6aa56b0"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c6e659b9a24d145e271c2faf3fa6dd1fcb3e5d3f4e17273d9e0350b6ab0fe6e2"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:d17832ba39374134c10e82d137e372b5f7478c4cceeb19d02ae3e3d1daed8721"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bc3003710e335e3f842ae3fd78efa55f11a863a89a72e9a07da214db3bf7e1f8"}, + {file = "yarl-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f5ffc6b7ace5b22d9e73b2a4c7305740a339fbd55301d52735f73e21d9eb3130"}, + {file = "yarl-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:48e424347a45568413deec6f6ee2d720de2cc0385019bedf44cd93e8638aa0ed"}, + {file = "yarl-1.17.2-py3-none-any.whl", hash = "sha256:dd7abf4f717e33b7487121faf23560b3a50924f80e4bef62b22dab441ded8f3b"}, + {file = "yarl-1.17.2.tar.gz", hash = "sha256:753eaaa0c7195244c84b5cc159dc8204b7fd99f716f11198f999f2332a86b178"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +propcache = ">=0.2.0" [[package]] name = "zipp" -version = "3.20.0" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, - {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [extras] +all = ["ansys-dpf-composites", "ansys-dpf-core", "ansys-mapdl-core", "ansys-mechanical-core", "matplotlib", "pyvista", "scipy"] examples = ["ansys-dpf-composites", "ansys-mapdl-core", "ansys-mechanical-core", "matplotlib", "scipy"] +plotting = ["pyvista"] [metadata] lock-version = "2.0" -python-versions = ">=3.9,<3.13" -content-hash = "8528b628233a1c28d985edc6e5e09888e8ae62c32bdf7941bbf60b8f73d684a2" +python-versions = ">=3.10,<3.13" +content-hash = "549b44884d6ece4316b4e6bf59f1c9387463f2e8601053235e4419818e488986" diff --git a/pyproject.toml b/pyproject.toml index 01f5ff50c8..21f128b98f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "ansys-acp-core" -version = "0.1b2" +version = "0.1rc1" description = "Python library for ACP - Ansys Composite PrepPost" readme = "README.rst" @@ -19,13 +19,13 @@ include = ["src/**/docker-compose.yaml"] # Less than critical but helpful classifiers = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python :: 3.9", + "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", + "Topic :: Scientific/Engineering", ] [tool.poetry.urls] @@ -33,23 +33,24 @@ Issues = "https://github.com/ansys/pyacp/issues" Releases = "https://github.com/ansys/pyacp/releases" [tool.poetry.dependencies] -python = ">=3.9,<3.13" +python = ">=3.10,<3.13" numpy = ">=1.22" grpcio-health-checking = ">=1.43" packaging = ">=15.0" typing-extensions = ">=4.5.0" -ansys-api-acp = "^0.1.dev9" +ansys-api-acp = "==0.2.0" ansys-tools-path = ">=0" ansys-tools-local-product-launcher = ">=0.1" ansys-tools-filetransfer = ">=0.1" -pyvista = ">=0.42.0" +networkx = ">=3.0.0" -# Dependencies for the examples. Update also the 'dev' group when -# these are updated. +# Optional dependencies which are included in the 'extras' section. +# Update also the 'dev' group when these are updated. +pyvista = { version = ">=0.42.0", optional = true, extras = ["jupyter", "trame"] } ansys-mapdl-core = { version = ">=0.68.3", optional = true } -ansys-dpf-composites = { version = ">=0.3", optional = true } -ansys-dpf-core = { version = ">=0.8", optional = true} -ansys-mechanical-core = { version = ">=0.10.0", optional = true, python = "<3.12" } +ansys-dpf-composites = { version = ">=0.6", optional = true } +ansys-dpf-core = { version = ">=0.13", optional = true} +ansys-mechanical-core = { version = ">=0.10.0", optional = true } matplotlib = { version = ">=3.8.3", optional = true } scipy = { version = ">=1.12.0", optional = true } @@ -65,25 +66,14 @@ types-protobuf = ">=4.22.0.2" Sphinx = ">=7.2.6" sphinx-copybutton = ">=0.5.2" -sphinx-autodoc-typehints = ">=2.0.0" numpydoc = ">=1.6.0" ansys-sphinx-theme = ">=1.0.0" pypandoc = ">=1.13" sphinx-gallery = ">=0.15.0" -sphinx-design = ">=0.5.0" # undeclared indirect dependency via PyVista +sphinx-design = ">=0.5.0" # undeclared indirect dependency via PyVista sphinx-jinja = ">=2.0.2" ipykernel = ">=6.22.0" -pyvista = { version = ">=0.42.0", extras = ["jupyter", "trame"] } - -# Repeat optional dependencies from the main group which are -# included in the 'examples' extra. This is done s.t. the install -# flag '--with=dev,test' will install all dependencies. -ansys-mapdl-core = ">=0.68.3" -ansys-dpf-composites = { version = ">=0.3"} -ansys-dpf-core = { version = ">=0.8"} -ansys-mechanical-core = { version = ">=0.10.0", python = "<3.12" } -matplotlib = ">=3.8.3" -scipy = ">=1.12.0" + [tool.poetry.group.test] optional = true @@ -99,6 +89,7 @@ docker = ">=7.0" [tool.poetry.extras] # For the examples, we keep an extra to simplify installing these dependencies for the # end user. +plotting = ["pyvista"] examples = [ "ansys-mapdl-core", "ansys-dpf-composites", @@ -106,6 +97,15 @@ examples = [ "matplotlib", "scipy", ] +all = [ + "pyvista", + "ansys-mapdl-core", + "ansys-dpf-composites", + "ansys-dpf-core", + "ansys-mechanical-core", + "matplotlib", + "scipy", +] [tool.poetry.plugins."ansys.tools.local_product_launcher.launcher"] "ACP.direct" = "ansys.acp.core._server.direct:DirectLauncher" @@ -119,7 +119,7 @@ priority = "primary" [tool.black] line-length = 100 -target-version = ['py38'] +target-version = ['py310'] [tool.isort] profile = "black" @@ -139,7 +139,7 @@ ignore-words-list = 'ans,uptodate,notuptodate,eyt' quiet-level = 3 [tool.mypy] -python_version = "3.9" +python_version = "3.10" mypy_path = "$MYPY_CONFIG_FILE_DIR/src:$MYPY_CONFIG_FILE_DIR/tests" disable_error_code = "type-abstract" show_error_context = true @@ -148,11 +148,14 @@ pretty = true [[tool.mypy.overrides]] module = [ "docker.*", - "grpc.*", "grpc_health.*", + "grpc.*", + "networkx", "scipy.optimize", "ansys.mapdl", "ansys.mapdl.core", + "ansys.mechanical", + "ansys.mechanical.*", "ansys.dpf.core", "ansys.dpf.core.core", "ansys.dpf.core.*", @@ -165,6 +168,7 @@ ignore_missing_imports = true # This section is required even if empty, so that pytest recognizes this # file as a pytest configuration file, and sets the containing directory # as its 'rootdir'. +markers = "plotting" [tool.coverage.run] branch = true diff --git a/src/.bandit b/src/.bandit new file mode 100644 index 0000000000..59de086a34 --- /dev/null +++ b/src/.bandit @@ -0,0 +1,8 @@ +# TODO: move this configuration into the main 'pyproject.toml' file +# once this is supported by the check-vulnerabilities action. + +# Skipping B101:assert_used. Assert statements should not be used to +# check for 'regular' error conditions, but rather for detecting internal +# errors that should never occur. +[bandit] +skips = B101 diff --git a/src/ansys/acp/core/__init__.py b/src/ansys/acp/core/__init__.py index 1a7291dbf4..16fd100fca 100644 --- a/src/ansys/acp/core/__init__.py +++ b/src/ansys/acp/core/__init__.py @@ -27,11 +27,18 @@ import importlib.metadata -from . import example_helpers, material_property_sets +from . import ( + dpf_integration_helpers, + extras, + material_property_sets, + mechanical_integration_helpers, + mesh_data, +) from ._model_printer import get_model_tree, print_model from ._plotter import get_directions_plotter +from ._recursive_copy import LinkedObjectHandling, recursive_copy from ._server import ( - ACP, + ACPInstance, ConnectLaunchConfig, DirectLaunchConfig, DockerComposeLaunchConfig, @@ -40,42 +47,57 @@ ) from ._tree_objects import ( AnalysisPly, - AnalysisPlyElementalData, - AnalysisPlyNodalData, ArrowType, + BaseElementMaterialHandling, BooleanOperationType, BooleanSelectionRule, - BooleanSelectionRuleElementalData, - BooleanSelectionRuleNodalData, + ButtJointSequence, CADComponent, CADGeometry, - CutoffMaterialType, - CutoffRuleType, - CutoffSelectionRule, - CutoffSelectionRuleElementalData, - CutoffSelectionRuleNodalData, + CoordinateTransformation, + CutOffGeometry, + CutOffGeometryOrientationType, + CutOffMaterialHandling, + CutOffRuleType, + CutOffSelectionRule, CylindricalSelectionRule, - CylindricalSelectionRuleElementalData, - CylindricalSelectionRuleNodalData, - DimensionType, - DrapingMaterialType, + DrapingMaterialModel, DrapingType, - DropoffMaterialType, + DropOffMaterialHandling, + DropOffSettings, + DropOffType, EdgeSet, EdgeSetType, ElementalDataType, ElementSet, - ElementSetElementalData, - ElementSetNodalData, + ElementTechnology, + ExtrusionGuide, + ExtrusionGuideType, + ExtrusionMethod, + ExtrusionType, Fabric, FabricWithAngle, FeFormat, + FieldDefinition, GeometricalRuleType, GeometricalSelectionRule, - GeometricalSelectionRuleElementalData, - GeometricalSelectionRuleNodalData, + HDF5CompositeCAEImportMode, + HDF5CompositeCAEProjectionMode, IgnorableEntity, + ImportedAnalysisPly, + ImportedModelingGroup, + ImportedModelingPly, + ImportedPlyDrapingType, + ImportedPlyOffsetType, + ImportedPlyThicknessType, + ImportedProductionPly, + ImportedSolidModel, + ImportedSolidModelExportSettings, + InterfaceLayer, + IntersectionType, Lamina, + LayupMappingObject, + LayupMappingRosetteSelectionMethod, LinkedSelectionRule, LookUpTable1D, LookUpTable1DColumn, @@ -84,113 +106,126 @@ LookUpTable3DInterpolationAlgorithm, LookUpTableColumnValueType, Material, - MeshData, + MeshImportType, Model, - ModelElementalData, ModelingGroup, ModelingPly, - ModelingPlyElementalData, - ModelingPlyNodalData, - ModelNodalData, NodalDataType, OffsetType, OrientedSelectionSet, - OrientedSelectionSetElementalData, - OrientedSelectionSetNodalData, ParallelSelectionRule, - ParallelSelectionRuleElementalData, - ParallelSelectionRuleNodalData, - PlyCutoffType, + PhysicalDimension, + PlyCutOffType, PlyGeometryExportFormat, PlyType, + PrimaryPly, ProductionPly, - ProductionPlyElementalData, - ProductionPlyNodalData, + ReinforcingBehavior, Rosette, RosetteSelectionMethod, RosetteType, - ScalarData, + SamplingPoint, + SectionCut, + SectionCutType, Sensor, SensorType, + ShellMappingProperties, + SnapToGeometry, + SnapToGeometryOrientationType, + SolidElementSet, + SolidMappingProperties, + SolidModel, + SolidModelExportFormat, + SolidModelExportSettings, + SolidModelImportFormat, + SolidModelOffsetDirectionType, + SolidModelSkinExportFormat, SphericalSelectionRule, - SphericalSelectionRuleElementalData, - SphericalSelectionRuleNodalData, Stackup, - StatusType, + Status, + StressStateType, SubLaminate, SubShape, SymmetryType, TaperEdge, ThicknessFieldType, ThicknessType, - TriangleMesh, TubeSelectionRule, - TubeSelectionRuleElementalData, - TubeSelectionRuleNodalData, UnitSystemType, VariableOffsetSelectionRule, - VariableOffsetSelectionRuleElementalData, - VariableOffsetSelectionRuleNodalData, - VectorData, VirtualGeometry, VirtualGeometryDimension, ) -from ._workflow import ACPWorkflow, get_composite_post_processing_files, get_dpf_unit_system __version__ = importlib.metadata.version(__name__.replace(".", "-")) __all__ = [ "__version__", - "ACP", - "ACPWorkflow", + "ACPInstance", "AnalysisPly", - "AnalysisPlyElementalData", - "AnalysisPlyNodalData", "ArrowType", + "BaseElementMaterialHandling", "BooleanOperationType", "BooleanSelectionRule", - "BooleanSelectionRuleElementalData", - "BooleanSelectionRuleNodalData", + "ButtJointSequence", "CADComponent", "CADGeometry", "ConnectLaunchConfig", - "CutoffMaterialType", - "CutoffRuleType", - "CutoffSelectionRule", - "CutoffSelectionRuleElementalData", - "CutoffSelectionRuleNodalData", + "CoordinateTransformation", + "CutOffGeometry", + "CutOffGeometryOrientationType", + "CutOffMaterialHandling", + "CutOffRuleType", + "CutOffSelectionRule", "CylindricalSelectionRule", - "CylindricalSelectionRuleElementalData", - "CylindricalSelectionRuleNodalData", - "DimensionType", "DirectLaunchConfig", "DockerComposeLaunchConfig", - "DrapingMaterialType", + "dpf_integration_helpers", + "DrapingMaterialModel", "DrapingType", - "DropoffMaterialType", + "DropOffMaterialHandling", + "DropOffSettings", + "DropOffType", "EdgeSet", "EdgeSetType", "ElementalDataType", "ElementSet", - "ElementSetElementalData", - "ElementSetNodalData", - "example_helpers", + "ElementTechnology", + "ExportSettings", + "extras", + "ExtrusionGuide", + "ExtrusionGuideType", + "ExtrusionMethod", + "ExtrusionType", "Fabric", "FabricWithAngle", "FeFormat", + "FieldDefinition", "GeometricalRuleType", "GeometricalSelectionRule", - "GeometricalSelectionRuleElementalData", - "GeometricalSelectionRuleNodalData", - "get_composite_post_processing_files", "get_directions_plotter", - "get_dpf_unit_system", "get_model_tree", + "HDF5CompositeCAEImportMode", + "HDF5CompositeCAEProjectionMode", "IgnorableEntity", + "ImportedAnalysisPly", + "ImportedModelingGroup", + "ImportedModelingPly", + "ImportedPlyDrapingType", + "ImportedPlyOffsetType", + "ImportedPlyThicknessType", + "ImportedProductionPly", + "ImportedSolidModel", + "ImportedSolidModelExportSettings", + "InterfaceLayer", + "IntersectionType", "Lamina", "launch_acp", "LaunchMode", + "LayupMappingObject", + "LayupMappingRosetteSelectionMethod", + "LinkedObjectHandling", "LinkedSelectionRule", "LookUpTable1D", "LookUpTable1DColumn", @@ -200,55 +235,57 @@ "LookUpTableColumnValueType", "material_property_sets", "Material", - "MeshData", + "mechanical_integration_helpers", + "mesh_data", + "MeshImportType", "Model", - "ModelElementalData", "ModelingGroup", "ModelingPly", - "ModelingPlyElementalData", - "ModelingPlyNodalData", - "ModelNodalData", "NodalDataType", "OffsetType", "OrientedSelectionSet", - "OrientedSelectionSetElementalData", - "OrientedSelectionSetNodalData", "ParallelSelectionRule", - "ParallelSelectionRuleElementalData", - "ParallelSelectionRuleNodalData", - "PlyCutoffType", + "PhysicalDimension", + "PlyCutOffType", "PlyGeometryExportFormat", "PlyType", + "PrimaryPly", "print_model", "ProductionPly", - "ProductionPlyElementalData", - "ProductionPlyNodalData", + "recursive_copy", + "ReinforcingBehavior", "Rosette", "RosetteSelectionMethod", "RosetteType", - "ScalarData", + "SamplingPoint", + "SectionCut", + "SectionCutType", "Sensor", "SensorType", + "ShellMappingProperties", + "SnapToGeometry", + "SnapToGeometryOrientationType", + "SolidElementSet", + "SolidMappingProperties", + "SolidModel", + "SolidModelExportFormat", + "SolidModelExportSettings", + "SolidModelImportFormat", + "SolidModelOffsetDirectionType", + "SolidModelSkinExportFormat", "SphericalSelectionRule", - "SphericalSelectionRuleElementalData", - "SphericalSelectionRuleNodalData", "Stackup", - "StatusType", + "Status", + "StressStateType", "SubLaminate", "SubShape", "SymmetryType", "TaperEdge", "ThicknessFieldType", "ThicknessType", - "TriangleMesh", "TubeSelectionRule", - "TubeSelectionRuleElementalData", - "TubeSelectionRuleNodalData", "UnitSystemType", "VariableOffsetSelectionRule", - "VariableOffsetSelectionRuleElementalData", - "VariableOffsetSelectionRuleNodalData", - "VectorData", "VirtualGeometry", "VirtualGeometryDimension", ] diff --git a/src/ansys/acp/core/_dependency_graph.py b/src/ansys/acp/core/_dependency_graph.py new file mode 100644 index 0000000000..87f2c259df --- /dev/null +++ b/src/ansys/acp/core/_dependency_graph.py @@ -0,0 +1,108 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from collections.abc import Iterable, Iterator +from dataclasses import dataclass + +import networkx as nx + +from ._tree_objects._grpc_helpers.linked_object_helpers import get_linked_paths +from ._tree_objects._grpc_helpers.mapping import Mapping +from ._tree_objects._grpc_helpers.polymorphic_from_pb import tree_object_from_resource_path +from ._tree_objects.base import CreatableTreeObject, TreeObject + + +@dataclass +class _WalkTreeOptions: + include_children: bool + include_linked_objects: bool + + +def _build_dependency_graph( + *, source_objects: Iterable[CreatableTreeObject], options: _WalkTreeOptions +) -> nx.DiGraph: + """Build a dependency graph of the given objects.""" + graph = nx.DiGraph() + + # We need to manually keep track of which objects have been visited, + # since the node may also be created when being linked to. + visited_objects: set[CreatableTreeObject] = set() + for tree_object in source_objects: + _build_dependency_graph_impl( + tree_object=tree_object, graph=graph, visited_objects=visited_objects, options=options + ) + return graph + + +def _build_dependency_graph_impl( + *, + tree_object: CreatableTreeObject, + graph: nx.DiGraph, + visited_objects: set[CreatableTreeObject], + options: _WalkTreeOptions, +) -> None: + + if tree_object in visited_objects: + return + + visited_objects.add(tree_object) + graph.add_node(tree_object) + + if options.include_children: + for child_object in _yield_child_objects(tree_object): + if not isinstance(child_object, CreatableTreeObject): + continue + graph.add_edge(child_object, tree_object) + _build_dependency_graph_impl( + tree_object=child_object, + graph=graph, + visited_objects=visited_objects, + options=options, + ) + if options.include_linked_objects: + for linked_object in _yield_linked_objects(tree_object): + graph.add_edge(tree_object, linked_object) + _build_dependency_graph_impl( + tree_object=linked_object, + graph=graph, + visited_objects=visited_objects, + options=options, + ) + + +def _yield_child_objects(tree_object: TreeObject) -> Iterator[TreeObject]: + for attr_name in tree_object._GRPC_PROPERTIES: + try: + attr = getattr(tree_object, attr_name) + except (AttributeError, RuntimeError): + continue + if isinstance(attr, Mapping): + yield from attr.values() + + +def _yield_linked_objects(tree_object: TreeObject) -> Iterator[CreatableTreeObject]: + for linked_path in get_linked_paths(tree_object._pb_object.properties): + linked_object = tree_object_from_resource_path( + linked_path, server_wrapper=tree_object._server_wrapper + ) + assert isinstance(linked_object, CreatableTreeObject) + yield linked_object diff --git a/src/ansys/acp/core/_model_printer.py b/src/ansys/acp/core/_model_printer.py index 8c3c38ac34..79fa82f6b1 100644 --- a/src/ansys/acp/core/_model_printer.py +++ b/src/ansys/acp/core/_model_printer.py @@ -21,10 +21,11 @@ # SOFTWARE. import os -from typing import Optional +from ._tree_objects._grpc_helpers.mapping import Mapping +from ._tree_objects.base import TreeObjectBase from ._tree_objects.model import Model -from ._utils.visualization import _replace_underscores_and_capitalize +from ._utils.string_manipulation import replace_underscores_and_capitalize __all__ = ["Node", "print_model", "get_model_tree"] @@ -40,11 +41,11 @@ class Node: Children of the node. """ - def __init__(self, label: str, children: Optional[list["Node"]] = None): + def __init__(self, label: str, children: list["Node"] | None = None): self.label = label self.children: list["Node"] = children if children else [] - def __str__(self, level: Optional[int] = 0) -> str: + def __str__(self, level: int | None = 0) -> str: assert level is not None four_spaces = " " ret = four_spaces * level + self.label + os.linesep @@ -53,95 +54,56 @@ def __str__(self, level: Optional[int] = 0) -> str: return ret -def _add_tree_part( - tree: Node, - container_name: str, - model: Model, -) -> None: - items = list(getattr(model, container_name).items()) - if len(items) == 0: - return - container = Node(_replace_underscores_and_capitalize(container_name)) - tree.children.append(container) - for entity_name, entity in items: - group_node = Node(entity_name) - container.children.append(group_node) - - -def print_model(model: Model) -> None: +def print_model(model: Model, *, hide_empty: bool = True) -> None: """Print a tree representation of the model. Parameters ---------- model: pyACP model + hide_empty : + Whether to hide empty collections. """ - return print(get_model_tree(model)) + return print(get_model_tree(model, hide_empty=hide_empty)) -def get_model_tree(model: Model) -> Node: +def get_model_tree(model: Model, *, hide_empty: bool = True) -> Node: """Get a tree representation of the model. Returns the root node. Parameters ---------- - model: - pyACP model. + model : + ACP model. + hide_empty : + Whether to hide empty collections. """ - model_node = Node("Model") - - material_data = Node("Material Data") - model_node.children.append(material_data) - _add_tree_part(material_data, "materials", model) - _add_tree_part(material_data, "fabrics", model) - _add_tree_part(material_data, "stackups", model) - _add_tree_part(material_data, "sublaminates", model) - - _add_tree_part(model_node, "element_sets", model) - _add_tree_part(model_node, "edge_sets", model) - - geometry = Node("Geometry") - model_node.children.append(geometry) - _add_tree_part(geometry, "cad_geometries", model) - _add_tree_part(geometry, "virtual_geometries", model) - - _add_tree_part(model_node, "rosettes", model) - - lookup_table = Node("Lookup Tables") - model_node.children.append(lookup_table) - _add_tree_part(lookup_table, "lookup_tables_1d", model) - _add_tree_part(lookup_table, "lookup_tables_3d", model) - - selection_rules = Node("Selection Rules") - model_node.children.append(selection_rules) - _add_tree_part(selection_rules, "parallel_selection_rules", model) - _add_tree_part(selection_rules, "cylindrical_selection_rules", model) - _add_tree_part(selection_rules, "spherical_selection_rules", model) - _add_tree_part(selection_rules, "tube_selection_rules", model) - _add_tree_part(selection_rules, "cutoff_selection_rules", model) - _add_tree_part(selection_rules, "geometrical_selection_rules", model) - _add_tree_part(selection_rules, "variable_offset_selection_rules", model) - _add_tree_part(selection_rules, "boolean_selection_rules", model) - - _add_tree_part(model_node, "oriented_selection_sets", model) - - modeling_groups = Node("Modeling Groups") - model_node.children.append(modeling_groups) - for modeling_group_name, modeling_group in model.modeling_groups.items(): - group_node = Node(modeling_group_name) - modeling_groups.children.append(group_node) - for modeling_ply_name, modeling_ply in modeling_group.modeling_plies.items(): - modeling_ply_node = Node(modeling_ply_name) - group_node.children.append(modeling_ply_node) - for production_ply_name, production_ply in modeling_ply.production_plies.items(): - production_ply_node = Node(production_ply_name) - modeling_ply_node.children.append(production_ply_node) - for analysis_ply_name, analysis_ply in production_ply.analysis_plies.items(): - analysis_ply_node = Node(analysis_ply_name) - production_ply_node.children.append(analysis_ply_node) - - _add_tree_part(model_node, "sensors", model) - - return model_node + return _get_model_tree_impl(obj=model, hide_empty=hide_empty) + + +def _get_model_tree_impl(obj: TreeObjectBase, *, hide_empty: bool) -> Node: + obj_node = Node(repr(_name_or_id(obj))) + for attr_name in obj._GRPC_PROPERTIES: + try: + attr = getattr(obj, attr_name) + except (AttributeError, RuntimeError): + continue + if isinstance(attr, Mapping): + collection_node = Node(replace_underscores_and_capitalize(attr_name)) + obj_node.children.append(collection_node) + for child_obj in attr.values(): + collection_node.children.append( + _get_model_tree_impl(child_obj, hide_empty=hide_empty) + ) + if hide_empty and not collection_node.children: + obj_node.children.pop() + return obj_node + + +def _name_or_id(obj: TreeObjectBase) -> str: + try: + return obj.name + except AttributeError: + return obj.id # type: ignore diff --git a/src/ansys/acp/core/_plotter.py b/src/ansys/acp/core/_plotter.py index 511ec68436..62ad06c8df 100644 --- a/src/ansys/acp/core/_plotter.py +++ b/src/ansys/acp/core/_plotter.py @@ -23,13 +23,14 @@ from collections.abc import Sequence from typing import TYPE_CHECKING, Any, Optional -import pyvista - -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover + import pyvista from ansys.acp.core import Model - from ansys.acp.core import VectorData + from ansys.acp.core.mesh_data import MeshData + from ansys.acp.core.mesh_data import VectorData -from ansys.acp.core._utils.visualization import _replace_underscores_and_capitalize +from ._utils.pyvista_import_check import requires_pyvista +from ._utils.string_manipulation import replace_underscores_and_capitalize __all__ = ["get_directions_plotter"] @@ -45,20 +46,27 @@ } +@requires_pyvista def get_directions_plotter( *, model: "Model", + mesh: "MeshData | None" = None, components: Sequence[Optional["VectorData"]], culling_factor: int = 1, length_factor: float = 1.0, **kwargs: Any, -) -> pyvista.Plotter: +) -> "pyvista.Plotter": """Get a pyvista plotter that shows the specified directions on the mesh. Parameters ---------- - model: - ACP Model. + model : + ACP model. Determines the average element size used for scaling the + arrows. Unless explicitly specified, the model also determines the + mesh to plot. + mesh : + Mesh defining the scope of the plot. If not provided, the full mesh + of the model is used. components: List of components to plot. culling_factor : @@ -70,9 +78,13 @@ def get_directions_plotter( kwargs : Keyword arguments passed to the PyVista object constructor. """ + import pyvista + + if mesh is None: + mesh = model.mesh plotter = pyvista.Plotter() - plotter.add_mesh(model.mesh.to_pyvista(), color="white", show_edges=True) + plotter.add_mesh(mesh.to_pyvista(), color="white", show_edges=True) for vector_data in components: if vector_data is None: @@ -80,13 +92,13 @@ def get_directions_plotter( color = _acp_direction_colors.get(vector_data.component_name, "black") plotter.add_mesh( vector_data.get_pyvista_glyphs( - mesh=model.mesh, + mesh=mesh, factor=model.average_element_size * length_factor, culling_factor=culling_factor, **kwargs, ), color=color, - label=_replace_underscores_and_capitalize(vector_data.component_name), + label=replace_underscores_and_capitalize(vector_data.component_name), ) plotter.add_legend(face=None, bcolor=[0.2, 0.2, 0.2], size=(0.25, 0.25)) return plotter diff --git a/src/ansys/acp/core/_recursive_copy.py b/src/ansys/acp/core/_recursive_copy.py new file mode 100644 index 0000000000..c21c5b5e90 --- /dev/null +++ b/src/ansys/acp/core/_recursive_copy.py @@ -0,0 +1,228 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import collections +from collections.abc import Iterable + +import networkx as nx + +from ._dependency_graph import _build_dependency_graph, _WalkTreeOptions +from ._tree_objects import LookUpTable1D, LookUpTable1DColumn, LookUpTable3D, LookUpTable3DColumn +from ._tree_objects._grpc_helpers.linked_object_helpers import get_linked_paths +from ._tree_objects.base import CreatableTreeObject, TreeObject +from ._utils.resource_paths import common_path, to_parts +from ._utils.typing_helper import StrEnum + +__all__ = ["recursive_copy", "LinkedObjectHandling"] + + +class LinkedObjectHandling(StrEnum): + """Defines options for handling linked objects when copying a tree of ACP objects.""" + + KEEP = "keep" + COPY = "copy" + DISCARD = "discard" + + +def recursive_copy( + *, + source_objects: Iterable[CreatableTreeObject], + parent_mapping: dict[TreeObject, TreeObject], + linked_object_handling: LinkedObjectHandling | str = "keep", +) -> dict[CreatableTreeObject, CreatableTreeObject]: + """Recursively copy a tree of ACP objects. + + This function copies a tree of ACP objects, starting from the given source objects. + The copied tree includes all child objects. Linked objects (such as a Fabric linked to + by a Modeling Ply) can optionally be included, controlled by the + ``linked_object_handling`` argument. + + To specify where the new objects should be stored, you must provide a dictionary + in the ``parent_mapping`` argument. The keys of the dictionary are the original + parent objects, and the values are the new parent objects. + Note that this mapping may need to contain parent objects that are not direct parents + of the source objects, if another branch of the tree is included via linked objects. + + The ``parent_mapping`` argument can also include objects which are part of the + ``source_objects`` list. In this case, the function will not create a new object for + the parent, but will use the existing object instead. + + The function returns a ``dict`` mapping the original objects to the newly created + objects. + + .. note:: + + Only attributes supported by PyACP are copied to the new objects. + + Parameters + ---------- + source_objects : + The starting point of the tree to copy. + parent_mapping : + A list of tuples defining where the new objects are stored. Each tuple contains + the original parent object as the first element and the new parent object as the + second element. + linked_object_handling : + Defines how linked objects are handled. An example of a linked object is a Fabric + linked to by a Modeling Ply. + + The following options are available: + + - ``"keep"``: Keep linking to the original objects, and do not + copy them (unless they are otherwise included in the tree). + - ``"copy"``: Copy the linked objects, and replace the links. + - ``"discard"``: Discard object links. + + Note that when copying objects between two models, only the ``"copy"`` and + ``"discard"`` options are valid. If you wish to use links to existing objects, + the ``"copy"`` option can be used, specifying how links should be replaced in + the ``parent_mapping`` argument. + + Returns + ------- + : + A mapping of the newly created objects. The keys are the original objects, + and the values are the new objects. + + Examples + -------- + To copy all Modeling Groups and associated definitions from one model to another, + you can use the following code: + + .. code-block:: python + + import ansys.acp.core as pyacp + + model1 = ... # loaded in some way + model2 = ... # loaded in some way + + pyacp.recursive_copy( + source_objects=model1.modeling_groups.values(), + parent_mapping={model1: model2}, + linked_object_handling="copy", + ) + + To copy all definitions from one model to another, you can use the following code: + + .. code-block:: python + + import ansys.acp.core as pyacp + + model1 = ... # loaded in some way + model2 = ... # loaded in some way + + pyacp.recursive_copy( + source_objects=[model1], + parent_mapping={model1: model2}, + linked_object_handling="copy", + ) + """ + # Check that the given source objects and parent mapping keys belong to the same + # model. + common_source_path = common_path( + *[obj._resource_path.value for obj in list(source_objects) + list(parent_mapping.keys())] + ) + if len(to_parts(common_source_path)) < 2: + raise ValueError( + "The 'source_objects' and 'parent_mapping' keys must all belong to the same model." + ) + common_target_path = common_path(*[obj._resource_path.value for obj in parent_mapping.values()]) + if len(to_parts(common_target_path)) < 2: + raise ValueError("The 'parent_mapping' values must all belong to the same model.") + if linked_object_handling == LinkedObjectHandling.KEEP: + if len(to_parts(common_path(common_source_path, common_target_path))) < 2: + raise ValueError( + "When using 'linked_object_handling=\"keep\"', objects cannot be copied from one model " + "to another. The objects in 'source_objects' and 'parent_mapping' must all belong to the " + 'same model. Use \'linked_object_handling="copy"\' or linked_object_handling="discard"\' ' + "to copy objects between models." + ) + + linked_object_handling = LinkedObjectHandling(linked_object_handling) + + options = _WalkTreeOptions( + include_children=True, + include_linked_objects=linked_object_handling == LinkedObjectHandling.COPY, + ) + # Build up a graph of the objects to clone. Graph edges represent a dependency: + # - from child to parent node + # - from source to target of a link + graph = _build_dependency_graph(source_objects=source_objects, options=options) + + new_object_mapping: dict[CreatableTreeObject, CreatableTreeObject] = {} + replacement_mapping = collections.ChainMap[TreeObject, TreeObject]( + new_object_mapping, parent_mapping # type: ignore + ) + + # keep track of the new resource paths for easy replacement of linked objects + resource_path_replacement_mapping = { + obj._resource_path.value: new_obj._resource_path.value + for obj, new_obj in parent_mapping.items() + } + + # The 'topological_sort' of the graph ensures that each node is only handled + # once its parent and linked objects are stored. + for tree_object in reversed(list(nx.topological_sort(graph))): + if tree_object in replacement_mapping: + # Skip nodes which are already copied (e.g. coming from the parent_mapping) + continue + + if isinstance(tree_object, (LookUpTable1DColumn, LookUpTable3DColumn)): + # handled explicitly while copying the LookUpTable object + if tree_object.name == "Location": + continue + + new_tree_object = tree_object.clone( + unlink=linked_object_handling == LinkedObjectHandling.DISCARD + ) + + # If the linked objects are also copied, replace them with the new objects. + # Otherwise, we can directly store the new object. + if linked_object_handling == LinkedObjectHandling.COPY: + for linked_resource_path in get_linked_paths(new_tree_object._pb_object.properties): + linked_resource_path.value = resource_path_replacement_mapping[ + linked_resource_path.value + ] + + try: + new_parent = replacement_mapping[tree_object.parent] + except KeyError as exc: + raise KeyError( + f"Parent object not found in 'parent_mapping' for object '{tree_object!r}'." + ) from exc + + new_tree_object.store(parent=new_parent) + + # NOTE: if there are more type-specific fixes needed, we may want + # to implement a more generic way to handle these. + # Explicit fix for LookUpTable, since the Location column needs to + # be set correctly s.t. other columns may be stored. + if isinstance(new_tree_object, (LookUpTable1D, LookUpTable3D)): + assert isinstance(tree_object, (LookUpTable1D, LookUpTable3D)) + new_tree_object.columns["Location"].data = tree_object.columns["Location"].data + + new_object_mapping[tree_object] = new_tree_object + resource_path_replacement_mapping[tree_object._resource_path.value] = ( + new_tree_object._resource_path.value + ) + + return new_object_mapping diff --git a/src/ansys/acp/core/_server/__init__.py b/src/ansys/acp/core/_server/__init__.py index b67aa21117..f9a0ce487d 100644 --- a/src/ansys/acp/core/_server/__init__.py +++ b/src/ansys/acp/core/_server/__init__.py @@ -20,7 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from .acp_instance import ACP +from .acp_instance import ACPInstance from .common import LaunchMode from .connect import ConnectLaunchConfig from .direct import DirectLaunchConfig @@ -28,7 +28,7 @@ from .launch import launch_acp __all__ = [ - "ACP", + "ACPInstance", "ConnectLaunchConfig", "DirectLaunchConfig", "DockerComposeLaunchConfig", diff --git a/src/ansys/acp/core/_server/acp_instance.py b/src/ansys/acp/core/_server/acp_instance.py index e790dd3aa4..bbcf848281 100644 --- a/src/ansys/acp/core/_server/acp_instance.py +++ b/src/ansys/acp/core/_server/acp_instance.py @@ -25,6 +25,7 @@ import os import pathlib import shutil +import typing from typing import Any, Generic, Protocol, TypeVar, cast import grpc @@ -33,55 +34,133 @@ from ansys.api.acp.v0.base_pb2 import CollectionPath, DeleteRequest, Empty, ListRequest from ansys.tools.filetransfer import Client as FileTransferClient -from .._tree_objects import Model from .._tree_objects._grpc_helpers.exceptions import wrap_grpc_errors -from .._tree_objects.base import ServerWrapper -from .._typing_helper import PATH as _PATH +from .._utils.typing_helper import PATH as _PATH from .common import ServerProtocol -__all__ = ["ACP"] +if typing.TYPE_CHECKING: # pragma: no cover + from .._tree_objects import Model -class FiletransferStrategy(Protocol): +__all__ = ["ACPInstance"] + + +class FileTransferStrategy(Protocol): def upload_file(self, local_path: _PATH) -> pathlib.PurePath: ... - def download_file(self, remote_filename: _PATH, local_path: _PATH) -> None: ... + def download_file(self, remote_path: _PATH, local_path: _PATH) -> None: ... + + def to_export_path(self, path: _PATH) -> _PATH: ... -class LocalFileTransferStrategy(FiletransferStrategy): +class LocalFileTransferStrategy(FileTransferStrategy): + def __init__(self, working_directory: _PATH) -> None: + self._working_directory = pathlib.Path(working_directory) + def upload_file(self, local_path: _PATH) -> pathlib.Path: - return pathlib.Path(local_path) + return self._get_remote_path(local_path) - def download_file(self, remote_filename: _PATH, local_path: _PATH) -> None: - # TODO: improve the distinction between remote filename and remote path - remote_filename = pathlib.Path(remote_filename) + def download_file(self, remote_path: _PATH, local_path: _PATH) -> None: + remote_path_aslocal = self._get_local_path(remote_path) local_filename = pathlib.Path(local_path) - if local_filename.exists() and local_filename.samefile(remote_filename): + if local_filename.exists() and local_filename.samefile(remote_path_aslocal): return - shutil.copyfile(remote_filename, local_path) + shutil.copyfile(remote_path_aslocal, local_path) + def to_export_path(self, path: _PATH) -> _PATH: + return self._get_remote_path(path) -class RemoteFileTransferStrategy(FiletransferStrategy): + def _get_remote_path(self, path: _PATH) -> pathlib.Path: + """Get the path in the format understood by the server. + + Convert the path from the format understood by the Python client to + the format understood by the server. + """ + return self._get_path_impl(path, pathlib.Path().cwd(), self._working_directory) + + def _get_local_path(self, path: _PATH) -> pathlib.Path: + """Get the path in the format understood by the Python client. + + Convert the path from the format understood by the server to + the format understood by the Python client. + """ + return self._get_path_impl(path, self._working_directory, pathlib.Path().cwd()) + + @staticmethod + def _get_path_impl( + path: _PATH, + initial_working_directory: pathlib.Path, + target_working_directory: pathlib.Path, + ) -> pathlib.Path: + path = pathlib.Path(path) + if path.is_absolute() or initial_working_directory == target_working_directory: + return path + path = (initial_working_directory / path).resolve() + try: + return path.relative_to(target_working_directory) + except ValueError: + # If the path cannot be made relative (e.g. since it is on a different drive), + # return the absolute path. + return path + + +class RemoteFileTransferStrategy(FileTransferStrategy): _ft_client: FileTransferClient def __init__(self, channel: grpc.Channel) -> None: self._ft_client = FileTransferClient(channel) def upload_file(self, local_path: _PATH) -> pathlib.PurePath: - remote_filename = os.path.basename(local_path) - self._ft_client.upload_file(local_filename=str(local_path), remote_filename=remote_filename) - return pathlib.PurePosixPath(remote_filename) + remote_path = os.path.basename(local_path) + self._ft_client.upload_file(local_filename=str(local_path), remote_filename=remote_path) + return pathlib.PurePosixPath(remote_path) - def download_file(self, remote_filename: _PATH, local_path: _PATH) -> None: + def download_file(self, remote_path: _PATH, local_path: _PATH) -> None: self._ft_client.download_file( - remote_filename=str(remote_filename), local_filename=str(local_path) + remote_filename=str(remote_path), local_filename=str(local_path) ) + def to_export_path(self, path: _PATH) -> _PATH: + # Export to the working directory of the server + return pathlib.Path(path).name + + +class FileTransferHandler: + def __init__( + self, filetransfer_strategy: FileTransferStrategy, auto_transfer_files: bool + ) -> None: + self._filetransfer_strategy = filetransfer_strategy + self._auto_transfer_files = auto_transfer_files + + def upload_file_if_autotransfer(self, local_path: _PATH) -> pathlib.PurePath: + if self._auto_transfer_files: + return self.upload_file(local_path) + return pathlib.Path(local_path) + + def download_file_if_autotransfer(self, remote_path: _PATH, local_path: _PATH) -> None: + if self._auto_transfer_files: + self.download_file(remote_path, local_path) + else: + # If auto-transfer is disabled, the export path should be the + # same as the local path. Otherwise, this is a bug in our code. + assert remote_path == local_path + + def to_export_path(self, path: _PATH) -> _PATH: + if self._auto_transfer_files: + return self._filetransfer_strategy.to_export_path(path) + return path + + def upload_file(self, local_path: _PATH) -> pathlib.PurePath: + return self._filetransfer_strategy.upload_file(local_path) + + def download_file(self, remote_path: _PATH, local_path: _PATH) -> None: + self._filetransfer_strategy.download_file(remote_path, local_path) + ServerT = TypeVar("ServerT", bound=ServerProtocol, covariant=True) -class ACP(Generic[ServerT]): +class ACPInstance(Generic[ServerT]): """Control an ACP instance. Supports the following operations to control an ACP instance: @@ -98,7 +177,7 @@ class ACP(Generic[ServerT]): """ _server: ServerT - _filetransfer_strategy: FiletransferStrategy + _filetransfer_handler: FileTransferHandler _channel: grpc.Channel _is_remote: bool @@ -107,12 +186,12 @@ def __init__( *, server: ServerT, channel: grpc.Channel, - filetransfer_strategy: FiletransferStrategy, + filetransfer_handler: FileTransferHandler, is_remote: bool, ) -> None: self._server = server self._channel = channel - self._filetransfer_strategy = filetransfer_strategy + self._filetransfer_handler = filetransfer_handler self._is_remote = is_remote @property @@ -132,9 +211,9 @@ def server_version(self) -> str: def import_model( self, + path: _PATH, *, name: str | None = None, - path: _PATH, format: str = "acp:h5", # pylint: disable=redefined-builtin **kwargs: Any, ) -> Model: @@ -161,8 +240,10 @@ def import_model( into ACP composite definitions. Available only when the format is not ``"acp:h5"``. unit_system: - Set the unit system of the model to the given value. Ignored - if the unit system is already set in the FE file. + Defines the unit system of the imported file. Must be set if the + input file does not have units. If the input file does have units, + ``unit_system`` must be either ``"from_file"``, or match the input + unit system. Available only when the format is not ``"acp:h5"``. Returns @@ -170,6 +251,9 @@ def import_model( : The loaded ``Model`` instance. """ + from .._tree_objects import Model + from .._tree_objects.base import ServerWrapper + server_wrapper = ServerWrapper.from_acp_instance(self) if format == "acp:h5": if kwargs: @@ -180,7 +264,10 @@ def import_model( model = Model._from_file(path=path, server_wrapper=server_wrapper) else: model = Model._from_fe_file( - path=path, server_wrapper=server_wrapper, format=format, **kwargs + path=path, + server_wrapper=server_wrapper, + format=format, + **kwargs, ) if name is not None: model.name = name @@ -192,11 +279,13 @@ def clear(self) -> None: Closes the models which are currently open, without first saving them to a file. """ + from .._tree_objects import Model + model_stub = model_pb2_grpc.ObjectServiceStub(self._channel) - for model in model_stub.List( - ListRequest(collection_path=CollectionPath(value=Model._COLLECTION_LABEL)) - ).objects: - with wrap_grpc_errors(): + with wrap_grpc_errors(): + for model in model_stub.List( + ListRequest(collection_path=CollectionPath(value=Model._COLLECTION_LABEL)) + ).objects: model_stub.Delete( DeleteRequest( resource_path=model.info.resource_path, version=model.info.version @@ -209,6 +298,8 @@ def models(self) -> tuple[Model, ...]: Note that the models are listed in arbitrary order. """ + from .._tree_objects import Model + model_stub = model_pb2_grpc.ObjectServiceStub(self._channel) return tuple( [ @@ -222,6 +313,12 @@ def models(self) -> tuple[Model, ...]: def upload_file(self, local_path: _PATH) -> pathlib.PurePath: """Upload a file to the server. + .. warning:: + + Do not execute this function with untrusted input parameters. + See the :ref:`security guide` + for details. + Parameters ---------- local_path : @@ -232,19 +329,25 @@ def upload_file(self, local_path: _PATH) -> pathlib.PurePath: : The path of the uploaded file on the server. """ - return self._filetransfer_strategy.upload_file(local_path) + return self._filetransfer_handler.upload_file(local_path) - def download_file(self, remote_filename: _PATH, local_path: _PATH) -> None: + def download_file(self, remote_path: _PATH, local_path: _PATH) -> None: """Download a file from the server. + .. warning:: + + Do not execute this function with untrusted input parameters. + See the :ref:`security guide` + for details. + Parameters ---------- - remote_filename : + remote_path : The path of the file on the server. local_path : The path of the file to be downloaded to. """ - self._filetransfer_strategy.download_file(remote_filename, local_path) + self._filetransfer_handler.download_file(remote_path, local_path) def check(self, timeout: float | None = None) -> bool: """Check if the ACP instance is running. diff --git a/src/ansys/acp/core/_server/common.py b/src/ansys/acp/core/_server/common.py index a8e93615c0..5992a4d869 100644 --- a/src/ansys/acp/core/_server/common.py +++ b/src/ansys/acp/core/_server/common.py @@ -26,17 +26,17 @@ import grpc -from .._typing_helper import StrEnum +from .._utils.typing_helper import StrEnum __all__ = ["LaunchMode"] -class ServerKey(StrEnum): # type: ignore +class ServerKey(StrEnum): MAIN = "main" FILE_TRANSFER = "file_transfer" -class LaunchMode(StrEnum): # type: ignore +class LaunchMode(StrEnum): """Available launch modes for ACP.""" DIRECT = "direct" diff --git a/src/ansys/acp/core/_server/direct.py b/src/ansys/acp/core/_server/direct.py index f3be053f28..8190986e6a 100644 --- a/src/ansys/acp/core/_server/direct.py +++ b/src/ansys/acp/core/_server/direct.py @@ -22,8 +22,9 @@ import dataclasses import os -import subprocess -from typing import Optional, TextIO, Union +import pathlib +import subprocess # nosec B404 +from typing import TextIO import grpc @@ -34,33 +35,16 @@ LauncherProtocol, ServerType, ) -from ansys.tools.path import get_available_ansys_installations +from ansys.tools.path import get_latest_ansys_installation from .common import ServerKey __all__ = ["DirectLaunchConfig"] -def _get_latest_ansys_installation() -> str: - """Get the latest installed Ansys installation.""" - - installations = get_available_ansys_installations() - if not installations: - raise ValueError("No Ansys installation found.") - - def sort_key(version_nr: int) -> Union[int, float]: - # prefer regular over student installs - if version_nr < 0: - return abs(version_nr) - 0.5 - return version_nr - - latest_key = max(installations, key=sort_key) - return installations[latest_key] - - def _get_default_binary_path() -> str: try: - ans_root = _get_latest_ansys_installation() + _, ans_root = get_latest_ansys_installation() binary_path = os.path.join(ans_root, "ACP", "acp_grpcserver") if os.name == "nt": binary_path += ".exe" @@ -94,7 +78,7 @@ class DirectLauncher(LauncherProtocol[DirectLaunchConfig]): def __init__(self, *, config: DirectLaunchConfig): self._config = config self._url: str - self._process: subprocess.Popen[str] + self._process: subprocess.Popen[str] | None = None self._stdout: TextIO self._stderr: TextIO @@ -103,11 +87,15 @@ def start(self) -> None: stdout_file = self._config.stdout_file stderr_file = self._config.stderr_file + binary = pathlib.Path(self._config.binary_path) + if not binary.exists(): + raise FileNotFoundError(f"Binary not found: '{binary}'") + port = find_free_ports()[0] self._url = f"localhost:{port}" self._stdout = open(stdout_file, mode="w", encoding="utf-8") self._stderr = open(stderr_file, mode="w", encoding="utf-8") - self._process = subprocess.Popen( + self._process = subprocess.Popen( # nosec B603: documented in 'security_considerations.rst' [ self._config.binary_path, f"--server-address=0.0.0.0:{port}", @@ -117,7 +105,10 @@ def start(self) -> None: text=True, ) - def stop(self, *, timeout: Optional[float] = None) -> None: + def stop(self, *, timeout: float | None = None) -> None: + if self._process is None: + # The process has not been started, and therefore doesn't need to be stopped + return self._process.terminate() try: self._process.wait(timeout=timeout) @@ -127,7 +118,7 @@ def stop(self, *, timeout: Optional[float] = None) -> None: self._stdout.close() self._stderr.close() - def check(self, timeout: Optional[float] = None) -> bool: + def check(self, timeout: float | None = None) -> bool: channel = grpc.insecure_channel(self.urls[ServerKey.MAIN]) return check_grpc_health(channel=channel, timeout=timeout) diff --git a/src/ansys/acp/core/_server/docker-compose.yaml b/src/ansys/acp/core/_server/docker-compose.yaml index 48261b00c9..4fc5287392 100644 --- a/src/ansys/acp/core/_server/docker-compose.yaml +++ b/src/ansys/acp/core/_server/docker-compose.yaml @@ -1,12 +1,11 @@ -version: '3.8' services: acp-grpc-server: restart: unless-stopped - image: ${IMAGE_NAME_PYACP:-ghcr.io/ansys/acp:latest} + image: ${IMAGE_NAME_ACP:-ghcr.io/ansys/acp:latest} environment: - ANSYSLMD_LICENSE_FILE=${ANSYSLMD_LICENSE_FILE} ports: - - "${PORT_PYACP:-50555}:50051" + - "${PORT_ACP:-50555}:50051" working_dir: /home/container/workdir volumes: - "acp_data:/home/container/workdir/" diff --git a/src/ansys/acp/core/_server/docker_compose.py b/src/ansys/acp/core/_server/docker_compose.py index 78df5c0688..3cb8c13eee 100644 --- a/src/ansys/acp/core/_server/docker_compose.py +++ b/src/ansys/acp/core/_server/docker_compose.py @@ -29,8 +29,7 @@ import math import os import pathlib -import subprocess -from typing import Optional +import subprocess # nosec B404 import uuid import grpc @@ -57,14 +56,11 @@ def _get_default_license_server() -> str: return "" -_COMPOSE_FILE_DEFAULT_KEY = "default" - - @dataclasses.dataclass class DockerComposeLaunchConfig: """Configuration options for launching ACP through docker compose.""" - image_name_pyacp: str = dataclasses.field( + image_name_acp: str = dataclasses.field( default="ghcr.io/ansys/acp:latest", metadata={METADATA_KEY_DOC: "Docker image running the ACP gRPC server."}, ) @@ -85,7 +81,7 @@ class DockerComposeLaunchConfig: default=False, metadata={METADATA_KEY_DOC: "If true, keep the volume after docker compose is stopped."}, ) - compose_file: Optional[str] = dataclasses.field( + compose_file: str | None = dataclasses.field( default=None, metadata={ METADATA_KEY_DOC: ( @@ -126,7 +122,7 @@ def __init__(self, *, config: DockerComposeLaunchConfig): self._env = copy.deepcopy(os.environ) self._env.update( - IMAGE_NAME_PYACP=config.image_name_pyacp, + IMAGE_NAME_ACP=config.image_name_acp, IMAGE_NAME_FILETRANSFER=config.image_name_filetransfer, ANSYSLMD_LICENSE_FILE=config.license_server, ) @@ -134,23 +130,27 @@ def __init__(self, *, config: DockerComposeLaunchConfig): self._keep_volume = config.keep_volume if config.compose_file is not None: - self._compose_file: Optional[pathlib.Path] = pathlib.Path(config.compose_file) + self._compose_file: pathlib.Path | None = pathlib.Path(config.compose_file) else: self._compose_file = None try: self._compose_version = parse_version( - subprocess.check_output( + subprocess.check_output( # nosec B603, B607: documented in 'security_considerations.rst' ["docker", "compose", "version", "--short"], text=True - ).replace("-", "+") + ).replace( + "-", "+" + ) ) self._compose_cmds = ["docker", "compose"] except subprocess.CalledProcessError: # If 'docker compose does not work, try 'docker-compose' instead. self._compose_version = parse_version( - subprocess.check_output( + subprocess.check_output( # nosec B603, B607: documented in 'security_considerations.rst' ["docker-compose", "version", "--short"], text=True - ).replace("-", "+") + ).replace( + "-", "+" + ) ) self._compose_cmds = ["docker-compose"] @@ -171,7 +171,7 @@ def start(self) -> None: } env = collections.ChainMap( - {"PORT_PYACP": str(port_acp), "PORT_FILETRANSFER": str(port_ft)}, self._env + {"PORT_ACP": str(port_acp), "PORT_FILETRANSFER": str(port_ft)}, self._env ) # The compose_file may be temporary, in particular if the package is a zipfile. @@ -189,11 +189,11 @@ def start(self) -> None: # The '--wait' flag is only available from version >= 2.1.1 of docker compose: # https://github.com/docker/compose/commit/72e4519cbfb6cdfc600e6ebfa377ce4b8e162c78 cmd.append("--wait") - subprocess.check_call( + subprocess.check_call( # nosec B603: documented in 'security_considerations.rst' cmd, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL ) - def stop(self, *, timeout: Optional[float] = None) -> None: + def stop(self, *, timeout: float | None = None) -> None: # The compose file needs to be passed for all commands with docker-compose 1.X. # With docker-compose 2.X, this no longer seems to be necessary. with self._get_compose_file() as compose_file: @@ -209,9 +209,13 @@ def stop(self, *, timeout: Optional[float] = None) -> None: cmd.extend(["--timeout", str(math.ceil(timeout))]) if not self._keep_volume: cmd.append("--volumes") - subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.check_call( # nosec B603: documented in 'security_considerations.rst' + cmd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) - def check(self, timeout: Optional[float] = None) -> bool: + def check(self, timeout: float | None = None) -> bool: for url in self.urls.values(): channel = grpc.insecure_channel(url) if not check_grpc_health(channel=channel, timeout=timeout): diff --git a/src/ansys/acp/core/_server/launch.py b/src/ansys/acp/core/_server/launch.py index 1609179db0..16e1287423 100644 --- a/src/ansys/acp/core/_server/launch.py +++ b/src/ansys/acp/core/_server/launch.py @@ -22,6 +22,8 @@ from __future__ import annotations +import os + from packaging import version from ansys.tools.local_product_launcher.config import get_launch_mode_for @@ -29,8 +31,9 @@ from ansys.tools.local_product_launcher.launch import launch_product from .acp_instance import ( - ACP, - FiletransferStrategy, + ACPInstance, + FileTransferHandler, + FileTransferStrategy, LocalFileTransferStrategy, RemoteFileTransferStrategy, ) @@ -45,24 +48,41 @@ def launch_acp( config: DirectLaunchConfig | DockerComposeLaunchConfig | None = None, launch_mode: LaunchMode | None = None, timeout: float | None = 30.0, -) -> ACP[ControllableServerProtocol]: + auto_transfer_files: bool = True, +) -> ACPInstance[ControllableServerProtocol]: """Launch an ACP instance. Launch the ACP gRPC server with the given configuration. If no configuration is provided, the configured default is used. + .. warning:: + + Do not execute this function with untrusted input parameters. + See the :ref:`security guide` for details. + Parameters ---------- config : The configuration used for launching ACP. If unspecified, the default for the given launch mode is used. launch_mode : - Specifies which ACP launcher is used. One of ``direct`` or - ``docker_compose``. If unspecified, the configured default is - used. If no default is configured, ``direct`` is used. + Specifies which ACP launcher is used. One of ``direct``, + ``docker_compose``, or ``connect``. If unspecified, the + configured default is used. If no default is configured, + ``direct`` is used. timeout : Timeout to wait until ACP responds. If ``None`` is specified, the check that ACP has started is skipped. + auto_transfer_files : + Determines whether input and output files are automatically + transferred (up- or downloaded) to the server. If ``True``, + files are automatically transferred, and all paths in the + import or export methods are *local* paths. If ``False``, + file transfer needs to be handled manually, and the paths + are relative to the server working directory. + If the ``launch_mode`` is ``"direct"``, this only has an + effect if the current working directory is changed after + launching the server. Returns ------- @@ -76,24 +96,21 @@ def launch_acp( ) # The fallback launch mode for ACP is the direct launch mode. if launch_mode_evaluated in (LaunchMode.DIRECT, FALLBACK_LAUNCH_MODE_NAME): - filetransfer_strategy: FiletransferStrategy = LocalFileTransferStrategy() + filetransfer_strategy: FileTransferStrategy = LocalFileTransferStrategy(os.getcwd()) is_remote = False - elif launch_mode_evaluated == LaunchMode.DOCKER_COMPOSE: - filetransfer_strategy = RemoteFileTransferStrategy( - channel=server_instance.channels[ServerKey.FILE_TRANSFER] - ) - is_remote = True - elif launch_mode_evaluated == LaunchMode.CONNECT: + elif launch_mode_evaluated in (LaunchMode.DOCKER_COMPOSE, LaunchMode.CONNECT): filetransfer_strategy = RemoteFileTransferStrategy( - channel=server_instance.channels[ServerKey.FILE_TRANSFER] + channel=server_instance.channels[ServerKey.FILE_TRANSFER], ) is_remote = True else: raise ValueError("Invalid launch mode for ACP: " + str(launch_mode_evaluated)) - acp = ACP( + acp = ACPInstance( server=server_instance, - filetransfer_strategy=filetransfer_strategy, + filetransfer_handler=FileTransferHandler( + filetransfer_strategy, auto_transfer_files=auto_transfer_files + ), channel=server_instance.channels[ServerKey.MAIN], is_remote=is_remote, ) diff --git a/src/ansys/acp/core/_tree_objects/__init__.py b/src/ansys/acp/core/_tree_objects/__init__.py index 852459051f..7604c9c27f 100644 --- a/src/ansys/acp/core/_tree_objects/__init__.py +++ b/src/ansys/acp/core/_tree_objects/__init__.py @@ -20,19 +20,22 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from ._mesh_data import ScalarData, VectorData +from ._elemental_or_nodal_data import ScalarData, VectorData +from ._mesh_data import MeshData from .analysis_ply import AnalysisPly, AnalysisPlyElementalData, AnalysisPlyNodalData from .boolean_selection_rule import ( BooleanSelectionRule, BooleanSelectionRuleElementalData, BooleanSelectionRuleNodalData, ) +from .butt_joint_sequence import ButtJointSequence, PrimaryPly from .cad_component import CADComponent from .cad_geometry import CADGeometry, TriangleMesh -from .cutoff_selection_rule import ( - CutoffSelectionRule, - CutoffSelectionRuleElementalData, - CutoffSelectionRuleNodalData, +from .cut_off_geometry import CutOffGeometry +from .cut_off_selection_rule import ( + CutOffSelectionRule, + CutOffSelectionRuleElementalData, + CutOffSelectionRuleNodalData, ) from .cylindrical_selection_rule import ( CylindricalSelectionRule, @@ -43,46 +46,90 @@ from .element_set import ElementSet, ElementSetElementalData, ElementSetNodalData from .enums import ( ArrowType, + BaseElementMaterialHandling, BooleanOperationType, - CutoffMaterialType, - CutoffRuleType, - DimensionType, - DrapingMaterialType, + CutOffGeometryOrientationType, + CutOffMaterialHandling, + CutOffRuleType, + DrapingMaterialModel, DrapingType, - DropoffMaterialType, + DropOffMaterialHandling, + DropOffType, EdgeSetType, ElementalDataType, + ElementTechnology, + ExtrusionGuideType, + ExtrusionMethod, + ExtrusionType, GeometricalRuleType, + ImportedPlyDrapingType, + ImportedPlyOffsetType, + ImportedPlyThicknessType, + IntersectionType, LookUpTable3DInterpolationAlgorithm, LookUpTableColumnValueType, + MeshImportType, NodalDataType, OffsetType, - PlyCutoffType, + PhysicalDimension, + PlyCutOffType, PlyGeometryExportFormat, PlyType, + ReinforcingBehavior, RosetteSelectionMethod, RosetteType, + SectionCutType, SensorType, - StatusType, + SnapToGeometryOrientationType, + SolidModelExportFormat, + SolidModelOffsetDirectionType, + SolidModelSkinExportFormat, + Status, + StressStateType, SymmetryType, ThicknessFieldType, ThicknessType, UnitSystemType, VirtualGeometryDimension, ) +from .extrusion_guide import ExtrusionGuide from .fabric import Fabric +from .field_definition import FieldDefinition from .geometrical_selection_rule import ( GeometricalSelectionRule, GeometricalSelectionRuleElementalData, GeometricalSelectionRuleNodalData, ) +from .imported_analysis_ply import ImportedAnalysisPly +from .imported_modeling_group import ImportedModelingGroup +from .imported_modeling_ply import ImportedModelingPly +from .imported_production_ply import ImportedProductionPly +from .imported_solid_model import ( + ImportedSolidModel, + ImportedSolidModelElementalData, + ImportedSolidModelExportSettings, + ImportedSolidModelNodalData, + SolidModelImportFormat, +) +from .interface_layer import InterfaceLayer +from .layup_mapping_object import LayupMappingObject, LayupMappingRosetteSelectionMethod from .linked_selection_rule import LinkedSelectionRule from .lookup_table_1d import LookUpTable1D from .lookup_table_1d_column import LookUpTable1DColumn from .lookup_table_3d import LookUpTable3D from .lookup_table_3d_column import LookUpTable3DColumn from .material import Material -from .model import FeFormat, IgnorableEntity, MeshData, Model, ModelElementalData, ModelNodalData +from .model import ( + FeFormat, + HDF5CompositeCAEImportMode, + HDF5CompositeCAEProjectionMode, + IgnorableEntity, + Model, + ModelElementalData, + ModelNodalData, + ShellMappingProperties, + SolidMappingProperties, +) from .modeling_group import ModelingGroup from .modeling_ply import ModelingPly, ModelingPlyElementalData, ModelingPlyNodalData, TaperEdge from .oriented_selection_set import ( @@ -97,7 +144,22 @@ ) from .production_ply import ProductionPly, ProductionPlyElementalData, ProductionPlyNodalData from .rosette import Rosette +from .sampling_point import SamplingPoint +from .section_cut import SectionCut from .sensor import Sensor +from .snap_to_geometry import SnapToGeometry +from .solid_element_set import ( + SolidElementSet, + SolidElementSetElementalData, + SolidElementSetNodalData, +) +from .solid_model import ( + DropOffSettings, + SolidModel, + SolidModelElementalData, + SolidModelExportSettings, + SolidModelNodalData, +) from .spherical_selection_rule import ( SphericalSelectionRule, SphericalSelectionRuleElementalData, @@ -110,6 +172,7 @@ TubeSelectionRuleElementalData, TubeSelectionRuleNodalData, ) +from .utils import CoordinateTransformation from .variable_offset_selection_rule import ( VariableOffsetSelectionRule, VariableOffsetSelectionRuleElementalData, @@ -122,41 +185,70 @@ "AnalysisPlyElementalData", "AnalysisPlyNodalData", "ArrowType", + "BaseElementMaterialHandling", "BooleanOperationType", "BooleanSelectionRule", "BooleanSelectionRuleElementalData", "BooleanSelectionRuleNodalData", + "ButtJointSequence", "CADComponent", "CADGeometry", - "CutoffMaterialType", - "CutoffRuleType", - "CutoffSelectionRule", - "CutoffSelectionRuleElementalData", - "CutoffSelectionRuleNodalData", + "CoordinateTransformation", + "CutOffGeometry", + "CutOffGeometryOrientationType", + "CutOffMaterialHandling", + "CutOffRuleType", + "CutOffSelectionRule", + "CutOffSelectionRuleElementalData", + "CutOffSelectionRuleNodalData", "CylindricalSelectionRule", "CylindricalSelectionRuleElementalData", "CylindricalSelectionRuleNodalData", - "DimensionType", - "DrapingMaterialType", + "DrapingMaterialModel", "DrapingType", - "DropoffMaterialType", + "DropOffMaterialHandling", + "DropOffSettings", + "DropOffType", "EdgeSet", "EdgeSetType", "ElementalDataType", "ElementSet", "ElementSetElementalData", "ElementSetNodalData", + "ElementTechnology", + "ExtrusionGuide", + "ExtrusionGuideType", + "ExtrusionMethod", + "ExtrusionType", "Fabric", "FabricWithAngle", "FeFormat", + "FieldDefinition", "FieldVariable", "GeometricalRuleType", "GeometricalSelectionRule", "GeometricalSelectionRuleElementalData", "GeometricalSelectionRuleNodalData", + "HDF5CompositeCAEImportMode", + "HDF5CompositeCAEProjectionMode", "IgnorableEntity", + "ImportedAnalysisPly", + "ImportedModelingGroup", + "ImportedModelingPly", + "ImportedPlyDrapingType", + "ImportedPlyOffsetType", + "ImportedPlyThicknessType", + "ImportedProductionPly", + "ImportedSolidModel", + "ImportedSolidModelElementalData", + "ImportedSolidModelExportSettings", + "ImportedSolidModelNodalData", + "InterfaceLayer", "InterpolationOptions", + "IntersectionType", "Lamina", + "LayupMappingObject", + "LayupMappingRosetteSelectionMethod", "LinkedSelectionRule", "LookUpTable1D", "LookUpTable1DColumn", @@ -166,6 +258,7 @@ "LookUpTableColumnValueType", "Material", "MeshData", + "MeshImportType", "Model", "ModelElementalData", "ModelingGroup", @@ -181,24 +274,46 @@ "ParallelSelectionRule", "ParallelSelectionRuleElementalData", "ParallelSelectionRuleNodalData", - "PlyCutoffType", + "PhysicalDimension", + "PlyCutOffType", "PlyGeometryExportFormat", "PlyType", + "PrimaryPly", "ProductionPly", "ProductionPlyElementalData", "ProductionPlyNodalData", "PuckMaterialType", + "ReinforcingBehavior", "Rosette", "RosetteSelectionMethod", "RosetteType", + "SamplingPoint", "ScalarData", + "SectionCut", + "SectionCutType", "Sensor", "SensorType", + "ShellMappingProperties", + "SnapToGeometry", + "SnapToGeometryOrientationType", + "SolidElementSet", + "SolidElementSetElementalData", + "SolidElementSetNodalData", + "SolidMappingProperties", + "SolidModel", + "SolidModelElementalData", + "SolidModelExportFormat", + "SolidModelExportSettings", + "SolidModelImportFormat", + "SolidModelNodalData", + "SolidModelOffsetDirectionType", + "SolidModelSkinExportFormat", "SphericalSelectionRule", "SphericalSelectionRuleElementalData", "SphericalSelectionRuleNodalData", "Stackup", - "StatusType", + "Status", + "StressStateType", "SubLaminate", "SubShape", "SymmetryType", diff --git a/src/ansys/acp/core/_tree_objects/_elemental_or_nodal_data.py b/src/ansys/acp/core/_tree_objects/_elemental_or_nodal_data.py new file mode 100644 index 0000000000..94bee0d680 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/_elemental_or_nodal_data.py @@ -0,0 +1,468 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +import dataclasses +import typing +from typing import Any, ClassVar, Literal, cast + +import numpy as np +import numpy.typing as npt +from typing_extensions import Self + +if typing.TYPE_CHECKING: # pragma: no cover + from pyvista.core.pointset import PolyData, UnstructuredGrid + +from ansys.acp.core._utils.array_conversions import dataarray_to_numpy, to_numpy +from ansys.api.acp.v0 import mesh_query_pb2, mesh_query_pb2_grpc + +from .._utils.property_protocols import ReadOnlyProperty +from .._utils.typing_helper import StrEnum +from ._mesh_data import MeshData +from .base import TreeObject +from .enums import ( + elemental_data_type_from_pb, + elemental_data_type_to_pb, + nodal_data_type_from_pb, + nodal_data_type_to_pb, +) + +__all__ = [ + "ElementalData", + "NodalData", + "elemental_data_property", + "nodal_data_property", + "ScalarData", + "VectorData", +] + + +@dataclasses.dataclass +class _LabelInfo: + mesh_labels: npt.NDArray[np.int32] + data_labels: npt.NDArray[np.int32] + mesh_label_to_index_map: dict[int, int] + + +def _get_labels( + *, + field_names: _LabelAndPyvistaFieldNames, + labels: npt.NDArray[np.int32], + mesh: MeshData, +) -> _LabelInfo: + mesh_labels = getattr(mesh, field_names.LABEL_FIELD_NAME) + mesh_label_to_index_map = {label: idx for idx, label in enumerate(mesh_labels)} + return _LabelInfo( + mesh_labels=mesh_labels, data_labels=labels, mesh_label_to_index_map=mesh_label_to_index_map + ) + + +def _expand_array( + *, + array: npt.NDArray[ScalarDataT], + labels: _LabelInfo, + culling_factor: int = 1, +) -> npt.NDArray[np.float64]: + """Expand the array to the size of the mesh.""" + target_shape = tuple([labels.mesh_labels.size] + list(array.shape[1:])) + target_array = np.ones(target_shape, dtype=np.float64) * np.nan + for idx, (label, value) in enumerate(zip(labels.data_labels, array)): + if idx % culling_factor == 0: + try: + target_array[labels.mesh_label_to_index_map[label]] = value + except KeyError: + pass + return target_array + + +def _get_pyvista_mesh_with_all_data( + *, + mesh_data_base: ElementalOrNodalDataBase, + mesh: MeshData, +) -> UnstructuredGrid: + pv_mesh = mesh.to_pyvista() + + mesh_data_field = getattr( + pv_mesh, mesh_data_base._LABEL_AND_PYVISTA_FIELD_NAMES.PYVISTA_FIELD_NAME + ) + field_labels = getattr( + mesh_data_base, mesh_data_base._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME + ).values + labels = _get_labels( + field_names=mesh_data_base._LABEL_AND_PYVISTA_FIELD_NAMES, mesh=mesh, labels=field_labels + ) + + for name in mesh_data_base._field_names(): + values = getattr(mesh_data_base, name).values + target_array = _expand_array(array=values, labels=labels) + mesh_data_field[name] = target_array + return pv_mesh + + +def _get_mesh_with_scalar_pyvista_data( + *, + labels: npt.NDArray[np.int32], + field_names: _LabelAndPyvistaFieldNames, + mesh: MeshData, + values: npt.NDArray[ScalarDataT], + component_name: str, +) -> UnstructuredGrid: + all_labels = _get_labels(field_names=field_names, labels=labels, mesh=mesh) + + pv_mesh = mesh.to_pyvista() + mesh_data_field = getattr(pv_mesh, field_names.PYVISTA_FIELD_NAME) + + target_array = _expand_array(array=values, labels=all_labels) + component_label = component_name + mesh_data_field[component_label] = target_array + return pv_mesh + + +def _get_pyvista_glyphs( + *, + labels: npt.NDArray[np.int32], + field_names: _LabelAndPyvistaFieldNames, + mesh: MeshData, + values: npt.NDArray[np.float64], + component_name: str, + culling_factor: int = 1, + scaling_factor: float = 1.0, + **kwargs: Any, +) -> PolyData: + all_labels = _get_labels(field_names=field_names, labels=labels, mesh=mesh) + + pv_mesh = mesh.to_pyvista() + mesh_data_field = getattr(pv_mesh, field_names.PYVISTA_FIELD_NAME) + + target_array = _expand_array(array=values, labels=all_labels, culling_factor=culling_factor) + component_label = component_name + mesh_data_field[component_label] = target_array + + magnitude_name = f"{component_label}_magnitude" + mesh_data_field[magnitude_name] = np.linalg.norm(target_array, axis=-1) * scaling_factor + return pv_mesh.glyph(orient=component_label, scale=magnitude_name, **kwargs) # type: ignore + + +ScalarDataT = typing.TypeVar("ScalarDataT", np.float64, np.int32) + + +class ScalarData(typing.Generic[ScalarDataT]): + """Class that encapsulates scalar data.""" + + def __init__( + self, + field_names: _LabelAndPyvistaFieldNames, + labels: npt.NDArray[np.int32], + values: npt.NDArray[ScalarDataT], + component_name: str, + ): + self._field_names = field_names + self._labels = labels + self._values: npt.NDArray[ScalarDataT] = values + self._component_name = component_name + + @property + def values(self) -> npt.NDArray[ScalarDataT]: + """Scalar data values as a numpy array.""" + return self._values + + @property + def component_name(self) -> str: + """Name of the component.""" + return self._component_name + + def get_pyvista_mesh( + self, + mesh: MeshData, + ) -> UnstructuredGrid: + """Convert the mesh data to a PyVista object. + + Parameters + ---------- + mesh : + The mesh to which the data is associated. + """ + return _get_mesh_with_scalar_pyvista_data( + labels=self._labels, + field_names=self._field_names, + mesh=mesh, + values=self._values, + component_name=self._component_name, + ) + + +class VectorData: + """Class that encapsulates vector data.""" + + def __init__( + self, + field_names: _LabelAndPyvistaFieldNames, + labels: npt.NDArray[np.int32], + values: npt.NDArray[np.float64], + component_name: str, + ): + self._field_names = field_names + self._labels = labels + self._values = values + self._component_name = component_name + + @property + def values(self) -> npt.NDArray[np.float64]: + """Vector data values as a numpy array.""" + return self._values + + @property + def component_name(self) -> str: + """Name of the component.""" + return self._component_name + + def get_pyvista_glyphs( + self, + *, + mesh: MeshData, + culling_factor: int = 1, + scaling_factor: float = 1.0, + **kwargs: Any, + ) -> PolyData: + """Get a pyvista glyph object from the vector data. + + Parameters + ---------- + mesh : + The mesh to which the data is associated. + culling_factor : + If set to a value other than ``1``, add only every n-th data + point to the PyVista object. This is useful especially for + vector data, where the arrows can be too dense. + scaling_factor : + Factor to scale the length of the arrows. + kwargs : + Keyword arguments passed to the PyVista object constructor. + """ + return _get_pyvista_glyphs( + labels=self._labels, + field_names=self._field_names, + mesh=mesh, + values=self._values, + component_name=self._component_name, + culling_factor=culling_factor, + scaling_factor=scaling_factor, + **kwargs, + ) + + +def _check_field_type(klass: Any, field_name: str, actual_field_type: str) -> None: + """Check that the type declared in the dataclass (klass) matches the actual type.""" + declared_field_types: typing.Sequence[str] = cast( + typing.Sequence[str], + [field.type for field in dataclasses.fields(klass) if field.name == field_name], + ) + if len(declared_field_types) != 1: + raise RuntimeError("Failed to find field in dataclass.") + declared_field_type = declared_field_types[0].removesuffix(" | None") + if declared_field_type != actual_field_type: + raise RuntimeError( + f"Declared type does not match actual data type. " + f"Declared type: {declared_field_type}, actual type: {actual_field_type}. " + f"Field name: {field_name}" + ) + + +@dataclasses.dataclass +class _LabelAndPyvistaFieldNames: + LABEL_FIELD_NAME: str + PYVISTA_FIELD_NAME: str + + +@dataclasses.dataclass +class ElementalOrNodalDataBase: + """Base class for nodal or elemental mesh data. + + Implements the construction from a protobuf response and the conversion + to a PyVista object. + """ + + _LABEL_AND_PYVISTA_FIELD_NAMES: ClassVar[_LabelAndPyvistaFieldNames] + _FIELD_NAME_FROM_PB_VALUE: ClassVar[typing.Callable[[int], StrEnum]] + _PB_VALUE_FROM_FIELD_NAME: ClassVar[typing.Callable[[StrEnum], int]] + + @classmethod + def _field_names(cls) -> list[str]: + return [ + field.name + for field in dataclasses.fields(cls) + if field.name != cls._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME + ] + + @classmethod + def _from_pb(cls, response: mesh_query_pb2.ElementalData | mesh_query_pb2.NodalData) -> Self: + """Construct a mesh data object from a protobuf response.""" + labels = to_numpy(response.labels) + kwargs: dict[str, Any] = { + cls._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME: ScalarData( + field_names=cls._LABEL_AND_PYVISTA_FIELD_NAMES, + labels=labels, + values=labels, + component_name=cls._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME, + ) + } + for data_type, array in zip(response.data_types, response.data_arrays): + field_name = cls._FIELD_NAME_FROM_PB_VALUE(data_type).value + values = cast( + npt.NDArray[np.float64], dataarray_to_numpy(array, dtype=np.float64) + ) # todo: handle other dtypes + kwargs[field_name] = values + data_wrapper: VectorData | ScalarData[np.float64] + if len(values.shape) == 2 and values.shape[1] == 3: + data_wrapper = VectorData( + field_names=cls._LABEL_AND_PYVISTA_FIELD_NAMES, + labels=labels, + values=values, + component_name=field_name, + ) + _check_field_type(klass=cls, field_name=field_name, actual_field_type="VectorData") + + else: + data_wrapper = ScalarData( + field_names=cls._LABEL_AND_PYVISTA_FIELD_NAMES, + labels=labels, + values=values, + component_name=field_name, + ) + _check_field_type( + klass=cls, field_name=field_name, actual_field_type="ScalarData[np.float64]" + ) + kwargs[field_name] = data_wrapper + + instance = cls(**kwargs) + return instance + + def get_pyvista_mesh( + self, + mesh: MeshData, + ) -> UnstructuredGrid: + """Get a pyvista mesh with all data. + + Parameters + ---------- + mesh : + The mesh to which the data is associated. + """ + return _get_pyvista_mesh_with_all_data(mesh_data_base=self, mesh=mesh) + + +_NODE_FIELD_NAMES = _LabelAndPyvistaFieldNames( + LABEL_FIELD_NAME="node_labels", + PYVISTA_FIELD_NAME="point_data", +) + +_ELEMENT_FIELD_NAMES = _LabelAndPyvistaFieldNames( + LABEL_FIELD_NAME="element_labels", + PYVISTA_FIELD_NAME="cell_data", +) + + +@dataclasses.dataclass +class NodalData(ElementalOrNodalDataBase): + """Base class for nodal data.""" + + node_labels: ScalarData[np.int32] + _LABEL_AND_PYVISTA_FIELD_NAMES = _NODE_FIELD_NAMES + _PB_VALUE_FROM_FIELD_NAME = nodal_data_type_to_pb + _FIELD_NAME_FROM_PB_VALUE = nodal_data_type_from_pb + + +@dataclasses.dataclass +class ElementalData(ElementalOrNodalDataBase): + """Base class for elemental data.""" + + element_labels: ScalarData[np.int32] + _LABEL_AND_PYVISTA_FIELD_NAMES = _ELEMENT_FIELD_NAMES + _PB_VALUE_FROM_FIELD_NAME = elemental_data_type_to_pb + _FIELD_NAME_FROM_PB_VALUE = elemental_data_type_from_pb + + +T = typing.TypeVar("T", bound=ElementalOrNodalDataBase) + + +ElementalDataT = typing.TypeVar("ElementalDataT", bound=ElementalData) + + +def elemental_data_property( + wrapped_cls: type[ElementalDataT], +) -> ReadOnlyProperty[ElementalDataT]: + """Create a property to get elemental data from a tree object.""" + return _mesh_data_property_impl( + wrapped_cls=wrapped_cls, + request_name="GetElementalData", + request_type=mesh_query_pb2.GetElementalDataRequest, + ) + + +NodalDataT = typing.TypeVar("NodalDataT", bound=NodalData) + + +def nodal_data_property( + wrapped_cls: type[NodalDataT], +) -> ReadOnlyProperty[NodalDataT]: + """Create a property to get nodal data from a tree object.""" + return _mesh_data_property_impl( + wrapped_cls=wrapped_cls, + request_name="GetNodalData", + request_type=mesh_query_pb2.GetNodalDataRequest, + ) + + +MeshDataT = typing.TypeVar("MeshDataT", bound=ElementalOrNodalDataBase) + + +def _mesh_data_property_impl( + wrapped_cls: type[MeshDataT], + request_name: Literal["GetNodalData", "GetElementalData"], + request_type: ( + type[mesh_query_pb2.GetNodalDataRequest] | type[mesh_query_pb2.GetElementalDataRequest] + ), +) -> ReadOnlyProperty[MeshDataT]: + """Create a mesh data property. + + Implementation of the mesh data property helpers ``nodal_data_property`` + and ``elemental_data_property``. + """ + + def getter(self: TreeObject) -> MeshDataT: + if not self._is_stored: + raise RuntimeError("Cannot get mesh data from an unstored object") + stub = mesh_query_pb2_grpc.MeshQueryServiceStub(self._channel) + request_func = getattr(stub, request_name) + response = request_func( + request=request_type( + resource_path=self._resource_path, + data_types=[ + wrapped_cls._PB_VALUE_FROM_FIELD_NAME(name) # type: ignore + for name in wrapped_cls._field_names() + ], + ), + ) + return wrapped_cls._from_pb(response) + + return property(getter) diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/edge_property_list.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/edge_property_list.py index 032abbb79f..0b284b1347 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/edge_property_list.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/edge_property_list.py @@ -22,17 +22,19 @@ from __future__ import annotations -from collections.abc import Iterable, Iterator, MutableSequence +from collections.abc import Callable, Iterable, Iterator, MutableSequence +import inspect import sys import textwrap -from typing import Any, Callable, Protocol, TypeVar, cast, overload +from typing import Any, Concatenate, Protocol, TypeVar, cast, overload from google.protobuf.message import Message -from typing_extensions import Concatenate, ParamSpec, Self +from typing_extensions import ParamSpec, Self from .._object_cache import ObjectCacheMixin, constructor_with_cache from ..base import CreatableTreeObject from .property_helper import _exposed_grpc_property, _wrap_doc, grpc_data_getter, grpc_data_setter +from .protocols import GrpcObjectBase __all__ = [ "EdgePropertyList", @@ -42,7 +44,7 @@ ] -class GenericEdgePropertyType(Protocol): +class GenericEdgePropertyType(GrpcObjectBase, Protocol): """Protocol for the definition of ACP edge properties such as FabricWithAngle.""" def __init__(self, *kwargs: Any) -> None: ... @@ -61,6 +63,10 @@ def _check(self) -> bool: ... def _set_callback_apply_changes(self, callback_apply_changes: Callable[[], None]) -> None: ... + def clone(self) -> Self: + """Create a new unstored object with the same properties.""" + raise NotImplementedError + ValueT = TypeVar("ValueT", bound=GenericEdgePropertyType) @@ -170,7 +176,7 @@ def _object_list(self) -> list[ValueT]: # There are two scenarios when the parent object becomes # stored: # - The _object_list already contained some values. In this case, we - # simply keep it, and make a (inexaustive) check that the size + # simply keep it, and make a (inexhaustive) check that the size # matches. # - The parent object was default-constructed and then its _pb_object # was then replaced (e.g. by a call to _from_object_info). In this @@ -441,9 +447,21 @@ def inner(self: ParentT, /, *args: P.args, **kwargs: P.kwargs) -> ValueT: f"""\ Add a {value_type.__name__} to the {parent_class_name}. - See :class:`.{value_type.__name__}` for the available parameters. """ ) + found_parameters = False + if value_type.__doc__ is not None: + doc_lines = textwrap.dedent(value_type.__doc__).splitlines() + if "Parameters" in doc_lines: + doc_lines = doc_lines[doc_lines.index("Parameters") :] + inner.__doc__ += "\n".join(doc_lines) + found_parameters = True + if not found_parameters: + inner.__doc__ += "See :class:`" + value_type.__name__ + "` for the available parameters.\n" + + parameters = [inspect.signature(inner).parameters["self"]] + parameters.extend(inspect.signature(value_type).parameters.values()) + inner.__signature__ = inspect.Signature(parameters, return_annotation=value_type) # type: ignore inner.__name__ = func_name inner.__qualname__ = f"{parent_class_name}.{func_name}" diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/enum_wrapper.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/enum_wrapper.py index d1cae9bb9a..d4f10ed147 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/enum_wrapper.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/enum_wrapper.py @@ -20,15 +20,19 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Any, Callable +from collections.abc import Callable, Mapping +import types +from typing import Any __all__ = ["wrap_to_string_enum"] -from ansys.acp.core._typing_helper import StrEnum - +from ansys.acp.core._utils.typing_helper import StrEnum # mypy doesn't understand this dynamically created Enum, so we have to # fall back to 'Any'. +_StrEnumT = Any + + def wrap_to_string_enum( class_name: str, proto_enum: Any, @@ -37,11 +41,30 @@ def wrap_to_string_enum( key_converter: Callable[[str], str] = lambda val: val, value_converter: Callable[[str], str] = lambda val: val.lower(), doc: str, -) -> tuple[StrEnum, Callable[[StrEnum], int], Callable[[int], StrEnum]]: + explicit_value_list: tuple[int, ...] | None = None, + extra_aliases: Mapping[str, tuple[str, str]] = types.MappingProxyType({}), +) -> tuple[_StrEnumT, Callable[[_StrEnumT], int], Callable[[int], _StrEnumT]]: """Create a string Enum with the same keys as the given protobuf Enum. Values of the enum are the keys, converted to lowercase. + Parameters + ---------- + key_converter : + A callable which converts the protobuf field names to the string enum field names. + value_converter : + A callable which converts the protobuf field names to the string enum values. + doc : + The docstring of the enum. + explicit_value_list : + A list of values that should be included in the enum. If None, all values are included. + extra_aliases : + Allows defining additional fields in the enum which correspond to the same protobuf value. + The keys are the primary enum field values, and the values are tuples of the alias field name + and the alias field value. + Note that the alias will not be used when converting from the protobuf value to the string + enum: the primary field name will be used instead. + Returns ------- : @@ -54,19 +77,26 @@ def wrap_to_string_enum( to_pb_conversion_dict: dict[Any, int] = {} from_pb_conversion_dict: dict[int, Any] = {} for key, pb_value in proto_enum.items(): + if explicit_value_list is not None: + if pb_value not in explicit_value_list: + continue enum_key = key_converter(key) enum_value = value_converter(key) fields.append((enum_key, enum_value)) to_pb_conversion_dict[enum_value] = pb_value from_pb_conversion_dict[pb_value] = enum_value + for primary_enum_value, (alias_enum_key, alias_enum_value) in extra_aliases.items(): + fields.append((alias_enum_key, alias_enum_value)) + to_pb_conversion_dict[alias_enum_value] = to_pb_conversion_dict[primary_enum_value] - res_enum = StrEnum(class_name, fields, module=module) + res_enum: _StrEnumT = StrEnum(class_name, fields, module=module) # type: ignore res_enum.__doc__ = doc - def to_pb_conversion_func(val: StrEnum) -> int: + def to_pb_conversion_func(val: _StrEnumT) -> int: + val = res_enum(val) # generates a nicer error if 'val' is not a valid enum value return to_pb_conversion_dict[val] - def from_pb_conversion_func(val: int) -> StrEnum: + def from_pb_conversion_func(val: int) -> _StrEnumT: return res_enum(from_pb_conversion_dict[val]) return ( diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_helpers.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_helpers.py index bf4204cb4d..1110ece089 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_helpers.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_helpers.py @@ -21,33 +21,45 @@ # SOFTWARE. from collections.abc import Iterable -from typing import Union from google.protobuf.descriptor import FieldDescriptor from google.protobuf.message import Message -from ansys.api.acp.v0.base_pb2 import CollectionPath, ResourcePath +from ansys.api.acp.v0.base_pb2 import ResourcePath -__all__ = ("unlink_objects", "linked_path_fields") +__all__ = ("unlink_objects", "get_linked_paths") def unlink_objects(pb_object: Message) -> None: """Remove all ResourcePaths and CollectionPaths from a protobuf object.""" - for parent_message, field_descriptor, _ in linked_path_fields(pb_object): + for parent_message, field_descriptor, _ in _linked_path_fields(pb_object): parent_message.ClearField(field_descriptor.name) -def linked_path_fields( +def get_linked_paths(pb_object: Message) -> Iterable[ResourcePath]: + """Get all resource paths present in a protobuf object.""" + for _, field_descriptor, field_value in _linked_path_fields(pb_object): + if field_descriptor.label == field_descriptor.LABEL_REPEATED: + yield from field_value # type: ignore + else: + yield field_value # type: ignore + + +def _linked_path_fields( pb_object: Message, -) -> Iterable[tuple[Message, FieldDescriptor, Union[ResourcePath, CollectionPath]]]: - """Get all linked paths from a protobuf object. +) -> Iterable[tuple[Message, FieldDescriptor, Message]]: + """Get the field field information for resource paths in the message. - Get tuples (parent_message, field_descriptor, {resource_path or collection_path}) - describing all resource or collection paths present in the protobuf - object. + Get tuples (parent_message, field_descriptor, field_value) describing + all resource paths present in the protobuf object. Note that the fields + can also be repeated (containing multiple resource paths). """ for field_descriptor, field_value in pb_object.ListFields(): - if isinstance(field_value, (ResourcePath, CollectionPath)): + if getattr(field_descriptor.message_type, "name", None) == "ResourcePath": yield (pb_object, field_descriptor, field_value) - elif isinstance(field_value, Message): - yield from linked_path_fields(field_value) + elif field_descriptor.type == field_descriptor.TYPE_MESSAGE: + if field_descriptor.label == field_descriptor.LABEL_REPEATED: + for sub_obj in field_value: + yield from _linked_path_fields(sub_obj) + else: + yield from _linked_path_fields(field_value) diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_list.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_list.py index 3bad490829..86bc86d7a8 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_list.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/linked_object_list.py @@ -22,10 +22,10 @@ from __future__ import annotations -from collections.abc import Iterable, Iterator, MutableSequence +from collections.abc import Callable, Iterable, Iterator, MutableSequence from functools import partial import sys -from typing import Any, Callable, TypeVar, cast, overload +from typing import Any, TypeVar, cast, overload from grpc import Channel import numpy as np @@ -34,14 +34,14 @@ from ansys.api.acp.v0.base_pb2 import ResourcePath from .._object_cache import ObjectCacheMixin, constructor_with_cache -from ..base import CreatableTreeObject, TreeObject +from ..base import TreeObject, TreeObjectBase from .polymorphic_from_pb import tree_object_from_resource_path from .property_helper import _exposed_grpc_property, _wrap_doc, grpc_data_getter, grpc_data_setter -ValueT = TypeVar("ValueT", bound=CreatableTreeObject) +ValueT = TypeVar("ValueT", bound=TreeObjectBase) -__all__ = ["LinkedObjectList", "define_linked_object_list"] +__all__ = ["LinkedObjectList", "define_linked_object_list", "define_polymorphic_linked_object_list"] class LinkedObjectList(ObjectCacheMixin, MutableSequence[ValueT]): @@ -70,11 +70,13 @@ def _initialize_with_cache( parent_object: TreeObject, attribute_name: str, object_constructor: Callable[[ResourcePath, Channel], ValueT], + allowed_types: tuple[type[ValueT], ...], ) -> Self: return cls( _parent_object=parent_object, _attribute_name=attribute_name, _object_constructor=object_constructor, + _allowed_types=allowed_types, ) @staticmethod @@ -95,6 +97,7 @@ def __init__( _parent_object: TreeObject, _attribute_name: str, _object_constructor: Callable[[ResourcePath, Channel], ValueT], + _allowed_types: tuple[Any, ...], ) -> None: getter = grpc_data_getter(_attribute_name, from_protobuf=list) setter = grpc_data_setter(_attribute_name, to_protobuf=lambda x: x) @@ -113,6 +116,8 @@ def set_resourcepath_list(value: list[ResourcePath]) -> None: self._object_constructor: Callable[[ResourcePath], ValueT] = ( lambda resource_path: _object_constructor(resource_path, _parent_object._server_wrapper) ) + self._allowed_types = _allowed_types + self._allowed_types_str = ", ".join([cls.__name__ for cls in _allowed_types]) def __len__(self) -> int: return len(self._get_resourcepath_list()) @@ -138,13 +143,16 @@ def __setitem__(self, key: slice, value: Iterable[ValueT]) -> None: ... def __setitem__(self, key: int | slice, value: ValueT | Iterable[ValueT]) -> None: resource_path_list = self._get_resourcepath_list() - if isinstance(value, TreeObject): + if isinstance(value, TreeObjectBase): + self._check_type(value) if not isinstance(key, int): raise TypeError("Cannot assign to a slice with a single object.") resource_path_list[key] = value._resource_path else: if not isinstance(key, slice): raise TypeError("Cannot assign to a single index with an iterable.") + for item in value: + self._check_type(item) resource_path_list[key] = [item._resource_path for item in value] self._set_resourcepath_list(resource_path_list) @@ -174,6 +182,7 @@ def append(self, object: ValueT) -> None: object: Object to append. """ + self._check_type(object) resource_path_list = self._get_resourcepath_list() resource_path_list.append(object._resource_path) self._set_resourcepath_list(resource_path_list) @@ -207,6 +216,8 @@ def extend(self, iterable: Iterable[ValueT]) -> None: Iterable of objects to append. """ resource_path_list = self._get_resourcepath_list() + for it in iterable: + self._check_type(it) resource_path_list.extend([it._resource_path for it in iterable]) self._set_resourcepath_list(resource_path_list) @@ -220,6 +231,7 @@ def insert(self, index: int, object: ValueT) -> None: object: Object to insert. """ + self._check_type(object) resource_path_list = self._get_resourcepath_list() resource_path_list.insert(index, object._resource_path) self._set_resourcepath_list(resource_path_list) @@ -279,8 +291,15 @@ def __eq__(self, other: Any) -> Any: def __repr__(self) -> str: return f"" + def _check_type(self, object: Any) -> None: + if not isinstance(object, self._allowed_types): + raise TypeError( + f"List items must be of type {self._allowed_types_str}, not {type(object).__name__}." + ) -ChildT = TypeVar("ChildT", bound=CreatableTreeObject) + +ParentT = TypeVar("ParentT", bound=TreeObject) +ChildT = TypeVar("ChildT", bound=TreeObjectBase) def define_linked_object_list( @@ -288,34 +307,45 @@ def define_linked_object_list( ) -> Any: """Define a list of linked tree objects.""" - def getter(self: ValueT) -> LinkedObjectList[ChildT]: + def getter(self: ParentT) -> LinkedObjectList[ChildT]: return LinkedObjectList._initialize_with_cache( parent_object=self, attribute_name=attribute_name, object_constructor=object_class._from_resource_path, + allowed_types=(object_class,), ) - def setter(self: ValueT, value: list[ChildT]) -> None: + def setter(self: ParentT, value: list[ChildT]) -> None: getter(self)[:] = value return _wrap_doc(_exposed_grpc_property(getter).setter(setter), doc=doc) def define_polymorphic_linked_object_list( - attribute_name: str, allowed_types: tuple[Any, ...] + attribute_name: str, + allowed_types: tuple[Any, ...] | None = None, + allowed_types_getter: Callable[[], tuple[Any, ...]] | None = None, ) -> Any: """Define a list of linked tree objects with polymorphic types.""" + if allowed_types is None != allowed_types_getter is None: + raise ValueError("Exactly one of allowed_types and allowed_types_getter must be provided.") + + def getter(self: ParentT) -> LinkedObjectList[Any]: + nonlocal allowed_types + if allowed_types_getter is not None: + allowed_types = allowed_types_getter() - def getter(self: ValueT) -> LinkedObjectList[Any]: + assert allowed_types is not None return LinkedObjectList( _parent_object=self, _attribute_name=attribute_name, _object_constructor=partial( tree_object_from_resource_path, allowed_types=allowed_types ), + _allowed_types=allowed_types, ) - def setter(self: ValueT, value: list[Any]) -> None: + def setter(self: ParentT, value: list[Any]) -> None: getter(self)[:] = value return _exposed_grpc_property(getter).setter(setter) diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/mapping.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/mapping.py index c5590d27a9..35e8a8fafc 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/mapping.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/mapping.py @@ -22,11 +22,13 @@ from __future__ import annotations -from collections.abc import Iterator -from typing import Any, Callable, Generic, TypeVar +from collections.abc import Callable, Iterator +import inspect +from typing import Any, Concatenate, Generic, TypeVar from grpc import Channel -from typing_extensions import Concatenate, ParamSpec, Self +from packaging.version import parse as parse_version +from typing_extensions import ParamSpec, Self from ansys.api.acp.v0.base_pb2 import CollectionPath, DeleteRequest, ListRequest @@ -34,7 +36,7 @@ from ..._utils.resource_paths import join as _rp_join from .._object_cache import ObjectCacheMixin, constructor_with_cache from ..base import CreatableTreeObject, ServerWrapper, TreeObject, TreeObjectBase -from ..enums import StatusType +from ..enums import Status from .exceptions import wrap_grpc_errors from .property_helper import _exposed_grpc_property, _wrap_doc from .protocols import EditableAndReadableResourceStub, ObjectInfo, ReadableResourceStub @@ -42,7 +44,13 @@ ValueT = TypeVar("ValueT", bound=TreeObjectBase) CreatableValueT = TypeVar("CreatableValueT", bound=CreatableTreeObject) -__all__ = ["Mapping", "MutableMapping", "define_mutable_mapping", "define_create_method"] +__all__ = [ + "Mapping", + "MutableMapping", + "define_mutable_mapping", + "define_create_method", + "get_read_only_collection_property", +] class Mapping(ObjectCacheMixin, Generic[ValueT]): @@ -252,7 +260,7 @@ def get_read_only_collection_property( """Define a read-only mapping of child tree objects.""" def collection_property(self: ParentT) -> Mapping[ValueT]: - if requires_uptodate and hasattr(self, "status") and not self.status == StatusType.UPTODATE: + if requires_uptodate and hasattr(self, "status") and not self.status == Status.UPTODATE: raise RuntimeError( f"The object {self.name} must be up-to-date to access {object_class.__name__}." ) @@ -288,6 +296,10 @@ def inner(self: ParentT, /, *args: P.args, **kwargs: P.kwargs) -> CreatableValue # on the class itself, instead of the __init__ method. inner.__doc__ = object_class.__doc__ + parameters = [inspect.signature(inner).parameters["self"]] + parameters.extend(inspect.signature(object_class).parameters.values()) + inner.__signature__ = inspect.Signature(parameters, return_annotation=object_class) # type: ignore + inner.__name__ = func_name inner.__qualname__ = f"{parent_class_name}.{func_name}" inner.__module__ = module_name @@ -302,6 +314,14 @@ def define_mutable_mapping( """Define a mutable mapping of child tree objects.""" def collection_property(self: ParentT) -> MutableMapping[CreatableValueT]: + if self._server_version is not None: + if self._server_version < parse_version(object_class._SUPPORTED_SINCE): + raise RuntimeError( + f"The '{object_class.__name__}' object is only supported since version " + f"{object_class._SUPPORTED_SINCE} of the ACP gRPC server. The current server version is " + f"{self._server_version}." + ) + return MutableMapping._initialize_with_cache( server_wrapper=self._server_wrapper, collection_path=CollectionPath( diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/property_helper.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/property_helper.py index 0c628d51a0..f458ea9759 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/property_helper.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/property_helper.py @@ -27,8 +27,10 @@ """ from __future__ import annotations +from collections.abc import Callable from functools import reduce -from typing import Any, Callable, TypeVar +import sys +from typing import TYPE_CHECKING, Any, TypeVar from google.protobuf.message import Message @@ -37,6 +39,7 @@ from ..._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty from .polymorphic_from_pb import CreatableFromResourcePath, tree_object_from_resource_path from .protocols import Editable, GrpcObjectBase, ObjectInfo, Readable +from .supported_since import supported_since as supported_since_decorator # Note: The typing of the protobuf objects is fairly loose, maybe it could # be improved. The main challenge is that we do not encode the structure of @@ -50,14 +53,21 @@ _FROM_PROTOBUF_T = Callable[[_PROTOBUF_T], _GET_T] -class _exposed_grpc_property(property): - """Mark a property as exposed via gRPC. +if TYPE_CHECKING: # pragma: no cover + # This is needed because mypy does not understand custom property + # subclasses. + # See https://github.com/python/mypy/issues/6158 + _exposed_grpc_property = property +else: - Wrapper around 'property', used to signal that the object should - be collected into the '_GRPC_PROPERTIES' class attribute. - """ + class _exposed_grpc_property(property): + """Mark a property as exposed via gRPC. + + Wrapper around 'property', used to signal that the object should + be collected into the '_GRPC_PROPERTIES' class attribute. + """ - pass + pass T = TypeVar("T", bound=type[GrpcObjectBase]) @@ -82,17 +92,46 @@ def mark_grpc_properties(cls: T) -> T: if name not in props_unique: props_unique.append(name) cls._GRPC_PROPERTIES = tuple(props_unique) + + # The 'mark_grpc_properties' decorator is also used on intermediate base + # classes which do not have the '_SUPPORTED_SINCE' attribute. We only want + # to add the version information to the final class. + if hasattr(cls, "_SUPPORTED_SINCE"): + if isinstance(cls.__doc__, str): + # When adding to the docstring, we need to match the existing + # indentation of the docstring (except the first line). + # See PEP 257 'Handling Docstring Indentation'. + # Alternatively, we could strip the common indentation from the + # docstring. + indent = sys.maxsize + for line in cls.__doc__.splitlines()[1:]: + stripped = line.lstrip() + if stripped: # ignore empty lines + indent = min(indent, len(line) - len(stripped)) + if indent == sys.maxsize: + indent = 0 + cls.__doc__ += ( + f"\n\n{indent * ' '}*Added in ACP server version {cls._SUPPORTED_SINCE}.*\n" + ) return cls -def grpc_linked_object_getter(name: str) -> Callable[[Readable], Any]: +def grpc_linked_object_getter( + name: str, readable_since: str | None = None +) -> Callable[[Readable], Any]: """Create a getter method which obtains the linked server object.""" + @supported_since_decorator( + readable_since, + # The default error message uses 'inner' as the method name, which is confusing + err_msg_tpl=( + f"The property '{name.split('.')[-1]}' is only readable since version {{required_version}} " + f"of the ACP gRPC server. The current server version is {{server_version}}." + ), + ) def inner(self: Readable) -> CreatableFromResourcePath | None: - # Import here to avoid circular references. Cannot use the registry before - # all the object have been imported. if not self._is_stored: - raise Exception("Cannot get linked object from unstored object") + raise RuntimeError(f"Cannot get linked object '{name}' from unstored object") self._get() object_resource_path = _get_data_attribute(self._pb_object, name) @@ -103,8 +142,21 @@ def inner(self: Readable) -> CreatableFromResourcePath | None: return inner +def _get_data_attribute(pb_obj: Message, name: str, check_optional: bool = False) -> _PROTOBUF_T: + name_parts = name.split(".") + if check_optional: + parent_obj = reduce(getattr, name_parts[:-1], pb_obj) + if hasattr(parent_obj, "HasField") and not parent_obj.HasField(name_parts[-1]): + return None + return reduce(getattr, name_parts, pb_obj) + + def grpc_data_getter( - name: str, from_protobuf: _FROM_PROTOBUF_T[_GET_T], check_optional: bool = False + name: str, + from_protobuf: _FROM_PROTOBUF_T[_GET_T], + check_optional: bool = False, + getter_func: Callable[[Message, str, bool], _PROTOBUF_T] = _get_data_attribute, + supported_since: str | None = None, ) -> Callable[[Readable], _GET_T]: """Create a getter method which obtains the server object via the gRPC Get endpoint. @@ -119,9 +171,17 @@ def grpc_data_getter( will be used. """ + @supported_since_decorator( + supported_since, + # The default error message uses 'inner' as the method name, which is confusing + err_msg_tpl=( + f"The property '{name.split('.')[-1]}' is only readable since version {{required_version}} " + f"of the ACP gRPC server. The current server version is {{server_version}}." + ), + ) def inner(self: Readable) -> Any: self._get_if_stored() - pb_attribute = _get_data_attribute(self._pb_object, name, check_optional=check_optional) + pb_attribute = getter_func(self._pb_object, name, check_optional) if check_optional and pb_attribute is None: return None return from_protobuf(pb_attribute) @@ -130,10 +190,10 @@ def inner(self: Readable) -> Any: def grpc_linked_object_setter( - name: str, to_protobuf: _TO_PROTOBUF_T[Readable | None] + name: str, to_protobuf: _TO_PROTOBUF_T[Readable | None], writable_since: str | None = None ) -> Callable[[Editable, Readable | None], None]: """Create a setter method which updates the linked object via the gRPC Put endpoint.""" - func = grpc_data_setter(name, to_protobuf) + func = grpc_data_setter(name=name, to_protobuf=to_protobuf, supported_since=writable_since) def inner(self: Editable, value: Readable | None) -> None: if value is not None and not value._is_stored: @@ -143,35 +203,6 @@ def inner(self: Editable, value: Readable | None) -> None: return inner -def grpc_data_setter( - name: str, to_protobuf: _TO_PROTOBUF_T[_SET_T] -) -> Callable[[Editable, _SET_T], None]: - """Create a setter method which updates the server object via the gRPC Put endpoint.""" - - def inner(self: Editable, value: _SET_T) -> None: - self._get_if_stored() - current_value = _get_data_attribute(self._pb_object, name) - value_pb = to_protobuf(value) - try: - needs_updating = current_value != value_pb - except TypeError: - needs_updating = True - if needs_updating: - _set_data_attribute(self._pb_object, name, value_pb) - self._put_if_stored() - - return inner - - -def _get_data_attribute(pb_obj: Message, name: str, check_optional: bool = False) -> _PROTOBUF_T: - name_parts = name.split(".") - if check_optional: - parent_obj = reduce(getattr, name_parts[:-1], pb_obj) - if hasattr(parent_obj, "HasField") and not parent_obj.HasField(name_parts[-1]): - return None - return reduce(getattr, name_parts, pb_obj) - - def _set_data_attribute(pb_obj: ObjectInfo, name: str, value: _PROTOBUF_T) -> None: name_parts = name.split(".") @@ -191,6 +222,37 @@ def _set_data_attribute(pb_obj: ObjectInfo, name: str, value: _PROTOBUF_T) -> No target_object.add().CopyFrom(item) +def grpc_data_setter( + name: str, + to_protobuf: _TO_PROTOBUF_T[_SET_T], + setter_func: Callable[[ObjectInfo, str, _PROTOBUF_T], None] = _set_data_attribute, + supported_since: str | None = None, +) -> Callable[[Editable, _SET_T], None]: + """Create a setter method which updates the server object via the gRPC Put endpoint.""" + + @supported_since_decorator( + supported_since, + # The default error message uses 'inner' as the method name, which is confusing + err_msg_tpl=( + f"The property '{name.split('.')[-1]}' is only editable since version {{required_version}} " + f"of the ACP gRPC server. The current server version is {{server_version}}." + ), + ) + def inner(self: Editable, value: _SET_T) -> None: + self._get_if_stored() + current_value = _get_data_attribute(self._pb_object, name) + value_pb = to_protobuf(value) + try: + needs_updating = current_value != value_pb + except TypeError: + needs_updating = True + if needs_updating: + setter_func(self._pb_object, name, value_pb) + self._put_if_stored() + + return inner + + AnyT = TypeVar("AnyT") @@ -206,6 +268,10 @@ def grpc_data_property( from_protobuf: _FROM_PROTOBUF_T[_GET_T] = lambda x: x, check_optional: bool = False, doc: str | None = None, + setter_func: Callable[[ObjectInfo, str, _PROTOBUF_T], None] = _set_data_attribute, + getter_func: Callable[[Message, str, bool], _PROTOBUF_T] = _get_data_attribute, + readable_since: str | None = None, + writable_since: str | None = None, ) -> ReadWriteProperty[_GET_T, _SET_T]: """Define a property which is synchronized with the backend via gRPC. @@ -228,6 +294,18 @@ def grpc_data_property( will be used. doc : Docstring for the property. + setter_func : + Function to set the property value. Can be customized to + implement additional checks or in case properties depend + on each other. + getter_func : + Function to get the property value. Can be customized to + implement additional checks or in case properties depend + on each other + readable_since : + Version since which the property is supported for reading. + writable_since : + Version since which the property is supported for setting. """ # Note jvonrick August 2023: We don't ensure with typechecks that the property returned here is # compatible with the class on which this property is created. For example: @@ -238,8 +316,21 @@ def grpc_data_property( # https://github.com/python/typing/issues/985 return _wrap_doc( _exposed_grpc_property( - grpc_data_getter(name, from_protobuf=from_protobuf, check_optional=check_optional) - ).setter(grpc_data_setter(name, to_protobuf=to_protobuf)), + grpc_data_getter( + name, + from_protobuf=from_protobuf, + check_optional=check_optional, + getter_func=getter_func, + supported_since=readable_since, + ) + ).setter( + grpc_data_setter( + name, + to_protobuf=to_protobuf, + setter_func=setter_func, + supported_since=writable_since, + ) + ), doc=doc, ) @@ -249,6 +340,7 @@ def grpc_data_property_read_only( from_protobuf: _FROM_PROTOBUF_T[_GET_T] = lambda x: x, check_optional: bool = False, doc: str | None = None, + supported_since: str | None = None, ) -> ReadOnlyProperty[_GET_T]: """Define a read-only property which is synchronized with the backend via gRPC. @@ -269,10 +361,17 @@ def grpc_data_property_read_only( will be used. doc : Docstring for the property. + supported_since : + Version since which the property is supported. """ return _wrap_doc( _exposed_grpc_property( - grpc_data_getter(name, from_protobuf=from_protobuf, check_optional=check_optional) + grpc_data_getter( + name, + from_protobuf=from_protobuf, + check_optional=check_optional, + supported_since=supported_since, + ) ), doc=doc, ) @@ -283,6 +382,8 @@ def grpc_link_property( *, doc: str | None = None, allowed_types: type[GrpcObjectBase] | tuple[type[GrpcObjectBase], ...], + readable_since: str | None = None, + writable_since: str | None = None, ) -> Any: """Define a gRPC-backed property linking to another object. @@ -298,6 +399,10 @@ def grpc_link_property( allowed_types : Types which are allowed to be set on the property. An error will be raised if an object of a different type is set. + readable_since : + Version since which the property is supported for reading. + writable_since : + Version since which the property is supported for setting. """ def to_protobuf(obj: Readable | None) -> ResourcePath: @@ -312,9 +417,13 @@ def to_protobuf(obj: Readable | None) -> ResourcePath: return obj._resource_path return _wrap_doc( - _exposed_grpc_property(grpc_linked_object_getter(name)).setter( + _exposed_grpc_property( + grpc_linked_object_getter(name=name, readable_since=readable_since) + ).setter( # Resource path represents an object that is not set as an empty string - grpc_linked_object_setter(name=name, to_protobuf=to_protobuf) + grpc_linked_object_setter( + name=name, to_protobuf=to_protobuf, writable_since=writable_since + ) ), doc=doc, ) diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/protocols.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/protocols.py index fdc2410dda..9ffeac391f 100644 --- a/src/ansys/acp/core/_tree_objects/_grpc_helpers/protocols.py +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/protocols.py @@ -29,6 +29,7 @@ from google.protobuf.message import Message import grpc +from packaging.version import Version from ansys.api.acp.v0.base_pb2 import ( BasicInfo, @@ -150,6 +151,7 @@ class GrpcObjectBase(Protocol): __slots__: Iterable[str] = tuple() _GRPC_PROPERTIES: tuple[str, ...] = tuple() + _SUPPORTED_SINCE: str def __str__(self) -> str: string_items = [] @@ -188,6 +190,9 @@ def _resource_path(self) -> ResourcePath: ... _pb_object: Any + @property + def _server_version(self) -> Version | None: ... + class Editable(Readable, Protocol): """Interface definition for editable objects.""" diff --git a/src/ansys/acp/core/_tree_objects/_grpc_helpers/supported_since.py b/src/ansys/acp/core/_tree_objects/_grpc_helpers/supported_since.py new file mode 100644 index 0000000000..2b67cdf4b5 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/_grpc_helpers/supported_since.py @@ -0,0 +1,84 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from collections.abc import Callable +from functools import wraps +from typing import Concatenate, TypeAlias, TypeVar + +from packaging.version import parse as parse_version +from typing_extensions import ParamSpec + +from .protocols import Readable + +T = TypeVar("T", bound=Readable) +P = ParamSpec("P") +R = TypeVar("R") +_WRAPPED_T: TypeAlias = Callable[Concatenate[T, P], R] + + +def supported_since( + version: str | None, err_msg_tpl: str | None = None +) -> Callable[[_WRAPPED_T[T, P, R]], _WRAPPED_T[T, P, R]]: + """Mark a TreeObjectBase method as supported since a specific server version. + + Raises an exception if the current server version does not match the required version. + If either the given `version` or the server version is `None`, the decorator does nothing. + + Parameters + ---------- + version : + The server version since which the method is supported. If ``None``, the + decorator does nothing. + err_msg_tpl : + A custom error message template. If ``None``, a default error message is used. + """ + if version is None: + # return a trivial decorator if no version is specified + def trivial_decorator(func: _WRAPPED_T[T, P, R]) -> _WRAPPED_T[T, P, R]: + return func + + return trivial_decorator + + required_version = parse_version(version) + + def decorator(func: _WRAPPED_T[T, P, R]) -> _WRAPPED_T[T, P, R]: + @wraps(func) + def inner(self: T, /, *args: P.args, **kwargs: P.kwargs) -> R: + server_version = self._server_version + # If the object is not stored, we cannot check the server version. + if server_version is not None: + if server_version < required_version: + if err_msg_tpl is None: + err_msg = ( + f"The '{func.__name__}' method is only supported since version {version} " + f"of the ACP gRPC server. The current server version is {server_version}." + ) + else: + err_msg = err_msg_tpl.format( + required_version=required_version, server_version=server_version + ) + raise RuntimeError(err_msg) + return func(self, *args, **kwargs) + + return inner + + return decorator diff --git a/src/ansys/acp/core/_tree_objects/_mesh_data.py b/src/ansys/acp/core/_tree_objects/_mesh_data.py index 415382bf13..f622a3e69d 100644 --- a/src/ansys/acp/core/_tree_objects/_mesh_data.py +++ b/src/ansys/acp/core/_tree_objects/_mesh_data.py @@ -24,442 +24,102 @@ import dataclasses import typing -from typing import Any, ClassVar, Literal, cast import numpy as np import numpy.typing as npt -from pyvista.core.pointset import PolyData, UnstructuredGrid -from typing_extensions import Self +from packaging.version import parse as parse_version -from ansys.acp.core._utils.array_conversions import dataarray_to_numpy, to_numpy -from ansys.api.acp.v0 import mesh_query_pb2, mesh_query_pb2_grpc +if typing.TYPE_CHECKING: # pragma: no cover + from pyvista.core.pointset import UnstructuredGrid + +from ansys.api.acp.v0 import base_pb2, mesh_query_pb2, mesh_query_pb2_grpc -from .._typing_helper import StrEnum +from .._utils.array_conversions import to_numpy from .._utils.property_protocols import ReadOnlyProperty +from .._utils.pyvista_import_check import requires_pyvista from .base import TreeObject -from .enums import ( - elemental_data_type_from_pb, - elemental_data_type_to_pb, - nodal_data_type_from_pb, - nodal_data_type_to_pb, -) - -if typing.TYPE_CHECKING: # pragma: no cover - from .model import MeshData # avoid circular import __all__ = [ - "ElementalData", - "NodalData", - "elemental_data_property", - "nodal_data_property", - "ScalarData", - "VectorData", + "MeshData", + "full_mesh_property", + "shell_mesh_property", + "solid_mesh_property", ] @dataclasses.dataclass -class _Labels: - mesh_labels: npt.NDArray[np.int32] - data_labels: npt.NDArray[np.int32] - mesh_label_to_index_map: dict[int, int] - - -def _get_labels( - *, - field_names: _LabelAndPyvistaFieldNames, - labels: npt.NDArray[np.int32], - mesh: MeshData, -) -> _Labels: - mesh_labels = getattr(mesh, field_names.LABEL_FIELD_NAME) - mesh_label_to_index_map = {label: idx for idx, label in enumerate(mesh_labels)} - return _Labels( - mesh_labels=mesh_labels, data_labels=labels, mesh_label_to_index_map=mesh_label_to_index_map - ) - - -def _expand_array( - *, - array: npt.NDArray[ScalarDataT], - labels: _Labels, - culling_factor: int = 1, -) -> npt.NDArray[np.float64]: - """Expand the array to the size of the mesh.""" - target_shape = tuple([labels.mesh_labels.size] + list(array.shape[1:])) - target_array = np.ones(target_shape, dtype=np.float64) * np.nan - for idx, (label, value) in enumerate(zip(labels.data_labels, array)): - if idx % culling_factor == 0: - target_array[labels.mesh_label_to_index_map[label]] = value - return target_array - - -def _get_pyvista_mesh_with_all_data( - *, - mesh_data_base: MeshDataBase, - mesh: MeshData, -) -> UnstructuredGrid: - pv_mesh = mesh.to_pyvista() - - mesh_data_field = getattr( - pv_mesh, mesh_data_base._LABEL_AND_PYVISTA_FIELD_NAMES.PYVISTA_FIELD_NAME - ) - field_labels = getattr( - mesh_data_base, mesh_data_base._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME - ).values - labels = _get_labels( - field_names=mesh_data_base._LABEL_AND_PYVISTA_FIELD_NAMES, mesh=mesh, labels=field_labels - ) - - for name in mesh_data_base._field_names(): - values = getattr(mesh_data_base, name).values - target_array = _expand_array(array=values, labels=labels) - mesh_data_field[name] = target_array - return pv_mesh - - -def _get_mesh_with_scalar_pyvista_data( - *, - labels: npt.NDArray[np.int32], - field_names: _LabelAndPyvistaFieldNames, - mesh: MeshData, - values: npt.NDArray[ScalarDataT], - component_name: str, -) -> UnstructuredGrid: - all_labels = _get_labels(field_names=field_names, labels=labels, mesh=mesh) - - pv_mesh = mesh.to_pyvista() - mesh_data_field = getattr(pv_mesh, field_names.PYVISTA_FIELD_NAME) - - target_array = _expand_array(array=values, labels=all_labels) - component_label = component_name - mesh_data_field[component_label] = target_array - return pv_mesh - - -def _get_pyvista_glyphs( - *, - labels: npt.NDArray[np.int32], - field_names: _LabelAndPyvistaFieldNames, - mesh: MeshData, - values: npt.NDArray[np.float64], - component_name: str, - culling_factor: int = 1, - scaling_factor: float = 1.0, - **kwargs: Any, -) -> PolyData: - all_labels = _get_labels(field_names=field_names, labels=labels, mesh=mesh) - - pv_mesh = mesh.to_pyvista() - mesh_data_field = getattr(pv_mesh, field_names.PYVISTA_FIELD_NAME) - - target_array = _expand_array(array=values, labels=all_labels, culling_factor=culling_factor) - component_label = component_name - mesh_data_field[component_label] = target_array - - magnitude_name = f"{component_label}_magnitude" - mesh_data_field[magnitude_name] = np.linalg.norm(target_array, axis=-1) * scaling_factor - return pv_mesh.glyph(orient=component_label, scale=magnitude_name, **kwargs) # type: ignore - - -ScalarDataT = typing.TypeVar("ScalarDataT", np.float64, np.int32) - - -class ScalarData(typing.Generic[ScalarDataT]): - """Class that encapsulates scalar data.""" - - def __init__( - self, - field_names: _LabelAndPyvistaFieldNames, - labels: npt.NDArray[np.int32], - values: npt.NDArray[ScalarDataT], - component_name: str, - ): - self._field_names = field_names - self._labels = labels - self._values: npt.NDArray[ScalarDataT] = values - self._component_name = component_name - - @property - def values(self) -> npt.NDArray[ScalarDataT]: - """Scalar data values as a numpy array.""" - return self._values - - @property - def component_name(self) -> str: - """Name of the component.""" - return self._component_name - - def get_pyvista_mesh( - self, - mesh: MeshData, - ) -> UnstructuredGrid: - """Convert the mesh data to a PyVista object. - - Parameters - ---------- - mesh : - The mesh to which the data is associated. - """ - return _get_mesh_with_scalar_pyvista_data( - labels=self._labels, - field_names=self._field_names, - mesh=mesh, - values=self._values, - component_name=self._component_name, - ) - - -class VectorData: - """Class that encapsulates vector data.""" - - def __init__( - self, - field_names: _LabelAndPyvistaFieldNames, - labels: npt.NDArray[np.int32], - values: npt.NDArray[np.float64], - component_name: str, - ): - self._field_names = field_names - self._labels = labels - self._values = values - self._component_name = component_name - - @property - def values(self) -> npt.NDArray[np.float64]: - """Vector data values as a numpy array.""" - return self._values - - @property - def component_name(self) -> str: - """Name of the component.""" - return self._component_name - - def get_pyvista_glyphs( - self, - *, - mesh: MeshData, - culling_factor: int = 1, - scaling_factor: float = 1.0, - **kwargs: Any, - ) -> PolyData: - """Get a pyvista glyph object from the vector data. - - Parameters - ---------- - mesh : - The mesh to which the data is associated. - culling_factor : - If set to a value other than ``1``, add only every n-th data - point to the PyVista object. This is useful especially for - vector data, where the arrows can be too dense. - scaling_factor : - Factor to scale the length of the arrows. - kwargs : - Keyword arguments passed to the PyVista object constructor. - """ - return _get_pyvista_glyphs( - labels=self._labels, - field_names=self._field_names, - mesh=mesh, - values=self._values, - component_name=self._component_name, - culling_factor=culling_factor, - scaling_factor=scaling_factor, - **kwargs, - ) - - -def _check_field_type(klass: Any, field_name: str, actual_field_type: str) -> None: - """Check that the type declared in the dataclass (klass) matches the actual type.""" - declared_field_types: typing.Sequence[str] = cast( - typing.Sequence[str], - [field.type for field in dataclasses.fields(klass) if field.name == field_name], - ) - if len(declared_field_types) != 1: - raise RuntimeError("Failed to find field in dataclass.") - declared_field_type = declared_field_types[0].removesuffix(" | None") - if declared_field_type != actual_field_type: - raise RuntimeError( - f"Declared type does not match actual data type. " - f"Declared type: {declared_field_type}, actual type: {actual_field_type}. " - f"Field name: {field_name}" +class MeshData: + """Container for the mesh data of an ACP Model.""" + + node_labels: npt.NDArray[np.int32] + node_coordinates: npt.NDArray[np.float64] + element_labels: npt.NDArray[np.int32] + element_types: npt.NDArray[np.int32] + element_nodes: npt.NDArray[np.int32] + element_nodes_offsets: npt.NDArray[np.int32] + + @requires_pyvista + def to_pyvista(self) -> UnstructuredGrid: + """Convert the mesh data to a PyVista mesh.""" + from pyvista.core.pointset import UnstructuredGrid + + from .._utils.visualization import to_pyvista_faces, to_pyvista_types + + return UnstructuredGrid( + to_pyvista_faces( + element_types=self.element_types, + element_nodes=self.element_nodes, + element_nodes_offsets=self.element_nodes_offsets, + ), + to_pyvista_types(self.element_types), + self.node_coordinates, ) -@dataclasses.dataclass -class _LabelAndPyvistaFieldNames: - LABEL_FIELD_NAME: str - PYVISTA_FIELD_NAME: str +def _mesh_property_impl( + element_scoping: mesh_query_pb2.ElementScopingType.ValueType, doc: str +) -> ReadOnlyProperty[MeshData]: + def getter(self: TreeObject) -> MeshData: + mesh_query_stub = mesh_query_pb2_grpc.MeshQueryServiceStub(self._channel) + assert self._server_version is not None + if self._server_version < parse_version("25.1"): + from .model import Model - -@dataclasses.dataclass -class MeshDataBase: - """Base class for nodal or elemental mesh data. - - Implements the construction from a protobuf response and the conversion - to a PyVista object. - """ - - _LABEL_AND_PYVISTA_FIELD_NAMES: ClassVar[_LabelAndPyvistaFieldNames] - _FIELD_NAME_FROM_PB_VALUE: ClassVar[typing.Callable[[int], StrEnum]] - _PB_VALUE_FROM_FIELD_NAME: ClassVar[typing.Callable[[StrEnum], int]] - - @classmethod - def _field_names(cls) -> list[str]: - return [ - field.name - for field in dataclasses.fields(cls) - if field.name != cls._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME - ] - - @classmethod - def _from_pb(cls, response: mesh_query_pb2.ElementalData | mesh_query_pb2.NodalData) -> Self: - """Construct a mesh data object from a protobuf response.""" - labels = to_numpy(response.labels) - kwargs: dict[str, Any] = { - cls._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME: ScalarData( - field_names=cls._LABEL_AND_PYVISTA_FIELD_NAMES, - labels=labels, - values=labels, - component_name=cls._LABEL_AND_PYVISTA_FIELD_NAMES.LABEL_FIELD_NAME, - ) - } - for data_type, array in zip(response.data_types, response.data_arrays): - field_name = cls._FIELD_NAME_FROM_PB_VALUE(data_type).value - values = cast( - npt.NDArray[np.float64], dataarray_to_numpy(array, dtype=np.float64) - ) # todo: handle other dtypes - kwargs[field_name] = values - data_wrapper: VectorData | ScalarData[np.float64] - if len(values.shape) == 2 and values.shape[1] == 3: - data_wrapper = VectorData( - field_names=cls._LABEL_AND_PYVISTA_FIELD_NAMES, - labels=labels, - values=values, - component_name=field_name, - ) - _check_field_type(klass=cls, field_name=field_name, actual_field_type="VectorData") - - else: - data_wrapper = ScalarData( - field_names=cls._LABEL_AND_PYVISTA_FIELD_NAMES, - labels=labels, - values=values, - component_name=field_name, + if not isinstance(self, Model): + raise RuntimeError( + "Mesh attributes for object types other than 'Model' are only supported " + "for server versions 25.1 and later." ) - _check_field_type( - klass=cls, field_name=field_name, actual_field_type="ScalarData[np.float64]" + if element_scoping != mesh_query_pb2.ElementScopingType.ALL: + raise RuntimeError( + "Element scoping is only supported for server versions 25.1 and later." ) - kwargs[field_name] = data_wrapper - - instance = cls(**kwargs) - return instance - - def get_pyvista_mesh( - self, - mesh: MeshData, - ) -> UnstructuredGrid: - """Get a pyvista mesh with all data. + request: base_pb2.GetRequest | mesh_query_pb2.GetMeshDataRequest = base_pb2.GetRequest( + resource_path=self._resource_path + ) + else: + request = mesh_query_pb2.GetMeshDataRequest( + resource_path=self._resource_path, element_scoping=element_scoping + ) + reply = mesh_query_stub.GetMeshData(request) + return MeshData( + node_labels=to_numpy(reply.node_labels), + node_coordinates=to_numpy(reply.node_coordinates), + element_labels=to_numpy(reply.element_labels), + element_types=to_numpy(reply.element_types), + element_nodes=to_numpy(reply.element_nodes), + element_nodes_offsets=to_numpy(reply.element_nodes_offsets), + ) - Parameters - ---------- - mesh : - The mesh to which the data is associated. - """ - return _get_pyvista_mesh_with_all_data(mesh_data_base=self, mesh=mesh) + return property(getter, doc=doc) -_NODE_FIELD_NAMES = _LabelAndPyvistaFieldNames( - LABEL_FIELD_NAME="node_labels", - PYVISTA_FIELD_NAME="point_data", +full_mesh_property = _mesh_property_impl( + mesh_query_pb2.ElementScopingType.ALL, doc="Full mesh associated with the object." ) - -_ELEMENT_FIELD_NAMES = _LabelAndPyvistaFieldNames( - LABEL_FIELD_NAME="element_labels", - PYVISTA_FIELD_NAME="cell_data", +shell_mesh_property = _mesh_property_impl( + mesh_query_pb2.ElementScopingType.SHELL, doc="Shell mesh associated with the object." +) +solid_mesh_property = _mesh_property_impl( + mesh_query_pb2.ElementScopingType.SOLID, doc="Solid mesh associated with the object." ) - - -@dataclasses.dataclass -class NodalData(MeshDataBase): - """Base class for nodal data.""" - - node_labels: ScalarData[np.int32] - _LABEL_AND_PYVISTA_FIELD_NAMES = _NODE_FIELD_NAMES - _PB_VALUE_FROM_FIELD_NAME = nodal_data_type_to_pb - _FIELD_NAME_FROM_PB_VALUE = nodal_data_type_from_pb - - -@dataclasses.dataclass -class ElementalData(MeshDataBase): - """Base class for elemental data.""" - - element_labels: ScalarData[np.int32] - _LABEL_AND_PYVISTA_FIELD_NAMES = _ELEMENT_FIELD_NAMES - _PB_VALUE_FROM_FIELD_NAME = elemental_data_type_to_pb - _FIELD_NAME_FROM_PB_VALUE = elemental_data_type_from_pb - - -T = typing.TypeVar("T", bound=MeshDataBase) - - -ElementalDataT = typing.TypeVar("ElementalDataT", bound=ElementalData) - - -def elemental_data_property( - wrapped_cls: type[ElementalDataT], -) -> ReadOnlyProperty[ElementalDataT]: - """Create a property to get elemental data from a tree object.""" - return _mesh_data_property_impl( - wrapped_cls=wrapped_cls, - request_name="GetElementalData", - request_type=mesh_query_pb2.GetElementalDataRequest, - ) - - -NodalDataT = typing.TypeVar("NodalDataT", bound=NodalData) - - -def nodal_data_property( - wrapped_cls: type[NodalDataT], -) -> ReadOnlyProperty[NodalDataT]: - """Create a property to get nodal data from a tree object.""" - return _mesh_data_property_impl( - wrapped_cls=wrapped_cls, - request_name="GetNodalData", - request_type=mesh_query_pb2.GetNodalDataRequest, - ) - - -MeshDataT = typing.TypeVar("MeshDataT", bound=MeshDataBase) - - -def _mesh_data_property_impl( - wrapped_cls: type[MeshDataT], - request_name: Literal["GetNodalData", "GetElementalData"], - request_type: ( - type[mesh_query_pb2.GetNodalDataRequest] | type[mesh_query_pb2.GetElementalDataRequest] - ), -) -> ReadOnlyProperty[MeshDataT]: - """Create a mesh data property. - - Implementation of the mesh data property helpers ``nodal_data_property`` - and ``elemental_data_property``. - """ - - def getter(self: TreeObject) -> MeshDataT: - if not self._is_stored: - raise RuntimeError("Cannot get mesh data from an unstored object") - stub = mesh_query_pb2_grpc.MeshQueryServiceStub(self._channel) - request_func = getattr(stub, request_name) - response = request_func( - request=request_type( - resource_path=self._resource_path, - data_types=[ - wrapped_cls._PB_VALUE_FROM_FIELD_NAME(name) # type: ignore - for name in wrapped_cls._field_names() - ], - ), - ) - return wrapped_cls._from_pb(response) - - return property(getter) diff --git a/src/ansys/acp/core/_tree_objects/_object_cache.py b/src/ansys/acp/core/_tree_objects/_object_cache.py index 63ee2fa388..7c4dbcab2c 100644 --- a/src/ansys/acp/core/_tree_objects/_object_cache.py +++ b/src/ansys/acp/core/_tree_objects/_object_cache.py @@ -20,11 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from collections.abc import Iterable -from typing import Any, Callable, TypeVar +from collections.abc import Callable, Iterable +from typing import Any, Concatenate, TypeAlias, TypeVar from weakref import WeakValueDictionary -from typing_extensions import Concatenate, ParamSpec, Self, TypeAlias +from typing_extensions import ParamSpec, Self __all__ = ["ObjectCacheMixin", "constructor_with_cache"] diff --git a/src/ansys/acp/core/_tree_objects/_solid_model_export.py b/src/ansys/acp/core/_tree_objects/_solid_model_export.py new file mode 100644 index 0000000000..328d1bb7bc --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/_solid_model_export.py @@ -0,0 +1,89 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +import typing + +from ansys.api.acp.v0 import solid_model_export_pb2 + +from .._utils.typing_helper import PATH as _PATH +from ._grpc_helpers.exceptions import wrap_grpc_errors +from .base import CreatableTreeObject +from .enums import ( + SolidModelExportFormat, + SolidModelSkinExportFormat, + solid_model_export_format_to_pb, + solid_model_skin_export_format_to_pb, +) + +__all__ = ["SolidModelExportMixin"] + + +class SolidModelExportMixin(CreatableTreeObject): + """Mixin class for adding export functionality to the solid model and imported solid model classes.""" + + def export(self, path: _PATH, *, format: SolidModelExportFormat) -> None: + """Export the solid model to a file. + + Parameters + ---------- + path : + Path to the file where the solid model is saved. + format : + Format of the exported file. Available formats are ``"ansys:h5"`` + and ``"ansys:cdb"``. + + """ + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + self._get_stub().ExportToFile( # type: ignore + solid_model_export_pb2.ExportToFileRequest( + resource_path=self._resource_path, + path=export_path, + format=typing.cast(typing.Any, solid_model_export_format_to_pb(format)), + ) + ) + + def export_skin(self, path: _PATH, *, format: SolidModelSkinExportFormat) -> None: + """Export the skin of the solid model to a file. + + Parameters + ---------- + path : + Path to the file where the solid model skin is saved. + format : + Format of the exported file. Available formats are ``"ansys:cdb"``, + ``"step"``, ``"iges"``, and ``"stl"``. + + """ + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + self._get_stub().ExportSkin( # type: ignore + solid_model_export_pb2.ExportSkinRequest( + resource_path=self._resource_path, + path=export_path, + format=typing.cast( + typing.Any, solid_model_skin_export_format_to_pb(format) + ), + ) + ) diff --git a/src/ansys/acp/core/_tree_objects/analysis_ply.py b/src/ansys/acp/core/_tree_objects/analysis_ply.py index 485c427e07..ab56c7023e 100644 --- a/src/ansys/acp/core/_tree_objects/analysis_ply.py +++ b/src/ansys/acp/core/_tree_objects/analysis_ply.py @@ -30,12 +30,7 @@ from ansys.api.acp.v0 import analysis_ply_pb2, analysis_ply_pb2_grpc from .._utils.property_protocols import ReadOnlyProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property_read_only, - grpc_link_property_read_only, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, ScalarData, @@ -43,6 +38,12 @@ elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property_read_only, + grpc_link_property_read_only, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property, solid_mesh_property from .base import IdTreeObject, ReadOnlyTreeObject from .enums import status_type_from_pb from .object_registry import register @@ -96,6 +97,8 @@ class AnalysisPly(ReadOnlyTreeObject, IdTreeObject): Material of the Analysis ply. angle: float Angle of the Analysis ply in degrees. + thickness: float + Thickness of the Analysis ply active_in_post_mode: bool If False, deactivates the failure analysis for this ply during post-processing. @@ -105,6 +108,7 @@ class AnalysisPly(ReadOnlyTreeObject, IdTreeObject): _COLLECTION_LABEL = "analysis_plies" _OBJECT_INFO_TYPE = analysis_ply_pb2.ObjectInfo + _SUPPORTED_SINCE = "24.2" def _create_stub(self) -> analysis_ply_pb2_grpc.ObjectServiceStub: return analysis_ply_pb2_grpc.ObjectServiceStub(self._channel) @@ -112,8 +116,13 @@ def _create_stub(self) -> analysis_ply_pb2_grpc.ObjectServiceStub: status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) material = grpc_link_property_read_only("properties.material") angle: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.angle") + thickness: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.thickness") active_in_post_mode: ReadOnlyProperty[bool] = grpc_data_property_read_only( "properties.active_in_post_mode" ) + + mesh = full_mesh_property + shell_mesh = shell_mesh_property + solid_mesh = solid_mesh_property elemental_data = elemental_data_property(AnalysisPlyElementalData) nodal_data = nodal_data_property(AnalysisPlyNodalData) diff --git a/src/ansys/acp/core/_tree_objects/base.py b/src/ansys/acp/core/_tree_objects/base.py index 42c7afac8e..08da2df912 100644 --- a/src/ansys/acp/core/_tree_objects/base.py +++ b/src/ansys/acp/core/_tree_objects/base.py @@ -24,27 +24,34 @@ from __future__ import annotations from abc import abstractmethod -from collections.abc import Iterable +from collections.abc import Callable, Iterable, Iterator +import contextlib from dataclasses import dataclass -from functools import wraps import typing -from typing import Any, Callable, Generic, TypeVar, cast +from typing import Any, Generic, TypeVar, cast from grpc import Channel from packaging.version import Version from packaging.version import parse as parse_version -from typing_extensions import Concatenate, ParamSpec, Self, TypeAlias +from typing_extensions import Self from ansys.api.acp.v0.base_pb2 import CollectionPath, DeleteRequest, GetRequest, ResourcePath +from .._server.acp_instance import ACPInstance, FileTransferHandler +from .._utils.path_to_str import path_to_str_checked from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty from .._utils.resource_paths import common_path from .._utils.resource_paths import join as _rp_join from .._utils.resource_paths import to_parts +from .._utils.typing_helper import PATH from ._grpc_helpers.exceptions import wrap_grpc_errors -from ._grpc_helpers.linked_object_helpers import linked_path_fields, unlink_objects -from ._grpc_helpers.polymorphic_from_pb import CreatableFromResourcePath +from ._grpc_helpers.linked_object_helpers import get_linked_paths, unlink_objects +from ._grpc_helpers.polymorphic_from_pb import ( + CreatableFromResourcePath, + tree_object_from_resource_path, +) from ._grpc_helpers.property_helper import ( + _exposed_grpc_property, _get_data_attribute, grpc_data_property, grpc_data_property_read_only, @@ -62,9 +69,6 @@ ) from ._object_cache import ObjectCacheMixin, constructor_with_cache -if typing.TYPE_CHECKING: # pragma: no cover - from .._server import ACP - @mark_grpc_properties class TreeObjectBase(ObjectCacheMixin, GrpcObjectBase): @@ -74,6 +78,7 @@ class TreeObjectBase(ObjectCacheMixin, GrpcObjectBase): _COLLECTION_LABEL: str _OBJECT_INFO_TYPE: type[ObjectInfo] + _SUPPORTED_SINCE: str _pb_object: ObjectInfo name: ReadOnlyProperty[str] @@ -100,6 +105,9 @@ def __eq__(self: Self, other: Any) -> bool: return self is other return self._resource_path.value == other._resource_path.value + def __hash__(self) -> int: + return id(self) + @classmethod @constructor_with_cache( key_getter=lambda object_info, *args, **kwargs: object_info.info.resource_path.value, @@ -141,10 +149,33 @@ def _server_wrapper(self) -> ServerWrapper: assert self._server_wrapper_store is not None return self._server_wrapper_store + @property + def _server_version(self) -> Version | None: + if not self._is_stored: + return None + return self._server_wrapper.version + @property def _is_stored(self) -> bool: return self._server_wrapper_store is not None + @property + def parent(self) -> CreatableFromResourcePath: + """The parent of the object.""" + if not self._is_stored: + raise RuntimeError("Cannot get the parent of an unstored object.") + rp_parts = to_parts(self._resource_path.value) + if len(rp_parts) < 3: + raise RuntimeError("The object does not have a parent.") + + parent_path = _rp_join(*rp_parts[:-2]) + parent = tree_object_from_resource_path( + ResourcePath(value=parent_path), server_wrapper=self._server_wrapper + ) + if parent is None: + raise RuntimeError("The parent object could not be found.") + return parent + def __repr__(self) -> str: return f"<{type(self).__name__} with name '{self.name}'>" @@ -164,14 +195,34 @@ class ServerWrapper: channel: Channel version: Version + filetransfer_handler: FileTransferHandler @classmethod - def from_acp_instance(cls, acp_instance: ACP[Any]) -> ServerWrapper: + def from_acp_instance(cls, acp_instance: ACPInstance[Any]) -> ServerWrapper: """Convert an ACP instance into the wrapper needed by tree objects.""" return cls( - channel=acp_instance._channel, version=parse_version(acp_instance.server_version) + channel=acp_instance._channel, + version=parse_version(acp_instance.server_version), + filetransfer_handler=acp_instance._filetransfer_handler, + ) + + def auto_upload(self, local_path: PATH | None, allow_none: bool = False) -> str: + """Handle auto-transfer of a file to the server.""" + if local_path is None: + if allow_none: + return "" + raise TypeError("Expected a Path or str, not 'None'.") + return path_to_str_checked( + self.filetransfer_handler.upload_file_if_autotransfer(local_path) ) + @contextlib.contextmanager + def auto_download(self, local_path: PATH) -> Iterator[str]: + """Handle auto-transfer of a file from the server.""" + export_path = self.filetransfer_handler.to_export_path(local_path) + yield path_to_str_checked(export_path) + self.filetransfer_handler.download_file_if_autotransfer(export_path, local_path) + class StubStore(Generic[StubT]): """Stores a gRPC stub, and creates it on demand.""" @@ -285,6 +336,9 @@ def clone(self: Self, *, unlink: bool = False) -> Self: new_object_info.properties.CopyFrom(self._pb_object.properties) if unlink: unlink_objects(new_object_info.properties) + # Since there may be links in the unknown fields, we need to + # discard them to avoid errors when storing the object. + new_object_info.properties.DiscardUnknownFields() # type: ignore new_object_info.info.name = self._pb_object.info.name return type(self)._from_object_info(object_info=new_object_info) @@ -297,6 +351,13 @@ def store(self: Self, parent: TreeObject) -> None: Parent object to store the object under. """ self._server_wrapper_store = parent._server_wrapper + assert self._server_version is not None + if self._server_version < parse_version(self._SUPPORTED_SINCE): + raise RuntimeError( + f"The '{type(self).__name__}' object is only supported since version " + f"{self._SUPPORTED_SINCE} of the ACP gRPC server. The current server version is " + f"{self._server_version}." + ) collection_path = CollectionPath( value=_rp_join(parent._resource_path.value, self._COLLECTION_LABEL) @@ -304,7 +365,7 @@ def store(self: Self, parent: TreeObject) -> None: # check that all linked objects are located in the same model path_values = [collection_path.value] + [ - path.value for _, _, path in linked_path_fields(self._pb_object.properties) + path.value for path in get_linked_paths(self._pb_object.properties) ] # filter out empty paths path_values = [path for path in path_values if path] @@ -394,6 +455,12 @@ def _is_stored(self) -> bool: return False return self._parent_object._is_stored + @property + def _server_wrapper(self) -> ServerWrapper: + if self._parent_object is None: + raise RuntimeError("The parent object is not set.") + return self._parent_object._server_wrapper + class PolymorphicMixin(TreeObjectAttributeReadOnly): """Mixin class for attributes which can have multiple types, through a 'oneof' definition.""" @@ -452,32 +519,52 @@ def _put_if_stored(self) -> None: self._put() -T = TypeVar("T", bound=TreeObjectBase) -P = ParamSpec("P") -R = TypeVar("R") -_WRAPPED_T: TypeAlias = Callable[Concatenate[T, P], R] +class TreeObjectAttributeWithCache(ObjectCacheMixin, TreeObjectAttribute): + """Tree object attribute with instance caching.""" + + @classmethod + @constructor_with_cache( + key_getter=lambda parent_object, attribute_path: ( + parent_object, + attribute_path, + ), + raise_on_invalid_key=True, + ) + def _from_parent(cls: type[Self], /, parent_object: TreeObject, attribute_path: str) -> Self: + return cls(_parent_object=parent_object, _attribute_path=attribute_path) + + @staticmethod + def _cache_key_valid(key: tuple[Editable, str]) -> bool: + parent_object, attribute_path = key + return parent_object is not None and bool(attribute_path) + + +AttribT = TypeVar("AttribT", bound=TreeObjectAttributeWithCache) -def supported_since(version: str) -> Callable[[_WRAPPED_T[T, P, R]], _WRAPPED_T[T, P, R]]: - """Mark a TreeObjectBase method as supported since a specific server version. +def nested_grpc_object_property( + pb_path: str, + object_type: type[AttribT], +) -> ReadWriteProperty[AttribT, AttribT]: + """Create a property for a nested object attribute. - Raises an exception if the current server version does not match the required version. + Creates a property for a nested object which is backed by the parent + object's protobuf object. The property is read-write, and instances + are cached. """ - required_version = parse_version(version) - - def decorator(func: _WRAPPED_T[T, P, R]) -> _WRAPPED_T[T, P, R]: - @wraps(func) - def inner(self: T, /, *args: P.args, **kwargs: P.kwargs) -> R: - if self._server_wrapper.version < required_version: - raise RuntimeError( - f"The method '{func.__name__}' is only supported since version {version} of the ACP " - f"gRPC server. The current server version is {self._server_wrapper.version}." - ) - return func(self, *args, **kwargs) - return inner + def _getter(self: TreeObject) -> AttribT: + return object_type._from_parent(parent_object=self, attribute_path=pb_path) + + def _setter(self: TreeObject, value: AttribT) -> None: + if not isinstance(value, object_type): + raise TypeError( + f"Expected an object of type '{object_type.__name__}', got '{type(value).__name__}'." + ) + _getter(self)._pb_object.CopyFrom(value._pb_object) + self._put_if_stored() - return decorator + return _exposed_grpc_property(_getter).setter(_setter) if typing.TYPE_CHECKING: # pragma: no cover diff --git a/src/ansys/acp/core/_tree_objects/boolean_selection_rule.py b/src/ansys/acp/core/_tree_objects/boolean_selection_rule.py index 4688496ea2..a8f5615e92 100644 --- a/src/ansys/acp/core/_tree_objects/boolean_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/boolean_selection_rule.py @@ -28,19 +28,20 @@ from ansys.api.acp.v0 import boolean_selection_rule_pb2, boolean_selection_rule_pb2_grpc from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.edge_property_list import define_add_method, define_edge_property_list -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.edge_property_list import define_add_method, define_edge_property_list +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .enums import status_type_from_pb from .linked_selection_rule import LinkedSelectionRule @@ -49,7 +50,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "BooleanSelectionRule", @@ -81,7 +82,7 @@ class BooleanSelectionRule(CreatableTreeObject, IdTreeObject): Name of the Boolean Selection Rule. selection_rules : - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. """ @@ -91,17 +92,18 @@ class BooleanSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "boolean_selection_rules" _OBJECT_INFO_TYPE = boolean_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = boolean_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, *, name: str = "BooleanSelectionrule", selection_rules: Iterable[LinkedSelectionRule] = (), - include_rule_type: bool = True, + include_rule: bool = True, ): super().__init__(name=name) self.selection_rules = selection_rules - self.include_rule_type = include_rule_type + self.include_rule = include_rule def _create_stub(self) -> boolean_selection_rule_pb2_grpc.ObjectServiceStub: return boolean_selection_rule_pb2_grpc.ObjectServiceStub(self._channel) @@ -117,9 +119,11 @@ def _create_stub(self) -> boolean_selection_rule_pb2_grpc.ObjectServiceStub: module_name=__module__, ) - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") + + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(BooleanSelectionRuleElementalData) nodal_data = nodal_data_property(BooleanSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/butt_joint_sequence.py b/src/ansys/acp/core/_tree_objects/butt_joint_sequence.py new file mode 100644 index 0000000000..c45d19a2a2 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/butt_joint_sequence.py @@ -0,0 +1,225 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Callable, Iterable, Sequence +from typing import TYPE_CHECKING, Any, Union, cast + +from typing_extensions import Self + +from ansys.api.acp.v0 import butt_joint_sequence_pb2, butt_joint_sequence_pb2_grpc + +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.edge_property_list import ( + GenericEdgePropertyType, + define_add_method, + define_edge_property_list, +) +from ._grpc_helpers.linked_object_list import define_polymorphic_linked_object_list +from ._grpc_helpers.polymorphic_from_pb import tree_object_from_resource_path +from ._grpc_helpers.property_helper import ( + _exposed_grpc_property, + grpc_data_property, + grpc_data_property_read_only, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .enums import status_type_from_pb +from .modeling_ply import ModelingPly +from .object_registry import register + +if TYPE_CHECKING: # pragma: no cover + # Creates a circular import if imported at the top-level, since the ButtJointSequence + # is a direct child of the ModelingGroup. + from .modeling_group import ModelingGroup + +__all__ = ["ButtJointSequence", "PrimaryPly"] + + +@mark_grpc_properties +class PrimaryPly(GenericEdgePropertyType): + """Defines a primary ply of a butt joint sequence. + + Parameters + ---------- + sequence : + Modeling group or modeling ply defining the primary ply. + level : + Level of the primary ply. Plies with a higher level inherit the thickness + from adjacent plies with a lower level. + + """ + + _SUPPORTED_SINCE = "25.1" + + def __init__(self, sequence: ModelingGroup | ModelingPly, level: int = 1): + self._callback_apply_changes: Callable[[], None] | None = None + self.sequence = sequence + self.level = level + + @_exposed_grpc_property + def sequence(self) -> ModelingGroup | ModelingPly: + """Linked sequence.""" + return self._sequence + + @sequence.setter + def sequence(self, value: ModelingGroup | ModelingPly) -> None: + from .modeling_group import ModelingGroup + + if not isinstance(value, (ModelingGroup, ModelingPly)): + raise TypeError(f"Expected a ModelingGroup or ModelingPly, got {type(value)}") + self._sequence = value + if self._callback_apply_changes: + self._callback_apply_changes() + + @_exposed_grpc_property + def level(self) -> int: + """Level of the primary ply. + + Plies with a higher level inherit the thickness from adjacent plies with a lower level. + """ + return self._level + + @level.setter + def level(self, value: int) -> None: + self._level = value + if self._callback_apply_changes: + self._callback_apply_changes() + + def _set_callback_apply_changes(self, callback_apply_changes: Callable[[], None]) -> None: + self._callback_apply_changes = callback_apply_changes + + @classmethod + def _from_pb_object( + cls, + parent_object: CreatableTreeObject, + message: butt_joint_sequence_pb2.PrimaryPly, + apply_changes: Callable[[], None], + ) -> Self: + from .modeling_group import ModelingGroup # imported here to avoid circular import + + new_obj = cls( + sequence=cast( + Union["ModelingGroup", ModelingPly], + tree_object_from_resource_path( + message.sequence, + server_wrapper=parent_object._server_wrapper, + allowed_types=(ModelingGroup, ModelingPly), + ), + ), + level=message.level, + ) + new_obj._set_callback_apply_changes(apply_changes) + return new_obj + + def _to_pb_object(self) -> butt_joint_sequence_pb2.PrimaryPly: + return butt_joint_sequence_pb2.PrimaryPly( + sequence=self.sequence._resource_path, level=self.level + ) + + def _check(self) -> bool: + # Check for empty resource paths + return bool(self.sequence._resource_path.value) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, self.__class__): + return ( + self.sequence._resource_path == other.sequence._resource_path + and self.level == other.level + ) + + return False + + def __repr__(self) -> str: + return f"PrimaryPly(sequence={self.sequence.__repr__()}, level={self.level})" + + def clone(self) -> Self: + """Create a new unstored PrimaryPly with the same properties.""" + return type(self)(sequence=self.sequence, level=self.level) + + +def _get_allowed_secondary_ply_types() -> tuple[type, ...]: + from .modeling_group import ModelingGroup + + return (ModelingGroup, ModelingPly) + + +@mark_grpc_properties +@register +class ButtJointSequence(CreatableTreeObject, IdTreeObject): + """Instantiate a ButtJointSequence. + + Parameters + ---------- + name : + Name of the butt joint sequence. + primary_plies : + Primary plies are the source of a butt joint and they pass the thickness to + adjacent plies. Plies with a higher level inherit the thickness from those + with a lower level. + secondary_plies : + Secondary plies are butt-joined to adjacent primary plies and they inherit + the thickness. + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "butt_joint_sequences" + _OBJECT_INFO_TYPE = butt_joint_sequence_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = butt_joint_sequence_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "ButtJointSequence", + active: bool = True, + global_ply_nr: int = 0, + primary_plies: Sequence[PrimaryPly] = (), + secondary_plies: Sequence[ModelingGroup | ModelingPly] = (), + ): + super().__init__(name=name) + self.active = active + self.global_ply_nr = global_ply_nr + self.primary_plies = primary_plies + self.secondary_plies = secondary_plies + + def _create_stub(self) -> butt_joint_sequence_pb2_grpc.ObjectServiceStub: + return butt_joint_sequence_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + global_ply_nr: ReadWriteProperty[int, int] = grpc_data_property("properties.global_ply_nr") + + primary_plies = define_edge_property_list("properties.primary_plies", PrimaryPly) + add_primary_ply = define_add_method( + PrimaryPly, + attribute_name="primary_plies", + func_name="add_primary_ply", + parent_class_name="ButtJointSequence", + module_name=__module__, + ) + + secondary_plies = define_polymorphic_linked_object_list( + "properties.secondary_plies", allowed_types_getter=_get_allowed_secondary_ply_types + ) diff --git a/src/ansys/acp/core/_tree_objects/cad_component.py b/src/ansys/acp/core/_tree_objects/cad_component.py index 6d88892eb3..586da9c437 100644 --- a/src/ansys/acp/core/_tree_objects/cad_component.py +++ b/src/ansys/acp/core/_tree_objects/cad_component.py @@ -49,6 +49,7 @@ class CADComponent(ReadOnlyTreeObject, IdTreeObject): __slots__: Iterable[str] = tuple() _COLLECTION_LABEL = "cad_components" _OBJECT_INFO_TYPE = cad_component_pb2.ObjectInfo + _SUPPORTED_SINCE = "24.2" def _create_stub(self) -> cad_component_pb2_grpc.ObjectServiceStub: return cad_component_pb2_grpc.ObjectServiceStub(self._channel) diff --git a/src/ansys/acp/core/_tree_objects/cad_geometry.py b/src/ansys/acp/core/_tree_objects/cad_geometry.py index 98bca072ff..a801b6d3f3 100644 --- a/src/ansys/acp/core/_tree_objects/cad_geometry.py +++ b/src/ansys/acp/core/_tree_objects/cad_geometry.py @@ -24,13 +24,15 @@ from collections.abc import Iterable import dataclasses -from typing import cast +from typing import TYPE_CHECKING, cast import numpy as np import numpy.typing as npt -import pyvista as pv from typing_extensions import Self +if TYPE_CHECKING: # pragma: no cover + import pyvista + from ansys.api.acp.v0 import ( base_pb2, cad_component_pb2_grpc, @@ -38,10 +40,11 @@ cad_geometry_pb2_grpc, ) -from .._typing_helper import PATH from .._utils.array_conversions import to_numpy from .._utils.path_to_str import path_to_str_checked from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty +from .._utils.pyvista_import_check import requires_pyvista +from .._utils.typing_helper import PATH from ._grpc_helpers.exceptions import wrap_grpc_errors from ._grpc_helpers.mapping import get_read_only_collection_property from ._grpc_helpers.property_helper import ( @@ -71,11 +74,14 @@ def _from_pb(cls, response: cad_geometry_pb2.TriangleMeshData) -> Self: to_numpy(response.element_nodes), ) + @requires_pyvista def to_pyvista( self, - ) -> pv.PolyData: + ) -> pyvista.PolyData: """Convert the mesh data to a PyVista object.""" - return pv.PolyData.from_regular_faces( + import pyvista + + return pyvista.PolyData.from_regular_faces( points=self.node_coordinates, faces=self.element_nodes, ) @@ -108,6 +114,7 @@ class CADGeometry(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "cad_geometries" _OBJECT_INFO_TYPE = cad_geometry_pb2.ObjectInfo _CREATE_REQUEST_TYPE = cad_geometry_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -172,8 +179,15 @@ def visualization_mesh(self) -> TriangleMesh: requires_uptodate=True, ) - def refresh(self) -> None: - """Reload the geometry from its external source.""" + def refresh(self, path: PATH) -> None: + """Reload the geometry from its external source. + + Parameters + ---------- + path : + Path of the new input file. + """ + self.external_path = self._server_wrapper.auto_upload(path) stub = cast(cad_geometry_pb2_grpc.ObjectServiceStub, self._get_stub()) with wrap_grpc_errors(): stub.Refresh( diff --git a/src/ansys/acp/core/_tree_objects/cut_off_geometry.py b/src/ansys/acp/core/_tree_objects/cut_off_geometry.py new file mode 100644 index 0000000000..4a096a9eb7 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/cut_off_geometry.py @@ -0,0 +1,106 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable + +from ansys.api.acp.v0 import cut_off_geometry_pb2, cut_off_geometry_pb2_grpc + +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .enums import ( + CutOffGeometryOrientationType, + cut_off_geometry_orientation_type_from_pb, + cut_off_geometry_orientation_type_to_pb, + status_type_from_pb, +) +from .object_registry import register +from .virtual_geometry import VirtualGeometry + +__all__ = ["CutOffGeometry"] + + +@mark_grpc_properties +@register +class CutOffGeometry(CreatableTreeObject, IdTreeObject): + """Instantiate a cut-off geometry. + + Parameters + ---------- + name : + Name of the cut-off geometry. + active : + Inactive cut-off geometries are not used in the solid model extrusion. + cad_geometry : + The geometry defining the cut-off. + orientation_type : + Determines the cutting orientation of a surface/body geometry. Allows to + switch between include and exclude. + relative_merge_tolerance : + Set the merging tolerance for neighboring nodes relative to the element size. + """ + + __slots__: Iterable[str] = () + + _COLLECTION_LABEL = "cut_off_geometries" + _OBJECT_INFO_TYPE = cut_off_geometry_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = cut_off_geometry_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "CutOffGeometry", + active: bool = True, + cad_geometry: VirtualGeometry | None = None, + orientation_type: CutOffGeometryOrientationType = CutOffGeometryOrientationType.UP, + relative_merge_tolerance: float = 0.1, + ): + super().__init__( + name=name, + ) + self.active = active + self.cad_geometry = cad_geometry + self.orientation_type = orientation_type + self.relative_merge_tolerance = relative_merge_tolerance + + def _create_stub(self) -> cut_off_geometry_pb2_grpc.ObjectServiceStub: + return cut_off_geometry_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + cad_geometry = grpc_link_property("properties.cad_geometry", allowed_types=(VirtualGeometry,)) + orientation_type = grpc_data_property( + "properties.orientation", + from_protobuf=cut_off_geometry_orientation_type_from_pb, + to_protobuf=cut_off_geometry_orientation_type_to_pb, + ) + relative_merge_tolerance: ReadWriteProperty[float, float] = grpc_data_property( + "properties.relative_merge_tolerance" + ) diff --git a/src/ansys/acp/core/_tree_objects/cutoff_selection_rule.py b/src/ansys/acp/core/_tree_objects/cut_off_selection_rule.py similarity index 66% rename from src/ansys/acp/core/_tree_objects/cutoff_selection_rule.py rename to src/ansys/acp/core/_tree_objects/cut_off_selection_rule.py index 390fbd5674..402abcfaa1 100644 --- a/src/ansys/acp/core/_tree_objects/cutoff_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/cut_off_selection_rule.py @@ -28,28 +28,29 @@ from ansys.api.acp.v0 import cutoff_selection_rule_pb2, cutoff_selection_rule_pb2_grpc from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - grpc_link_property, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .edge_set import EdgeSet from .enums import ( - CutoffRuleType, - PlyCutoffType, - cutoff_rule_type_from_pb, - cutoff_rule_type_to_pb, - ply_cutoff_type_from_pb, - ply_cutoff_type_to_pb, + CutOffRuleType, + PlyCutOffType, + cut_off_rule_type_from_pb, + cut_off_rule_type_to_pb, + ply_cut_off_type_from_pb, + ply_cut_off_type_to_pb, status_type_from_pb, ) from .object_registry import register @@ -58,54 +59,54 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ - "CutoffSelectionRule", - "CutoffSelectionRuleElementalData", - "CutoffSelectionRuleNodalData", + "CutOffSelectionRule", + "CutOffSelectionRuleElementalData", + "CutOffSelectionRuleNodalData", ] @dataclasses.dataclass -class CutoffSelectionRuleElementalData(ElementalData): - """Represents elemental data for a Cutoff Selection Rule.""" +class CutOffSelectionRuleElementalData(ElementalData): + """Represents elemental data for a Cut-Off Selection Rule.""" normal: VectorData | None = None @dataclasses.dataclass -class CutoffSelectionRuleNodalData(NodalData): - """Represents nodal data for a Cutoff Selection Rule.""" +class CutOffSelectionRuleNodalData(NodalData): + """Represents nodal data for a Cut-Off Selection Rule.""" @mark_grpc_properties @register -class CutoffSelectionRule(CreatableTreeObject, IdTreeObject): - """Instantiate a Cutoff Selection Rule. +class CutOffSelectionRule(CreatableTreeObject, IdTreeObject): + """Instantiate a Cut-Off Selection Rule. Parameters ---------- name : - Name of the Cutoff Selection Rule. - cutoff_rule_type : + Name of the Cut-Off Selection Rule. + cut_off_rule_type : Determines if the cut-off is defined by a geometry or by a tapering edge. - cutoff_geometry : + cut_off_geometry : Geometry used to define the cut-off. Only applies if - ``cutoff_rule_type`` is GEOMETRY. + ``cut_off_rule_type`` is GEOMETRY. taper_edge_set : Edge used to define the cut-off. Only applies if - ``cutoff_rule_type`` is :attr:`.CutoffRuleType.TAPER`. + ``cut_off_rule_type`` is :attr:`.CutOffRuleType.TAPER`. offset : Moves the cutting plane along the out-of-plane direction. Always measured from the reference surface. angle : Defines the angle between the cutting plane and the reference surface. - ply_cutoff_type : + ply_cut_off_type : Either the complete production ply is cut-off - (:attr:`PlyCutoffType.PRODUCTION_PLY_CUTOFF`) or individual analysis plies - (:attr:`PlyCutoffType.ANALYSIS_PLY_CUTOFF`). + (:attr:`PlyCutOffType.PRODUCTION_PLY_CUTOFF`) or individual analysis plies + (:attr:`PlyCutOffType.ANALYSIS_PLY_CUTOFF`). ply_tapering : Whether the tapering of analysis plies is enabled. """ @@ -115,26 +116,27 @@ class CutoffSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "cutoff_selection_rules" _OBJECT_INFO_TYPE = cutoff_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = cutoff_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, *, - name: str = "CutoffSelectionrule", - cutoff_rule_type: CutoffRuleType = CutoffRuleType.GEOMETRY, - cutoff_geometry: VirtualGeometry | None = None, + name: str = "CutOffSelectionrule", + cut_off_rule_type: CutOffRuleType = CutOffRuleType.GEOMETRY, + cut_off_geometry: VirtualGeometry | None = None, taper_edge_set: EdgeSet | None = None, offset: float = 0.0, angle: float = 0.0, - ply_cutoff_type: PlyCutoffType = PlyCutoffType.PRODUCTION_PLY_CUTOFF, + ply_cut_off_type: PlyCutOffType = PlyCutOffType.PRODUCTION_PLY_CUTOFF, ply_tapering: bool = False, ): super().__init__(name=name) - self.cutoff_rule_type = cutoff_rule_type - self.cutoff_geometry = cutoff_geometry + self.cut_off_rule_type = cut_off_rule_type + self.cut_off_geometry = cut_off_geometry self.taper_edge_set = taper_edge_set self.offset = offset self.angle = angle - self.ply_cutoff_type = ply_cutoff_type + self.ply_cut_off_type = ply_cut_off_type self.ply_tapering = ply_tapering def _create_stub(self) -> cutoff_selection_rule_pb2_grpc.ObjectServiceStub: @@ -142,23 +144,26 @@ def _create_stub(self) -> cutoff_selection_rule_pb2_grpc.ObjectServiceStub: status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) - cutoff_rule_type = grpc_data_property( + cut_off_rule_type = grpc_data_property( "properties.cutoff_rule_type", - from_protobuf=cutoff_rule_type_from_pb, - to_protobuf=cutoff_rule_type_to_pb, + from_protobuf=cut_off_rule_type_from_pb, + to_protobuf=cut_off_rule_type_to_pb, ) - cutoff_geometry = grpc_link_property( + cut_off_geometry = grpc_link_property( "properties.cutoff_geometry", allowed_types=VirtualGeometry ) taper_edge_set = grpc_link_property("properties.taper_edge_set", allowed_types=EdgeSet) offset: ReadWriteProperty[float, float] = grpc_data_property("properties.offset") angle: ReadWriteProperty[float, float] = grpc_data_property("properties.angle") - ply_cutoff_type = grpc_data_property( + ply_cut_off_type = grpc_data_property( "properties.ply_cutoff_type", - from_protobuf=ply_cutoff_type_from_pb, - to_protobuf=ply_cutoff_type_to_pb, + from_protobuf=ply_cut_off_type_from_pb, + to_protobuf=ply_cut_off_type_to_pb, ) ply_tapering: ReadWriteProperty[bool, bool] = grpc_data_property("properties.ply_tapering") - elemental_data = elemental_data_property(CutoffSelectionRuleElementalData) - nodal_data = nodal_data_property(CutoffSelectionRuleNodalData) + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data + elemental_data = elemental_data_property(CutOffSelectionRuleElementalData) + nodal_data = nodal_data_property(CutOffSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/cylindrical_selection_rule.py b/src/ansys/acp/core/_tree_objects/cylindrical_selection_rule.py index b3e1e232c6..712d6e9775 100644 --- a/src/ansys/acp/core/_tree_objects/cylindrical_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/cylindrical_selection_rule.py @@ -29,19 +29,20 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - grpc_link_property, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .enums import status_type_from_pb from .object_registry import register @@ -50,7 +51,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "CylindricalSelectionRule", @@ -91,9 +92,9 @@ class CylindricalSelectionRule(CreatableTreeObject, IdTreeObject): Direction of the Cylindrical Selection Rule. radius : Radius of the cylinder. - relative_rule_type : + relative_rule : If True, parameters are evaluated relative to size of the object. - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. """ @@ -103,6 +104,7 @@ class CylindricalSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "cylindrical_selection_rules" _OBJECT_INFO_TYPE = cylindrical_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = cylindrical_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -113,8 +115,8 @@ def __init__( origin: tuple[float, ...] = (0.0, 0.0, 0.0), direction: tuple[float, ...] = (0.0, 0.0, 1.0), radius: float = 0.0, - relative_rule_type: bool = False, - include_rule_type: bool = True, + relative_rule: bool = False, + include_rule: bool = True, ): super().__init__(name=name) self.use_global_coordinate_system = use_global_coordinate_system @@ -122,8 +124,8 @@ def __init__( self.origin = origin self.direction = direction self.radius = radius - self.relative_rule_type = relative_rule_type - self.include_rule_type = include_rule_type + self.relative_rule = relative_rule + self.include_rule = include_rule def _create_stub(self) -> cylindrical_selection_rule_pb2_grpc.ObjectServiceStub: return cylindrical_selection_rule_pb2_grpc.ObjectServiceStub(self._channel) @@ -141,12 +143,13 @@ def _create_stub(self) -> cylindrical_selection_rule_pb2_grpc.ObjectServiceStub: "properties.direction", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array ) radius: ReadWriteProperty[float, float] = grpc_data_property("properties.radius") - relative_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( + relative_rule: ReadWriteProperty[bool, bool] = grpc_data_property( "properties.relative_rule_type" ) - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(CylindricalSelectionRuleElementalData) nodal_data = nodal_data_property(CylindricalSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/edge_set.py b/src/ansys/acp/core/_tree_objects/edge_set.py index 636cd25787..a02dafe226 100644 --- a/src/ansys/acp/core/_tree_objects/edge_set.py +++ b/src/ansys/acp/core/_tree_objects/edge_set.py @@ -75,6 +75,7 @@ class EdgeSet(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "edge_sets" _OBJECT_INFO_TYPE = edge_set_pb2.ObjectInfo _CREATE_REQUEST_TYPE = edge_set_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_tree_objects/element_set.py b/src/ansys/acp/core/_tree_objects/element_set.py index 4fa67900dc..0b9363d1cb 100644 --- a/src/ansys/acp/core/_tree_objects/element_set.py +++ b/src/ansys/acp/core/_tree_objects/element_set.py @@ -29,18 +29,19 @@ from .._utils.array_conversions import to_1D_int_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .enums import status_type_from_pb from .object_registry import register @@ -48,7 +49,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "ElementSet", @@ -88,6 +89,7 @@ class ElementSet(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "element_sets" _OBJECT_INFO_TYPE = element_set_pb2.ObjectInfo _CREATE_REQUEST_TYPE = element_set_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -112,5 +114,7 @@ def _create_stub(self) -> element_set_pb2_grpc.ObjectServiceStub: to_protobuf=to_1D_int_array, ) + mesh = full_mesh_property + shell_mesh = shell_mesh_property elemental_data = elemental_data_property(ElementSetElementalData) nodal_data = nodal_data_property(ElementSetNodalData) diff --git a/src/ansys/acp/core/_tree_objects/enums.py b/src/ansys/acp/core/_tree_objects/enums.py index 90b7fbeae8..7874dac778 100644 --- a/src/ansys/acp/core/_tree_objects/enums.py +++ b/src/ansys/acp/core/_tree_objects/enums.py @@ -21,20 +21,26 @@ # SOFTWARE. from ansys.api.acp.v0 import ( + cut_off_geometry_pb2, cut_off_material_pb2, cutoff_selection_rule_pb2, drop_off_material_pb2, edge_set_pb2, enum_types_pb2, + extrusion_guide_pb2, geometrical_selection_rule_pb2, + imported_modeling_ply_pb2, + layup_mapping_object_pb2, lookup_table_3d_pb2, lookup_table_column_type_pb2, mesh_query_pb2, modeling_ply_pb2, - ply_geometry_export_pb2, ply_material_pb2, rosette_pb2, + section_cut_pb2, sensor_pb2, + snap_to_geometry_pb2, + solid_model_pb2, unit_system_pb2, virtual_geometry_pb2, ) @@ -43,27 +49,45 @@ __all__ = [ "ArrowType", + "BaseElementMaterialHandling", "BooleanOperationType", - "CutoffMaterialType", - "CutoffRuleType", - "DimensionType", - "DrapingMaterialType", + "CutOffGeometryOrientationType", + "CutOffMaterialHandling", + "CutOffRuleType", + "DrapingMaterialModel", "DrapingType", - "DropoffMaterialType", + "DropOffMaterialHandling", + "DropOffType", "EdgeSetType", "ElementalDataType", + "ElementTechnology", + "ExtrusionGuideType", + "ExtrusionMethod", + "ExtrusionType", "GeometricalRuleType", + "ImportedPlyDrapingType", + "ImportedPlyOffsetType", + "ImportedPlyThicknessType", + "IntersectionType", "LookUpTable3DInterpolationAlgorithm", "LookUpTableColumnValueType", "NodalDataType", "OffsetType", - "PlyCutoffType", + "PhysicalDimension", + "PlyCutOffType", "PlyGeometryExportFormat", "PlyType", + "ReinforcingBehavior", "RosetteSelectionMethod", "RosetteType", + "SectionCutType", "SensorType", - "StatusType", + "SnapToGeometryOrientationType", + "SolidModelExportFormat", + "SolidModelOffsetDirectionType", + "SolidModelSkinExportFormat", + "Status", + "StressStateType", "SymmetryType", "ThicknessFieldType", "ThicknessType", @@ -71,8 +95,8 @@ "VirtualGeometryDimension", ] -(StatusType, status_type_to_pb, status_type_from_pb) = wrap_to_string_enum( - "StatusType", +(Status, status_type_to_pb, status_type_from_pb) = wrap_to_string_enum( + "Status", enum_types_pb2.StatusType, module=__name__, value_converter=lambda val: val, @@ -90,22 +114,22 @@ ) ( - CutoffMaterialType, + CutOffMaterialHandling, cut_off_material_type_to_pb, cut_off_material_type_from_pb, ) = wrap_to_string_enum( - "CutoffMaterialType", + "CutOffMaterialHandling", cut_off_material_pb2.MaterialHandlingType, module=__name__, doc="Options for how cut-off material is selected.", ) ( - DropoffMaterialType, + DropOffMaterialHandling, drop_off_material_type_to_pb, drop_off_material_type_from_pb, ) = wrap_to_string_enum( - "DropoffMaterialType", + "DropOffMaterialHandling", drop_off_material_pb2.MaterialHandlingType, module=__name__, doc="Options for how drop-off material is selected.", @@ -123,11 +147,26 @@ ) ( - DrapingMaterialType, + ImportedPlyDrapingType, + imported_ply_draping_type_to_pb, + imported_ply_draping_type_from_pb, +) = wrap_to_string_enum( + "ImportedPlyDrapingType", + ply_material_pb2.DrapingType, + module=__name__, + doc="Options for the draping algorithm used.", + explicit_value_list=( + ply_material_pb2.DrapingType.NO_DRAPING, + ply_material_pb2.DrapingType.TABULAR_VALUES, + ), +) + +( + DrapingMaterialModel, draping_material_type_to_pb, draping_material_type_from_pb, ) = wrap_to_string_enum( - "DrapingMaterialType", + "DrapingMaterialModel", ply_material_pb2.DrapingMaterialType, module=__name__, doc="Options for the material type used in the draping algorithm.", @@ -211,14 +250,17 @@ unit_system_pb2.UnitSystemType, module=__name__, doc="Available choices for the unit system.", + # When loading from a file, the value 'from_file' is more descriptive than 'undefined', + # so we add an alias for it. + extra_aliases={"undefined": ("FROM_FILE", "from_file")}, ) ( - DimensionType, - dimension_type_to_pb, - dimension_type_from_pb, + PhysicalDimension, + physical_dimension_to_pb, + physical_dimension_from_pb, ) = wrap_to_string_enum( - "DimensionType", + "PhysicalDimension", unit_system_pb2.DimensionType, module=__name__, doc="Options for the dimension (time, length, currency, ...) of data.", @@ -302,25 +344,25 @@ ) ( - CutoffRuleType, - cutoff_rule_type_to_pb, - cutoff_rule_type_from_pb, + CutOffRuleType, + cut_off_rule_type_to_pb, + cut_off_rule_type_from_pb, ) = wrap_to_string_enum( - "CutoffRuleType", + "CutOffRuleType", cutoff_selection_rule_pb2.CutoffRuleType, module=__name__, - doc="Options for how a cutoff rule is defined.", + doc="Options for how a cut off rule is defined.", ) ( - PlyCutoffType, - ply_cutoff_type_to_pb, - ply_cutoff_type_from_pb, + PlyCutOffType, + ply_cut_off_type_to_pb, + ply_cut_off_type_from_pb, ) = wrap_to_string_enum( - "PlyCutoffType", + "PlyCutOffType", cutoff_selection_rule_pb2.PlyCutoffType, module=__name__, - doc="Options for how ply cutoff is computed.", + doc="Options for how ply cut-off is computed.", ) ( @@ -344,6 +386,21 @@ doc="Options for how ply thickness is defined.", ) +( + ImportedPlyThicknessType, + imported_ply_thickness_type_to_pb, + imported_ply_thickness_type_from_pb, +) = wrap_to_string_enum( + "ImportedPlyThicknessType", + modeling_ply_pb2.ThicknessType, + module=__name__, + doc="Options for how ply thickness is defined.", + explicit_value_list=( + modeling_ply_pb2.ThicknessType.NOMINAL, + modeling_ply_pb2.ThicknessType.FROM_TABLE, + ), +) + ( ThicknessFieldType, thickness_field_type_to_pb, @@ -357,7 +414,192 @@ (PlyGeometryExportFormat, ply_geometry_export_format_to_pb, _) = wrap_to_string_enum( "PlyGeometryExportFormat", - ply_geometry_export_pb2.ExportFormat, + enum_types_pb2.FileFormat, module=__name__, doc="Options for the file format of the ply geometry export.", + explicit_value_list=( + enum_types_pb2.FileFormat.STEP, + enum_types_pb2.FileFormat.IGES, + enum_types_pb2.FileFormat.STL, + ), +) + +( + ImportedPlyOffsetType, + imported_ply_offset_type_to_pb, + imported_ply_offset_type_from_pb, +) = wrap_to_string_enum( + "ImportedPlyOffsetType", + enum_types_pb2.OffsetType, + module=__name__, + doc="Options for the definition of the offset.", + explicit_value_list=( + enum_types_pb2.OffsetType.MIDDLE_OFFSET, + enum_types_pb2.OffsetType.BOTTOM_OFFSET, + enum_types_pb2.OffsetType.TOP_OFFSET, + ), +) + +( + MeshImportType, + mesh_import_type_to_pb, + mesh_import_type_from_pb, +) = wrap_to_string_enum( + "MeshImportType", + imported_modeling_ply_pb2.MeshImportType, + module=__name__, + doc="Options for the definition of the source of the imported mesh.", +) + +(ExtrusionType, extrusion_type_to_pb, extrusion_type_from_pb) = wrap_to_string_enum( + "ExtrusionType", + section_cut_pb2.ExtrusionType, + module=__name__, + doc="Extrusion method used in a section cut.", +) + +(SectionCutType, section_cut_type_to_pb, section_cut_type_from_pb) = wrap_to_string_enum( + "SectionCutType", + section_cut_pb2.SectionCutType, + module=__name__, + doc="Determines whether the section cut is extruded by modeling ply, production ply, or analysis ply.", +) + +(IntersectionType, intersection_type_to_pb, intersection_type_from_pb) = wrap_to_string_enum( + "IntersectionType", + section_cut_pb2.IntersectionType, + module=__name__, + doc="Determines how the intersection is computed for wireframe section cuts.", +) + +(ExtrusionMethod, extrusion_method_type_to_pb, extrusion_method_type_from_pb) = wrap_to_string_enum( + "ExtrusionMethod", + solid_model_pb2.ExtrusionMethodType, + module=__name__, + doc="Extrusion method used in a solid model.", +) + +(ExtrusionGuideType, extrusion_guide_type_to_pb, extrusion_guide_type_from_pb) = ( + wrap_to_string_enum( + "ExtrusionGuideType", + extrusion_guide_pb2.ExtrusionGuideType, + module=__name__, + doc="Extrusion guide type used in an extrusion guide (solid model).", + ) +) + +(SolidModelOffsetDirectionType, offset_direction_type_to_pb, offset_direction_type_from_pb) = ( + wrap_to_string_enum( + "SolidModelOffsetDirectionType", + solid_model_pb2.OffsetDirectionType, + module=__name__, + doc=( + "Determines how the offset direction is evaluated in a solid model. With " + "``SURFACE_NORMAL``, the offset direction is re-evaluated based on the " + "surface of the solid. With ``SHELL_NORMAL``, the direction is based on the " + "shell surface." + ), + ) +) +(DropOffType, drop_off_type_to_pb, drop_off_type_from_pb) = wrap_to_string_enum( + "DropOffType", + solid_model_pb2.DropOffType, + module=__name__, + doc="Determines whether the drop off in solid models is inside or outside the ply boundary.", +) + +SolidModelExportFormat, solid_model_export_format_to_pb, _ = wrap_to_string_enum( + "SolidModelExportFormat", + enum_types_pb2.FileFormat, + module=__name__, + value_converter=lambda val: val.lower().replace("_", ":"), + doc="Options for the export format of solid models.", + explicit_value_list=( + enum_types_pb2.FileFormat.ANSYS_H5, + enum_types_pb2.FileFormat.ANSYS_CDB, + ), +) + +SolidModelSkinExportFormat, solid_model_skin_export_format_to_pb, _ = wrap_to_string_enum( + "SolidModelSkinExportFormat", + enum_types_pb2.FileFormat, + module=__name__, + value_converter=lambda val: val.lower().replace("_", ":"), + doc="Options for the export format of solid model skins.", + explicit_value_list=( + enum_types_pb2.FileFormat.ANSYS_CDB, + enum_types_pb2.FileFormat.STEP, + enum_types_pb2.FileFormat.IGES, + enum_types_pb2.FileFormat.STL, + ), +) + + +def _prefix_undefined(value: str) -> str: + if value == "UNDEFINED": + return "_UNDEFINED" + return value + + +( + SnapToGeometryOrientationType, + snap_to_geometry_orientation_type_to_pb, + snap_to_geometry_orientation_type_from_pb, +) = wrap_to_string_enum( + "SnapToGeometryOrientationType", + snap_to_geometry_pb2.OrientationType, + module=__name__, + key_converter=_prefix_undefined, + value_converter=lambda val: _prefix_undefined(val).lower(), + doc=( + "Determines which layup face a snap-to geometry is applies to. Note that the " + "``_UNDEFINED`` option should not be used. It is equivalent to using " + "``BOTTOM``, and included only for compatibility with existing models." + ), +) +( + CutOffGeometryOrientationType, + cut_off_geometry_orientation_type_to_pb, + cut_off_geometry_orientation_type_from_pb, +) = wrap_to_string_enum( + "CutOffGeometryOrientationType", + cut_off_geometry_pb2.OrientationType, + module=__name__, + doc="Determines the orientation of a cut-off geometry.", +) + +ElementTechnology, element_technology_to_pb, element_technology_from_pb = wrap_to_string_enum( + "ElementTechnology", + layup_mapping_object_pb2.ElementTechnology, + module=__name__, + doc=("Options for the element technology used in a layup mapping object."), +) + +ReinforcingBehavior, reinforcing_behavior_to_pb, reinforcing_behavior_from_pb = wrap_to_string_enum( + "ReinforcingBehavior", + layup_mapping_object_pb2.ReinforcingBehavior, + module=__name__, + doc=( + "Specifies whether the reinforcing elements carry tension and compression load, or only one of them." + ), +) + +( + BaseElementMaterialHandling, + base_element_material_handling_to_pb, + base_element_material_handling_from_pb, +) = wrap_to_string_enum( + "BaseElementMaterialHandling", + layup_mapping_object_pb2.BaseElementMaterialHandlingType, + module=__name__, + doc=( + "Determines how the base material is handled where it intersects with a reinforcing element." + ), +) +StressStateType, stress_state_type_to_pb, stress_state_type_from_pb = wrap_to_string_enum( + "StressStateType", + layup_mapping_object_pb2.StressStateType, + module=__name__, + doc="Specifies if the reinforcing elements should behave like a link, membrane, or shell " + "element (with or without bending).", ) diff --git a/src/ansys/acp/core/_tree_objects/extrusion_guide.py b/src/ansys/acp/core/_tree_objects/extrusion_guide.py new file mode 100644 index 0000000000..77893a8388 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/extrusion_guide.py @@ -0,0 +1,191 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable +from functools import reduce + +from google.protobuf.message import Message + +from ansys.api.acp.v0 import extrusion_guide_pb2, extrusion_guide_pb2_grpc + +from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.property_helper import ( + _PROTOBUF_T, + _get_data_attribute, + _set_data_attribute, + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._grpc_helpers.protocols import ObjectInfo +from .base import CreatableTreeObject, IdTreeObject +from .edge_set import EdgeSet +from .enums import ( + ExtrusionGuideType, + extrusion_guide_type_from_pb, + extrusion_guide_type_to_pb, + status_type_from_pb, +) +from .object_registry import register +from .virtual_geometry import VirtualGeometry + +# Workaround: these imports are needed to make sphinx_autodoc_typehints understand +# the inherited members of the Elemental- and NodalData classes. +import numpy as np # noqa: F401 isort:skip + +__all__ = [ + "ExtrusionGuide", +] + + +@mark_grpc_properties +@register +class ExtrusionGuide(CreatableTreeObject, IdTreeObject): + """Instantiate an Extrusion Guide of a Solid Model. + + Parameters + ---------- + name : + The name of the Oriented Selection Set. + active : + Inactive extrusion guides are not used in the solid model extrusion. + edge_set : + Edge Set along which the Extrusion Guide acts. + extrusion_guide_type : + Type of the extrusion such as by direction or by geometry. + cad_geometry : + CAD geometry along which the extrusion guide runs. + Needed if the extrusion type is set to :attr:`ExtrusionGuideType.BY_GEOMETRY`. + direction : + Direction along which the extrusion guide runs. Need if + the extrusion type is set to :attr:`ExtrusionGuideType.BY_DIRECTION`. + radius : + Controls the sphere of influence for mesh morphing. + depth : + Defines the bias of the mesh morphing. + use_curvature_correction : + Apply a curvature correction during the solid model extrusion which results in + a smoother extruded surface. Under certain circumstances, deactivating + curvature correction can lead to better extrusion results. + + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "extrusion_guides" + _OBJECT_INFO_TYPE = extrusion_guide_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = extrusion_guide_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "ExtrusionGuide", + active: bool = True, + edge_set: EdgeSet | None = None, + extrusion_guide_type: ExtrusionGuideType = "by_direction", + cad_geometry: VirtualGeometry | None = None, + direction: tuple[float, float, float] = (0.0, 0.0, 1.0), + radius: float = 0.0, + depth: float = 0.0, + use_curvature_correction: bool = False, + ): + super().__init__(name=name) + self.active = active + self.edge_set = edge_set + self.extrusion_guide_type = ExtrusionGuideType(extrusion_guide_type) + self.cad_geometry = cad_geometry + self.direction = direction + self.radius = radius + self.depth = depth + self.use_curvature_correction = use_curvature_correction + + def _create_stub(self) -> extrusion_guide_pb2_grpc.ObjectServiceStub: + return extrusion_guide_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + + edge_set = grpc_link_property("properties.edge_set", allowed_types=(EdgeSet,)) + + extrusion_guide_type = grpc_data_property( + "properties.extrusion_guide_type", + from_protobuf=extrusion_guide_type_from_pb, + to_protobuf=extrusion_guide_type_to_pb, + ) + + cad_geometry = grpc_link_property("properties.cad_geometry", allowed_types=(VirtualGeometry,)) + + radius: ReadWriteProperty[float, float] = grpc_data_property("properties.radius") + + depth: ReadWriteProperty[float, float] = grpc_data_property("properties.depth") + + use_curvature_correction: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.use_curvature_correction" + ) + + # The extrusion guide type is not stored by the backend directly. Instead, + # it is derived from the property direction. Therefore, setting and getting + # the direction is blocked if the extrusion guide type is not `by_direction`. + # See Feature 1122546 in ADO. Once resolved, this check becomes obsolete + # but ensure backward compatibility. + @staticmethod + def _set_direction_attribute(pb_obj: ObjectInfo, name: str, value: _PROTOBUF_T) -> None: + # name is "properties.direction" + if ( + hasattr(pb_obj.properties, "extrusion_guide_type") + and getattr(pb_obj.properties, "extrusion_guide_type") + != extrusion_guide_pb2.BY_DIRECTION + ): + array = to_tuple_from_1D_array(value) + if array and sum(array) != 0: + raise RuntimeError( + "Cannot set direction if extrusion guide type is not 'by_direction'!" + ) + _set_data_attribute(pb_obj, name, value) + + @staticmethod + def _get_direction_attribute(pb_obj: Message, name: str, check_optional: bool) -> _PROTOBUF_T: + # name is "properties.direction" + name_parts = name.split(".") + parent_obj = reduce(getattr, name_parts[:-1], pb_obj) + if ( + hasattr(parent_obj, "extrusion_guide_type") + and getattr(parent_obj, "extrusion_guide_type") != extrusion_guide_pb2.BY_DIRECTION + ): + raise RuntimeError( + "Cannot access direction if the extrusion guide type is not 'by_direction'!" + ) + return _get_data_attribute(pb_obj, name, check_optional) + + direction = grpc_data_property( + "properties.direction", + from_protobuf=to_tuple_from_1D_array, + to_protobuf=to_1D_double_array, + setter_func=_set_direction_attribute, + getter_func=_get_direction_attribute, + ) diff --git a/src/ansys/acp/core/_tree_objects/fabric.py b/src/ansys/acp/core/_tree_objects/fabric.py index 246d5ff458..5fd8e69afa 100644 --- a/src/ansys/acp/core/_tree_objects/fabric.py +++ b/src/ansys/acp/core/_tree_objects/fabric.py @@ -35,9 +35,9 @@ ) from .base import CreatableTreeObject, IdTreeObject from .enums import ( - CutoffMaterialType, - DrapingMaterialType, - DropoffMaterialType, + CutOffMaterialHandling, + DrapingMaterialModel, + DropOffMaterialHandling, cut_off_material_type_from_pb, cut_off_material_type_to_pb, draping_material_type_from_pb, @@ -71,8 +71,12 @@ class Fabric(CreatableTreeObject, IdTreeObject): Enable this option that the failure computation skips all plies made of this fabric. drop_off_material_handling : Defines the material of drop-off elements in the solid model extrusion. + drop_off_material : + Specify the material of drop-off elements in the solid model. cut_off_material_handling : Defines the material of cut-off elements in solid models if cut-off geometries are active. + cut_off_material : + Define the cut-off material if a ply with this material is shaped by a cut-off geometry. draping_material_model : Specifies the draping model of the fabric. draping_ud_coefficient : @@ -86,6 +90,7 @@ class Fabric(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "fabrics" _OBJECT_INFO_TYPE = fabric_pb2.ObjectInfo _CREATE_REQUEST_TYPE = fabric_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -95,9 +100,11 @@ def __init__( thickness: float = 0.0, area_price: float = 0.0, ignore_for_postprocessing: bool = False, - drop_off_material_handling: DropoffMaterialType = "global", - cut_off_material_handling: CutoffMaterialType = "computed", - draping_material_model: DrapingMaterialType = "woven", + drop_off_material_handling: DropOffMaterialHandling = "global", + drop_off_material: Material | None = None, + cut_off_material_handling: CutOffMaterialHandling = "computed", + cut_off_material: Material | None = None, + draping_material_model: DrapingMaterialModel = "woven", draping_ud_coefficient: float = 0.0, ): super().__init__(name=name) @@ -106,9 +113,11 @@ def __init__( self.thickness = thickness self.area_price = area_price self.ignore_for_postprocessing = ignore_for_postprocessing - self.drop_off_material_handling = DropoffMaterialType(drop_off_material_handling) - self.cut_off_material_handling = CutoffMaterialType(cut_off_material_handling) - self.draping_material_model = DrapingMaterialType(draping_material_model) + self.drop_off_material_handling = DropOffMaterialHandling(drop_off_material_handling) + self.drop_off_material = drop_off_material + self.cut_off_material_handling = CutOffMaterialHandling(cut_off_material_handling) + self.cut_off_material = cut_off_material + self.draping_material_model = DrapingMaterialModel(draping_material_model) self.draping_ud_coefficient = draping_ud_coefficient def _create_stub(self) -> fabric_pb2_grpc.ObjectServiceStub: @@ -129,17 +138,29 @@ def _create_stub(self) -> fabric_pb2_grpc.ObjectServiceStub: from_protobuf=drop_off_material_type_from_pb, to_protobuf=drop_off_material_type_to_pb, ) + drop_off_material = grpc_link_property( + "properties.drop_off_material", + allowed_types=Material, + readable_since="25.1", + writable_since="25.1", + ) cut_off_material_handling = grpc_data_property( "properties.cut_off_material_handling", from_protobuf=cut_off_material_type_from_pb, to_protobuf=cut_off_material_type_to_pb, ) + cut_off_material = grpc_link_property( + "properties.cut_off_material", + allowed_types=Material, + readable_since="25.1", + writable_since="25.1", + ) + draping_material_model = grpc_data_property( "properties.draping_material_model", from_protobuf=draping_material_type_from_pb, to_protobuf=draping_material_type_to_pb, ) - draping_ud_coefficient: ReadWriteProperty[float, float] = grpc_data_property( "properties.draping_ud_coefficient" ) diff --git a/src/ansys/acp/core/_tree_objects/field_definition.py b/src/ansys/acp/core/_tree_objects/field_definition.py new file mode 100644 index 0000000000..dc70e4391c --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/field_definition.py @@ -0,0 +1,145 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable, Sequence +import typing + +from ansys.api.acp.v0 import field_definition_pb2, field_definition_pb2_grpc + +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.linked_object_list import define_polymorphic_linked_object_list +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .element_set import ElementSet +from .enums import status_type_from_pb +from .lookup_table_1d_column import LookUpTable1DColumn +from .lookup_table_3d_column import LookUpTable3DColumn +from .modeling_ply import ModelingPly +from .object_registry import register +from .oriented_selection_set import OrientedSelectionSet + +__all__ = ["FieldDefinition"] + + +_SCOPE_ENTITIES_LINKABLE_TO_FIELD_DEFINITION: typing.TypeAlias = typing.Union[ + ElementSet, + OrientedSelectionSet, + ModelingPly, +] + + +@mark_grpc_properties +@register +class FieldDefinition(CreatableTreeObject, IdTreeObject): + """Instantiate a Field Definition. + + A field definition is used to configure the state of variable materials. + For instance, a Lookup Table can be used to define the distribution of a + state of material field (e.g. degradation). The field definition allows + to define the material field per element or per ply and element. + + Note: Field definitions are currently only supported through (Py)Mechanical. + The direct interface of PyACP to (Py)MADL ignores field definitions. + + Parameters + ---------- + name : + Name of the field definition. + active : + Inactive field definitions are ignored. + field_variable_name : + Links the material field to a field variable name. + The field variable name must be defined in the material properties. + Note that the ``Temperature`` and ``Shear Angle`` field variables are not available + for Field Definitions. Temperature is defined through the solution and `Shear Angle` + is defined via draping calculations. + scope_entities : + Define the scope of the field definition. You can use a combination + of Element Sets and Oriented Selection Sets for elemental scope. + For ply-wise field definitions, a combination of Modeling Plies can be selected. + Note that the field definition is only applied to the Analysis Plies attached to + the Modeling Plies. A combination of elemental and ply-wise definition is not supported. + scalar_field : + Select the scalar Look-Up Table column from which the state of the field + variable is interpolated from. + full_mapping : + Specify to include the shell offset of each analysis ply for the interpolation process. + The default is to interpolate the state of the field variables at the shell element centroid. + For solid elements, the position of each analysis ply is automatically considered. + + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "field_definitions" + _OBJECT_INFO_TYPE = field_definition_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = field_definition_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "FieldDefinition", + active: bool = True, + field_variable_name: str = "", + scope_entities: Sequence[_SCOPE_ENTITIES_LINKABLE_TO_FIELD_DEFINITION] = tuple(), + scalar_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, + full_mapping: bool = False, + ): + super().__init__(name=name) + + self.active = active + self.field_variable_name = field_variable_name + self.scope_entities = scope_entities + self.scalar_field = scalar_field + self.full_mapping = full_mapping + + def _create_stub(self) -> field_definition_pb2_grpc.ObjectServiceStub: + return field_definition_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + field_variable_name: ReadWriteProperty[str, str] = grpc_data_property( + "properties.field_variable_name" + ) + + scope_entities = define_polymorphic_linked_object_list( + "properties.scope_entities", + allowed_types=( + ElementSet, + OrientedSelectionSet, + ModelingPly, + ), + ) + + scalar_field = grpc_link_property( + "properties.scalar_field", allowed_types=(LookUpTable1DColumn, LookUpTable3DColumn) + ) + + full_mapping: ReadWriteProperty[bool, bool] = grpc_data_property("properties.full_mapping") diff --git a/src/ansys/acp/core/_tree_objects/geometrical_selection_rule.py b/src/ansys/acp/core/_tree_objects/geometrical_selection_rule.py index 68af4e1a25..fed15c5bdc 100644 --- a/src/ansys/acp/core/_tree_objects/geometrical_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/geometrical_selection_rule.py @@ -28,6 +28,13 @@ from ansys.api.acp.v0 import geometrical_selection_rule_pb2, geometrical_selection_rule_pb2_grpc from .._utils.property_protocols import ReadWriteProperty +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + VectorData, + elemental_data_property, + nodal_data_property, +) from ._grpc_helpers.linked_object_list import define_linked_object_list from ._grpc_helpers.property_helper import ( grpc_data_property, @@ -35,13 +42,7 @@ grpc_link_property, mark_grpc_properties, ) -from ._mesh_data import ( - ElementalData, - NodalData, - VectorData, - elemental_data_property, - nodal_data_property, -) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .element_set import ElementSet from .enums import ( @@ -56,7 +57,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "GeometricalSelectionRule", @@ -93,7 +94,7 @@ class GeometricalSelectionRule(CreatableTreeObject, IdTreeObject): Virtual geometry to use for the rule. element_sets : Element sets to use for the rule. - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. use_default_tolerances : @@ -111,6 +112,7 @@ class GeometricalSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "geometrical_selection_rules" _OBJECT_INFO_TYPE = geometrical_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = geometrical_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -119,7 +121,7 @@ def __init__( geometrical_rule_type: GeometricalRuleType = GeometricalRuleType.GEOMETRY, geometry: VirtualGeometry | None = None, element_sets: Iterable[ElementSet] = (), - include_rule_type: bool = True, + include_rule: bool = True, use_default_tolerances: bool = True, in_plane_capture_tolerance: float = 0.0, negative_capture_tolerance: float = 0.0, @@ -129,7 +131,7 @@ def __init__( self.geometrical_rule_type = geometrical_rule_type self.geometry = geometry self.element_sets = element_sets - self.include_rule_type = include_rule_type + self.include_rule = include_rule self.use_default_tolerances = use_default_tolerances self.in_plane_capture_tolerance = in_plane_capture_tolerance self.negative_capture_tolerance = negative_capture_tolerance @@ -147,9 +149,7 @@ def _create_stub(self) -> geometrical_selection_rule_pb2_grpc.ObjectServiceStub: ) geometry = grpc_link_property("properties.geometry", allowed_types=VirtualGeometry) element_sets = define_linked_object_list("properties.element_sets", object_class=ElementSet) - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") use_default_tolerances: ReadWriteProperty[bool, bool] = grpc_data_property( "properties.use_default_tolerances" ) @@ -163,5 +163,8 @@ def _create_stub(self) -> geometrical_selection_rule_pb2_grpc.ObjectServiceStub: "properties.positive_capture_tolerance" ) + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(GeometricalSelectionRuleElementalData) nodal_data = nodal_data_property(GeometricalSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/imported_analysis_ply.py b/src/ansys/acp/core/_tree_objects/imported_analysis_ply.py new file mode 100644 index 0000000000..7c41744fff --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/imported_analysis_ply.py @@ -0,0 +1,81 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable + +from ansys.api.acp.v0 import imported_analysis_ply_pb2, imported_analysis_ply_pb2_grpc + +from .._utils.property_protocols import ReadOnlyProperty +from ._grpc_helpers.property_helper import ( + grpc_data_property_read_only, + grpc_link_property_read_only, + mark_grpc_properties, +) +from ._mesh_data import solid_mesh_property +from .base import IdTreeObject, ReadOnlyTreeObject +from .enums import status_type_from_pb +from .object_registry import register + +__all__ = [ + "ImportedAnalysisPly", +] + + +@mark_grpc_properties +@register +class ImportedAnalysisPly(ReadOnlyTreeObject, IdTreeObject): + """Instantiate an Imported Analysis Ply. + + Parameters + ---------- + name: str + The name of the Imported Analysis Ply. + material: Material + Material of the Imported Analysis ply. + angle: float + Angle of the Analysis ply in degrees. + thickness: float + Thickness of the ply. + active_in_post_mode: bool + If False, deactivates the failure analysis for this ply during post-processing. + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "imported_analysis_plies" + _OBJECT_INFO_TYPE = imported_analysis_ply_pb2.ObjectInfo + _SUPPORTED_SINCE = "25.1" + + def _create_stub(self) -> imported_analysis_ply_pb2_grpc.ObjectServiceStub: + return imported_analysis_ply_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + material = grpc_link_property_read_only("properties.material") + angle: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.angle") + thickness: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.thickness") + active_in_post_mode: ReadOnlyProperty[bool] = grpc_data_property_read_only( + "properties.active_in_post_mode" + ) + + solid_mesh = solid_mesh_property diff --git a/src/ansys/acp/core/_tree_objects/imported_modeling_group.py b/src/ansys/acp/core/_tree_objects/imported_modeling_group.py new file mode 100644 index 0000000000..e51e64a8e7 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/imported_modeling_group.py @@ -0,0 +1,74 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable + +from ansys.api.acp.v0 import ( + imported_modeling_group_pb2, + imported_modeling_group_pb2_grpc, + imported_modeling_ply_pb2_grpc, +) + +from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping +from ._grpc_helpers.property_helper import mark_grpc_properties +from .base import CreatableTreeObject, IdTreeObject +from .imported_modeling_ply import ImportedModelingPly +from .object_registry import register + +__all__ = ["ImportedModelingGroup"] + + +@mark_grpc_properties +@register +class ImportedModelingGroup(CreatableTreeObject, IdTreeObject): + """Instantiate an imported modeling group. + + Parameters + ---------- + name : str + Name of the imported modeling group. + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "imported_modeling_groups" + _OBJECT_INFO_TYPE = imported_modeling_group_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = imported_modeling_group_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__(self, *, name: str = "ImportedModelingGroup"): + super().__init__(name=name) + + def _create_stub(self) -> imported_modeling_group_pb2_grpc.ObjectServiceStub: + return imported_modeling_group_pb2_grpc.ObjectServiceStub(self._channel) + + create_imported_modeling_ply = define_create_method( + ImportedModelingPly, + func_name="create_imported_modeling_ply", + parent_class_name="ImportedModelingGroup", + module_name=__module__, + ) + imported_modeling_plies = define_mutable_mapping( + ImportedModelingPly, imported_modeling_ply_pb2_grpc.ObjectServiceStub + ) diff --git a/src/ansys/acp/core/_tree_objects/imported_modeling_ply.py b/src/ansys/acp/core/_tree_objects/imported_modeling_ply.py new file mode 100644 index 0000000000..8614325c10 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/imported_modeling_ply.py @@ -0,0 +1,241 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable, Sequence + +from ansys.api.acp.v0 import ( + imported_modeling_ply_pb2, + imported_modeling_ply_pb2_grpc, + imported_production_ply_pb2_grpc, +) + +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.linked_object_list import define_linked_object_list +from ._grpc_helpers.mapping import get_read_only_collection_property +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .enums import ( + ImportedPlyDrapingType, + ImportedPlyOffsetType, + ImportedPlyThicknessType, + MeshImportType, + RosetteSelectionMethod, + ThicknessFieldType, + imported_ply_draping_type_from_pb, + imported_ply_draping_type_to_pb, + imported_ply_offset_type_from_pb, + imported_ply_offset_type_to_pb, + imported_ply_thickness_type_from_pb, + imported_ply_thickness_type_to_pb, + mesh_import_type_from_pb, + mesh_import_type_to_pb, + rosette_selection_method_from_pb, + rosette_selection_method_to_pb, + status_type_from_pb, + thickness_field_type_from_pb, + thickness_field_type_to_pb, +) +from .fabric import Fabric +from .imported_production_ply import ImportedProductionPly +from .lookup_table_1d_column import LookUpTable1DColumn +from .lookup_table_3d_column import LookUpTable3DColumn +from .object_registry import register +from .rosette import Rosette +from .virtual_geometry import VirtualGeometry + +__all__ = [ + "ImportedModelingPly", +] + + +@mark_grpc_properties +@register +class ImportedModelingPly(CreatableTreeObject, IdTreeObject): + """Instantiate an Imported Modeling Ply. + + Parameters + ---------- + name : + The name of the Modeling Ply + active : + Inactive plies are ignored in ACP and the downstream analysis. + offset_type : + Defines the location of the mesh (plane) with respect to the laminate. + One of ``BOTTOM_OFFSET``, ``MIDDLE_OFFSET``, and ``TOP_OFFSET``. + mesh_import_type : + Source of the imported mesh. Either from geometry or from hdf5. + mesh_geometry: + Link to the geometry with represents the ply surface. Only used if ``mesh_import_type`` + is ``FROM_GEOMETRY``. + rosette_selection_method : + Selection Method for Rosettes of the Oriented Selection Set. + rosettes : + Rosettes of the Oriented Selection Set. + reference_direction_field : + A 3D lookup table column that defines the reference directions. + rotation_angle : + Angle in degrees by which the initial reference directions are rotated around the orientations. + ply_material : + The material (only fabric is supported) of the ply. + ply_angle : + Design angle between the reference direction and the ply fiber direction. + draping_type : + Chooses between different draping formulations. ``NO_DRAPING`` by default. + draping_angle_1_field : + Correction angle between the fiber and draped fiber directions, in degree. + draping_angle_2_field : + Correction angle between the transverse and draped transverse directions, + in degree. Optional, uses the same values as ``draping_angle_1_field`` + (no shear) by default. + thickness_type : + Choose :attr:`ThicknessType.FROM_TABLE` to define a ply with variable thickness. + The default value is :attr:`ThicknessType.NOMINAL`, which means the ply + thickness is constant and determined by the thickness of the ply material. + thickness_field : + Defines the look-up table column used to determine the ply thickness. + Only applies if ``thickness_type`` is :attr:`ThicknessType.FROM_TABLE`. + thickness_field_type : + If ``thickness_type`` is :attr:`ThicknessType.FROM_TABLE`, this parameter + determines how the thickness values are interpreted. They can be either + absolute values (:attr:`ThicknessFieldType.ABSOLUTE_VALUES`) or relative + values (:attr:`ThicknessFieldType.RELATIVE_SCALING_FACTOR`). + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "imported_modeling_plies" + _OBJECT_INFO_TYPE = imported_modeling_ply_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = imported_modeling_ply_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "ModelingPly", + active: bool = True, + offset_type: ImportedPlyOffsetType = ImportedPlyOffsetType.MIDDLE_OFFSET, + mesh_import_type: MeshImportType = MeshImportType.FROM_GEOMETRY, + mesh_geometry: VirtualGeometry | None = None, + rosette_selection_method: RosetteSelectionMethod = "minimum_angle", + rosettes: Sequence[Rosette] = tuple(), + reference_direction_field: LookUpTable3DColumn | LookUpTable1DColumn | None = None, + rotation_angle: float = 0.0, + ply_material: Fabric | None = None, + ply_angle: float = 0.0, + draping_type: ImportedPlyDrapingType = ImportedPlyDrapingType.NO_DRAPING, + draping_angle_1_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, + draping_angle_2_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, + thickness_type: ImportedPlyThicknessType = ImportedPlyThicknessType.NOMINAL, + thickness_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, + thickness_field_type: ThicknessFieldType = ThicknessFieldType.ABSOLUTE_VALUES, + ): + super().__init__(name=name) + + self.active = active + self.offset_type = ImportedPlyOffsetType(offset_type) + self.mesh_import_type = MeshImportType(mesh_import_type) + self.mesh_geometry = mesh_geometry + self.rosette_selection_method = rosette_selection_method + self.rosettes = rosettes + self.reference_direction_field = reference_direction_field + self.rotation_angle = rotation_angle + + self.ply_material = ply_material + self.ply_angle = ply_angle + + self.draping_type = ImportedPlyDrapingType(draping_type) + self.draping_angle_1_field = draping_angle_1_field + self.draping_angle_2_field = draping_angle_2_field + + self.thickness_type = ImportedPlyThicknessType(thickness_type) + self.thickness_field = thickness_field + self.thickness_field_type = thickness_field_type + + def _create_stub(self) -> imported_modeling_ply_pb2_grpc.ObjectServiceStub: + return imported_modeling_ply_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + offset_type = grpc_data_property( + "properties.offset_type", + from_protobuf=imported_ply_offset_type_from_pb, + to_protobuf=imported_ply_offset_type_to_pb, + ) + mesh_import_type = grpc_data_property( + "properties.mesh_import_type", + from_protobuf=mesh_import_type_from_pb, + to_protobuf=mesh_import_type_to_pb, + ) + mesh_geometry = grpc_link_property("properties.mesh_geometry", allowed_types=VirtualGeometry) + rosette_selection_method = grpc_data_property( + "properties.rosette_selection_method", + from_protobuf=rosette_selection_method_from_pb, + to_protobuf=rosette_selection_method_to_pb, + ) + rosettes = define_linked_object_list("properties.rosettes", Rosette) + reference_direction_field = grpc_link_property( + "properties.reference_direction_field", + allowed_types=(LookUpTable1DColumn, LookUpTable3DColumn), + ) + rotation_angle: ReadWriteProperty[float, float] = grpc_data_property( + "properties.rotation_angle" + ) + + ply_material = grpc_link_property("properties.ply_material", allowed_types=(Fabric,)) + ply_angle: ReadWriteProperty[float, float] = grpc_data_property("properties.ply_angle") + + draping_type = grpc_data_property( + "properties.draping", + from_protobuf=imported_ply_draping_type_from_pb, + to_protobuf=imported_ply_draping_type_to_pb, + ) + draping_angle_1_field = grpc_link_property( + "properties.draping_angle_1_field", allowed_types=(LookUpTable1DColumn, LookUpTable3DColumn) + ) + draping_angle_2_field = grpc_link_property( + "properties.draping_angle_2_field", allowed_types=(LookUpTable1DColumn, LookUpTable3DColumn) + ) + + thickness_type = grpc_data_property( + "properties.thickness_type", + from_protobuf=imported_ply_thickness_type_from_pb, + to_protobuf=imported_ply_thickness_type_to_pb, + ) + thickness_field = grpc_link_property( + "properties.thickness_field", allowed_types=(LookUpTable1DColumn, LookUpTable3DColumn) + ) + thickness_field_type = grpc_data_property( + "properties.thickness_field_type", + from_protobuf=thickness_field_type_from_pb, + to_protobuf=thickness_field_type_to_pb, + ) + + imported_production_plies = get_read_only_collection_property( + ImportedProductionPly, imported_production_ply_pb2_grpc.ObjectServiceStub + ) diff --git a/src/ansys/acp/core/_tree_objects/imported_production_ply.py b/src/ansys/acp/core/_tree_objects/imported_production_ply.py new file mode 100644 index 0000000000..f94a59bb6b --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/imported_production_ply.py @@ -0,0 +1,84 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable + +from ansys.api.acp.v0 import ( + imported_analysis_ply_pb2_grpc, + imported_production_ply_pb2, + imported_production_ply_pb2_grpc, +) + +from .._utils.property_protocols import ReadOnlyProperty +from ._grpc_helpers.mapping import get_read_only_collection_property +from ._grpc_helpers.property_helper import ( + grpc_data_property_read_only, + grpc_link_property_read_only, + mark_grpc_properties, +) +from .base import IdTreeObject, ReadOnlyTreeObject +from .enums import status_type_from_pb +from .imported_analysis_ply import ImportedAnalysisPly +from .object_registry import register + +__all__ = [ + "ImportedProductionPly", +] + + +@mark_grpc_properties +@register +class ImportedProductionPly(ReadOnlyTreeObject, IdTreeObject): + """Instantiate an Imported Production Ply. + + Parameters + ---------- + name: str + The name of the imported production ply. + material: Material + Material of the imported production ply. + angle: float + Angle of the imported production ply in degrees. + thickness: float + Thickness of the imported production ply in degrees. + + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "imported_production_plies" + _OBJECT_INFO_TYPE = imported_production_ply_pb2.ObjectInfo + _SUPPORTED_SINCE = "25.1" + + def _create_stub(self) -> imported_production_ply_pb2_grpc.ObjectServiceStub: + return imported_production_ply_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + material = grpc_link_property_read_only("properties.material") + angle: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.angle") + thickness: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.thickness") + + imported_analysis_plies = get_read_only_collection_property( + ImportedAnalysisPly, imported_analysis_ply_pb2_grpc.ObjectServiceStub + ) diff --git a/src/ansys/acp/core/_tree_objects/imported_solid_model.py b/src/ansys/acp/core/_tree_objects/imported_solid_model.py new file mode 100644 index 0000000000..eb56434ede --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/imported_solid_model.py @@ -0,0 +1,385 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable +import dataclasses +from typing import Any + +from ansys.api.acp.v0 import ( + cut_off_geometry_pb2_grpc, + enum_types_pb2, + imported_solid_model_pb2, + imported_solid_model_pb2_grpc, + layup_mapping_object_pb2_grpc, + solid_element_set_pb2_grpc, + solid_model_pb2, +) + +from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty +from .._utils.typing_helper import PATH as _PATH +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + elemental_data_property, + nodal_data_property, +) +from ._grpc_helpers.enum_wrapper import wrap_to_string_enum +from ._grpc_helpers.exceptions import wrap_grpc_errors +from ._grpc_helpers.mapping import ( + define_create_method, + define_mutable_mapping, + get_read_only_collection_property, +) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import solid_mesh_property +from ._solid_model_export import SolidModelExportMixin +from .base import ( + CreatableTreeObject, + IdTreeObject, + TreeObjectAttributeWithCache, + nested_grpc_object_property, +) +from .cut_off_geometry import CutOffGeometry +from .enums import ( + UnitSystemType, + status_type_from_pb, + unit_system_type_from_pb, + unit_system_type_to_pb, +) +from .layup_mapping_object import LayupMappingObject +from .material import Material +from .object_registry import register +from .solid_element_set import SolidElementSet + +__all__ = [ + "ImportedSolidModel", + "SolidModelImportFormat", + "ImportedSolidModelExportSettings", + "ImportedSolidModelElementalData", + "ImportedSolidModelNodalData", +] + + +SolidModelImportFormat, solid_model_import_format_to_pb, solid_model_import_format_from_pb = ( + wrap_to_string_enum( + "SolidModelImportFormat", + enum_types_pb2.FileFormat, + module=__name__, + value_converter=lambda val: val.lower().replace("_", ":"), + doc="Options for the file format when importing a solid model.", + explicit_value_list=( + enum_types_pb2.FileFormat.ANSYS_H5, + enum_types_pb2.FileFormat.ANSYS_CDB, + enum_types_pb2.FileFormat.ANSYS_DAT, + ), + ) +) + + +@dataclasses.dataclass +class ImportedSolidModelElementalData(ElementalData): + """Represents elemental data for an imported solid model.""" + + +@dataclasses.dataclass +class ImportedSolidModelNodalData(NodalData): + """Represents nodal data for an imported solid model.""" + + +@mark_grpc_properties +class ImportedSolidModelExportSettings(TreeObjectAttributeWithCache): + """Defines the settings for exporting an imported solid model. + + Parameters + ---------- + use_default_section_index : + Use the default start index for sections. + section_index : + Custom start index for sections. + Only used if ``use_default_section_index`` is False. + use_default_coordinate_system_index : + Use the default start index for coordinate systems. + coordinate_system_index : + Custom start index for coordinate systems. + Only used if ``use_default_coordinate_system_index`` is False. + use_default_material_index : + Use the default start index for materials. + material_index : + Custom start index for materials. + Only used if ``use_default_material_index`` is False. + use_default_node_index : + Use the default start index for nodes. + node_index : + Custom start index for nodes. + Only used if ``use_default_node_index`` is False. + use_default_element_index : + Use the default start index for elements. + element_index : + Custom start index for elements. + Only used if ``use_default_element_index`` is False. + use_solsh_elements : + When True, export linear layered elements as Solsh (Solid190). + drop_hanging_nodes : + When True, the hanging nodes of quadratic solid meshes are dropped. + use_solid_model_prefix : + Use the imported solid model name as a prefix for the exported file. + + """ + + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + use_default_section_index: bool = True, + section_index: int = 0, + use_default_coordinate_system_index: bool = True, + coordinate_system_index: int = 0, + use_default_material_index: bool = True, + material_index: int = 0, + use_default_node_index: bool = True, + node_index: int = 0, + use_default_element_index: bool = True, + element_index: int = 0, + use_solsh_elements: bool = False, + drop_hanging_nodes: bool = True, + use_solid_model_prefix: bool = True, + _parent_object: ImportedSolidModel | None = None, + _pb_object: Any | None = None, + _attribute_path: str | None = None, + ): + super().__init__( + _parent_object=_parent_object, + _pb_object=_pb_object, + _attribute_path=_attribute_path, + ) + # See comment on DropOffSettings.__init__ for the logic here. + if _parent_object is None and _pb_object is None: + self.use_default_section_index = use_default_section_index + self.section_index = section_index + self.use_default_coordinate_system_index = use_default_coordinate_system_index + self.coordinate_system_index = coordinate_system_index + self.use_default_material_index = use_default_material_index + self.material_index = material_index + self.use_default_node_index = use_default_node_index + self.node_index = node_index + self.use_default_element_index = use_default_element_index + self.element_index = element_index + self.use_solsh_elements = use_solsh_elements + self.drop_hanging_nodes = drop_hanging_nodes + self.use_solid_model_prefix = use_solid_model_prefix + + @classmethod + def _create_default_pb_object(self) -> solid_model_pb2.ExportSettings: + # See comment on DropOffSettings._create_default_pb_object + return solid_model_pb2.ExportSettings() + + use_default_section_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_section_index" + ) + section_index: ReadWriteProperty[int, int] = grpc_data_property("section_index") + use_default_coordinate_system_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_coordinate_system_index" + ) + coordinate_system_index: ReadWriteProperty[int, int] = grpc_data_property( + "coordinate_system_index" + ) + use_default_material_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_material_index" + ) + material_index: ReadWriteProperty[int, int] = grpc_data_property("material_index") + use_default_node_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_node_index" + ) + node_index: ReadWriteProperty[int, int] = grpc_data_property("node_index") + use_default_element_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_element_index" + ) + element_index: ReadWriteProperty[int, int] = grpc_data_property("element_index") + use_solsh_elements: ReadWriteProperty[bool, bool] = grpc_data_property("use_solsh_elements") + drop_hanging_nodes: ReadWriteProperty[bool, bool] = grpc_data_property("drop_hanging_nodes") + use_solid_model_prefix: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_solid_model_prefix" + ) + + +@mark_grpc_properties +@register +class ImportedSolidModel(SolidModelExportMixin, CreatableTreeObject, IdTreeObject): + """Instantiate an imported solid model. + + Parameters + ---------- + name : + Name of the imported solid model. + active : + Inactive imported solid models are ignored in the analysis. + format : + Specifies the format of the file to be imported. + unit_system : + Specifies the unit system of the external solid mesh. + external_path : + Path of the file to be imported. + delete_bad_elements : + If True, run an element check and delete erroneous elements. Bad elements + can falsify the mapping. + warping_limit : + Maximum allowable warping. Elements with a warping above this limit are + deleted. + Only used if ``delete_bad_elements`` is True. + minimum_volume : + Solid elements with a volume smaller or equal to this value are deleted. + Only used if ``delete_bad_elements`` is True. + cut_off_material : + This material is assigned to the degenerated solid cut-off elements if + ``cut_off_material_handling`` is set to ``GLOBAL`` in the fabric + definition. + export_settings : + Defines the settings for exporting the imported solid model. + + """ + + __slots__: Iterable[str] = tuple() + _COLLECTION_LABEL = "imported_solid_models" + _OBJECT_INFO_TYPE = imported_solid_model_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = imported_solid_model_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "ImportedSolidModel", + active: bool = True, + format: SolidModelImportFormat = "ansys:cdb", # type: ignore + unit_system: UnitSystemType = "from_file", + external_path: _PATH = "", + delete_bad_elements: bool = True, + warping_limit: float = 0.4, + minimum_volume: float = 0.0, + cut_off_material: Material | None = None, + export_settings: ImportedSolidModelExportSettings = ImportedSolidModelExportSettings(), + ): + super().__init__(name=name) + self.active = active + self.format = format + self.unit_system = unit_system + self.external_path = external_path + self.delete_bad_elements = delete_bad_elements + self.warping_limit = warping_limit + self.minimum_volume = minimum_volume + self.cut_off_material = cut_off_material + self.export_settings = export_settings + + def _create_stub(self) -> imported_solid_model_pb2_grpc.ObjectServiceStub: + return imported_solid_model_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked") + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + format = grpc_data_property( + "properties.format", + from_protobuf=solid_model_import_format_from_pb, + to_protobuf=solid_model_import_format_to_pb, + ) + unit_system = grpc_data_property( + "properties.unit_system", + from_protobuf=unit_system_type_from_pb, + to_protobuf=unit_system_type_to_pb, + ) + external_path: ReadWriteProperty[str, _PATH] = grpc_data_property( + "properties.external_path", to_protobuf=str + ) + delete_bad_elements: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.delete_bad_elements" + ) + warping_limit: ReadWriteProperty[float, float] = grpc_data_property("properties.warping_limit") + minimum_volume: ReadWriteProperty[float, float] = grpc_data_property( + "properties.minimum_volume" + ) + cut_off_material = grpc_link_property("properties.cut_off_material", allowed_types=(Material,)) + export_settings = nested_grpc_object_property( + "properties.export_settings", ImportedSolidModelExportSettings + ) + + solid_element_sets = get_read_only_collection_property( + SolidElementSet, solid_element_set_pb2_grpc.ObjectServiceStub + ) + + elemental_data = elemental_data_property(ImportedSolidModelElementalData) + nodal_data = nodal_data_property(ImportedSolidModelNodalData) + + create_cut_off_geometry = define_create_method( + CutOffGeometry, + func_name="create_cut_off_geometry", + parent_class_name="ImportedSolidModel", + module_name=__module__, + ) + cut_off_geometries = define_mutable_mapping( + CutOffGeometry, cut_off_geometry_pb2_grpc.ObjectServiceStub + ) + + create_layup_mapping_object = define_create_method( + LayupMappingObject, + func_name="create_layup_mapping_object", + parent_class_name="ImportedSolidModel", + module_name=__name__, + ) + layup_mapping_objects = define_mutable_mapping( + LayupMappingObject, + layup_mapping_object_pb2_grpc.ObjectServiceStub, + ) + + def refresh(self, path: _PATH, format: SolidModelImportFormat | None = None) -> None: # type: ignore + """ + Re-import the solid model from the external file. + + Parameters + ---------- + path : + Path of the new input file. + format : + Switch format of the input file. Optional, uses the current format of the + imported solid model if not specified. + """ + if format is not None: + self.format = format + self.external_path = self._server_wrapper.auto_upload(path) + with wrap_grpc_errors(): + self._get_stub().Refresh( # type: ignore + imported_solid_model_pb2.RefreshRequest(resource_path=self._resource_path) + ) + + def import_initial_mesh(self) -> None: + """Import the solid mesh and its element sets.""" + with wrap_grpc_errors(): + self._get_stub().ImportInitialMesh( # type: ignore + imported_solid_model_pb2.ImportInitialMeshRequest(resource_path=self._resource_path) + ) + + solid_mesh = solid_mesh_property diff --git a/src/ansys/acp/core/_tree_objects/interface_layer.py b/src/ansys/acp/core/_tree_objects/interface_layer.py new file mode 100644 index 0000000000..4ee64d904c --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/interface_layer.py @@ -0,0 +1,133 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable +import dataclasses + +from ansys.api.acp.v0 import interface_layer_pb2, interface_layer_pb2_grpc + +from .._utils.property_protocols import ReadWriteProperty +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + VectorData, + elemental_data_property, + nodal_data_property, +) +from ._grpc_helpers.linked_object_list import ( + define_linked_object_list, + define_polymorphic_linked_object_list, +) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property +from .base import CreatableTreeObject, IdTreeObject +from .element_set import ElementSet +from .enums import status_type_from_pb +from .object_registry import register +from .oriented_selection_set import OrientedSelectionSet + + +@dataclasses.dataclass +class InterfaceLayerElementalData(ElementalData): + """Represents elemental data for a Modeling Ply.""" + + normal: VectorData | None = None + + +@dataclasses.dataclass +class InterfaceLayerNodalData(NodalData): + """Represents nodal data for a Modeling Ply.""" + + ply_offset: VectorData | None = None + + +@mark_grpc_properties +@register +class InterfaceLayer(CreatableTreeObject, IdTreeObject): + """Instantiate an interface layer. + + The interface layer is a separation layer in the stacking sequence. It can be + used to analyze the crack growth of existing cracks. They can also be used to + define contacts zones between two layers. + The topology is defined with an interface layer in ACP, while all other fracture + settings need to be specified in the downstream analysis (MAPDL or Mechanical). + + Parameters + ---------- + name : + Name of the interface layer. + global_ply_nr : + Global ply number for the stacking sequence. + active : + Inactive interface layers are ignored in ACP and the downstream analysis. + oriented_selection_sets : + Oriented Selection Set for the expansion of the interface layer. + open_area_sets : + Defines the initial crack of a Virtual Crack Closure Technique (VCCT) layer. + Can contain ``OrientedSelectionSet`` and ``ElementSet`` objects. + """ + + __slots__: Iterable[str] = tuple() + _COLLECTION_LABEL = "interface_layers" + _OBJECT_INFO_TYPE = interface_layer_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = interface_layer_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "InterfaceLayer", + global_ply_nr: int = 0, + active: bool = True, + oriented_selection_sets: Iterable[OrientedSelectionSet] = (), + open_area_sets: Iterable[ElementSet | OrientedSelectionSet] = (), + ): + super().__init__(name=name) + self.global_ply_nr = global_ply_nr + self.active = active + self.oriented_selection_sets = oriented_selection_sets + self.open_area_sets = open_area_sets + + def _create_stub(self) -> interface_layer_pb2_grpc.ObjectServiceStub: + return interface_layer_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + global_ply_nr: ReadWriteProperty[int, int] = grpc_data_property("properties.global_ply_nr") + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + oriented_selection_sets = define_linked_object_list( + "properties.oriented_selection_sets", OrientedSelectionSet + ) + open_area_sets = define_polymorphic_linked_object_list( + "properties.open_area_sets", allowed_types=(ElementSet, OrientedSelectionSet) + ) + + mesh = full_mesh_property + shell_mesh = shell_mesh_property + + elemental_data = elemental_data_property(InterfaceLayerElementalData) + nodal_data = nodal_data_property(InterfaceLayerNodalData) diff --git a/src/ansys/acp/core/_tree_objects/layup_mapping_object.py b/src/ansys/acp/core/_tree_objects/layup_mapping_object.py new file mode 100644 index 0000000000..45095c929a --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/layup_mapping_object.py @@ -0,0 +1,313 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable, Sequence + +from ansys.api.acp.v0 import enum_types_pb2, layup_mapping_object_pb2, layup_mapping_object_pb2_grpc + +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.enum_wrapper import wrap_to_string_enum +from ._grpc_helpers.linked_object_list import ( + define_linked_object_list, + define_polymorphic_linked_object_list, +) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .element_set import ElementSet +from .enums import ( + BaseElementMaterialHandling, + ElementTechnology, + ReinforcingBehavior, + StressStateType, + base_element_material_handling_from_pb, + base_element_material_handling_to_pb, + element_technology_from_pb, + element_technology_to_pb, + reinforcing_behavior_from_pb, + reinforcing_behavior_to_pb, + status_type_from_pb, + stress_state_type_from_pb, + stress_state_type_to_pb, +) +from .imported_modeling_group import ImportedModelingGroup +from .imported_modeling_ply import ImportedModelingPly +from .material import Material +from .modeling_group import ModelingGroup +from .modeling_ply import ModelingPly +from .object_registry import register +from .rosette import Rosette +from .solid_element_set import SolidElementSet + +__all__ = ["LayupMappingObject", "LayupMappingRosetteSelectionMethod"] + + +( + LayupMappingRosetteSelectionMethod, + layup_mapping_rosette_selection_method_to_pb, + layup_mapping_rosette_selection_method_from_pb, +) = wrap_to_string_enum( + "LayupMappingRosetteSelectionMethod", + enum_types_pb2.RosetteSelectionMethod, + module=__name__, + doc="Options for how the rosette is selected in the layup mapping.", + explicit_value_list=( + enum_types_pb2.RosetteSelectionMethod.MINIMUM_DISTANCE, + enum_types_pb2.RosetteSelectionMethod.MINIMUM_DISTANCE_SUPERPOSED, + ), +) + + +@mark_grpc_properties +@register +class LayupMappingObject(CreatableTreeObject, IdTreeObject): + """Instantiate a layup mapping object. + + Parameters + ---------- + name : + Name of the layup mapping object. + active : + Inactive layup mapping objects are not used. + element_technology : + Determines the element technology used for the layup mapping. + Can be either ``"layered_element"`` or ``"reinforcing"``. + Note that only brick and prism elements support the layered option, + while reinforcing technology can be combined with all types of + solid elements. + shell_element_sets : + Defines the shell area whose modeling plies are mapped onto the + solid mesh. + Used only if ``use_imported_plies`` is False. + use_imported_plies : + If True, imported modeling plies are mapped onto the solid mesh. + select_all_plies : + Determines whether all plies are selected for mapping, or only + the ones specified in the ``sequences`` parameter. + + * If ``use_imported_plies`` is False and ``select_all_plies`` is True, + all plies in the selected shell area are selected. + * If ``use_imported_plies`` is True and ``select_all_plies`` is True, + all imported modeling plies are selected. + + sequences : + Determines which plies are considered for mapping. The intersection + of shell elements and plies are then mapped onto the solid mesh. + + * If ``use_imported_plies`` is False, modeling groups and modeling + plies are allowed + * If ``use_imported_plies`` is True, imported modeling groups and + imported modeling plies are allowed + + entire_solid_mesh : + If True, the selected layup is mapped onto the entire solid mesh. + Otherwise, the ``solid_element_sets`` parameter is used to refine + the scope. + solid_element_sets : + List of solid element sets to which the mapping is applied. + scale_ply_thicknesses : + If True, scale the layer thickness to fill the gaps within an element, + instead of filling them with the void material. + Used only if ``element_technology`` is ``"layered_element"``. + void_material : + Material used to fill the gaps with. + Only used if ``scale_ply_thicknesses`` is False, and + the ``element_technology`` is ``"layered_element"``. + minimum_void_material_thickness : + Only gaps with a thickness greater than this value are filled with + the void material. + Only used if ``scale_ply_thicknesses`` is False, and + the ``element_technology`` is ``"layered_element"``. + delete_lost_elements : + If True, elements without layup and degenerated elements are deleted. + Otherwise, they are filled with the filler material. + Used only if ``element_technology`` is ``"layered_element"``. + filler_material : + Material used to fill the degenerated elements. + Only used if ``delete_lost_elements`` is False, and + the ``element_technology`` is ``"layered_element"``. + rosettes : + List of rosettes to set the coordinate system of the filler elements. + Used only if ``element_technology`` is ``"layered_element"``. + rosette_selection_method : + Defines how the coordinate systems are applied for the filler elements. + Used only if ``element_technology`` is ``"layered_element"``. + reinforcing_behavior : + Determines whether the reinforcing elements carry tension and / or + compression loads. + Used only if ``element_technology`` is ``"reinforcing"``. + base_element_material_handling : + Determines how the base material is handled in the area with reinforcing + elements. Can be either ``"remove"`` or ``"retain"``. + Used only if ``element_technology`` is ``"reinforcing"``. + stress_state : + Specifies the stress state of the reinforcing elements. + Used only if ``element_technology`` is ``"reinforcing"``. + base_material : + Define the initial material of the solid elements which will be reinforced + by the selected plies. + Used only if ``element_technology`` is ``"reinforcing"``. + base_element_rosettes : + List of rosettes to set the coordinate system of the base elements. + This is important if the base material has an orthotropic characteristic. + Used only if ``element_technology`` is ``"reinforcing"``. + base_element_rosette_selection_method : + Defines how the coordinate systems are applied for the base elements. + Used only if ``element_technology`` is ``"reinforcing"``. + + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "layup_mapping_objects" + _OBJECT_INFO_TYPE = layup_mapping_object_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = layup_mapping_object_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "LayupMappingObject", + active: bool = True, + element_technology: ElementTechnology = ElementTechnology.LAYERED_ELEMENT, + shell_element_sets: Sequence[ElementSet] = (), + use_imported_plies: bool = False, + select_all_plies: bool = True, + sequences: Sequence[ + ModelingGroup | ModelingPly | ImportedModelingGroup | ImportedModelingPly + ] = (), + entire_solid_mesh: bool = True, + solid_element_sets: Sequence[SolidElementSet] = (), + scale_ply_thicknesses: bool = True, + void_material: Material | None = None, + minimum_void_material_thickness: float = 1e-6, + delete_lost_elements: bool = True, + filler_material: Material | None = None, + rosettes: Sequence[Rosette] = (), + rosette_selection_method: LayupMappingRosetteSelectionMethod = LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, # type: ignore # noqa: E501 + reinforcing_behavior: ReinforcingBehavior = ReinforcingBehavior.TENSION_AND_COMPRESSION, + base_element_material_handling: BaseElementMaterialHandling = BaseElementMaterialHandling.REMOVE, # noqa: E501 + stress_state: StressStateType = StressStateType.PLANE_STRESS_STATE_WITH_TRANSVERSE_SHEAR_AND_BENDING_STIFFNESS, # noqa: E501 + base_material: Material | None = None, + base_element_rosettes: Sequence[Rosette] = (), + base_element_rosette_selection_method: LayupMappingRosetteSelectionMethod = LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, # type: ignore # noqa: E501 + ): + super().__init__(name=name) + self.active = active + self.element_technology = element_technology + self.shell_element_sets = shell_element_sets + self.use_imported_plies = use_imported_plies + self.select_all_plies = select_all_plies + self.sequences = sequences + self.entire_solid_mesh = entire_solid_mesh + self.solid_element_sets = solid_element_sets + self.scale_ply_thicknesses = scale_ply_thicknesses + self.void_material = void_material + self.minimum_void_material_thickness = minimum_void_material_thickness + self.delete_lost_elements = delete_lost_elements + self.filler_material = filler_material + self.rosettes = rosettes + self.rosette_selection_method = rosette_selection_method + self.reinforcing_behavior = reinforcing_behavior + self.base_element_material_handling = base_element_material_handling + self.stress_state = stress_state + self.base_material = base_material + self.base_element_rosettes = base_element_rosettes + self.base_element_rosette_selection_method = base_element_rosette_selection_method + + def _create_stub(self) -> layup_mapping_object_pb2_grpc.ObjectServiceStub: + return layup_mapping_object_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + element_technology = grpc_data_property( + "properties.element_technology", + from_protobuf=element_technology_from_pb, + to_protobuf=element_technology_to_pb, + ) + shell_element_sets = define_linked_object_list( + "properties.shell_element_sets", object_class=ElementSet + ) + use_imported_plies: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.use_imported_plies" + ) + select_all_plies: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.select_all_plies" + ) + sequences = define_polymorphic_linked_object_list( + "properties.sequences", + allowed_types=(ModelingGroup, ModelingPly, ImportedModelingGroup, ImportedModelingPly), + ) + entire_solid_mesh: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.entire_solid_mesh" + ) + solid_element_sets = define_linked_object_list( + "properties.solid_element_sets", object_class=SolidElementSet + ) + scale_ply_thicknesses: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.scale_ply_thicknesses" + ) + void_material = grpc_link_property("properties.void_material", allowed_types=(Material,)) + minimum_void_material_thickness: ReadWriteProperty[float, float] = grpc_data_property( + "properties.minimum_void_material_thickness" + ) + delete_lost_elements: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.delete_lost_elements" + ) + filler_material = grpc_link_property("properties.filler_material", allowed_types=(Material,)) + rosettes = define_linked_object_list("properties.rosettes", object_class=Rosette) + rosette_selection_method = grpc_data_property( + "properties.rosette_selection_method", + from_protobuf=layup_mapping_rosette_selection_method_from_pb, + to_protobuf=layup_mapping_rosette_selection_method_to_pb, + ) + reinforcing_behavior = grpc_data_property( + "properties.reinforcing_behavior", + from_protobuf=reinforcing_behavior_from_pb, + to_protobuf=reinforcing_behavior_to_pb, + ) + base_element_material_handling = grpc_data_property( + "properties.base_element_material_handling", + from_protobuf=base_element_material_handling_from_pb, + to_protobuf=base_element_material_handling_to_pb, + ) + stress_state = grpc_data_property( + "properties.stress_state", + from_protobuf=stress_state_type_from_pb, + to_protobuf=stress_state_type_to_pb, + ) + base_material = grpc_link_property("properties.base_material", allowed_types=(Material,)) + base_element_rosettes = define_linked_object_list( + "properties.base_element_rosettes", object_class=Rosette + ) + base_element_rosette_selection_method = grpc_data_property( + "properties.base_element_rosette_selection_method", + from_protobuf=layup_mapping_rosette_selection_method_from_pb, + to_protobuf=layup_mapping_rosette_selection_method_to_pb, + ) diff --git a/src/ansys/acp/core/_tree_objects/linked_selection_rule.py b/src/ansys/acp/core/_tree_objects/linked_selection_rule.py index f4df67d4fc..146f87b685 100644 --- a/src/ansys/acp/core/_tree_objects/linked_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/linked_selection_rule.py @@ -22,8 +22,9 @@ from __future__ import annotations +from collections.abc import Callable import typing -from typing import Callable, Union +from typing import TypeAlias, Union from typing_extensions import Self @@ -31,8 +32,9 @@ from ._grpc_helpers.edge_property_list import GenericEdgePropertyType from ._grpc_helpers.polymorphic_from_pb import tree_object_from_resource_path +from ._grpc_helpers.property_helper import _exposed_grpc_property, mark_grpc_properties from .base import CreatableTreeObject -from .cutoff_selection_rule import CutoffSelectionRule +from .cut_off_selection_rule import CutOffSelectionRule from .cylindrical_selection_rule import CylindricalSelectionRule from .enums import ( BooleanOperationType, @@ -50,18 +52,19 @@ # this would cause a circular import at run-time. from .boolean_selection_rule import BooleanSelectionRule - _LINKABLE_SELECTION_RULE_TYPES = Union[ - BooleanSelectionRule, - CutoffSelectionRule, - CylindricalSelectionRule, - GeometricalSelectionRule, - ParallelSelectionRule, - SphericalSelectionRule, - TubeSelectionRule, - VariableOffsetSelectionRule, - ] +_LINKABLE_SELECTION_RULE_TYPES: TypeAlias = Union[ + "BooleanSelectionRule", + CutOffSelectionRule, + CylindricalSelectionRule, + GeometricalSelectionRule, + ParallelSelectionRule, + SphericalSelectionRule, + TubeSelectionRule, + VariableOffsetSelectionRule, +] +@mark_grpc_properties class LinkedSelectionRule(GenericEdgePropertyType): r"""Defines selection rules linked to a Boolean Selection Rule or Modeling Ply. @@ -98,10 +101,12 @@ class LinkedSelectionRule(GenericEdgePropertyType): :class:`.BooleanSelectionRule` \- \- ====================================== ================================== =================== - Note that :class:`.CutoffSelectionRule` and :class:`.BooleanSelectionRule` objects cannot be linked to - a Boolean Selection Rule, only to a Modeling Ply. + Note that :class:`.CutOffSelectionRule` and :class:`.BooleanSelectionRule` objects cannot be linked to + a Boolean Selection Rule, only to a Modeling Ply.. """ + _SUPPORTED_SINCE = "24.2" + def __init__( self, selection_rule: _LINKABLE_SELECTION_RULE_TYPES, @@ -122,7 +127,7 @@ def __init__( self.parameter_1 = parameter_1 self.parameter_2 = parameter_2 - @property + @_exposed_grpc_property def selection_rule(self) -> _LINKABLE_SELECTION_RULE_TYPES: """Link to an existing selection rule.""" return self._selection_rule @@ -133,7 +138,7 @@ def selection_rule(self, value: _LINKABLE_SELECTION_RULE_TYPES) -> None: if self._callback_apply_changes is not None: self._callback_apply_changes() - @property + @_exposed_grpc_property def operation_type(self) -> BooleanOperationType: """Operation to combine the selection rule with other selection rules.""" return self._operation_type @@ -142,18 +147,18 @@ def operation_type(self) -> BooleanOperationType: def operation_type(self, value: BooleanOperationType) -> None: # The backend converts the operation automatically; this is confusing # in the scripting context where the associated warning may not be visible. - if isinstance(self._selection_rule, CutoffSelectionRule): + if isinstance(self._selection_rule, CutOffSelectionRule): if value != BooleanOperationType.INTERSECT: raise ValueError( "Cannot use a boolean operation other than 'INTERSECT' with a " - "CutoffSelectionRule." + "CutOffSelectionRule." ) self._operation_type = value if self._callback_apply_changes is not None: self._callback_apply_changes() - @property + @_exposed_grpc_property def template_rule(self) -> bool: """Whether the selection rule is a template rule.""" return self._template_rule @@ -164,7 +169,7 @@ def template_rule(self, value: bool) -> None: if self._callback_apply_changes is not None: self._callback_apply_changes() - @property + @_exposed_grpc_property def parameter_1(self) -> float: """First template parameter of the selection rule.""" return self._parameter_1 @@ -175,7 +180,7 @@ def parameter_1(self, value: float) -> None: if self._callback_apply_changes is not None: self._callback_apply_changes() - @property + @_exposed_grpc_property def parameter_2(self) -> float: """Second template parameter of the selection rule.""" return self._parameter_2 @@ -208,7 +213,7 @@ def _from_pb_object( VariableOffsetSelectionRule, ] if not isinstance(parent_object, BooleanSelectionRule): - allowed_types_list += [CutoffSelectionRule, BooleanSelectionRule] + allowed_types_list += [CutOffSelectionRule, BooleanSelectionRule] allowed_types = tuple(allowed_types_list) selection_rule = tree_object_from_resource_path( @@ -265,3 +270,13 @@ def __repr__(self) -> str: f"parameter_1={self.parameter_1}, " f"parameter_2={self.parameter_2})" ) + + def clone(self) -> LinkedSelectionRule: + """Create a new unstored LinkedSelectionRule with the same properties.""" + return LinkedSelectionRule( + selection_rule=self.selection_rule, + operation_type=self.operation_type, + template_rule=self.template_rule, + parameter_1=self.parameter_1, + parameter_2=self.parameter_2, + ) diff --git a/src/ansys/acp/core/_tree_objects/lookup_table_1d.py b/src/ansys/acp/core/_tree_objects/lookup_table_1d.py index 7dbb2f62b7..d1a8e39260 100644 --- a/src/ansys/acp/core/_tree_objects/lookup_table_1d.py +++ b/src/ansys/acp/core/_tree_objects/lookup_table_1d.py @@ -78,6 +78,7 @@ class LookUpTable1D(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "lookup_tables_1d" _OBJECT_INFO_TYPE = lookup_table_1d_pb2.ObjectInfo _CREATE_REQUEST_TYPE = lookup_table_1d_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_tree_objects/lookup_table_1d_column.py b/src/ansys/acp/core/_tree_objects/lookup_table_1d_column.py index 437f0a11f0..5273974d53 100644 --- a/src/ansys/acp/core/_tree_objects/lookup_table_1d_column.py +++ b/src/ansys/acp/core/_tree_objects/lookup_table_1d_column.py @@ -30,7 +30,7 @@ from ansys.api.acp.v0 import lookup_table_1d_column_pb2, lookup_table_1d_column_pb2_grpc from ._grpc_helpers.property_helper import mark_grpc_properties -from .enums import DimensionType, LookUpTableColumnValueType +from .enums import LookUpTableColumnValueType, PhysicalDimension from .lookup_table_column_base import LookUpTableColumnBase from .object_registry import register @@ -49,7 +49,7 @@ class LookUpTable1DColumn(LookUpTableColumnBase): directional (three entries per row). Note that the ``value_type`` can only be set when constructing the column, and is read-only afterwards. - dimension_type : + physical_dimension : Dimensionality (such as time, length, force, ...) of the column data. data : The column data. The shape of the data must match the ``value_type`` @@ -62,16 +62,19 @@ class LookUpTable1DColumn(LookUpTableColumnBase): _COLLECTION_LABEL = "lookup_table_1d_columns" _OBJECT_INFO_TYPE = lookup_table_1d_column_pb2.ObjectInfo _CREATE_REQUEST_TYPE = lookup_table_1d_column_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, *, name: str = "LookUpTable1DColumn", value_type: LookUpTableColumnValueType = LookUpTableColumnValueType.SCALAR, - dimension_type: DimensionType = DimensionType.DIMENSIONLESS, + physical_dimension: PhysicalDimension = PhysicalDimension.DIMENSIONLESS, data: npt.NDArray[np.float64] | None = None, ): - super().__init__(name=name, value_type=value_type, dimension_type=dimension_type, data=data) + super().__init__( + name=name, value_type=value_type, physical_dimension=physical_dimension, data=data + ) def _create_stub(self) -> lookup_table_1d_column_pb2_grpc.ObjectServiceStub: return lookup_table_1d_column_pb2_grpc.ObjectServiceStub(self._channel) diff --git a/src/ansys/acp/core/_tree_objects/lookup_table_3d.py b/src/ansys/acp/core/_tree_objects/lookup_table_3d.py index 78a3db0fa2..03b48b6807 100644 --- a/src/ansys/acp/core/_tree_objects/lookup_table_3d.py +++ b/src/ansys/acp/core/_tree_objects/lookup_table_3d.py @@ -87,6 +87,7 @@ class LookUpTable3D(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "lookup_tables_3d" _OBJECT_INFO_TYPE = lookup_table_3d_pb2.ObjectInfo _CREATE_REQUEST_TYPE = lookup_table_3d_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_tree_objects/lookup_table_3d_column.py b/src/ansys/acp/core/_tree_objects/lookup_table_3d_column.py index d34e9b56c1..24bf6c5a2a 100644 --- a/src/ansys/acp/core/_tree_objects/lookup_table_3d_column.py +++ b/src/ansys/acp/core/_tree_objects/lookup_table_3d_column.py @@ -30,7 +30,7 @@ from ansys.api.acp.v0 import lookup_table_3d_column_pb2, lookup_table_3d_column_pb2_grpc from ._grpc_helpers.property_helper import mark_grpc_properties -from .enums import DimensionType, LookUpTableColumnValueType +from .enums import LookUpTableColumnValueType, PhysicalDimension from .lookup_table_column_base import LookUpTableColumnBase from .object_registry import register @@ -49,7 +49,7 @@ class LookUpTable3DColumn(LookUpTableColumnBase): directional (three entries per row). Note that the ``value_type`` can only be set when constructing the column, and is read-only afterwards. - dimension_type : + physical_dimension : Dimensionality (such as time, length, force, ...) of the column data. data : The column data. The shape of the data must match the ``value_type`` @@ -62,16 +62,19 @@ class LookUpTable3DColumn(LookUpTableColumnBase): _COLLECTION_LABEL = "lookup_table_3d_columns" _OBJECT_INFO_TYPE = lookup_table_3d_column_pb2.ObjectInfo _CREATE_REQUEST_TYPE = lookup_table_3d_column_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, *, name: str = "LookUpTable3DColumn", value_type: LookUpTableColumnValueType = LookUpTableColumnValueType.SCALAR, - dimension_type: DimensionType = DimensionType.DIMENSIONLESS, + physical_dimension: PhysicalDimension = PhysicalDimension.DIMENSIONLESS, data: npt.NDArray[np.float64] | None = None, ): - super().__init__(name=name, value_type=value_type, dimension_type=dimension_type, data=data) + super().__init__( + name=name, value_type=value_type, physical_dimension=physical_dimension, data=data + ) def _create_stub(self) -> lookup_table_3d_column_pb2_grpc.ObjectServiceStub: return lookup_table_3d_column_pb2_grpc.ObjectServiceStub(self._channel) diff --git a/src/ansys/acp/core/_tree_objects/lookup_table_column_base.py b/src/ansys/acp/core/_tree_objects/lookup_table_column_base.py index 75ca05933d..b47e09d244 100644 --- a/src/ansys/acp/core/_tree_objects/lookup_table_column_base.py +++ b/src/ansys/acp/core/_tree_objects/lookup_table_column_base.py @@ -37,12 +37,12 @@ ) from .base import CreatableTreeObject, IdTreeObject from .enums import ( - DimensionType, LookUpTableColumnValueType, - dimension_type_from_pb, - dimension_type_to_pb, + PhysicalDimension, lookup_table_column_value_type_from_pb, lookup_table_column_value_type_to_pb, + physical_dimension_from_pb, + physical_dimension_to_pb, ) __all__ = ["LookUpTableColumnBase"] @@ -59,7 +59,7 @@ class LookUpTableColumnBase(CreatableTreeObject, IdTreeObject): directional (three entries per row). Note that the ``value_type`` can only be set when constructing the column, and is read-only afterwards. - dimension_type : + physical_dimension : Dimensionality (such as time, length, force, ...) of the column data. data : The column data. The shape of the data must match the ``value_type`` @@ -74,7 +74,7 @@ def __init__( *, name: str, value_type: LookUpTableColumnValueType = LookUpTableColumnValueType.SCALAR, - dimension_type: DimensionType = DimensionType.DIMENSIONLESS, + physical_dimension: PhysicalDimension = PhysicalDimension.DIMENSIONLESS, data: npt.NDArray[np.float64] | None = None, ): super().__init__(name=name) @@ -88,17 +88,20 @@ def __init__( self, value_type ) - self.dimension_type = dimension_type + self.physical_dimension = physical_dimension if data is not None: self.data = data value_type = grpc_data_property_read_only( "properties.value_type", from_protobuf=lookup_table_column_value_type_from_pb ) - dimension_type = grpc_data_property( + + # We renamed 'dimension_type' to 'physical_dimension' compared to the API, since + # 'dimension_type' could also be understood as the number of spatial dimensions. + physical_dimension = grpc_data_property( "properties.dimension_type", - from_protobuf=dimension_type_from_pb, - to_protobuf=dimension_type_to_pb, + from_protobuf=physical_dimension_from_pb, + to_protobuf=physical_dimension_to_pb, ) data: ReadWriteProperty[npt.NDArray[np.float64], npt.NDArray[np.float64]] = grpc_data_property( "properties.data", diff --git a/src/ansys/acp/core/_tree_objects/material/material.py b/src/ansys/acp/core/_tree_objects/material/material.py index 0cbf4ab40a..859c7022ed 100644 --- a/src/ansys/acp/core/_tree_objects/material/material.py +++ b/src/ansys/acp/core/_tree_objects/material/material.py @@ -128,6 +128,7 @@ class Material(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "materials" _OBJECT_INFO_TYPE = material_pb2.ObjectInfo _CREATE_REQUEST_TYPE = material_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/density.py b/src/ansys/acp/core/_tree_objects/material/property_sets/density.py index a5693511f2..aa3035c04e 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/density.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/density.py @@ -44,6 +44,7 @@ class ConstantDensity(_DensityMixin, _ConstantPropertySet): """Constant density material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -65,5 +66,6 @@ class VariableDensity(_DensityMixin, _VariablePropertySet): """Variable density material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" rho = variable_material_grpc_data_property("rho") diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/engineering_constants.py b/src/ansys/acp/core/_tree_objects/material/property_sets/engineering_constants.py index bdfe87d58e..5845873371 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/engineering_constants.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/engineering_constants.py @@ -70,6 +70,7 @@ class ConstantEngineeringConstants(_EngineeringConstantsMixin, _ConstantProperty """Constant engineering constants material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" @classmethod def from_isotropic_constants( @@ -173,6 +174,7 @@ class VariableEngineeringConstants(_EngineeringConstantsMixin, _VariableProperty """Variable engineering constants material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" E = variable_material_grpc_data_property("E", **_ISOTROPIC_KWARGS) nu = variable_material_grpc_data_property("nu", **_ISOTROPIC_KWARGS) diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/fabric_fiber_angle.py b/src/ansys/acp/core/_tree_objects/material/property_sets/fabric_fiber_angle.py index a14f47cca7..ae84305398 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/fabric_fiber_angle.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/fabric_fiber_angle.py @@ -48,6 +48,7 @@ class ConstantFabricFiberAngle(_FabricFiberAngleMixin, _ConstantPropertySet): """ _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -73,5 +74,6 @@ class VariableFabricFiberAngle(_FabricFiberAngleMixin, _VariablePropertySet): """ _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" fabric_fiber_angle = variable_material_grpc_data_property("fabric_fiber_angle") diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/larc_constants.py b/src/ansys/acp/core/_tree_objects/material/property_sets/larc_constants.py index ea3b606626..b5baf38527 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/larc_constants.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/larc_constants.py @@ -44,6 +44,7 @@ class ConstantLaRCConstants(_LaRCConstantsMixin, _ConstantPropertySet): """Constant LaRC failure criterion properties.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -79,6 +80,7 @@ class VariableLaRCConstants(_LaRCConstantsMixin, _VariablePropertySet): """Variable LaRC failure criterion properties.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" fracture_angle_under_compression = variable_material_grpc_data_property( "fracture_angle_under_compression" diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/puck_constants.py b/src/ansys/acp/core/_tree_objects/material/property_sets/puck_constants.py index a604c44f0c..024240c5c8 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/puck_constants.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/puck_constants.py @@ -60,6 +60,7 @@ class ConstantPuckConstants(_PuckConstantsMixin, _ConstantPropertySet): """Constant Puck constants material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -125,6 +126,7 @@ class VariablePuckConstants(_PuckConstantsMixin, _VariablePropertySet): """Variable Puck constants material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" p_21_pos = variable_material_grpc_data_property("p_21_pos") p_21_neg = variable_material_grpc_data_property("p_21_neg") diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/strain_limits.py b/src/ansys/acp/core/_tree_objects/material/property_sets/strain_limits.py index b40833b505..e00a3b4fa1 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/strain_limits.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/strain_limits.py @@ -70,6 +70,7 @@ class ConstantStrainLimits(_StrainLimitsMixin, _ConstantPropertySet): """Constant strain limits material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" @classmethod def from_isotropic_constants( @@ -172,6 +173,7 @@ class VariableStrainLimits(_StrainLimitsMixin, _VariablePropertySet): """Variable strain limits material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" effective_strain = variable_material_grpc_data_property("effective_strain", **_ISOTROPIC_KWARGS) eXc = variable_material_grpc_data_property("eXc", **_ORTHOTROPIC_KWARGS) diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/stress_limits.py b/src/ansys/acp/core/_tree_objects/material/property_sets/stress_limits.py index 284c1fbd15..2c895f4cb8 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/stress_limits.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/stress_limits.py @@ -70,6 +70,7 @@ class ConstantStressLimits(_StressLimitsMixin, _ConstantPropertySet): """Constant stress limits material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" @classmethod def from_isotropic_constants( @@ -172,6 +173,7 @@ class VariableStressLimits(_StressLimitsMixin, _VariablePropertySet): """Variable stress limits material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" effective_stress = variable_material_grpc_data_property("effective_stress", **_ISOTROPIC_KWARGS) Xc = variable_material_grpc_data_property("Xc", **_ORTHOTROPIC_KWARGS) diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/tsai_wu_constants.py b/src/ansys/acp/core/_tree_objects/material/property_sets/tsai_wu_constants.py index e0c0c590dd..b6439a159e 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/tsai_wu_constants.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/tsai_wu_constants.py @@ -44,6 +44,7 @@ class ConstantTsaiWuConstants(_TsaiWuConstantsMixin, _ConstantPropertySet): """Constant Tsai-Wu constants material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -71,6 +72,7 @@ class VariableTsaiWuConstants(_TsaiWuConstantsMixin, _VariablePropertySet): """Variable Tsai-Wu constants material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" XY = variable_material_grpc_data_property("XY") XZ = variable_material_grpc_data_property("XZ") diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/woven_characterization.py b/src/ansys/acp/core/_tree_objects/material/property_sets/woven_characterization.py index 98164ae487..d2262e9713 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/woven_characterization.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/woven_characterization.py @@ -44,6 +44,7 @@ class ConstantWovenCharacterization(_WovenCharacterizationMixin, _ConstantProper """Constant woven characterization material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -98,6 +99,7 @@ class VariableWovenCharacterization(_WovenCharacterizationMixin, _VariableProper """Variable woven characterization material property set.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" E1_1 = variable_material_grpc_data_property("E1_1") E2_1 = variable_material_grpc_data_property("E2_1") diff --git a/src/ansys/acp/core/_tree_objects/material/property_sets/woven_stress_limits.py b/src/ansys/acp/core/_tree_objects/material/property_sets/woven_stress_limits.py index eea7145dc3..b3f10a344f 100644 --- a/src/ansys/acp/core/_tree_objects/material/property_sets/woven_stress_limits.py +++ b/src/ansys/acp/core/_tree_objects/material/property_sets/woven_stress_limits.py @@ -44,6 +44,7 @@ class ConstantWovenStressLimits(_WovenStressLimitsMixin, _ConstantPropertySet): """Constant stress limits property set for woven materials.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -89,6 +90,7 @@ class VariableWovenStressLimits(_WovenStressLimitsMixin, _VariablePropertySet): """Variable stress limits property set for woven materials.""" _GRPC_PROPERTIES = tuple() + _SUPPORTED_SINCE = "24.2" Xc = variable_material_grpc_data_property("Xc") Yc = variable_material_grpc_data_property("Yc") diff --git a/src/ansys/acp/core/_tree_objects/model.py b/src/ansys/acp/core/_tree_objects/model.py index 5c7e4fd573..3c1adbcc70 100644 --- a/src/ansys/acp/core/_tree_objects/model.py +++ b/src/ansys/acp/core/_tree_objects/model.py @@ -22,30 +22,30 @@ from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Iterable, Sequence import dataclasses import typing from typing import Any, cast import numpy as np -import numpy.typing as npt -from pyvista.core.pointset import UnstructuredGrid from ansys.api.acp.v0 import ( - base_pb2, boolean_selection_rule_pb2_grpc, cad_geometry_pb2_grpc, cutoff_selection_rule_pb2_grpc, cylindrical_selection_rule_pb2_grpc, edge_set_pb2_grpc, element_set_pb2_grpc, + enum_types_pb2, fabric_pb2_grpc, + field_definition_pb2_grpc, geometrical_selection_rule_pb2_grpc, + imported_modeling_group_pb2_grpc, + imported_solid_model_pb2_grpc, lookup_table_1d_pb2_grpc, lookup_table_3d_pb2_grpc, material_pb2, material_pb2_grpc, - mesh_query_pb2_grpc, model_pb2, model_pb2_grpc, modeling_group_pb2_grpc, @@ -54,7 +54,10 @@ parallel_selection_rule_pb2_grpc, ply_geometry_export_pb2, rosette_pb2_grpc, + sampling_point_pb2_grpc, + section_cut_pb2_grpc, sensor_pb2_grpc, + solid_model_pb2_grpc, spherical_selection_rule_pb2_grpc, stackup_pb2_grpc, sublaminate_pb2_grpc, @@ -64,32 +67,34 @@ ) from ansys.api.acp.v0.base_pb2 import CollectionPath -from .._typing_helper import PATH as _PATH -from .._utils.array_conversions import to_numpy -from .._utils.path_to_str import path_to_str_checked from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty from .._utils.resource_paths import join as rp_join -from .._utils.visualization import to_pyvista_faces, to_pyvista_types +from .._utils.typing_helper import PATH as _PATH +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + ScalarData, + VectorData, + elemental_data_property, + nodal_data_property, +) from ._grpc_helpers.enum_wrapper import wrap_to_string_enum from ._grpc_helpers.exceptions import wrap_grpc_errors from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping from ._grpc_helpers.property_helper import ( + _PROTOBUF_T, + _set_data_attribute, grpc_data_property, grpc_data_property_read_only, mark_grpc_properties, ) -from ._mesh_data import ( - ElementalData, - NodalData, - ScalarData, - VectorData, - elemental_data_property, - nodal_data_property, -) -from .base import ServerWrapper, TreeObject, supported_since +from ._grpc_helpers.protocols import ObjectInfo +from ._grpc_helpers.supported_since import supported_since +from ._mesh_data import full_mesh_property, shell_mesh_property, solid_mesh_property +from .base import ServerWrapper, TreeObject from .boolean_selection_rule import BooleanSelectionRule from .cad_geometry import CADGeometry -from .cutoff_selection_rule import CutoffSelectionRule +from .cut_off_selection_rule import CutOffSelectionRule from .cylindrical_selection_rule import CylindricalSelectionRule from .edge_set import EdgeSet from .element_set import ElementSet @@ -105,38 +110,54 @@ unit_system_type_to_pb, ) from .fabric import Fabric +from .field_definition import FieldDefinition from .geometrical_selection_rule import GeometricalSelectionRule +from .imported_modeling_group import ImportedModelingGroup +from .imported_solid_model import ImportedSolidModel from .lookup_table_1d import LookUpTable1D from .lookup_table_3d import LookUpTable3D from .material import Material from .modeling_group import ModelingGroup from .modeling_ply import ModelingPly +from .object_registry import register from .oriented_selection_set import OrientedSelectionSet from .parallel_selection_rule import ParallelSelectionRule from .rosette import Rosette +from .sampling_point import SamplingPoint +from .section_cut import SectionCut from .sensor import Sensor +from .solid_model import SolidModel from .spherical_selection_rule import SphericalSelectionRule from .stackup import Stackup from .sublaminate import SubLaminate from .tube_selection_rule import TubeSelectionRule +from .utils import CoordinateTransformation from .variable_offset_selection_rule import VariableOffsetSelectionRule from .virtual_geometry import VirtualGeometry __all__ = [ - "MeshData", + "FeFormat", + "HDF5CompositeCAEImportMode", + "HDF5CompositeCAEProjectionMode", + "IgnorableEntity", "Model", "ModelElementalData", "ModelNodalData", - "FeFormat", - "IgnorableEntity", + "ShellMappingProperties", + "SolidMappingProperties", ] FeFormat, fe_format_to_pb, _ = wrap_to_string_enum( "FeFormat", - model_pb2.Format, + enum_types_pb2.FileFormat, module=__name__, value_converter=lambda val: val.lower().replace("_", ":"), doc="Options for the format of the FE file.", + explicit_value_list=( + enum_types_pb2.FileFormat.ANSYS_H5, + enum_types_pb2.FileFormat.ANSYS_CDB, + enum_types_pb2.FileFormat.ANSYS_DAT, + ), ) IgnorableEntity, ignorable_entity_to_pb, _ = wrap_to_string_enum( "IgnorableEntity", @@ -144,30 +165,18 @@ module=__name__, doc="Options for the entities to ignore when loading an FE file.", ) - - -@dataclasses.dataclass -class MeshData: - """Container for the mesh data of an ACP Model.""" - - node_labels: npt.NDArray[np.int32] - node_coordinates: npt.NDArray[np.float64] - element_labels: npt.NDArray[np.int32] - element_types: npt.NDArray[np.int32] - element_nodes: npt.NDArray[np.int32] - element_nodes_offsets: npt.NDArray[np.int32] - - def to_pyvista(self) -> UnstructuredGrid: - """Convert the mesh data to a PyVista mesh.""" - return UnstructuredGrid( - to_pyvista_faces( - element_types=self.element_types, - element_nodes=self.element_nodes, - element_nodes_offsets=self.element_nodes_offsets, - ), - to_pyvista_types(self.element_types), - self.node_coordinates, - ) +HDF5CompositeCAEImportMode, hdf5_composite_cae_import_mode_to_pb, _ = wrap_to_string_enum( + "HDF5CompositeCAEImportMode", + model_pb2.ImportMode, + module=__name__, + doc="Options for the import mode of the HDF5 Composite CAE file.", +) +HDF5CompositeCAEProjectionMode, hdf5_composite_cae_projection_mode_to_pb, _ = wrap_to_string_enum( + "HDF5CompositeCAEProjectionMode", + model_pb2.ProjectionMode, + module=__name__, + doc="Options for the projection mode of the HDF5 Composite CAE file.", +) @dataclasses.dataclass @@ -178,7 +187,13 @@ class ModelElementalData(ElementalData): thickness: ScalarData[np.float64] | None = None relative_thickness_correction: ScalarData[np.float64] | None = None area: ScalarData[np.float64] | None = None - price: ScalarData[np.float64] | None = None + # Retrieving the 'price' can crash the server if the model contains void + # analysis plies (on an imported solid model). + # This is fixed in the backend for 2025R2, but for now we simply comment + # out the property. In this way, the other properties can still be accessed, + # and we can avoid the crash. + # See https://github.com/ansys/pyacp/issues/717 + # price: ScalarData[np.float64] | None = None volume: ScalarData[np.float64] | None = None mass: ScalarData[np.float64] | None = None offset: ScalarData[np.float64] | None = None @@ -190,7 +205,27 @@ class ModelNodalData(NodalData): """Represents nodal data for a Model.""" +@dataclasses.dataclass +class ShellMappingProperties: + """Properties for mapping to the shell on importing HDF5 Composite CAE files.""" + + all_elements: bool = True + element_sets: Sequence[ElementSet] = () + relative_thickness_tolerance: float = 0.5 + relative_in_plane_tolerance: float = 0.01 + angle_tolerance: float = 35.0 + small_hole_threshold: float = 0.0 + + +@dataclasses.dataclass +class SolidMappingProperties: + """Properties for importing HDF5 Composite CAE files as imported plies.""" + + offset_type: OffsetType = OffsetType.BOTTOM_OFFSET + + @mark_grpc_properties +@register class Model(TreeObject): """Defines an ACP Model. @@ -217,6 +252,7 @@ class Model(TreeObject): _COLLECTION_LABEL = "models" _OBJECT_INFO_TYPE = model_pb2.ObjectInfo + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -259,8 +295,20 @@ def _create_stub(self) -> model_pb2_grpc.ObjectServiceStub: minimum_analysis_ply_thickness: ReadWriteProperty[float, float] = grpc_data_property( "properties.minimum_analysis_ply_thickness" ) - unit_system = grpc_data_property_read_only( - "properties.unit_system", from_protobuf=unit_system_type_from_pb + + @staticmethod + def _set_unit_system_data_attribute(pb_obj: ObjectInfo, name: str, value: _PROTOBUF_T) -> None: + # remove the 'minimum_analysis_ply_thickness' property from the pb object, to + # allow the backend to convert it to the new unit system. + pb_obj.properties.ClearField("minimum_analysis_ply_thickness") + _set_data_attribute(pb_obj, name, value) + + unit_system = grpc_data_property( + "properties.unit_system", + from_protobuf=unit_system_type_from_pb, + to_protobuf=unit_system_type_to_pb, + setter_func=_set_unit_system_data_attribute, + writable_since="25.1", ) average_element_size: ReadOnlyProperty[float] = grpc_data_property_read_only( @@ -278,9 +326,7 @@ def _from_file(cls, *, path: _PATH, server_wrapper: ServerWrapper) -> Model: server_wrapper: Representation of the ACP instance. """ - # Send absolute paths to the server, since its CWD may not match - # the Python CWD. - request = model_pb2.LoadFromFileRequest(path=path_to_str_checked(path)) + request = model_pb2.LoadFromFileRequest(path=server_wrapper.auto_upload(path)) with wrap_grpc_errors(): reply = model_pb2_grpc.ObjectServiceStub(server_wrapper.channel).LoadFromFile(request) return cls._from_object_info(object_info=reply, server_wrapper=server_wrapper) @@ -294,7 +340,7 @@ def _from_fe_file( format: FeFormat, # type: ignore ignored_entities: Iterable[IgnorableEntity] = (), # type: ignore convert_section_data: bool = False, - unit_system: UnitSystemType = UnitSystemType.UNDEFINED, + unit_system: UnitSystemType = UnitSystemType.FROM_FILE, ) -> Model: """Load the model from an FE file. @@ -316,14 +362,16 @@ def _from_fe_file( Whether to import the section data of a shell model and convert it into ACP composite definitions. unit_system: - Set the unit system of the model to the given value. Ignored - if the unit system is already set in the FE file. + Defines the unit system of the imported file. Must be set if the + input file does not have units. If the input file does have units, + ``unit_system`` must be either ``"from_file"``, or match the input + unit system. """ format_pb = fe_format_to_pb(format) ignored_entities_pb = [ignorable_entity_to_pb(val) for val in ignored_entities] request = model_pb2.LoadFromFEFileRequest( - path=path_to_str_checked(path), + path=server_wrapper.auto_upload(path), format=cast(Any, format_pb), ignored_entities=cast(Any, ignored_entities_pb), convert_section_data=convert_section_data, @@ -359,14 +407,15 @@ def save(self, path: _PATH, *, save_cache: bool = True) -> None: save_cache: Whether to store the update results such as Analysis Plies and solid models. """ - with wrap_grpc_errors(): - self._get_stub().SaveToFile( - model_pb2.SaveToFileRequest( - resource_path=self._resource_path, - path=path_to_str_checked(path), - save_cache=save_cache, + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + self._get_stub().SaveToFile( + model_pb2.SaveToFileRequest( + resource_path=self._resource_path, + path=export_path, + save_cache=save_cache, + ) ) - ) def export_analysis_model(self, path: _PATH) -> None: """Save the analysis model to a CDB file. @@ -376,11 +425,156 @@ def export_analysis_model(self, path: _PATH) -> None: path: Target file path. E.g. /tmp/ACPAnalysisModel.cdb """ + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + self._get_stub().SaveAnalysisModel( + model_pb2.SaveAnalysisModelRequest( + resource_path=self._resource_path, + path=export_path, + ) + ) + + @supported_since("25.1") + def export_hdf5_composite_cae( + self, + path: _PATH, + *, + remove_midside_nodes: bool = True, + layup_representation_3d: bool = False, + offset_type: OffsetType = OffsetType.BOTTOM_OFFSET, + all_elements: bool = True, + element_sets: Sequence[ElementSet | OrientedSelectionSet] = (), + all_plies: bool = True, + plies: Sequence[ModelingGroup | ModelingPly] = (), + ascii_encoding: bool = False, + ) -> None: + """ + Export the lay-up to the HDF5 Composite CAE format. + + Parameters + ---------- + path : + File path. + remove_midside_nodes : + If True, remove mid-side nodes from the exported mesh. This increases the + overall performance. + layup_representation_3d : + If True, the 3D representation of the lay-up is computed, and the offset ply + surfaces are exported. + offset_type : + Defines if the bottom, mid, or top surface of the plies is exported. + Only used if ``layup_representation_3d`` is True. + all_elements : + Whether to limit the export to some user-defined element sets or not. + element_sets : + Only plies defined on the selected element sets or oriented selection + sets will be exported. + Used only if ``all_elements`` is False. + all_plies : + Whether to export all plies or a user-defined set. + plies : + User-defined set of Modeling Plies and/or Modeling Groups. + Used only if ``all_plies`` is False. + ascii_encoding : + If True, use ASCII encoding when writing text attributes to the HDF5 CAE + file. This may be needed for compatibility with programs that don't fully + support unicode when reading the file. + """ + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + self._get_stub().ExportHDF5CompositeCAE( + model_pb2.ExportHDF5CompositeCAERequest( + resource_path=self._resource_path, + path=export_path, + remove_midside_nodes=remove_midside_nodes, + layup_representation_3d=layup_representation_3d, + offset_type=offset_type_to_pb(offset_type), # type: ignore + ascii_encoding=ascii_encoding, + all_elements=all_elements, + element_sets=[element_set._resource_path for element_set in element_sets], + all_plies=all_plies, + plies=[ply._resource_path for ply in plies], + ) + ) + + @supported_since("25.1") + def import_hdf5_composite_cae( + self, + path: _PATH, + import_mode: HDF5CompositeCAEImportMode = HDF5CompositeCAEImportMode.APPEND, # type: ignore + projection_mode: HDF5CompositeCAEProjectionMode = HDF5CompositeCAEProjectionMode.SHELL, # type: ignore + minimum_angle_tolerance: float = 0.001, + recompute_reference_directions: bool = False, + shell_mapping_properties: ShellMappingProperties = ShellMappingProperties(), + solid_mapping_properties: SolidMappingProperties = SolidMappingProperties(), + coordinate_transformation: CoordinateTransformation = CoordinateTransformation(), + ) -> None: + """Import the lay-up from an HDF5 Composite CAE file. + + Parameters + ---------- + path : + File path. + import_mode : + In :py:attr:`.HDF5CompositeCAEImportMode.APPEND` mode, the imported objects are + appended to existing layup. + In :py:attr:`.HDF5CompositeCAEImportMode.OVERWRITE` mode, existing objects in the model + with the same name are replaced if possible (not locked). + projection_mode : + Determines whether loaded plies are mapped onto the reference surface + (:py:attr:`.HDF5CompositeCAEProjectionMode.SHELL` mode) or exposed as + 3D plies (:py:attr:`.HDF5CompositeCAEProjectionMode.SOLID` mode). + minimum_angle_tolerance : + Minimum angle tolerance for which tabular correction angles for plies are computed. + recompute_reference_directions : + Whether reference directions should be recomputed from tabular angle data or not. + shell_mapping_properties : + Properties for mapping to the shell on importing HDF5 Composite CAE files. + Used only if ``projection_mode`` is set to ``"shell"``. + solid_mapping_properties : + Properties for importing HDF5 Composite CAE files as imported plies. + Used only if ``projection_mode`` is set to ``"solid"``. + coordinate_transformation : + Coordinate transformation applied to the imported lay-up. + """ + if projection_mode == HDF5CompositeCAEProjectionMode.SHELL: + mapping_properties_kwargs: ( + dict[str, model_pb2.ShellMappingProperties] + | dict[str, model_pb2.SolidMappingProperties] + ) = { + "shell_mapping_properties": model_pb2.ShellMappingProperties( + all_elements=shell_mapping_properties.all_elements, + element_sets=[ + element_set._resource_path + for element_set in shell_mapping_properties.element_sets + ], + relative_thickness_tolerance=shell_mapping_properties.relative_thickness_tolerance, + relative_in_plane_tolerance=shell_mapping_properties.relative_in_plane_tolerance, + angle_tolerance=shell_mapping_properties.angle_tolerance, + small_hole_threshold=shell_mapping_properties.small_hole_threshold, + ), + } + else: + assert projection_mode == HDF5CompositeCAEProjectionMode.SOLID + mapping_properties_kwargs = { + "solid_mapping_properties": model_pb2.SolidMappingProperties( + offset_type=offset_type_to_pb(solid_mapping_properties.offset_type) # type: ignore + ), + } + with wrap_grpc_errors(): - self._get_stub().SaveAnalysisModel( - model_pb2.SaveAnalysisModelRequest( + self._get_stub().ImportHDF5CompositeCAE( + model_pb2.ImportHDF5CompositeCAERequest( resource_path=self._resource_path, - path=path_to_str_checked(path), + path=self._server_wrapper.auto_upload(path), + import_mode=hdf5_composite_cae_import_mode_to_pb(import_mode), # type: ignore + projection_mode=hdf5_composite_cae_projection_mode_to_pb(projection_mode), # type: ignore + minimum_angle_tolerance=minimum_angle_tolerance, + recompute_reference_directions=recompute_reference_directions, + coordinate_transformation=model_pb2.CoordinateTransformation( + **dataclasses.asdict(coordinate_transformation) + ), + **mapping_properties_kwargs, ) ) @@ -393,10 +587,51 @@ def export_shell_composite_definitions(self, path: _PATH) -> None: path: File path. Eg. /tmp/ACPCompositeDefinitions.h5 """ + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + self._get_stub().SaveShellCompositeDefinitions( + model_pb2.SaveShellCompositeDefinitionsRequest( + resource_path=self._resource_path, path=export_path + ) + ) + + @supported_since("25.1") + def import_materials( + self, + matml_path: _PATH, + *, + material_apdl_path: _PATH | None = None, + ) -> None: + """ + Import materials from a MatML file. + + Import materials from a ``MatML.xml`` (Engineering Data) file. + + Optionally, a material APDL file can be defined. This is a pre-generated + solver snippet, needed in case of variable materials or non-standard + material models. The snippet is used when exporting solid models or + surface section cuts in the CDB format. + + Parameters + ---------- + matml_path: + File path to the MatML file. + material_apdl_path: + File path to the material APDL file. + """ + material_stub = material_pb2_grpc.ObjectServiceStub(self._channel) + collection_path = CollectionPath( + value=rp_join(self._resource_path.value, Material._COLLECTION_LABEL) + ) + with wrap_grpc_errors(): - self._get_stub().SaveShellCompositeDefinitions( - model_pb2.SaveShellCompositeDefinitionsRequest( - resource_path=self._resource_path, path=path_to_str_checked(path) + material_stub.ImportMaterialFiles( + material_pb2.ImportMaterialFilesRequest( + collection_path=collection_path, + matml_path=self._server_wrapper.auto_upload(matml_path), + material_apdl_path=self._server_wrapper.auto_upload( + material_apdl_path, allow_none=True + ), ) ) @@ -416,14 +651,15 @@ def export_materials(self, path: _PATH) -> None: collection_path = CollectionPath( value=rp_join(self._resource_path.value, Material._COLLECTION_LABEL) ) - with wrap_grpc_errors(): - material_stub.SaveToFile( - material_pb2.SaveToFileRequest( - collection_path=collection_path, - path=path_to_str_checked(path), - format=material_pb2.SaveToFileRequest.ANSYS_XML, + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + material_stub.SaveToFile( + material_pb2.SaveToFileRequest( + collection_path=collection_path, + path=export_path, + format=material_pb2.SaveToFileRequest.ANSYS_XML, + ) ) - ) @supported_since("25.1") def export_modeling_ply_geometries( @@ -485,23 +721,26 @@ def export_modeling_ply_geometries( if arrow_length is None: arrow_length = np.sqrt(self.average_element_size) - with wrap_grpc_errors(): - modeling_ply_stub.ExportGeometries( - ply_geometry_export_pb2.ExportGeometriesRequest( - path=path_to_str_checked(path), - plies=mp_resource_paths, - options=ply_geometry_export_pb2.ExportOptions( - format=typing.cast(typing.Any, ply_geometry_export_format_to_pb(format)), - offset_type=typing.cast(typing.Any, offset_type_to_pb(offset_type)), - include_surface=include_surface, - include_boundary=include_boundary, - include_first_material_direction=include_first_material_direction, - include_second_material_direction=include_second_material_direction, - arrow_length=arrow_length, - arrow_type=typing.cast(typing.Any, arrow_type_to_pb(arrow_type)), - ), + with self._server_wrapper.auto_download(path) as export_path: + with wrap_grpc_errors(): + modeling_ply_stub.ExportGeometries( + ply_geometry_export_pb2.ExportGeometriesRequest( + path=export_path, + plies=mp_resource_paths, + options=ply_geometry_export_pb2.ExportOptions( + format=typing.cast( + typing.Any, ply_geometry_export_format_to_pb(format) + ), + offset_type=typing.cast(typing.Any, offset_type_to_pb(offset_type)), + include_surface=include_surface, + include_boundary=include_boundary, + include_first_material_direction=include_first_material_direction, + include_second_material_direction=include_second_material_direction, + arrow_length=arrow_length, + arrow_type=typing.cast(typing.Any, arrow_type_to_pb(arrow_type)), + ), + ) ) - ) create_material = define_create_method( Material, func_name="create_material", parent_class_name="Model", module_name=__module__ @@ -622,14 +861,14 @@ def export_modeling_ply_geometries( TubeSelectionRule, tube_selection_rule_pb2_grpc.ObjectServiceStub ) - create_cutoff_selection_rule = define_create_method( - CutoffSelectionRule, - func_name="create_cutoff_selection_rule", + create_cut_off_selection_rule = define_create_method( + CutOffSelectionRule, + func_name="create_cut_off_selection_rule", parent_class_name="Model", module_name=__module__, ) - cutoff_selection_rules = define_mutable_mapping( - CutoffSelectionRule, cutoff_selection_rule_pb2_grpc.ObjectServiceStub + cut_off_selection_rules = define_mutable_mapping( + CutOffSelectionRule, cutoff_selection_rule_pb2_grpc.ObjectServiceStub ) create_geometrical_selection_rule = define_create_method( @@ -671,6 +910,7 @@ def export_modeling_ply_geometries( oriented_selection_sets = define_mutable_mapping( OrientedSelectionSet, oriented_selection_set_pb2_grpc.ObjectServiceStub ) + create_modeling_group = define_create_method( ModelingGroup, func_name="create_modeling_group", @@ -681,24 +921,69 @@ def export_modeling_ply_geometries( ModelingGroup, modeling_group_pb2_grpc.ObjectServiceStub ) + create_imported_modeling_group = define_create_method( + ImportedModelingGroup, + func_name="create_imported_modeling_group", + parent_class_name="Model", + module_name=__module__, + ) + imported_modeling_groups = define_mutable_mapping( + ImportedModelingGroup, imported_modeling_group_pb2_grpc.ObjectServiceStub + ) + + create_sampling_point = define_create_method( + SamplingPoint, + func_name="create_sampling_point", + parent_class_name="Model", + module_name=__module__, + ) + sampling_points = define_mutable_mapping( + SamplingPoint, sampling_point_pb2_grpc.ObjectServiceStub + ) + + create_section_cut = define_create_method( + SectionCut, + func_name="create_section_cut", + parent_class_name="Model", + module_name=__module__, + ) + section_cuts = define_mutable_mapping(SectionCut, section_cut_pb2_grpc.ObjectServiceStub) + + create_solid_model = define_create_method( + SolidModel, + func_name="create_solid_model", + parent_class_name="Model", + module_name=__module__, + ) + solid_models = define_mutable_mapping(SolidModel, solid_model_pb2_grpc.ObjectServiceStub) + + create_imported_solid_model = define_create_method( + ImportedSolidModel, + func_name="create_imported_solid_model", + parent_class_name="Model", + module_name=__module__, + ) + imported_solid_models = define_mutable_mapping( + ImportedSolidModel, imported_solid_model_pb2_grpc.ObjectServiceStub + ) + create_sensor = define_create_method( Sensor, func_name="create_sensor", parent_class_name="Model", module_name=__module__ ) sensors = define_mutable_mapping(Sensor, sensor_pb2_grpc.ObjectServiceStub) - @property - def mesh(self) -> MeshData: - """Mesh on which the model is defined.""" - mesh_query_stub = mesh_query_pb2_grpc.MeshQueryServiceStub(self._channel) - reply = mesh_query_stub.GetMeshData(base_pb2.GetRequest(resource_path=self._resource_path)) - return MeshData( - node_labels=to_numpy(reply.node_labels), - node_coordinates=to_numpy(reply.node_coordinates), - element_labels=to_numpy(reply.element_labels), - element_types=to_numpy(reply.element_types), - element_nodes=to_numpy(reply.element_nodes), - element_nodes_offsets=to_numpy(reply.element_nodes_offsets), - ) + create_field_definition = define_create_method( + FieldDefinition, + func_name="create_field_definition", + parent_class_name="Model", + module_name=__module__, + ) + field_definitions = define_mutable_mapping( + FieldDefinition, field_definition_pb2_grpc.ObjectServiceStub + ) + mesh = full_mesh_property + shell_mesh = shell_mesh_property + solid_mesh = solid_mesh_property elemental_data = elemental_data_property(ModelElementalData) nodal_data = nodal_data_property(ModelNodalData) diff --git a/src/ansys/acp/core/_tree_objects/modeling_group.py b/src/ansys/acp/core/_tree_objects/modeling_group.py index cfeb18d771..35a95d672a 100644 --- a/src/ansys/acp/core/_tree_objects/modeling_group.py +++ b/src/ansys/acp/core/_tree_objects/modeling_group.py @@ -25,18 +25,27 @@ from collections.abc import Iterable import dataclasses -from ansys.api.acp.v0 import modeling_group_pb2, modeling_group_pb2_grpc, modeling_ply_pb2_grpc +from ansys.api.acp.v0 import ( + butt_joint_sequence_pb2_grpc, + interface_layer_pb2_grpc, + modeling_group_pb2, + modeling_group_pb2_grpc, + modeling_ply_pb2_grpc, +) -from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping -from ._grpc_helpers.property_helper import mark_grpc_properties -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.mapping import define_create_method, define_mutable_mapping +from ._grpc_helpers.property_helper import mark_grpc_properties +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject +from .butt_joint_sequence import ButtJointSequence +from .interface_layer import InterfaceLayer from .modeling_ply import ModelingPly from .object_registry import register @@ -45,14 +54,14 @@ @dataclasses.dataclass class ModelingGroupElementalData(ElementalData): - """Represents elemental data for an Modeling Group.""" + """Represents elemental data for a Modeling Group.""" normal: VectorData | None = None @dataclasses.dataclass class ModelingGroupNodalData(NodalData): - """Represents nodal data for an Modeling Group.""" + """Represents nodal data for a Modeling Group.""" @mark_grpc_properties @@ -62,7 +71,7 @@ class ModelingGroup(CreatableTreeObject, IdTreeObject): Parameters ---------- - name + name : Name of the modeling group. """ @@ -71,6 +80,7 @@ class ModelingGroup(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "modeling_groups" _OBJECT_INFO_TYPE = modeling_group_pb2.ObjectInfo _CREATE_REQUEST_TYPE = modeling_group_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__(self, *, name: str = "ModelingGroup"): super().__init__(name=name) @@ -85,6 +95,28 @@ def _create_stub(self) -> modeling_group_pb2_grpc.ObjectServiceStub: module_name=__module__, ) modeling_plies = define_mutable_mapping(ModelingPly, modeling_ply_pb2_grpc.ObjectServiceStub) + create_interface_layer = define_create_method( + InterfaceLayer, + func_name="create_interface_layer", + parent_class_name="ModelingGroup", + module_name=__module__, + ) + interface_layers = define_mutable_mapping( + InterfaceLayer, interface_layer_pb2_grpc.ObjectServiceStub + ) + + create_butt_joint_sequence = define_create_method( + ButtJointSequence, + func_name="create_butt_joint_sequence", + parent_class_name="ModelingGroup", + module_name=__module__, + ) + butt_joint_sequences = define_mutable_mapping( + ButtJointSequence, butt_joint_sequence_pb2_grpc.ObjectServiceStub + ) + + mesh = full_mesh_property + shell_mesh = shell_mesh_property elemental_data = elemental_data_property(ModelingGroupElementalData) nodal_data = nodal_data_property(ModelingGroupNodalData) diff --git a/src/ansys/acp/core/_tree_objects/modeling_ply.py b/src/ansys/acp/core/_tree_objects/modeling_ply.py index 97d9160e65..6883f5eabe 100644 --- a/src/ansys/acp/core/_tree_objects/modeling_ply.py +++ b/src/ansys/acp/core/_tree_objects/modeling_ply.py @@ -22,9 +22,9 @@ from __future__ import annotations -from collections.abc import Container, Iterable +from collections.abc import Callable, Container, Iterable import dataclasses -from typing import Any, Callable +from typing import Any import numpy as np from typing_extensions import Self @@ -33,6 +33,14 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + ScalarData, + VectorData, + elemental_data_property, + nodal_data_property, +) from ._grpc_helpers.edge_property_list import ( GenericEdgePropertyType, define_add_method, @@ -41,19 +49,13 @@ from ._grpc_helpers.linked_object_list import define_linked_object_list from ._grpc_helpers.mapping import get_read_only_collection_property from ._grpc_helpers.property_helper import ( + _exposed_grpc_property, grpc_data_property, grpc_data_property_read_only, grpc_link_property, mark_grpc_properties, ) -from ._mesh_data import ( - ElementalData, - NodalData, - ScalarData, - VectorData, - elemental_data_property, - nodal_data_property, -) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .edge_set import EdgeSet from .enums import ( @@ -114,6 +116,7 @@ class ModelingPlyNodalData(NodalData): ply_offset: VectorData | None = None +@mark_grpc_properties class TaperEdge(GenericEdgePropertyType): """Defines a taper edge. @@ -129,13 +132,15 @@ class TaperEdge(GenericEdgePropertyType): offset is ``-offset/tan(angle)``. """ + _SUPPORTED_SINCE = "24.2" + def __init__(self, edge_set: EdgeSet, *, angle: float, offset: float): self._callback_apply_changes: Callable[[], None] | None = None self.edge_set = edge_set self.angle = angle self.offset = offset - @property + @_exposed_grpc_property def edge_set(self) -> EdgeSet: """Edge along which the ply tapering is applied.""" return self._edge_set @@ -148,7 +153,7 @@ def edge_set(self, edge_set: EdgeSet) -> None: if self._callback_apply_changes is not None: self._callback_apply_changes() - @property + @_exposed_grpc_property def angle(self) -> float: """Angle between the cutting plane and the reference surface.""" return self._angle @@ -159,7 +164,7 @@ def angle(self, angle: float) -> None: if self._callback_apply_changes is not None: self._callback_apply_changes() - @property + @_exposed_grpc_property def offset(self) -> float: """Move the cutting plane along the out-of-plane direction. @@ -221,6 +226,14 @@ def __repr__(self) -> str: f"angle={self.angle!r}, offset={self.offset!r})" ) + def clone(self) -> Self: + """Create a new unstored TaperEdge with the same properties.""" + return type(self)( + edge_set=self.edge_set, + angle=self.angle, + offset=self.offset, + ) + @mark_grpc_properties @register @@ -243,7 +256,7 @@ class ModelingPly(CreatableTreeObject, IdTreeObject): Defines the global ply order. selection_rules : Selection Rules which may limit the extent of the ply. - draping : + draping_type : Chooses between different draping formulations. draping_seed_point : Starting point of the draping algorithm. @@ -292,6 +305,7 @@ class ModelingPly(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "modeling_plies" _OBJECT_INFO_TYPE = modeling_ply_pb2.ObjectInfo _CREATE_REQUEST_TYPE = modeling_ply_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -306,7 +320,7 @@ def __init__( # if global_ply_nr == 0 global_ply_nr: int = 0, selection_rules: Iterable[LinkedSelectionRule] = (), - draping: DrapingType = DrapingType.NO_DRAPING, + draping_type: DrapingType = DrapingType.NO_DRAPING, draping_seed_point: tuple[float, float, float] = (0.0, 0.0, 0.0), auto_draping_direction: bool = True, draping_direction: tuple[float, float, float] = (1.0, 0.0, 0.0), @@ -330,7 +344,7 @@ def __init__( self.active = active self.global_ply_nr = global_ply_nr self.selection_rules = selection_rules - self.draping = draping + self.draping_type = draping_type self.draping_seed_point = draping_seed_point self.auto_draping_direction = auto_draping_direction self.draping_direction = draping_direction @@ -365,7 +379,7 @@ def _create_stub(self) -> modeling_ply_pb2_grpc.ObjectServiceStub: active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") global_ply_nr: ReadWriteProperty[int, int] = grpc_data_property("properties.global_ply_nr") - draping = grpc_data_property( + draping_type = grpc_data_property( "properties.draping", from_protobuf=draping_type_from_pb, to_protobuf=draping_type_to_pb ) draping_seed_point = grpc_data_property( @@ -406,10 +420,6 @@ def _create_stub(self) -> modeling_ply_pb2_grpc.ObjectServiceStub: module_name=__module__, ) - production_plies = get_read_only_collection_property( - ProductionPly, production_ply_pb2_grpc.ObjectServiceStub - ) - thickness_type = grpc_data_property( "properties.thickness_type", from_protobuf=thickness_type_from_pb, @@ -436,5 +446,12 @@ def _create_stub(self) -> modeling_ply_pb2_grpc.ObjectServiceStub: module_name=__module__, ) + production_plies = get_read_only_collection_property( + ProductionPly, production_ply_pb2_grpc.ObjectServiceStub + ) + + mesh = full_mesh_property + shell_mesh = shell_mesh_property + elemental_data = elemental_data_property(ModelingPlyElementalData) nodal_data = nodal_data_property(ModelingPlyNodalData) diff --git a/src/ansys/acp/core/_tree_objects/oriented_selection_set.py b/src/ansys/acp/core/_tree_objects/oriented_selection_set.py index f3b52fbcf6..6822a23ab5 100644 --- a/src/ansys/acp/core/_tree_objects/oriented_selection_set.py +++ b/src/ansys/acp/core/_tree_objects/oriented_selection_set.py @@ -30,6 +30,13 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + VectorData, + elemental_data_property, + nodal_data_property, +) from ._grpc_helpers.linked_object_list import ( define_linked_object_list, define_polymorphic_linked_object_list, @@ -40,19 +47,13 @@ grpc_link_property, mark_grpc_properties, ) -from ._mesh_data import ( - ElementalData, - NodalData, - VectorData, - elemental_data_property, - nodal_data_property, -) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .boolean_selection_rule import BooleanSelectionRule from .cylindrical_selection_rule import CylindricalSelectionRule from .element_set import ElementSet from .enums import ( - DrapingMaterialType, + DrapingMaterialModel, RosetteSelectionMethod, draping_material_type_from_pb, draping_material_type_to_pb, @@ -60,6 +61,7 @@ rosette_selection_method_to_pb, status_type_from_pb, ) +from .lookup_table_1d_column import LookUpTable1DColumn from .lookup_table_3d_column import LookUpTable3DColumn from .object_registry import register from .parallel_selection_rule import ParallelSelectionRule @@ -68,11 +70,6 @@ from .tube_selection_rule import TubeSelectionRule from .variable_offset_selection_rule import VariableOffsetSelectionRule -# Workaround: these imports are needed to make sphinx_autodoc_typehints understand -# the inherited members of the Elemental- and NodalData classes. -import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip - __all__ = [ "OrientedSelectionSet", "OrientedSelectionSetElementalData", @@ -84,15 +81,15 @@ # this would cause a circular import at run-time. from .. import BooleanSelectionRule, GeometricalSelectionRule - _SELECTION_RULES_LINKABLE_TO_OSS = typing.Union[ - ParallelSelectionRule, - CylindricalSelectionRule, - SphericalSelectionRule, - TubeSelectionRule, - GeometricalSelectionRule, - VariableOffsetSelectionRule, - BooleanSelectionRule, - ] +_SELECTION_RULES_LINKABLE_TO_OSS: typing.TypeAlias = typing.Union[ + ParallelSelectionRule, + CylindricalSelectionRule, + SphericalSelectionRule, + TubeSelectionRule, + "GeometricalSelectionRule", + VariableOffsetSelectionRule, + "BooleanSelectionRule", +] @dataclasses.dataclass @@ -148,7 +145,9 @@ class OrientedSelectionSet(CreatableTreeObject, IdTreeObject): draping_ud_coefficient : Value between ``0`` and ``1`` which determines the amount of deformation in the transverse direction if the draping material model is set to - :attr:`DrapingMaterialType.UD`. + :attr:`DrapingMaterialModel.UD`. + rotation_angle : + Angle in degrees by which the initial reference directions are rotated around the orientations. reference_direction_field : A 3D lookup table column that defines the reference directions. @@ -159,6 +158,7 @@ class OrientedSelectionSet(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "oriented_selection_sets" _OBJECT_INFO_TYPE = oriented_selection_set_pb2.ObjectInfo _CREATE_REQUEST_TYPE = oriented_selection_set_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -176,10 +176,10 @@ def __init__( draping_direction: tuple[float, float, float] = (0.0, 0.0, 1.0), use_default_draping_mesh_size: bool = True, draping_mesh_size: float = 0.0, - draping_material_model: DrapingMaterialType = DrapingMaterialType.WOVEN, + draping_material_model: DrapingMaterialModel = DrapingMaterialModel.WOVEN, draping_ud_coefficient: float = 0.0, rotation_angle: float = 0.0, - reference_direction_field: LookUpTable3DColumn | None = None, + reference_direction_field: LookUpTable1DColumn | LookUpTable3DColumn | None = None, ): super().__init__(name=name) self.element_sets = element_sets @@ -194,7 +194,7 @@ def __init__( self.draping_direction = draping_direction self.use_default_draping_mesh_size = use_default_draping_mesh_size self.draping_mesh_size = draping_mesh_size - self.draping_material_model = DrapingMaterialType(draping_material_model) + self.draping_material_model = DrapingMaterialModel(draping_material_model) self.draping_ud_coefficient = draping_ud_coefficient self.rotation_angle = rotation_angle self.reference_direction_field = reference_direction_field @@ -270,8 +270,12 @@ def _create_stub(self) -> oriented_selection_set_pb2_grpc.ObjectServiceStub: ) reference_direction_field = grpc_link_property( - "properties.reference_direction_field", allowed_types=LookUpTable3DColumn + "properties.reference_direction_field", + allowed_types=(LookUpTable3DColumn, LookUpTable1DColumn), ) + mesh = full_mesh_property + shell_mesh = shell_mesh_property + elemental_data = elemental_data_property(OrientedSelectionSetElementalData) nodal_data = nodal_data_property(OrientedSelectionSetNodalData) diff --git a/src/ansys/acp/core/_tree_objects/parallel_selection_rule.py b/src/ansys/acp/core/_tree_objects/parallel_selection_rule.py index a6c28cd42a..2b625d96bd 100644 --- a/src/ansys/acp/core/_tree_objects/parallel_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/parallel_selection_rule.py @@ -29,19 +29,20 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - grpc_link_property, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .enums import status_type_from_pb from .object_registry import register @@ -50,7 +51,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "ParallelSelectionRule", @@ -93,9 +94,9 @@ class ParallelSelectionRule(CreatableTreeObject, IdTreeObject): Negative distance of the Parallel Selection Rule. upper_limit : Positive distance of the Parallel Selection Rule. - relative_rule_type : + relative_rule : If True, parameters are evaluated relative to size of the object. - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. """ @@ -105,6 +106,7 @@ class ParallelSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "parallel_selection_rules" _OBJECT_INFO_TYPE = parallel_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = parallel_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -116,8 +118,8 @@ def __init__( direction: tuple[float, ...] = (1.0, 0.0, 0.0), lower_limit: float = 0.0, upper_limit: float = 0.0, - relative_rule_type: bool = False, - include_rule_type: bool = True, + relative_rule: bool = False, + include_rule: bool = True, ): super().__init__(name=name) self.use_global_coordinate_system = use_global_coordinate_system @@ -126,8 +128,8 @@ def __init__( self.direction = direction self.lower_limit = lower_limit self.upper_limit = upper_limit - self.relative_rule_type = relative_rule_type - self.include_rule_type = include_rule_type + self.relative_rule = relative_rule + self.include_rule = include_rule def _create_stub(self) -> parallel_selection_rule_pb2_grpc.ObjectServiceStub: return parallel_selection_rule_pb2_grpc.ObjectServiceStub(self._channel) @@ -146,12 +148,14 @@ def _create_stub(self) -> parallel_selection_rule_pb2_grpc.ObjectServiceStub: ) lower_limit: ReadWriteProperty[float, float] = grpc_data_property("properties.lower_limit") upper_limit: ReadWriteProperty[float, float] = grpc_data_property("properties.upper_limit") - relative_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( + relative_rule: ReadWriteProperty[bool, bool] = grpc_data_property( "properties.relative_rule_type" ) - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") + + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(ParallelSelectionRuleElementalData) nodal_data = nodal_data_property(ParallelSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/production_ply.py b/src/ansys/acp/core/_tree_objects/production_ply.py index ff9d7b2eb5..283c639a74 100644 --- a/src/ansys/acp/core/_tree_objects/production_ply.py +++ b/src/ansys/acp/core/_tree_objects/production_ply.py @@ -30,13 +30,7 @@ from ansys.api.acp.v0 import analysis_ply_pb2_grpc, production_ply_pb2, production_ply_pb2_grpc from .._utils.property_protocols import ReadOnlyProperty -from ._grpc_helpers.mapping import get_read_only_collection_property -from ._grpc_helpers.property_helper import ( - grpc_data_property_read_only, - grpc_link_property_read_only, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, ScalarData, @@ -44,6 +38,13 @@ elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.mapping import get_read_only_collection_property +from ._grpc_helpers.property_helper import ( + grpc_data_property_read_only, + grpc_link_property_read_only, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .analysis_ply import AnalysisPly from .base import IdTreeObject, ReadOnlyTreeObject from .enums import status_type_from_pb @@ -92,11 +93,13 @@ class ProductionPly(ReadOnlyTreeObject, IdTreeObject): Parameters ---------- name: str - The name of the ProductionPly. + The name of the production ply. material: Material Material of the production ply. angle: float Angle of the production ply in degrees. + thickness: float + Thickness of the production ply. """ @@ -104,6 +107,7 @@ class ProductionPly(ReadOnlyTreeObject, IdTreeObject): _COLLECTION_LABEL = "production_plies" _OBJECT_INFO_TYPE = production_ply_pb2.ObjectInfo + _SUPPORTED_SINCE = "24.2" def _create_stub(self) -> production_ply_pb2_grpc.ObjectServiceStub: return production_ply_pb2_grpc.ObjectServiceStub(self._channel) @@ -111,6 +115,11 @@ def _create_stub(self) -> production_ply_pb2_grpc.ObjectServiceStub: status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) material = grpc_link_property_read_only("properties.material") angle: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.angle") + thickness: ReadOnlyProperty[float] = grpc_data_property_read_only("properties.thickness") + + mesh = full_mesh_property + shell_mesh = shell_mesh_property + elemental_data = elemental_data_property(ProductionPlyElementalData) nodal_data = nodal_data_property(ProductionPlyNodalData) diff --git a/src/ansys/acp/core/_tree_objects/rosette.py b/src/ansys/acp/core/_tree_objects/rosette.py index 37c179d99a..bf51eddfe5 100644 --- a/src/ansys/acp/core/_tree_objects/rosette.py +++ b/src/ansys/acp/core/_tree_objects/rosette.py @@ -68,6 +68,7 @@ class Rosette(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "rosettes" _OBJECT_INFO_TYPE = rosette_pb2.ObjectInfo _CREATE_REQUEST_TYPE = rosette_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_tree_objects/sampling_point.py b/src/ansys/acp/core/_tree_objects/sampling_point.py new file mode 100644 index 0000000000..b60e32c814 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/sampling_point.py @@ -0,0 +1,126 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable + +from ansys.api.acp.v0 import sampling_point_pb2, sampling_point_pb2_grpc + +from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array +from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .enums import status_type_from_pb +from .object_registry import register +from .rosette import Rosette + +__all__ = ["SamplingPoint"] + + +@mark_grpc_properties +@register +class SamplingPoint(CreatableTreeObject, IdTreeObject): + """Instantiate a Sampling Point. + + Parameters + ---------- + name : + Name of the sampling point. + point : + Coordinates of the sampling point. + direction : + Direction of the sampling point. + use_default_reference_direction : + Whether to use the element coordinate system when computing the laminate + properties (CLT). + rosette : + Rosette defining the coordinate system used when computing the laminate + properties (CLT). Only used when ``use_default_reference_direction`` is False. + offset_is_middle : + Activate this option to offset the reference surface to the mid-plane of the + laminate for the computation of the ABD matrices and laminate properties (CLT). + consider_coupling_effect : + Whether the computation of the laminate engineering constants should consider + the coupling effect if the B-Matrix of the ABD-Matrix is not zero. Computation + of the ABD matrices is not affected by this parameter. + + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "sampling_points" + _OBJECT_INFO_TYPE = sampling_point_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = sampling_point_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "SamplingPoint", + point: tuple[float, float, float] = (0.0, 0.0, 0.0), + direction: tuple[float, float, float] = (0.0, 0.0, 0.0), + use_default_reference_direction: bool = True, + rosette: Rosette | None = None, + offset_is_middle: bool = True, + consider_coupling_effect: bool = True, + ): + super().__init__(name=name) + + self.point = point + self.direction = direction + self.use_default_reference_direction = use_default_reference_direction + self.rosette = rosette + self.offset_is_middle = offset_is_middle + self.consider_coupling_effect = consider_coupling_effect + + def _create_stub(self) -> sampling_point_pb2_grpc.ObjectServiceStub: + return sampling_point_pb2_grpc.ObjectServiceStub(self._channel) + + locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked") + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + point = grpc_data_property( + "properties.point", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array + ) + direction = grpc_data_property( + "properties.direction", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array + ) + use_default_reference_direction: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.use_default_reference_direction" + ) + rosette = grpc_link_property("properties.rosette", allowed_types=Rosette) + reference_direction = grpc_data_property_read_only( + "properties.reference_direction", + from_protobuf=to_tuple_from_1D_array, + doc="Local x-direction used in the computation of laminate properties (CLT).", + ) + offset_is_middle: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.offset_is_middle" + ) + consider_coupling_effect: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.consider_coupling_effect" + ) diff --git a/src/ansys/acp/core/_tree_objects/section_cut.py b/src/ansys/acp/core/_tree_objects/section_cut.py new file mode 100644 index 0000000000..254c2903b2 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/section_cut.py @@ -0,0 +1,227 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Sequence + +from ansys.api.acp.v0 import section_cut_pb2, section_cut_pb2_grpc + +from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array +from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty +from ._grpc_helpers.linked_object_list import define_linked_object_list +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .element_set import ElementSet +from .enums import ( + ExtrusionType, + IntersectionType, + SectionCutType, + extrusion_type_from_pb, + extrusion_type_to_pb, + intersection_type_from_pb, + intersection_type_to_pb, + section_cut_type_from_pb, + section_cut_type_to_pb, + status_type_from_pb, +) +from .object_registry import register + +__all__ = ["SectionCut"] + + +@mark_grpc_properties +@register +class SectionCut(CreatableTreeObject, IdTreeObject): + r"""Instantiate a Section Cut. + + Parameters + ---------- + name : + Name of the section cut. + active : + Inactive section cuts are not evaluated. + origin : + Defines the origin of the section cut plane. + normal : + Defines the normal vector of the section cut plane. + in_plane_reference_direction1 : + Defines the in-plane transverse direction of the beam. Used for the surface + section cut and section cut measures. + scope_entire_model : + If True, the section cut is applied to the entire model. Otherwise, the + section cut is applied only to the element sets specified in + ``scope_element_sets``. + scope_element_sets : + The element sets to which the section cut is applied. Used only if + ``scope_entire_model`` is False. + extrusion_type : + Determines the extrusion method used to create the section cut. + scale_factor : + Scale factor applied to the ply thicknesses. + core_scale_factor : + Scale factor applied to the core thicknesses. + section_cut_type : + Determines whether the section cut is extruded by modeling ply, production + ply, or analysis ply. + intersection_type : + Determines the method used to compute a wire frame section cut. Used only + if ``extrusion_type`` is ``ExtrusionType.WIRE_FRAME``. + use_default_tolerance : + If True, the segment tolerance is computed as 0.1\% of the averaged element size. + Otherwise, the ``tolerance`` value is used. + Used only if ``extrusion_type`` is ``ExtrusionType.SURFACE_NORMAL`` or + ``ExtrusionType.SURFACE_SWEEP_BASED``. + tolerance : + Defines the minimum length of the segments. Segments shorter than this value + are merged. + Used only if ``extrusion_type`` is ``ExtrusionType.SURFACE_NORMAL`` or + ``ExtrusionType.SURFACE_SWEEP_BASED``, and ``use_default_tolerance`` is + False. + use_default_interpolation_settings : + If True, default interpolation settings are used by the sweep-based extrusion. + Used only if ``extrusion_type`` is ``ExtrusionType.SURFACE_SWEEP_BASED``. + search_radius : + Search radius of the interpolation algorithm used in the sweep-based extrusion. + Used only if ``extrusion_type`` is ``ExtrusionType.SURFACE_SWEEP_BASED`` and + ``use_default_interpolation_settings`` is False. + number_of_interpolation_points : + Number of interpolation points of the interpolation algorithm used in the + sweep-based extrusion. + Used only if ``extrusion_type`` is ``ExtrusionType.SURFACE_SWEEP_BASED`` and + ``use_default_interpolation_settings`` is False. + + """ + + __slots__ = () + + _COLLECTION_LABEL = "section_cuts" + _OBJECT_INFO_TYPE = section_cut_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = section_cut_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "SectionCut", + active: bool = True, + origin: tuple[float, float, float] = (0.0, 0.0, 0.0), + normal: tuple[float, float, float] = (0.0, 0.0, 1.0), + in_plane_reference_direction1: tuple[float, float, float] = (1.0, 0.0, 0.0), + scope_entire_model: bool = True, + scope_element_sets: Sequence[ElementSet] = tuple(), + extrusion_type: ExtrusionType = ExtrusionType.WIRE_FRAME, + scale_factor: float = 1.0, + core_scale_factor: float = 1.0, + section_cut_type: SectionCutType = SectionCutType.MODELING_PLY_WISE, + intersection_type: IntersectionType = IntersectionType.NORMAL_TO_SURFACE, + use_default_tolerance: bool = True, + tolerance: float = 0.0, + use_default_interpolation_settings: bool = True, + search_radius: float = 0.0, + number_of_interpolation_points: int = 1, + ) -> None: + super().__init__(name=name) + self.active = active + self.origin = origin + self.normal = normal + self.in_plane_reference_direction1 = in_plane_reference_direction1 + self.scope_entire_model = scope_entire_model + self.scope_element_sets = scope_element_sets + self.extrusion_type = extrusion_type + self.scale_factor = scale_factor + self.core_scale_factor = core_scale_factor + self.section_cut_type = section_cut_type + self.intersection_type = intersection_type + self.use_default_tolerance = use_default_tolerance + self.tolerance = tolerance + self.use_default_interpolation_settings = use_default_interpolation_settings + self.search_radius = search_radius + self.number_of_interpolation_points = number_of_interpolation_points + + def _create_stub(self) -> section_cut_pb2_grpc.ObjectServiceStub: + return section_cut_pb2_grpc.ObjectServiceStub(self._channel) + + # general properties + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked") + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + + # position properties + origin = grpc_data_property( + "properties.origin", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array + ) + normal = grpc_data_property( + "properties.normal", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array + ) + in_plane_reference_direction1 = grpc_data_property( + "properties.in_plane_reference_direction1", + from_protobuf=to_tuple_from_1D_array, + to_protobuf=to_1D_double_array, + ) + + # scoping properties + scope_entire_model: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.scope_entire_model" + ) + scope_element_sets = define_linked_object_list("properties.scope_element_sets", ElementSet) + + # extrusion properties + extrusion_type = grpc_data_property( + "properties.extrusion_type", + from_protobuf=extrusion_type_from_pb, + to_protobuf=extrusion_type_to_pb, + ) + scale_factor: ReadWriteProperty[float, float] = grpc_data_property("properties.scale_factor") + core_scale_factor: ReadWriteProperty[float, float] = grpc_data_property( + "properties.core_scale_factor" + ) + section_cut_type = grpc_data_property( + "properties.section_cut_type", + from_protobuf=section_cut_type_from_pb, + to_protobuf=section_cut_type_to_pb, + ) + + # wireframe properties + intersection_type = grpc_data_property( + "properties.intersection_type", + from_protobuf=intersection_type_from_pb, + to_protobuf=intersection_type_to_pb, + ) + + # surface properties - general + use_default_tolerance: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.use_default_tolerance" + ) + tolerance: ReadWriteProperty[float, float] = grpc_data_property("properties.tolerance") + # surface properties - sweep-based + use_default_interpolation_settings: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.use_default_interpolation_settings" + ) + search_radius: ReadWriteProperty[float, float] = grpc_data_property("properties.search_radius") + number_of_interpolation_points: ReadWriteProperty[int, int] = grpc_data_property( + "properties.number_of_interpolation_points" + ) diff --git a/src/ansys/acp/core/_tree_objects/sensor.py b/src/ansys/acp/core/_tree_objects/sensor.py index 8c47d03e3f..243dca0e48 100644 --- a/src/ansys/acp/core/_tree_objects/sensor.py +++ b/src/ansys/acp/core/_tree_objects/sensor.py @@ -22,7 +22,7 @@ from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Iterable, Sequence from typing import Union, get_args from ansys.api.acp.v0 import sensor_pb2, sensor_pb2_grpc @@ -76,6 +76,7 @@ class Sensor(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "sensors" _OBJECT_INFO_TYPE = sensor_pb2.ObjectInfo _CREATE_REQUEST_TYPE = sensor_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -83,7 +84,7 @@ def __init__( name: str = "Sensor", sensor_type: SensorType = SensorType.SENSOR_BY_AREA, active: bool = True, - entities: Iterable[_LINKABLE_ENTITY_TYPES] = (), + entities: Sequence[_LINKABLE_ENTITY_TYPES] = (), ): super().__init__(name=name) self.active = active diff --git a/src/ansys/acp/core/_tree_objects/snap_to_geometry.py b/src/ansys/acp/core/_tree_objects/snap_to_geometry.py new file mode 100644 index 0000000000..5a4e5fd8e1 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/snap_to_geometry.py @@ -0,0 +1,107 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable + +from ansys.api.acp.v0 import snap_to_geometry_pb2, snap_to_geometry_pb2_grpc + +from .._utils.property_protocols import ReadWriteProperty +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from .base import CreatableTreeObject, IdTreeObject +from .enums import ( + SnapToGeometryOrientationType, + snap_to_geometry_orientation_type_from_pb, + snap_to_geometry_orientation_type_to_pb, + status_type_from_pb, +) +from .object_registry import register +from .oriented_selection_set import OrientedSelectionSet +from .virtual_geometry import VirtualGeometry + +__all__ = ["SnapToGeometry"] + + +@mark_grpc_properties +@register +class SnapToGeometry(CreatableTreeObject, IdTreeObject): + """Instantiate a snap-to geometry. + + Parameters + ---------- + name : + Name of the snap-to geometry. + active : + Inactive snap-to geometries are not used in the solid model extrusion. + orientation_type : + Determines whether the snap-to geometry is applied to the top or bottom + surface of the layup. + cad_geometry : + The geometry to snap to. + oriented_selection_set : + Defines the extent over which the snap-to geometry is applied. The normal + of the oriented selection set is used to determine the top and bottom + surfaces of the layup. + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "snap_to_geometries" + _OBJECT_INFO_TYPE = snap_to_geometry_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = snap_to_geometry_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "SnapToGeometry", + active: bool = True, + orientation_type: SnapToGeometryOrientationType = SnapToGeometryOrientationType.TOP, + cad_geometry: VirtualGeometry | None = None, + oriented_selection_set: OrientedSelectionSet | None = None, + ): + super().__init__(name=name) + self.active = active + self.orientation_type = orientation_type + self.cad_geometry = cad_geometry + self.oriented_selection_set = oriented_selection_set + + def _create_stub(self) -> snap_to_geometry_pb2_grpc.ObjectServiceStub: + return snap_to_geometry_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + orientation_type = grpc_data_property( + "properties.orientation_type", + from_protobuf=snap_to_geometry_orientation_type_from_pb, + to_protobuf=snap_to_geometry_orientation_type_to_pb, + ) + cad_geometry = grpc_link_property("properties.cad_geometry", allowed_types=(VirtualGeometry,)) + oriented_selection_set = grpc_link_property( + "properties.oriented_selection_set", allowed_types=(OrientedSelectionSet,) + ) diff --git a/src/ansys/acp/core/_tree_objects/solid_element_set.py b/src/ansys/acp/core/_tree_objects/solid_element_set.py new file mode 100644 index 0000000000..501beee85b --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/solid_element_set.py @@ -0,0 +1,81 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable +import dataclasses + +from ansys.api.acp.v0 import solid_element_set_pb2, solid_element_set_pb2_grpc + +from .._utils.array_conversions import to_tuple_from_1D_array +from .._utils.property_protocols import ReadOnlyProperty +from ._elemental_or_nodal_data import ElementalData, NodalData +from ._grpc_helpers.property_helper import grpc_data_property_read_only, mark_grpc_properties +from ._mesh_data import solid_mesh_property +from .base import IdTreeObject, ReadOnlyTreeObject +from .enums import status_type_from_pb +from .object_registry import register + +__all__ = ["SolidElementSet", "SolidElementSetElementalData", "SolidElementSetNodalData"] + + +@dataclasses.dataclass +class SolidElementSetElementalData(ElementalData): + """Represents elemental data for a Solid Element Set.""" + + +@dataclasses.dataclass +class SolidElementSetNodalData(NodalData): + """Represents nodal data for a Solid Element Set.""" + + +@mark_grpc_properties +@register +class SolidElementSet(ReadOnlyTreeObject, IdTreeObject): + """Instantiate a Solid Element Set. + + Parameters + ---------- + name: str + The name of the production ply. + element_labels : + Label of elements. + + """ + + __slots__: Iterable[str] = tuple() + + _COLLECTION_LABEL = "solid_element_sets" + _OBJECT_INFO_TYPE = solid_element_set_pb2.ObjectInfo + _SUPPORTED_SINCE = "25.1" + + def _create_stub(self) -> solid_element_set_pb2_grpc.ObjectServiceStub: + return solid_element_set_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked") + element_labels: ReadOnlyProperty[tuple[int, ...]] = grpc_data_property_read_only( + "properties.element_labels", from_protobuf=to_tuple_from_1D_array + ) + + solid_mesh = solid_mesh_property diff --git a/src/ansys/acp/core/_tree_objects/solid_model.py b/src/ansys/acp/core/_tree_objects/solid_model.py new file mode 100644 index 0000000000..32c36c3b81 --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/solid_model.py @@ -0,0 +1,535 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import annotations + +from collections.abc import Iterable +import dataclasses +from typing import Any + +from ansys.api.acp.v0 import ( + analysis_ply_pb2_grpc, + cut_off_geometry_pb2_grpc, + extrusion_guide_pb2_grpc, + snap_to_geometry_pb2_grpc, + solid_element_set_pb2_grpc, + solid_model_pb2, + solid_model_pb2_grpc, +) + +from .._utils.property_protocols import ReadOnlyProperty, ReadWriteProperty +from ._elemental_or_nodal_data import ( + ElementalData, + NodalData, + VectorData, + elemental_data_property, + nodal_data_property, +) +from ._grpc_helpers.linked_object_list import ( + define_linked_object_list, + define_polymorphic_linked_object_list, +) +from ._grpc_helpers.mapping import ( + define_create_method, + define_mutable_mapping, + get_read_only_collection_property, +) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._solid_model_export import SolidModelExportMixin +from .analysis_ply import AnalysisPly +from .base import ( + CreatableTreeObject, + IdTreeObject, + TreeObjectAttributeWithCache, + nested_grpc_object_property, +) +from .cut_off_geometry import CutOffGeometry +from .edge_set import EdgeSet +from .element_set import ElementSet +from .enums import ( + DropOffType, + ExtrusionMethod, + SolidModelOffsetDirectionType, + drop_off_type_from_pb, + drop_off_type_to_pb, + extrusion_method_type_from_pb, + extrusion_method_type_to_pb, + offset_direction_type_from_pb, + offset_direction_type_to_pb, + status_type_from_pb, +) +from .extrusion_guide import ExtrusionGuide +from .material import Material +from .modeling_ply import ModelingPly +from .object_registry import register +from .oriented_selection_set import OrientedSelectionSet +from .snap_to_geometry import SnapToGeometry +from .solid_element_set import SolidElementSet + +__all__ = [ + "SolidModel", + "DropOffSettings", + "SolidModelExportSettings", + "SolidModelElementalData", + "SolidModelNodalData", +] + + +@dataclasses.dataclass +class SolidModelElementalData(ElementalData): + """Represents elemental data for a Solid Model.""" + + normal: VectorData | None = None + + +@dataclasses.dataclass +class SolidModelNodalData(NodalData): + """Represents nodal data for a Solid Model.""" + + +@mark_grpc_properties +class DropOffSettings(TreeObjectAttributeWithCache): + """Defines the drop-off settings for a solid model. + + Parameters + ---------- + drop_off_type : + Determines whether the ply's drop-off is inside or outside the boundary + of the ply. + disable_drop_offs_on_bottom : + Whether to remove drop-offs on the bottom surface of the laminate. + drop_off_disabled_on_bottom_sets : + Element sets or oriented selection sets on which drop-offs at the bottom + surface are disabled. + disable_drop_offs_on_top : + Whether to remove drop-offs on the top surface of the laminate. + drop_off_disabled_on_top_sets : + Element sets or oriented selection sets on which drop-offs at the top + surface are disabled. + connect_butt_joined_plies : + Prevent an element drop-off of two adjacent, sequential plies in the same + modeling group. + """ + + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + drop_off_type: DropOffType = DropOffType.INSIDE_PLY, + disable_drop_offs_on_bottom: bool = False, + drop_off_disabled_on_bottom_sets: Iterable[ElementSet | OrientedSelectionSet] = (), + disable_drop_offs_on_top: bool = False, + drop_off_disabled_on_top_sets: Iterable[ElementSet | OrientedSelectionSet] = (), + connect_butt_joined_plies: bool = True, + _parent_object: SolidModel | None = None, + _pb_object: Any | None = None, + _attribute_path: str | None = None, + ): + super().__init__( + _parent_object=_parent_object, + _pb_object=_pb_object, + _attribute_path=_attribute_path, + ) + # The '__init__' method can be called either with the explicit values + # defined below, or from a parent object or protobuf object. In the case + # where a parent object or protobuf object is provided, the explicit values + # must not be set, otherwise the default values will override the existing + # values. + if _parent_object is None and _pb_object is None: + self.drop_off_type = drop_off_type + self.disable_drop_offs_on_bottom = disable_drop_offs_on_bottom + self.drop_off_disabled_on_bottom_sets = drop_off_disabled_on_bottom_sets + self.disable_drop_offs_on_top = disable_drop_offs_on_top + self.drop_off_disabled_on_top_sets = drop_off_disabled_on_top_sets + self.connect_butt_joined_plies = connect_butt_joined_plies + + @classmethod + def _create_default_pb_object(self) -> solid_model_pb2.DropOffSettings: + # There's no need to define the 'real' default values here, since + # this method is only called when the object is created from scratch. + # In that case, the '__init__' method will be called with the default + # values defined there. + # NOTE dgresch Oct'24: I'm not sure if '_create_default_pb_object' is + # the right abstraction overall. + # The concept _could_ be useful for avoiding the duplicate logic of + # checking '_parent_object is None and _pb_object is None' (this class's + # '__init__', and the 'TreeObjectAttribute.__init__'). + # But, it would need to be extended to allow also _overriding_ the defaults + # on calling __init__. + return solid_model_pb2.DropOffSettings() + + drop_off_type = grpc_data_property( + "drop_off_type", + from_protobuf=drop_off_type_from_pb, + to_protobuf=drop_off_type_to_pb, + ) + + disable_drop_offs_on_bottom: ReadWriteProperty[bool, bool] = grpc_data_property( + "disable_dropoffs_on_bottom" + ) + drop_off_disabled_on_bottom_sets = define_polymorphic_linked_object_list( + "dropoff_disabled_on_bottom_sets", allowed_types=(ElementSet, OrientedSelectionSet) + ) + + disable_drop_offs_on_top: ReadWriteProperty[bool, bool] = grpc_data_property( + "disable_dropoffs_on_top" + ) + drop_off_disabled_on_top_sets = define_polymorphic_linked_object_list( + "dropoff_disabled_on_top_sets", allowed_types=(ElementSet, OrientedSelectionSet) + ) + + connect_butt_joined_plies: ReadWriteProperty[bool, bool] = grpc_data_property( + "connect_butt_joined_plies" + ) + + +@mark_grpc_properties +class SolidModelExportSettings(TreeObjectAttributeWithCache): + """Defines the settings for exporting a solid model. + + Parameters + ---------- + use_default_section_index : + Use the default start index for sections. + section_index : + Custom start index for sections. + Only used if ``use_default_section_index`` is False. + use_default_coordinate_system_index : + Use the default start index for coordinate systems. + coordinate_system_index : + Custom start index for coordinate systems. + Only used if ``use_default_coordinate_system_index`` is False. + use_default_material_index : + Use the default start index for materials. + material_index : + Custom start index for materials. + Only used if ``use_default_material_index`` is False. + use_default_node_index : + Use the default start index for nodes. + node_index : + Custom start index for nodes. + Only used if ``use_default_node_index`` is False. + use_default_element_index : + Use the default start index for elements. + element_index : + Custom start index for elements. + Only used if ``use_default_element_index`` is False. + use_solsh_elements : + When True, export linear layered elements as Solsh (Solid190). + write_degenerated_elements : + Whether to export drop-off and cut-off elements. + drop_hanging_nodes : + When True, the hanging nodes of quadratic solid meshes are dropped. + use_solid_model_prefix : + Use the solid model name as a prefix for the exported file. + transfer_all_sets : + When True, all element sets and edge sets are exported. + transferred_element_sets : + Element sets to be exported. + Only used if ``transfer_all_sets`` is False. + transferred_edge_sets : + Edge sets to be exported. + Only used if ``transfer_all_sets`` is False. + + """ + + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + use_default_section_index: bool = True, + section_index: int = 0, + use_default_coordinate_system_index: bool = True, + coordinate_system_index: int = 0, + use_default_material_index: bool = True, + material_index: int = 0, + use_default_node_index: bool = True, + node_index: int = 0, + use_default_element_index: bool = True, + element_index: int = 0, + use_solsh_elements: bool = False, + write_degenerated_elements: bool = True, + drop_hanging_nodes: bool = True, + use_solid_model_prefix: bool = True, + transfer_all_sets: bool = True, + transferred_element_sets: Iterable[ElementSet] = (), + transferred_edge_sets: Iterable[EdgeSet] = (), + _parent_object: SolidModel | None = None, + _pb_object: Any | None = None, + _attribute_path: str | None = None, + ): + super().__init__( + _parent_object=_parent_object, + _pb_object=_pb_object, + _attribute_path=_attribute_path, + ) + # See comment on DropOffSettings.__init__ for the logic here. + if _parent_object is None and _pb_object is None: + self.use_default_section_index = use_default_section_index + self.section_index = section_index + self.use_default_coordinate_system_index = use_default_coordinate_system_index + self.coordinate_system_index = coordinate_system_index + self.use_default_material_index = use_default_material_index + self.material_index = material_index + self.use_default_node_index = use_default_node_index + self.node_index = node_index + self.use_default_element_index = use_default_element_index + self.element_index = element_index + self.use_solsh_elements = use_solsh_elements + self.write_degenerated_elements = write_degenerated_elements + self.drop_hanging_nodes = drop_hanging_nodes + self.use_solid_model_prefix = use_solid_model_prefix + self.transfer_all_sets = transfer_all_sets + self.transferred_element_sets = transferred_element_sets + self.transferred_edge_sets = transferred_edge_sets + + @classmethod + def _create_default_pb_object(self) -> solid_model_pb2.ExportSettings: + # See comment on DropOffSettings._create_default_pb_object + return solid_model_pb2.ExportSettings() + + use_default_section_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_section_index" + ) + section_index: ReadWriteProperty[int, int] = grpc_data_property("section_index") + use_default_coordinate_system_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_coordinate_system_index" + ) + coordinate_system_index: ReadWriteProperty[int, int] = grpc_data_property( + "coordinate_system_index" + ) + use_default_material_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_material_index" + ) + material_index: ReadWriteProperty[int, int] = grpc_data_property("material_index") + use_default_node_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_node_index" + ) + node_index: ReadWriteProperty[int, int] = grpc_data_property("node_index") + use_default_element_index: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_default_element_index" + ) + element_index: ReadWriteProperty[int, int] = grpc_data_property("element_index") + use_solsh_elements: ReadWriteProperty[bool, bool] = grpc_data_property("use_solsh_elements") + write_degenerated_elements: ReadWriteProperty[bool, bool] = grpc_data_property( + "write_degenerated_elements" + ) + drop_hanging_nodes: ReadWriteProperty[bool, bool] = grpc_data_property("drop_hanging_nodes") + use_solid_model_prefix: ReadWriteProperty[bool, bool] = grpc_data_property( + "use_solid_model_prefix" + ) + transfer_all_sets: ReadWriteProperty[bool, bool] = grpc_data_property("transfer_all_sets") + transferred_element_sets = define_linked_object_list("transferred_element_sets", ElementSet) + transferred_edge_sets = define_linked_object_list("transferred_edge_sets", EdgeSet) + + +@mark_grpc_properties +@register +class SolidModel(SolidModelExportMixin, CreatableTreeObject, IdTreeObject): + """Instantiate a solid model. + + Parameters + ---------- + name : + Name of the solid model. + active : + Inactive solid models are not computed, and ignored in the analysis. + element_sets : + Element sets or oriented selection sets determining the extent of + the solid model. + extrusion_method : + Determines how plies are bundled in the layered solid elements. + max_element_thickness : + Maximum thickness of the layered solid elements. A new element is + introduced if the thickness exceeds this value. + Only used if the ``extrusion_method`` is one of ``SPECIFY_THICKNESS``, + ``MATERIAL_WISE``, or ``SANDWICH_WISE``. + ply_group_pointers : + Explicitly defines modeling plies where a new element is introduced. + Only used if the ``extrusion_method`` is ``USER_DEFINED``. + offset_direction_type : + Determines how the extrusion direction is defined. With ``SHELL_NORMAL``, + the normal direction of the shell is used during the entire extrusion. + With ``SURFACE_NORMAL``, the offset direction is re-evaluated based + on the surface of the solid model during the extrusion. + skip_elements_without_plies : + If True, elements without plies are automatically removed from the + region of extrusion. This means that no drop-off elements are created + for these elements. + drop_off_material : + This material is assigned to the layered solid drop-off elements if + ``drop_off_material_handling`` is set to ``GLOBAL`` in the fabric + definition. + cut_off_material : + This material is assigned to the degenerated solid cut-off elements if + ``cut_off_material_handling`` is set to ``GLOBAL`` in the fabric + definition. + delete_bad_elements : + If True, a final element check is performed to remove erroneous elements. + warping_limit : + Maximum allowable warping limit used in the element shape check. Elements + with a warping limit exceeding this value are removed. + Only used if ``delete_bad_elements`` is True. + minimum_volume : + Solid elements with a volume smaller or equal to this value are removed. + With the default value of ``0``, only inverted or zero-volume elements + are removed. + Only used if ``delete_bad_elements`` is True. + drop_off_settings : + Determines how drop-off elements are handled in the solid model extrusion. + export_settings : + Defines the settings for exporting the solid model. + + """ + + __slots__: Iterable[str] = tuple() + _COLLECTION_LABEL = "solid_models" + _OBJECT_INFO_TYPE = solid_model_pb2.ObjectInfo + _CREATE_REQUEST_TYPE = solid_model_pb2.CreateRequest + _SUPPORTED_SINCE = "25.1" + + def __init__( + self, + *, + name: str = "SolidModel", + active: bool = True, + element_sets: Iterable[ElementSet | OrientedSelectionSet] = (), + extrusion_method: ExtrusionMethod = ExtrusionMethod.ANALYSIS_PLY_WISE, + max_element_thickness: float = 1.0, + ply_group_pointers: Iterable[ModelingPly] = (), + offset_direction_type: SolidModelOffsetDirectionType = SolidModelOffsetDirectionType.SHELL_NORMAL, + skip_elements_without_plies: bool = False, + drop_off_material: Material | None = None, + cut_off_material: Material | None = None, + delete_bad_elements: bool = True, + warping_limit: float = 0.4, + minimum_volume: float = 0.0, + drop_off_settings: DropOffSettings = DropOffSettings(), + export_settings: SolidModelExportSettings = SolidModelExportSettings(), + ): + super().__init__( + name=name, + ) + self.active = active + self.element_sets = element_sets + self.extrusion_method = extrusion_method + self.max_element_thickness = max_element_thickness + self.ply_group_pointers = ply_group_pointers + self.offset_direction_type = offset_direction_type + self.skip_elements_without_plies = skip_elements_without_plies + self.drop_off_material = drop_off_material + self.cut_off_material = cut_off_material + self.delete_bad_elements = delete_bad_elements + self.warping_limit = warping_limit + self.minimum_volume = minimum_volume + self.drop_off_settings = drop_off_settings + self.export_settings = export_settings + + def _create_stub(self) -> solid_model_pb2_grpc.ObjectServiceStub: + return solid_model_pb2_grpc.ObjectServiceStub(self._channel) + + status = grpc_data_property_read_only("properties.status", from_protobuf=status_type_from_pb) + locked: ReadOnlyProperty[bool] = grpc_data_property_read_only("properties.locked") + active: ReadWriteProperty[bool, bool] = grpc_data_property("properties.active") + + element_sets = define_polymorphic_linked_object_list( + "properties.element_sets", allowed_types=(ElementSet, OrientedSelectionSet) + ) + extrusion_method = grpc_data_property( + "properties.extrusion_method", + from_protobuf=extrusion_method_type_from_pb, + to_protobuf=extrusion_method_type_to_pb, + ) + max_element_thickness: ReadWriteProperty[float, float] = grpc_data_property( + "properties.max_element_thickness" + ) + ply_group_pointers = define_linked_object_list("properties.ply_group_pointers", ModelingPly) + offset_direction_type = grpc_data_property( + "properties.offset_direction", + from_protobuf=offset_direction_type_from_pb, + to_protobuf=offset_direction_type_to_pb, + ) + skip_elements_without_plies: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.skip_elements_without_plies" + ) + drop_off_material = grpc_link_property("properties.drop_off_material", allowed_types=Material) + cut_off_material = grpc_link_property("properties.cut_off_material", allowed_types=Material) + delete_bad_elements: ReadWriteProperty[bool, bool] = grpc_data_property( + "properties.delete_bad_elements" + ) + warping_limit: ReadWriteProperty[float, float] = grpc_data_property("properties.warping_limit") + minimum_volume: ReadWriteProperty[float, float] = grpc_data_property( + "properties.minimum_volume" + ) + + drop_off_settings = nested_grpc_object_property("properties.drop_off_settings", DropOffSettings) + export_settings = nested_grpc_object_property( + "properties.export_settings", SolidModelExportSettings + ) + + create_extrusion_guide = define_create_method( + ExtrusionGuide, + func_name="create_extrusion_guide", + parent_class_name="SolidModel", + module_name=__module__, + ) + extrusion_guides = define_mutable_mapping( + ExtrusionGuide, extrusion_guide_pb2_grpc.ObjectServiceStub + ) + + create_snap_to_geometry = define_create_method( + SnapToGeometry, + func_name="create_snap_to_geometry", + parent_class_name="SolidModel", + module_name=__module__, + ) + snap_to_geometries = define_mutable_mapping( + SnapToGeometry, snap_to_geometry_pb2_grpc.ObjectServiceStub + ) + + solid_element_sets = get_read_only_collection_property( + SolidElementSet, solid_element_set_pb2_grpc.ObjectServiceStub + ) + + create_cut_off_geometry = define_create_method( + CutOffGeometry, + func_name="create_cut_off_geometry", + parent_class_name="SolidModel", + module_name=__module__, + ) + cut_off_geometries = define_mutable_mapping( + CutOffGeometry, cut_off_geometry_pb2_grpc.ObjectServiceStub + ) + + analysis_plies = get_read_only_collection_property( + AnalysisPly, analysis_ply_pb2_grpc.ObjectServiceStub + ) + + elemental_data = elemental_data_property(SolidModelElementalData) + nodal_data = nodal_data_property(SolidModelNodalData) diff --git a/src/ansys/acp/core/_tree_objects/spherical_selection_rule.py b/src/ansys/acp/core/_tree_objects/spherical_selection_rule.py index 433c8c1ae4..35a35173ab 100644 --- a/src/ansys/acp/core/_tree_objects/spherical_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/spherical_selection_rule.py @@ -29,19 +29,20 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - grpc_link_property, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .enums import status_type_from_pb from .object_registry import register @@ -50,7 +51,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "SphericalSelectionRule", @@ -89,9 +90,9 @@ class SphericalSelectionRule(CreatableTreeObject, IdTreeObject): Origin of the sphere. radius : Radius of the sphere. - relative_rule_type : + relative_rule : If True, parameters are evaluated relative to size of the object. - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. """ @@ -101,6 +102,7 @@ class SphericalSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "spherical_selection_rules" _OBJECT_INFO_TYPE = spherical_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = spherical_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -110,16 +112,16 @@ def __init__( rosette: Rosette | None = None, origin: tuple[float, ...] = (0.0, 0.0, 0.0), radius: float = 0.0, - relative_rule_type: bool = False, - include_rule_type: bool = True, + relative_rule: bool = False, + include_rule: bool = True, ): super().__init__(name=name) self.use_global_coordinate_system = use_global_coordinate_system self.rosette = rosette self.origin = origin self.radius = radius - self.relative_rule_type = relative_rule_type - self.include_rule_type = include_rule_type + self.relative_rule = relative_rule + self.include_rule = include_rule def _create_stub(self) -> spherical_selection_rule_pb2_grpc.ObjectServiceStub: return spherical_selection_rule_pb2_grpc.ObjectServiceStub(self._channel) @@ -137,12 +139,13 @@ def _create_stub(self) -> spherical_selection_rule_pb2_grpc.ObjectServiceStub: "properties.direction", from_protobuf=to_tuple_from_1D_array, to_protobuf=to_1D_double_array ) radius: ReadWriteProperty[float, float] = grpc_data_property("properties.radius") - relative_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( + relative_rule: ReadWriteProperty[bool, bool] = grpc_data_property( "properties.relative_rule_type" ) - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(SphericalSelectionRuleElementalData) nodal_data = nodal_data_property(SphericalSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/stackup.py b/src/ansys/acp/core/_tree_objects/stackup.py index 0bd55ec11e..031ecfe599 100644 --- a/src/ansys/acp/core/_tree_objects/stackup.py +++ b/src/ansys/acp/core/_tree_objects/stackup.py @@ -22,8 +22,10 @@ from __future__ import annotations -from collections.abc import Iterable, Sequence -from typing import Any, Callable +from collections.abc import Callable, Iterable, Sequence +from typing import Any + +from typing_extensions import Self from ansys.api.acp.v0 import stackup_pb2, stackup_pb2_grpc @@ -34,6 +36,7 @@ define_edge_property_list, ) from ._grpc_helpers.property_helper import ( + _exposed_grpc_property, grpc_data_property, grpc_data_property_read_only, grpc_link_property, @@ -41,9 +44,9 @@ ) from .base import CreatableTreeObject, IdTreeObject from .enums import ( - CutoffMaterialType, - DrapingMaterialType, - DropoffMaterialType, + CutOffMaterialHandling, + DrapingMaterialModel, + DropOffMaterialHandling, SymmetryType, cut_off_material_type_from_pb, cut_off_material_type_to_pb, @@ -62,6 +65,7 @@ __all__ = ["Stackup", "FabricWithAngle"] +@mark_grpc_properties class FabricWithAngle(GenericEdgePropertyType): """Defines a fabric of a stackup. @@ -74,12 +78,14 @@ class FabricWithAngle(GenericEdgePropertyType): """ + _SUPPORTED_SINCE = "24.2" + def __init__(self, fabric: Fabric, angle: float = 0.0): self._callback_apply_changes: Callable[[], None] | None = None self.fabric = fabric self.angle = angle - @property + @_exposed_grpc_property def fabric(self) -> Fabric: """Linked fabric.""" return self._fabric @@ -92,7 +98,7 @@ def fabric(self, value: Fabric) -> None: if self._callback_apply_changes: self._callback_apply_changes() - @property + @_exposed_grpc_property def angle(self) -> float: """Orientation angle in degree of the fabric with respect to the reference direction.""" return self._angle @@ -139,6 +145,10 @@ def __eq__(self, other: Any) -> bool: def __repr__(self) -> str: return f"FabricWithAngle(fabric={self.fabric.__repr__()}, angle={self.angle})" + def clone(self) -> Self: + """Create a new unstored FabricWithAngle with the same properties.""" + return type(self)(fabric=self.fabric, angle=self.angle) + @mark_grpc_properties @register @@ -177,6 +187,7 @@ class Stackup(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "stackups" _OBJECT_INFO_TYPE = stackup_pb2.ObjectInfo _CREATE_REQUEST_TYPE = stackup_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -186,11 +197,11 @@ def __init__( topdown: bool = True, fabrics: Sequence[FabricWithAngle] = tuple(), area_price: float = 0.0, - drop_off_material_handling: DropoffMaterialType = "global", + drop_off_material_handling: DropOffMaterialHandling = "global", drop_off_material: Material | None = None, cut_off_material: Material | None = None, - cut_off_material_handling: CutoffMaterialType = "computed", - draping_material_model: DrapingMaterialType = "woven", + cut_off_material_handling: CutOffMaterialHandling = "computed", + draping_material_model: DrapingMaterialModel = "woven", draping_ud_coefficient: float = 0.0, ): super().__init__(name=name) @@ -199,11 +210,11 @@ def __init__( self.topdown = topdown self.area_price = area_price self.fabrics = fabrics - self.drop_off_material_handling = DropoffMaterialType(drop_off_material_handling) + self.drop_off_material_handling = DropOffMaterialHandling(drop_off_material_handling) self.drop_off_material = drop_off_material - self.cut_off_material_handling = CutoffMaterialType(cut_off_material_handling) + self.cut_off_material_handling = CutOffMaterialHandling(cut_off_material_handling) self.cut_off_material = cut_off_material - self.draping_material_model = DrapingMaterialType(draping_material_model) + self.draping_material_model = DrapingMaterialModel(draping_material_model) self.draping_ud_coefficient = draping_ud_coefficient def _create_stub(self) -> stackup_pb2_grpc.ObjectServiceStub: diff --git a/src/ansys/acp/core/_tree_objects/sublaminate.py b/src/ansys/acp/core/_tree_objects/sublaminate.py index 7fe1b01fe4..d900363280 100644 --- a/src/ansys/acp/core/_tree_objects/sublaminate.py +++ b/src/ansys/acp/core/_tree_objects/sublaminate.py @@ -22,9 +22,11 @@ from __future__ import annotations -from collections.abc import Iterable, Sequence +from collections.abc import Callable, Iterable, Sequence import typing -from typing import Any, Callable, Union, get_args +from typing import Any, TypeAlias, Union, get_args + +from typing_extensions import Self from ansys.api.acp.v0 import sublaminate_pb2, sublaminate_pb2_grpc @@ -36,6 +38,7 @@ ) from ._grpc_helpers.polymorphic_from_pb import tree_object_from_resource_path from ._grpc_helpers.property_helper import ( + _exposed_grpc_property, grpc_data_property, grpc_data_property_read_only, mark_grpc_properties, @@ -48,9 +51,10 @@ __all__ = ["SubLaminate", "Lamina"] -_LINKABLE_MATERIAL_TYPES = Union[Fabric, Stackup] +_LINKABLE_MATERIAL_TYPES: TypeAlias = Union[Fabric, Stackup] +@mark_grpc_properties class Lamina(GenericEdgePropertyType): """ Class to link a material with a sub-laminate. @@ -64,12 +68,14 @@ class Lamina(GenericEdgePropertyType): """ + _SUPPORTED_SINCE = "24.2" + def __init__(self, material: _LINKABLE_MATERIAL_TYPES, angle: float = 0.0): self._callback_apply_changes: Callable[[], None] | None = None self.material = material self.angle = angle - @property + @_exposed_grpc_property def material(self) -> _LINKABLE_MATERIAL_TYPES: """Link to an existing fabric or stackup.""" return self._material @@ -85,7 +91,7 @@ def material(self, value: _LINKABLE_MATERIAL_TYPES) -> None: if self._callback_apply_changes: self._callback_apply_changes() - @property + @_exposed_grpc_property def angle(self) -> float: """Orientation angle in degree of the material with respect to the reference direction.""" return self._angle @@ -143,6 +149,10 @@ def __eq__(self, other: Any) -> bool: def __repr__(self) -> str: return f"Lamina(material={self.material.__repr__()}, angle={self.angle})" + def clone(self) -> Self: + """Create a new unstored Lamina with the same properties.""" + return type(self)(material=self.material, angle=self.angle) + @mark_grpc_properties @register @@ -166,6 +176,7 @@ class SubLaminate(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "sublaminates" _OBJECT_INFO_TYPE = sublaminate_pb2.ObjectInfo _CREATE_REQUEST_TYPE = sublaminate_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_tree_objects/tube_selection_rule.py b/src/ansys/acp/core/_tree_objects/tube_selection_rule.py index 7920289228..37c9da4e3a 100644 --- a/src/ansys/acp/core/_tree_objects/tube_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/tube_selection_rule.py @@ -29,19 +29,20 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - grpc_link_property, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .edge_set import EdgeSet from .enums import status_type_from_pb @@ -50,7 +51,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "TubeSelectionRule", @@ -86,7 +87,7 @@ class TubeSelectionRule(CreatableTreeObject, IdTreeObject): Outer radius of the tube. inner_radius : Inner radius of the tube. - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. extend_endings : @@ -109,6 +110,7 @@ class TubeSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "tube_selection_rules" _OBJECT_INFO_TYPE = tube_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = tube_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -117,7 +119,7 @@ def __init__( edge_set: EdgeSet | None = None, outer_radius: float = 1.0, inner_radius: float = 0.0, - include_rule_type: bool = True, + include_rule: bool = True, extend_endings: bool = False, symmetrical_extension: bool = True, head: tuple[float, float, float] = (0.0, 0.0, 0.0), @@ -128,7 +130,7 @@ def __init__( self.edge_set = edge_set self.outer_radius = outer_radius self.inner_radius = inner_radius - self.include_rule_type = include_rule_type + self.include_rule = include_rule self.extend_endings = extend_endings self.symmetrical_extension = symmetrical_extension self.head = head @@ -143,9 +145,7 @@ def _create_stub(self) -> tube_selection_rule_pb2_grpc.ObjectServiceStub: edge_set = grpc_link_property("properties.edge_set", allowed_types=EdgeSet) outer_radius: ReadWriteProperty[float, float] = grpc_data_property("properties.outer_radius") inner_radius: ReadWriteProperty[float, float] = grpc_data_property("properties.inner_radius") - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") extend_endings: ReadWriteProperty[bool, bool] = grpc_data_property("properties.extend_endings") symmetrical_extension: ReadWriteProperty[bool, bool] = grpc_data_property( "properties.symmetrical_extension" @@ -160,5 +160,8 @@ def _create_stub(self) -> tube_selection_rule_pb2_grpc.ObjectServiceStub: "properties.tail_extension" ) + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(TubeSelectionRuleElementalData) nodal_data = nodal_data_property(TubeSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/utils.py b/src/ansys/acp/core/_tree_objects/utils.py new file mode 100644 index 0000000000..48dbb54ffb --- /dev/null +++ b/src/ansys/acp/core/_tree_objects/utils.py @@ -0,0 +1,46 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import dataclasses + +__all__ = ["CoordinateTransformation"] + + +@dataclasses.dataclass +class CoordinateTransformation: + """Defines a coordinate transformation via rotation angles and translations. + + All angles are in degrees. The operations are performed in the following order: + + 1. Rotation about the x-axis + 2. Rotation about the y-axis + 3. Rotation about the z-axis + 4. Translation + + """ + + rotation_angle_x: float = 0.0 + rotation_angle_y: float = 0.0 + rotation_angle_z: float = 0.0 + translation_x: float = 0.0 + translation_y: float = 0.0 + translation_z: float = 0.0 diff --git a/src/ansys/acp/core/_tree_objects/variable_offset_selection_rule.py b/src/ansys/acp/core/_tree_objects/variable_offset_selection_rule.py index 4a0be106b8..b81c14fa8f 100644 --- a/src/ansys/acp/core/_tree_objects/variable_offset_selection_rule.py +++ b/src/ansys/acp/core/_tree_objects/variable_offset_selection_rule.py @@ -32,19 +32,20 @@ from .._utils.array_conversions import to_1D_double_array, to_tuple_from_1D_array from .._utils.property_protocols import ReadWriteProperty -from ._grpc_helpers.property_helper import ( - grpc_data_property, - grpc_data_property_read_only, - grpc_link_property, - mark_grpc_properties, -) -from ._mesh_data import ( +from ._elemental_or_nodal_data import ( ElementalData, NodalData, VectorData, elemental_data_property, nodal_data_property, ) +from ._grpc_helpers.property_helper import ( + grpc_data_property, + grpc_data_property_read_only, + grpc_link_property, + mark_grpc_properties, +) +from ._mesh_data import full_mesh_property, shell_mesh_property from .base import CreatableTreeObject, IdTreeObject from .edge_set import EdgeSet from .element_set import ElementSet @@ -55,7 +56,7 @@ # Workaround: these imports are needed to make sphinx_autodoc_typehints understand # the inherited members of the Elemental- and NodalData classes. import numpy as np # noqa: F401 isort:skip -from ._mesh_data import ScalarData # noqa: F401 isort:skip +from ._elemental_or_nodal_data import ScalarData # noqa: F401 isort:skip __all__ = [ "VariableOffsetSelectionRule", @@ -91,7 +92,7 @@ class VariableOffsetSelectionRule(CreatableTreeObject, IdTreeObject): Defines the in-plane offset. Cuts elements which are closer to the edge than this value. angles : Defines the angle between the reference surface and the cutting plane. - include_rule_type : + include_rule : Include or exclude area in rule. Setting this to ``False`` inverts the selection. use_offset_correction : @@ -116,6 +117,7 @@ class VariableOffsetSelectionRule(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "variable_offset_selection_rules" _OBJECT_INFO_TYPE = variable_offset_selection_rule_pb2.ObjectInfo _CREATE_REQUEST_TYPE = variable_offset_selection_rule_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, @@ -124,7 +126,7 @@ def __init__( edge_set: EdgeSet | None = None, offsets: LookUpTable1DColumn | None = None, angles: LookUpTable1DColumn | None = None, - include_rule_type: bool = True, + include_rule: bool = True, use_offset_correction: bool = False, element_set: ElementSet | None = None, inherit_from_lookup_table: bool = True, @@ -136,7 +138,7 @@ def __init__( self.edge_set = edge_set self.offsets = offsets self.angles = angles - self.include_rule_type = include_rule_type + self.include_rule = include_rule self.use_offset_correction = use_offset_correction self.element_set = element_set self.inherit_from_lookup_table = inherit_from_lookup_table @@ -152,9 +154,7 @@ def _create_stub(self) -> variable_offset_selection_rule_pb2_grpc.ObjectServiceS edge_set = grpc_link_property("properties.edge_set", allowed_types=EdgeSet) offsets = grpc_link_property("properties.offsets", allowed_types=LookUpTable1DColumn) angles = grpc_link_property("properties.angles", allowed_types=LookUpTable1DColumn) - include_rule_type: ReadWriteProperty[bool, bool] = grpc_data_property( - "properties.include_rule_type" - ) + include_rule: ReadWriteProperty[bool, bool] = grpc_data_property("properties.include_rule_type") use_offset_correction: ReadWriteProperty[bool, bool] = grpc_data_property( "properties.use_offset_correction" ) @@ -176,5 +176,8 @@ def _create_stub(self) -> variable_offset_selection_rule_pb2_grpc.ObjectServiceS "properties.distance_along_edge" ) + mesh = full_mesh_property + shell_mesh = shell_mesh_property + # selection rules don't have solid mesh data elemental_data = elemental_data_property(VariableOffsetSelectionRuleElementalData) nodal_data = nodal_data_property(VariableOffsetSelectionRuleNodalData) diff --git a/src/ansys/acp/core/_tree_objects/virtual_geometry.py b/src/ansys/acp/core/_tree_objects/virtual_geometry.py index 2a25ace61f..f7ab2dcb7a 100644 --- a/src/ansys/acp/core/_tree_objects/virtual_geometry.py +++ b/src/ansys/acp/core/_tree_objects/virtual_geometry.py @@ -22,9 +22,11 @@ from __future__ import annotations -from collections.abc import Iterable +from collections.abc import Callable, Iterable import typing -from typing import Any, Callable +from typing import Any + +from typing_extensions import Self from ansys.api.acp.v0 import base_pb2, virtual_geometry_pb2, virtual_geometry_pb2_grpc @@ -33,7 +35,11 @@ define_add_method, define_edge_property_list, ) -from ._grpc_helpers.property_helper import grpc_data_property_read_only, mark_grpc_properties +from ._grpc_helpers.property_helper import ( + _exposed_grpc_property, + grpc_data_property_read_only, + mark_grpc_properties, +) from .base import CreatableTreeObject, IdTreeObject from .cad_geometry import CADGeometry from .enums import status_type_from_pb, virtual_geometry_dimension_from_pb @@ -43,15 +49,18 @@ from .cad_component import CADComponent +@mark_grpc_properties class SubShape(GenericEdgePropertyType): """Represents a sub-shape of a virtual geometry.""" + _SUPPORTED_SINCE = "24.2" + def __init__(self, cad_geometry: CADGeometry, path: str): self._callback_apply_changes: Callable[[], None] | None = None self.cad_geometry = cad_geometry self.path = path - @property + @_exposed_grpc_property def cad_geometry(self) -> CADGeometry: """Linked CAD geometry.""" return self._cad_geometry @@ -64,7 +73,7 @@ def cad_geometry(self, value: CADGeometry) -> None: if self._callback_apply_changes: self._callback_apply_changes() - @property + @_exposed_grpc_property def path(self) -> str: """Topological path of the sub-shape within the CAD geometry.""" return self._path @@ -115,6 +124,10 @@ def __eq__(self, other: Any) -> bool: def __repr__(self) -> str: return f"SubShape(cad_geometry={self._cad_geometry.__repr__()}, path={self._path})" + def clone(self) -> Self: + """Create a new unstored SubShape with the same properties.""" + return type(self)(cad_geometry=self.cad_geometry, path=self.path) + @mark_grpc_properties @register @@ -139,6 +152,7 @@ class VirtualGeometry(CreatableTreeObject, IdTreeObject): _COLLECTION_LABEL = "virtual_geometries" _OBJECT_INFO_TYPE = virtual_geometry_pb2.ObjectInfo _CREATE_REQUEST_TYPE = virtual_geometry_pb2.CreateRequest + _SUPPORTED_SINCE = "24.2" def __init__( self, diff --git a/src/ansys/acp/core/_utils/array_conversions.py b/src/ansys/acp/core/_utils/array_conversions.py index 036670705f..e6d771a46d 100644 --- a/src/ansys/acp/core/_utils/array_conversions.py +++ b/src/ansys/acp/core/_utils/array_conversions.py @@ -21,7 +21,7 @@ # SOFTWARE. from collections.abc import Collection -from typing import Any, Union, overload +from typing import Any, overload import numpy as np import numpy.typing as npt @@ -40,7 +40,7 @@ def to_1D_int_array(data: Collection[int]) -> IntArray: return IntArray(shape=[len(data)], data=tuple(data)) -def to_tuple_from_1D_array(array: Union[IntArray, DoubleArray]) -> tuple[Any, ...]: +def to_tuple_from_1D_array(array: IntArray | DoubleArray) -> tuple[Any, ...]: """Convert a 1D IntArray or DoubleArray protobuf message to a tuple.""" if not len(array.shape) == 1: raise RuntimeError(f"Cannot convert {len(array.shape)}-dimensional array to tuple!") @@ -66,8 +66,8 @@ def to_numpy(array_pb: DoubleArray) -> npt.NDArray[np.float64]: ... def to_numpy( - array_pb: Union[IntArray, Int32Array, DoubleArray] -) -> Union[npt.NDArray[np.int64], npt.NDArray[np.int32], npt.NDArray[np.float64]]: + array_pb: IntArray | Int32Array | DoubleArray, +) -> npt.NDArray[np.int64] | npt.NDArray[np.int32] | npt.NDArray[np.float64]: """Convert a protubuf array message to a numpy array.""" dtype = { IntArray: np.int64, @@ -79,8 +79,8 @@ def to_numpy( def dataarray_to_numpy( array_pb: DataArray, - dtype: Union[type[np.int32], type[np.int64], type[np.float64]], -) -> Union[npt.NDArray[np.int64], npt.NDArray[np.int32], npt.NDArray[np.float64]]: + dtype: type[np.int32] | type[np.int64] | type[np.float64], +) -> npt.NDArray[np.int64] | npt.NDArray[np.int32] | npt.NDArray[np.float64]: """Convert a DataArray protobuf message to a numpy array.""" data_array_attribute = array_pb.WhichOneof("data") if data_array_attribute is None: diff --git a/src/ansys/acp/core/_utils/path_to_str.py b/src/ansys/acp/core/_utils/path_to_str.py index 10379ea12c..b100a3d829 100644 --- a/src/ansys/acp/core/_utils/path_to_str.py +++ b/src/ansys/acp/core/_utils/path_to_str.py @@ -22,7 +22,7 @@ from pathlib import PurePath -from .._typing_helper import PATH +from .typing_helper import PATH __all__ = ["path_to_str_checked"] diff --git a/src/ansys/acp/core/_utils/pyvista_import_check.py b/src/ansys/acp/core/_utils/pyvista_import_check.py new file mode 100644 index 0000000000..a7df22501c --- /dev/null +++ b/src/ansys/acp/core/_utils/pyvista_import_check.py @@ -0,0 +1,49 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from collections.abc import Callable +from functools import wraps +from typing import ParamSpec, TypeVar + +P = ParamSpec("P") +T = TypeVar("T") + + +def requires_pyvista(func: Callable[P, T]) -> Callable[P, T]: + """Decorate a function as requiring the 'pyvista' package. + + Checks if the 'pyvista' package is installed before executing the + decorated function, and provides a helpful error message if it is not. + """ + + @wraps(func) + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + try: + import pyvista # noqa + except ImportError as exc: + raise ImportError( + f"The '{func.__name__}' function requires the 'pyvista' package. " + "Please reinstall PyACP with 'pip install ansys-acp-core[plotting]'." + ) from exc + return func(*args, **kwargs) + + return inner diff --git a/src/ansys/acp/core/_utils/string_manipulation.py b/src/ansys/acp/core/_utils/string_manipulation.py new file mode 100644 index 0000000000..e630fc66ad --- /dev/null +++ b/src/ansys/acp/core/_utils/string_manipulation.py @@ -0,0 +1,26 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +def replace_underscores_and_capitalize(input_string: str) -> str: + """Replace underscores with spaces and capitalize each word.""" + return " ".join(part.capitalize() for part in input_string.split("_")) diff --git a/src/ansys/acp/core/_typing_helper.py b/src/ansys/acp/core/_utils/typing_helper.py similarity index 66% rename from src/ansys/acp/core/_typing_helper.py rename to src/ansys/acp/core/_utils/typing_helper.py index 10f301aa63..9334f5b316 100644 --- a/src/ansys/acp/core/_typing_helper.py +++ b/src/ansys/acp/core/_utils/typing_helper.py @@ -21,24 +21,34 @@ # SOFTWARE. """Helpers for defining type annotations.""" +import enum import os -from typing import Union +from typing import TYPE_CHECKING, Union __all__ = ["PATH", "StrEnum"] PATH = Union[str, os.PathLike[str]] -try: - from enum import StrEnum # type: ignore -except ImportError: - # For Python 3.10 and below, emulate the behavior of StrEnum by - # inheriting from str and enum.Enum. - # Note that this does *not* work on Python 3.11+, since the default - # Enum format method has changed and will not return the value of - # the enum member. - import enum - - class StrEnum(str, enum.Enum): # type: ignore +# For Python 3.10 and below, emulate the behavior of StrEnum by +# inheriting from str and enum.Enum. +# Note that this does *not* work on Python 3.11+, since the default +# Enum format method has changed and will not return the value of +# the enum member. +# When type checking, always use the Python 3.10 workaround, otherwise +# the StrEnum resolves as 'Any'. +if TYPE_CHECKING: # pragma: no cover + + class StrEnum(str, enum.Enum): """String enum.""" - pass +else: + try: + from enum import StrEnum + except ImportError: + + import enum + + class StrEnum(str, enum.Enum): + """String enum.""" + + pass diff --git a/src/ansys/acp/core/_utils/visualization.py b/src/ansys/acp/core/_utils/visualization.py index e99f9758d4..85b512db09 100644 --- a/src/ansys/acp/core/_utils/visualization.py +++ b/src/ansys/acp/core/_utils/visualization.py @@ -154,7 +154,3 @@ def to_pyvista_faces( def to_pyvista_types(element_types: npt.NDArray[np.int32]) -> npt.NDArray[np.int32]: """Convert ACP element types to PyVista cell types.""" return np.array([ELEMENT_TO_PYVISTA_TYPE[el_type] for el_type in element_types]) - - -def _replace_underscores_and_capitalize(input_string: str) -> str: - return " ".join(part.capitalize() for part in input_string.split("_")) diff --git a/src/ansys/acp/core/_workflow.py b/src/ansys/acp/core/_workflow.py deleted file mode 100644 index fcbdf6adbc..0000000000 --- a/src/ansys/acp/core/_workflow.py +++ /dev/null @@ -1,402 +0,0 @@ -# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import pathlib -import shutil -import tempfile -import typing -from typing import Any, Callable, Optional, Protocol - -from . import CADGeometry, UnitSystemType -from ._server.acp_instance import ACP -from ._server.common import ServerProtocol -from ._tree_objects import Model -from ._typing_helper import PATH - -# Avoid dependencies on pydpf-composites and dpf-core if it is not used -if typing.TYPE_CHECKING: # pragma: no cover - from ansys.dpf.composites.data_sources import ContinuousFiberCompositesFiles - from ansys.dpf.core import UnitSystem - -__all__ = ["ACPWorkflow", "get_composite_post_processing_files", "get_dpf_unit_system"] - - -class _LocalWorkingDir: - def __init__(self, path: Optional[PATH] = None): - self._user_defined_working_dir = None - self._temp_working_dir = None - if path is None: - self._temp_working_dir = tempfile.TemporaryDirectory() - else: - self._user_defined_working_dir = pathlib.Path(path) - - @property - def path(self) -> pathlib.Path: - if self._user_defined_working_dir is not None: - return self._user_defined_working_dir - else: - # Make typechecker happy - assert self._temp_working_dir is not None - return pathlib.Path(self._temp_working_dir.name) - - -class _FileStrategy(Protocol): - def get_file( - self, get_file_callable: Callable[[pathlib.Path], None], filename: str - ) -> pathlib.Path: ... - - def copy_input_file_to_local_workdir(self, path: pathlib.Path) -> pathlib.Path: ... - - def upload_input_file_to_server(self, path: pathlib.Path) -> pathlib.PurePath: ... - - -def _copy_file_workdir(path: pathlib.Path, working_directory: pathlib.Path) -> pathlib.Path: - try: - shutil.copy(path, working_directory) - except shutil.SameFileError: - pass - return working_directory / path.name - - -class _LocalFileTransferStrategy: - """File transfer strategy for local workflows. - - Save output files to the local working directory and do nothing for input files. - """ - - def __init__(self, local_working_directory: _LocalWorkingDir): - self._local_working_directory = local_working_directory - - def get_file( - self, get_file_callable: Callable[[pathlib.Path], None], filename: str - ) -> pathlib.Path: - local_path = self._local_working_directory.path / filename - get_file_callable(self._local_working_directory.path / filename) - return local_path - - def copy_input_file_to_local_workdir(self, path: pathlib.Path) -> pathlib.Path: - return _copy_file_workdir(path=path, working_directory=self._local_working_directory.path) - - def upload_input_file_to_server(self, path: pathlib.Path) -> pathlib.PurePath: - return path - - -class _RemoteFileTransferStrategy: - """File transfer strategy for remote workflows. - - Download output files from the server to the local working directory and upload - input files to the server. - """ - - def __init__(self, local_working_directory: _LocalWorkingDir, acp: ACP[ServerProtocol]): - self._local_working_directory = local_working_directory - self._acp_instance = acp - - def get_file( - self, get_file_callable: Callable[[pathlib.Path], None], filename: str - ) -> pathlib.Path: - get_file_callable(pathlib.Path(filename)) - local_path = self._local_working_directory.path / filename - self._acp_instance.download_file(remote_filename=filename, local_path=str(local_path)) - return local_path - - def copy_input_file_to_local_workdir(self, path: pathlib.Path) -> pathlib.Path: - return _copy_file_workdir(path=path, working_directory=self._local_working_directory.path) - - def upload_input_file_to_server(self, path: pathlib.Path) -> pathlib.PurePath: - return self._acp_instance.upload_file(local_path=path) - - -def _get_file_transfer_strategy( - acp: ACP[ServerProtocol], local_working_dir: _LocalWorkingDir -) -> _FileStrategy: - if acp.is_remote: - return _RemoteFileTransferStrategy( - local_working_directory=local_working_dir, - acp=acp, - ) - else: - return _LocalFileTransferStrategy( - local_working_directory=local_working_dir, - ) - - -# Todo: Add automated tests for local and remote workflow -class ACPWorkflow: - r"""Instantiate an ACP Workflow. - - Use the class methods :meth:`.from_cdb_or_dat_file` and - :meth:`.from_acph5_file` to instantiate the workflow. - - Parameters - ---------- - acp - The ACP Client. - local_file_path : - Path of the file to load. - file_format : - Format of the file to load. Options are ``"acp:h5"``, ``"ansys:cdb"``, - ``"ansys:dat"``, and ``"ansys:h5"``. - kwargs : - Additional keyword arguments to pass to the :meth:`.ACP.import_model` method. - - """ - - def __init__( - self, - *, - acp: ACP[ServerProtocol], - local_working_directory: Optional[PATH] = None, - local_file_path: PATH, - file_format: str, - **kwargs: Any, - ): - self._acp_instance = acp - self._local_working_dir = _LocalWorkingDir(local_working_directory) - self._file_transfer_strategy = _get_file_transfer_strategy( - acp=self._acp_instance, - local_working_dir=self._local_working_dir, - ) - - uploaded_file = self._add_input_file(path=pathlib.Path(local_file_path)) - self._model = self._acp_instance.import_model( - path=uploaded_file, format=file_format, **kwargs - ) - - @classmethod - def from_acph5_file( - cls, - acp: ACP[ServerProtocol], - acph5_file_path: PATH, - local_working_directory: Optional[PATH] = None, - ) -> "ACPWorkflow": - """Instantiate an ACP Workflow from an acph5 file. - - Parameters - ---------- - acp - The ACP Client. - acph5_file_path: - The path to the acph5 file. - local_working_directory: - The local working directory. If None, a temporary directory will be created. - """ - - return cls( - acp=acp, - local_file_path=acph5_file_path, - local_working_directory=local_working_directory, - file_format="acp:h5", - ) - - @classmethod - def from_cdb_or_dat_file( - cls, - *, - acp: ACP[ServerProtocol], - cdb_or_dat_file_path: PATH, - unit_system: UnitSystemType = UnitSystemType.UNDEFINED, - local_working_directory: Optional[PATH] = None, - ) -> "ACPWorkflow": - """Instantiate an ACP Workflow from a cdb file. - - Parameters - ---------- - acp - The ACP Client. - cdb_or_dat_file_path: - The path to the cdb or dat file. - unit_system: - Has to be ``UnitSystemType.UNDEFINED`` if the unit system - is specified in the cdb or dat file. Needs to be set to a defined unit system - if the unit system is not set in the cdb or dat file. - local_working_directory: - The local working directory. If None, a temporary directory will be created. - """ - - instance = cls( - acp=acp, - local_file_path=cdb_or_dat_file_path, - local_working_directory=local_working_directory, - file_format="ansys:cdb", - unit_system=unit_system, - ) - - if instance.model.unit_system == UnitSystemType.UNDEFINED: - raise ValueError( - "The input file does not provide a unit system. Please specify the unit system." - ) - return instance - - @property - def model(self) -> Model: - """Get the ACP Model.""" - return self._model - - @property - def working_directory(self) -> _LocalWorkingDir: - """Get the working directory.""" - return self._local_working_dir - - def get_local_cdb_file(self) -> pathlib.Path: - """Get the cdb file on the local machine. - - Write the analysis model including the layup definition in cdb format, - copy it to the local working directory and return its path. - """ - return self._file_transfer_strategy.get_file( - self._model.export_analysis_model, self._model.name + ".cdb" - ) - - def get_local_materials_file(self) -> pathlib.Path: - """Get the materials.xml file on the local machine. - - Write the materials.xml file, copy it to the local working directory and return its path. - """ - return self._file_transfer_strategy.get_file(self._model.export_materials, "materials.xml") - - def get_local_composite_definitions_file(self) -> pathlib.Path: - """Get the composite definitions file on the local machine. - - Write the composite definitions file, copy it to the local working - directory and return its path. - """ - return self._file_transfer_strategy.get_file( - self._model.export_shell_composite_definitions, "ACPCompositeDefinitions.h5" - ) - - def get_local_acph5_file(self) -> pathlib.Path: - """Get the ACP Project file (in acph5 format) on the local machine. - - Save the acp model to an acph5 file, copy it to the local working - directory and return its path. - """ - return self._file_transfer_strategy.get_file(self._model.save, self._model.name + ".acph5") - - def add_cad_geometry_from_local_file(self, path: pathlib.Path) -> CADGeometry: - """Add a local CAD geometry to the ACP model. - - Parameters - ---------- - path: - The path to the CAD geometry file. - """ - uploaded_file = self._add_input_file(path=path) - return self._model.create_cad_geometry(external_path=str(uploaded_file)) - - def refresh_cad_geometry_from_local_file( - self, path: pathlib.Path, cad_geometry: CADGeometry - ) -> None: - """Refresh the CAD geometry from a local file. - - Parameters - ---------- - path: - The path to the CAD geometry file. - cad_geometry: - The CADGeometry object to refresh. - """ - uploaded_file_path = self._add_input_file(path=path) - cad_geometry.external_path = uploaded_file_path - cad_geometry.refresh() - - def _add_input_file(self, path: pathlib.Path) -> pathlib.PurePath: - self._file_transfer_strategy.copy_input_file_to_local_workdir(path=path) - return self._file_transfer_strategy.upload_input_file_to_server(path=path) - - -def get_composite_post_processing_files( - acp_workflow: ACPWorkflow, local_rst_file_path: PATH -) -> "ContinuousFiberCompositesFiles": - """Get the files object needed for pydpf-composites from the workflow and the rst path. - - Only supports the shell workflow. - - Parameters - ---------- - acp_workflow: - The ACPWorkflow object. - local_rst_file_path: - Local path to the rst file. - """ - - # Only import here to avoid dependency on ansys.dpf.composites if it is not used - try: - from ansys.dpf.composites.data_sources import ( - CompositeDefinitionFiles, - ContinuousFiberCompositesFiles, - ) - except ImportError as e: - raise ImportError( - "The composite post processing files can only be retrieved if the " - "ansys-dpf-composites package is installed." - ) from e - - composite_files = ContinuousFiberCompositesFiles( - rst=local_rst_file_path, - composite={ - "shell": CompositeDefinitionFiles( - definition=acp_workflow.get_local_composite_definitions_file() - ), - }, - engineering_data=acp_workflow.get_local_materials_file(), - ) - return composite_files - - -def get_dpf_unit_system(unit_system: UnitSystemType) -> "UnitSystem": - """Convert pyACP unit system to DPF unit system. - - Parameters - ---------- - unit_system - The pyACP unit system. - """ - try: - from ansys.dpf.core import unit_systems - except ImportError as e: - raise ImportError( - "The pyACP unit system can only be converted to a DPF unit system if the " - "ansys-dpf-core package is installed." - ) from e - - unit_systems_map = { - UnitSystemType.UNDEFINED: unit_systems.undefined, - # looks like the only difference from MKS to SI is - # that temperature is defined as Kelvin in SI and °C in MKS. - # We should still force the user to use MKS in this case. - UnitSystemType.SI: None, - UnitSystemType.MKS: unit_systems.solver_mks, - UnitSystemType.uMKS: unit_systems.solver_umks, - UnitSystemType.CGS: unit_systems.solver_cgs, - # MPA is equivalent to nmm - UnitSystemType.MPA: unit_systems.solver_nmm, - UnitSystemType.BFT: unit_systems.solver_bft, - UnitSystemType.BIN: unit_systems.solver_bin, - } - - if unit_systems_map[unit_system] is None: - raise ValueError(f"Unit system {unit_system} not supported. Use MKS instead of SI.") - if unit_system not in unit_systems_map: - raise ValueError(f"Unit system {unit_system} not supported.") - - return unit_systems_map[unit_system] diff --git a/src/ansys/acp/core/dpf_integration_helpers.py b/src/ansys/acp/core/dpf_integration_helpers.py new file mode 100644 index 0000000000..a7b37a0a98 --- /dev/null +++ b/src/ansys/acp/core/dpf_integration_helpers.py @@ -0,0 +1,72 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Helper functions for exchanging data between PyACP and PyDPF or PyDPF Composites.""" + +import typing + +from ._tree_objects.enums import UnitSystemType + +# Avoid dependencies on pydpf-composites and dpf-core if it is not used +if typing.TYPE_CHECKING: # pragma: no cover + from ansys.dpf.core import UnitSystem + +__all__ = ["get_dpf_unit_system"] + + +def get_dpf_unit_system(unit_system: UnitSystemType) -> "UnitSystem": + """Convert pyACP unit system to DPF unit system. + + Parameters + ---------- + unit_system + The pyACP unit system. + """ + try: + from ansys.dpf.core import unit_systems + except ImportError as e: + raise ImportError( + "The pyACP unit system can only be converted to a DPF unit system if the " + "ansys-dpf-core package is installed." + ) from e + + unit_systems_map = { + UnitSystemType.UNDEFINED: unit_systems.undefined, + # looks like the only difference from MKS to SI is + # that temperature is defined as Kelvin in SI and °C in MKS. + # We should still force the user to use MKS in this case. + UnitSystemType.SI: None, + UnitSystemType.MKS: unit_systems.solver_mks, + UnitSystemType.uMKS: unit_systems.solver_umks, + UnitSystemType.CGS: unit_systems.solver_cgs, + # MPA is equivalent to nmm + UnitSystemType.MPA: unit_systems.solver_nmm, + UnitSystemType.BFT: unit_systems.solver_bft, + UnitSystemType.BIN: unit_systems.solver_bin, + } + + if unit_systems_map[unit_system] is None: + raise ValueError(f"Unit system {unit_system} not supported. Use MKS instead of SI.") + if unit_system not in unit_systems_map: + raise ValueError(f"Unit system {unit_system} not supported.") + + return unit_systems_map[unit_system] diff --git a/src/ansys/acp/core/example_helpers.py b/src/ansys/acp/core/example_helpers.py deleted file mode 100644 index b42b70bc97..0000000000 --- a/src/ansys/acp/core/example_helpers.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Helpers used in the PyACP examples. - -These utilities can download the input files used in the PyACP examples. -""" - - -import dataclasses -from enum import Enum, auto -import pathlib -import urllib.request - -__all__ = ["ExampleKeys", "get_example_file"] - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from ansys.acp.core import ACPWorkflow - -_EXAMPLE_REPO = "https://github.com/ansys/example-data/raw/master/pyacp/" - - -@dataclasses.dataclass -class _ExampleLocation: - directory: str - filename: str - - -class ExampleKeys(Enum): - """Keys for the example files.""" - - BASIC_FLAT_PLATE_DAT = auto() - BASIC_FLAT_PLATE_ACPH5 = auto() - RACE_CAR_NOSE_ACPH5 = auto() - RACE_CAR_NOSE_STEP = auto() - CUT_OFF_GEOMETRY = auto() - RULE_GEOMETRY_TRIANGLE = auto() - THICKNESS_GEOMETRY = auto() - MINIMAL_FLAT_PLATE = auto() - OPTIMIZATION_EXAMPLE_DAT = auto() - - -EXAMPLE_FILES: dict[ExampleKeys, _ExampleLocation] = { - ExampleKeys.BASIC_FLAT_PLATE_DAT: _ExampleLocation( - directory="basic_flat_plate_example", filename="flat_plate_input.dat" - ), - ExampleKeys.BASIC_FLAT_PLATE_ACPH5: _ExampleLocation( - directory="basic_flat_plate_example", filename="flat_plate.acph5" - ), - ExampleKeys.RACE_CAR_NOSE_ACPH5: _ExampleLocation( - directory="race_car_nose", filename="race_car_nose.acph5" - ), - ExampleKeys.RACE_CAR_NOSE_STEP: _ExampleLocation( - directory="race_car_nose", filename="race_car_nose.stp" - ), - ExampleKeys.CUT_OFF_GEOMETRY: _ExampleLocation( - directory="geometries", filename="cut_off_geometry.stp" - ), - ExampleKeys.RULE_GEOMETRY_TRIANGLE: _ExampleLocation( - directory="geometries", filename="rule_geometry_triangle.stp" - ), - ExampleKeys.THICKNESS_GEOMETRY: _ExampleLocation( - directory="geometries", filename="thickness_geometry.stp" - ), - ExampleKeys.MINIMAL_FLAT_PLATE: _ExampleLocation( - directory="basic_flat_plate_example", filename="minimal_model_single_ply.acph5" - ), - ExampleKeys.OPTIMIZATION_EXAMPLE_DAT: _ExampleLocation( - directory="optimization_example", filename="optimization_model.dat" - ), -} - - -def get_example_file(example_key: ExampleKeys, working_directory: pathlib.Path) -> pathlib.Path: - """Download an example file from the example-data repo to the working directory. - - Parameters - ---------- - example_key - Key for the example file. - working_directory - Working directory to download the example file to. - """ - example_location = EXAMPLE_FILES[example_key] - _download_file(example_location, working_directory / example_location.filename) - return working_directory / example_location.filename - - -def _get_file_url(example_location: _ExampleLocation) -> str: - return _EXAMPLE_REPO + "/".join([example_location.directory, example_location.filename]) - - -def _download_file(example_location: _ExampleLocation, local_path: pathlib.Path) -> None: - file_url = _get_file_url(example_location) - urllib.request.urlretrieve(file_url, local_path) - - -def _run_analysis(workflow: "ACPWorkflow") -> None: - """Run the model with mapdl and do a post-processing analysis. - - Uses a max strain criteria, which means strain limits have to be defined. - This function can be called at the end of examples to verify the prepared model - actually solves and can be postprocessed. - """ - from ansys.mapdl.core import launch_mapdl - - model = workflow.model - model.update() - - # Launch the MAPDL instance - mapdl = launch_mapdl() - mapdl.clear() - - # Load the CDB file into PyMAPDL - mapdl.input(str(workflow.get_local_cdb_file())) - - # Solve the model - mapdl.allsel() - mapdl.slashsolu() - mapdl.solve() - - # Download the rst file for composite specific post-processing - rstfile_name = f"{mapdl.jobname}.rst" - rst_file_local_path = workflow.working_directory.path / rstfile_name - mapdl.download(rstfile_name, str(workflow.working_directory.path)) - - from ansys.acp.core import get_composite_post_processing_files, get_dpf_unit_system - from ansys.dpf.composites.composite_model import CompositeModel - from ansys.dpf.composites.constants import FailureOutput - from ansys.dpf.composites.failure_criteria import CombinedFailureCriterion, MaxStrainCriterion - from ansys.dpf.composites.server_helpers import connect_to_or_start_server - - dpf_server = connect_to_or_start_server() - - max_strain = MaxStrainCriterion() - - cfc = CombinedFailureCriterion( - name="Combined Failure Criterion", - failure_criteria=[max_strain], - ) - - composite_model = CompositeModel( - get_composite_post_processing_files(workflow, rst_file_local_path), - default_unit_system=get_dpf_unit_system(model.unit_system), - server=dpf_server, - ) - - output_all_elements = composite_model.evaluate_failure_criteria(cfc) - irf_field = output_all_elements.get_field({"failure_label": FailureOutput.FAILURE_VALUE}) - - # %% - # Release composite model to close open streams to result file. - composite_model = None # type: ignore diff --git a/src/ansys/acp/core/extras/__init__.py b/src/ansys/acp/core/extras/__init__.py new file mode 100644 index 0000000000..c1e7427e45 --- /dev/null +++ b/src/ansys/acp/core/extras/__init__.py @@ -0,0 +1,38 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Extras of the Ansys Composites PrepPost module.""" + +from ansys.acp.core.extras.example_helpers import ( + FLAT_PLATE_SHELL_CAMERA, + FLAT_PLATE_SOLID_CAMERA, + RACE_CARE_NOSE_CAMERA_METER, + ExampleKeys, + get_example_file, +) + +__all__ = [ + "ExampleKeys", + "get_example_file", + "FLAT_PLATE_SHELL_CAMERA", + "FLAT_PLATE_SOLID_CAMERA", + "RACE_CARE_NOSE_CAMERA_METER", +] diff --git a/src/ansys/acp/core/extras/example_helpers.py b/src/ansys/acp/core/extras/example_helpers.py new file mode 100644 index 0000000000..337b51db38 --- /dev/null +++ b/src/ansys/acp/core/extras/example_helpers.py @@ -0,0 +1,266 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Helpers used in the PyACP examples. + +These utilities can download the input files used in the PyACP examples. +""" +import dataclasses +from enum import Enum, auto +import pathlib +import shutil +import sys +import tempfile +import urllib.parse +import urllib.request + +__all__ = [ + "ExampleKeys", + "get_example_file", + "FLAT_PLATE_SHELL_CAMERA", + "FLAT_PLATE_SOLID_CAMERA", + "RACE_CARE_NOSE_CAMERA_METER", +] + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from ansys.acp.core import Model + +_EXAMPLE_REPO = "https://github.com/ansys/example-data/raw/master/pyacp/" + + +# _EXAMPLE_REPO = "D:\\ANSYSDev\\pyansys-example-data\\pyacp\\" + + +# Order of inputs: position, rotation point, orientation +FLAT_PLATE_SHELL_CAMERA = [ + (-0.0053, 0.0168, 0.0220), + (0.0022, 0.0041, 0.0104), + (0.3510, 0.7368, -0.5779), +] +FLAT_PLATE_SOLID_CAMERA = [ + (0.0251, 0.0144, 0.0256), + (0.0086, 0.0041, 0.0089), + (-0.2895, 0.9160, -0.2776), +] + +# Order of inputs: position, rotation point, orientation +RACE_CARE_NOSE_CAMERA_METER = [ + (1.614, 1.154, 2.243), + (0.450, 0.238, -0.181), + (-0.1094, 0.9460, -0.3050), +] + + +@dataclasses.dataclass +class _ExampleLocation: + directory: str + filename: str + + +class ExampleKeys(Enum): + """Keys for the example files.""" + + BASIC_FLAT_PLATE_DAT = auto() + BASIC_FLAT_PLATE_REFINED_DAT = auto() + BASIC_FLAT_PLATE_ACPH5 = auto() + BASIC_FLAT_PLATE_SOLID_MESH_CDB = auto() + RACE_CAR_NOSE_ACPH5 = auto() + RACE_CAR_NOSE_STEP = auto() + CUT_OFF_GEOMETRY = auto() + RULE_GEOMETRY_TRIANGLE = auto() + THICKNESS_GEOMETRY = auto() + MINIMAL_FLAT_PLATE = auto() + OPTIMIZATION_EXAMPLE_DAT = auto() + CLASS40_AGDB = auto() + CLASS40_CDB = auto() + MATERIALS_XML = auto() + IMPORTED_SOLID_MODEL_ACPH5 = auto() + IMPORTED_SOLID_MODEL_SOLID_MESH = auto() + SNAP_TO_GEOMETRY = auto() + CUT_OFF_GEOMETRY_SOLID_MODEL = auto() + + +EXAMPLE_FILES: dict[ExampleKeys, _ExampleLocation] = { + ExampleKeys.BASIC_FLAT_PLATE_DAT: _ExampleLocation( + directory="basic_flat_plate_example", filename="flat_plate_input.dat" + ), + ExampleKeys.BASIC_FLAT_PLATE_REFINED_DAT: _ExampleLocation( + directory="basic_flat_plate_example", filename="flat_plate_input_refined.dat" + ), + ExampleKeys.BASIC_FLAT_PLATE_ACPH5: _ExampleLocation( + directory="basic_flat_plate_example", filename="flat_plate.acph5" + ), + ExampleKeys.BASIC_FLAT_PLATE_SOLID_MESH_CDB: _ExampleLocation( + directory="basic_flat_plate_example", filename="solid_mesh.cdb" + ), + ExampleKeys.RACE_CAR_NOSE_ACPH5: _ExampleLocation( + directory="race_car_nose", filename="race_car_nose.acph5" + ), + ExampleKeys.RACE_CAR_NOSE_STEP: _ExampleLocation( + directory="race_car_nose", filename="race_car_nose.stp" + ), + ExampleKeys.CUT_OFF_GEOMETRY: _ExampleLocation( + directory="geometries", filename="cut_off_geometry.stp" + ), + ExampleKeys.RULE_GEOMETRY_TRIANGLE: _ExampleLocation( + directory="geometries", filename="rule_geometry_triangle.stp" + ), + ExampleKeys.THICKNESS_GEOMETRY: _ExampleLocation( + directory="geometries", filename="thickness_geometry.stp" + ), + ExampleKeys.MINIMAL_FLAT_PLATE: _ExampleLocation( + directory="basic_flat_plate_example", filename="minimal_model_single_ply.acph5" + ), + ExampleKeys.OPTIMIZATION_EXAMPLE_DAT: _ExampleLocation( + directory="optimization_example", filename="optimization_model.dat" + ), + ExampleKeys.CLASS40_AGDB: _ExampleLocation(directory="class40", filename="class40.agdb"), + ExampleKeys.CLASS40_CDB: _ExampleLocation(directory="class40", filename="class40.cdb"), + ExampleKeys.MATERIALS_XML: _ExampleLocation(directory="materials", filename="materials.engd"), + ExampleKeys.IMPORTED_SOLID_MODEL_ACPH5: _ExampleLocation( + directory="imported_solid_model", filename="t-joint-ACP-Pre.acph5" + ), + ExampleKeys.IMPORTED_SOLID_MODEL_SOLID_MESH: _ExampleLocation( + directory="imported_solid_model", filename="t-joint.solid.h5" + ), + ExampleKeys.SNAP_TO_GEOMETRY: _ExampleLocation( + directory="geometries", filename="snap_to_geometry.stp" + ), + ExampleKeys.CUT_OFF_GEOMETRY_SOLID_MODEL: _ExampleLocation( + directory="geometries", filename="cut_off_geometry_solid_model.stp" + ), +} + + +def get_example_file(example_key: ExampleKeys, working_directory: pathlib.Path) -> pathlib.Path: + """Download an example file from the example-data repo to the working directory. + + Parameters + ---------- + example_key + Key for the example file. + working_directory + Working directory to download the example file to. + """ + example_location = EXAMPLE_FILES[example_key] + _download_file(example_location, working_directory / example_location.filename) + return working_directory / example_location.filename + + +def _is_url(path_url: str) -> bool: # pragma: no cover + return urllib.parse.urlparse(path_url).scheme in ["http", "https"] + + +def _get_file_url(example_location: _ExampleLocation) -> str: # pragma: no cover + if sys.platform == "win32" and not _is_url(_EXAMPLE_REPO): + return _EXAMPLE_REPO + "\\".join([example_location.directory, example_location.filename]) + else: + return _EXAMPLE_REPO + "/".join([example_location.directory, example_location.filename]) + + +def _download_file( + example_location: _ExampleLocation, local_path: pathlib.Path +) -> None: # pragma: no cover + file_url = _get_file_url(example_location) + # The URL is hard-coded to start with the example repository URL, so it is safe to use + if _is_url(file_url): + urllib.request.urlretrieve(file_url, local_path) # nosec: B310 + else: + shutil.copyfile(file_url, local_path) + + +def _run_analysis(model: "Model") -> None: + """Run the model with mapdl and do a post-processing analysis. + + Uses a max strain criteria, which means strain limits have to be defined. + This function can be called at the end of examples to verify the prepared model + actually solves and can be postprocessed. + """ + from ansys.mapdl.core import launch_mapdl + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_dir_path = pathlib.Path(tmp_dir) + model.update() + cdb_out_path = tmp_dir_path / "model.cdb" + model.export_analysis_model(cdb_out_path) + + # Launch the MAPDL instance + mapdl = launch_mapdl() + mapdl.clear() + + # Load the CDB file into PyMAPDL + mapdl.input(str(cdb_out_path)) + + # Solve the model + mapdl.allsel() + mapdl.slashsolu() + mapdl.solve() + + # Download the rst file for composite specific post-processing + rstfile_name = f"{mapdl.jobname}.rst" + rst_file_local_path = tmp_dir_path / rstfile_name + mapdl.download(rstfile_name, str(tmp_dir_path)) + + from ansys.acp.core.dpf_integration_helpers import get_dpf_unit_system + from ansys.dpf.composites.composite_model import CompositeModel + from ansys.dpf.composites.constants import FailureOutput + from ansys.dpf.composites.data_sources import ( + CompositeDefinitionFiles, + ContinuousFiberCompositesFiles, + ) + from ansys.dpf.composites.failure_criteria import ( + CombinedFailureCriterion, + MaxStrainCriterion, + ) + from ansys.dpf.composites.server_helpers import connect_to_or_start_server + + dpf_server = connect_to_or_start_server() + + max_strain = MaxStrainCriterion() + + cfc = CombinedFailureCriterion( + name="Combined Failure Criterion", + failure_criteria=[max_strain], + ) + + materials_file_path = tmp_dir_path / "materials.xml" + model.export_materials(materials_file_path) + composite_definitions_file_path = tmp_dir_path / "ACPCompositeDefinitions.h5" + model.export_shell_composite_definitions(composite_definitions_file_path) + composite_model = CompositeModel( + composite_files=ContinuousFiberCompositesFiles( + rst=rst_file_local_path, + composite={"shell": CompositeDefinitionFiles(composite_definitions_file_path)}, + engineering_data=materials_file_path, + ), + default_unit_system=get_dpf_unit_system(model.unit_system), + server=dpf_server, + ) + + output_all_elements = composite_model.evaluate_failure_criteria(cfc) + irf_field = output_all_elements.get_field({"failure_label": FailureOutput.FAILURE_VALUE}) + + # %% + # Release composite model to close open streams to result file. + composite_model = None # type: ignore diff --git a/src/ansys/acp/core/mechanical_integration_helpers.py b/src/ansys/acp/core/mechanical_integration_helpers.py new file mode 100644 index 0000000000..319d1b657d --- /dev/null +++ b/src/ansys/acp/core/mechanical_integration_helpers.py @@ -0,0 +1,173 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Helper functions for exchanging data between PyACP and PyMechanical.""" + +import pathlib +import shutil +import textwrap +import typing + +if typing.TYPE_CHECKING: + import ansys.mechanical.core as pymechanical + +from ._utils.typing_helper import PATH + +__all__ = [ + "export_mesh_for_acp", + "import_acp_composite_definitions", + "import_acp_mesh_from_cdb", +] + + +def export_mesh_for_acp(*, mechanical: "pymechanical.Mechanical", path: PATH) -> None: + """Export the mesh from PyMechanical for use in PyACP. + + Parameters + ---------- + mechanical : + The PyMechanical instance. This must be a remote instance. + path : + The path to save the mesh to. The extension must be '.h5'. + """ + path = pathlib.Path(path) + + if path.suffix != ".h5": + raise ValueError(f"The output path extension must be '.h5', not '{path.suffix}'.") + output_path_str = str(path) + mechanical.run_python_script( + textwrap.dedent( + f"""\ + geometry_type = Ansys.Mechanical.DataModel.Enums.GeometryType.Sheet + unit = Ansys.Mechanical.DataModel.Enums.WBUnitSystemType.ConsistentMKS + dsid = 0 + + Model.InternalObject.WriteHDF5TransferFile(geometry_type, {output_path_str!r}, unit, dsid) + """ + ) + ) + + +def import_acp_mesh_from_cdb(*, mechanical: "pymechanical.Mechanical", cdb_path: PATH) -> None: + """Import an ACP CDB mesh into Mechanical. + + Import a mesh exported from ACP in CDB format into Mechanical. This function + does not import the ACP layup definition, use :func:`import_acp_composite_definitions` + for this purpose. + + .. warning:: + + The named selections exported from ACP are only partially imported. + + Parameters + ---------- + mechanical : + The PyMechanical instance. This must be a remote instance. + cdb_path : + The path of the CDB file to import. The extension must be '.cdb'. + """ + cdb_path = pathlib.Path(cdb_path) + + if cdb_path.suffix != ".cdb": + raise ValueError(f"The CDB file extension must be '.cdb', not '{cdb_path.suffix}'.") + cdb_path_str = str(cdb_path) + + mechanical.run_python_script( + textwrap.dedent( + f"""\ + initial_geometry_ids = {{obj.ObjectId for obj in Model.Geometry.Children}} + + model_import = Model.AddGeometryImportGroup().AddModelImport() + model_import.ModelImportSourceFilePath = {cdb_path_str!r} + model_import.ProcessValidBlockedCDBFile = False + model_import.ProcessModelData = False + model_import.Import() + + final_geometry_ids = {{obj.ObjectId for obj in Model.Geometry.Children}} + new_geometry_ids = final_geometry_ids - initial_geometry_ids + if len(new_geometry_ids) != 1: + raise ValueError("Expected 1 new geometry object, but found {{}}.".format(len(new_geometry_ids))) + new_geometry_id = new_geometry_ids.pop() + + new_geometry = DataModel.GetObjectById(new_geometry_id) + if len(new_geometry.Children) != 1: + raise ValueError("Expected 1 body, but got {{}}.".format(len(new_geometry.Children))) + + body = new_geometry.GetChildren( + Ansys.Mechanical.DataModel.Enums.DataModelObjectCategory.Body, True + )[0] + body.AddCommandSnippet().Input = "keyo,matid,3,1\\nkeyo,matid,8,1" + """ + ) + ) + + +def import_acp_composite_definitions(*, mechanical: "pymechanical.Mechanical", path: PATH) -> None: + """Import ACP composite definitions HDF5 into Mechanical. + + Imports the composite layup defined in ACP into Mechanical, as Imported Plies. + + This function does not import the solid mesh, use :func:`import_acp_mesh_from_cdb` + for this purpose. + + Parameters + ---------- + mechanical : + The PyMechanical instance. This must be a remote instance. + path : + The path of the file to import. The extension must be '.h5'. + """ + path = pathlib.Path(path) + setup_folder_name = "Setup" + + if path.suffix != ".h5": + raise ValueError( + f"The composite definitions file extension must be '.h5', not '{path.suffix}'." + ) + + setup_folder = pathlib.Path(mechanical.project_directory) / setup_folder_name + setup_folder.mkdir(exist_ok=True) + target_path = setup_folder / path.name + try: + shutil.copyfile(path, target_path) + except shutil.SameFileError: + pass + + target_path_str = f"{setup_folder_name}::{target_path.resolve()}".replace("\\", "\\\\") + + mechanical.run_python_script( + textwrap.dedent( + f"""\ + import clr + clr.AddReference("Ansys.Common.Interop.{mechanical.version}") + composite_definition_paths_coll = Ansys.Common.Interop.AnsCoreObjects.AnsBSTRColl() + composite_definition_paths_coll.Add({target_path_str!r}) + mapping_paths_coll = Ansys.Common.Interop.AnsCoreObjects.AnsVARIANTColl() + mapping_paths_coll.Add(None) + external_model = Model.InternalObject.AddExternalEnhancedModel( + Ansys.Common.Interop.DSObjectTypes.DSExternalEnhancedModelType.kEXTERNAL_ENHANCEDMODEL_LAYEREDSECTION + ) + external_model.Import(composite_definition_paths_coll, mapping_paths_coll) + external_model.Update() + """ + ) + ) diff --git a/src/ansys/acp/core/mesh_data.py b/src/ansys/acp/core/mesh_data.py new file mode 100644 index 0000000000..58ec11a016 --- /dev/null +++ b/src/ansys/acp/core/mesh_data.py @@ -0,0 +1,108 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +"""Move mesh_data into separate namespace. + +Remove classes and function which are not used by the public API +into a separate namespace. +""" + +from ._tree_objects import ( + AnalysisPlyElementalData, + AnalysisPlyNodalData, + BooleanSelectionRuleElementalData, + BooleanSelectionRuleNodalData, + CutOffSelectionRuleElementalData, + CutOffSelectionRuleNodalData, + CylindricalSelectionRuleElementalData, + CylindricalSelectionRuleNodalData, + ElementSetElementalData, + ElementSetNodalData, + GeometricalSelectionRuleElementalData, + GeometricalSelectionRuleNodalData, + ImportedSolidModelElementalData, + ImportedSolidModelNodalData, + MeshData, + ModelElementalData, + ModelingPlyElementalData, + ModelingPlyNodalData, + ModelNodalData, + OrientedSelectionSetElementalData, + OrientedSelectionSetNodalData, + ParallelSelectionRuleElementalData, + ParallelSelectionRuleNodalData, + ProductionPlyElementalData, + ProductionPlyNodalData, + ScalarData, + SolidElementSetElementalData, + SolidElementSetNodalData, + SolidModelElementalData, + SolidModelNodalData, + SphericalSelectionRuleElementalData, + SphericalSelectionRuleNodalData, + TriangleMesh, + TubeSelectionRuleElementalData, + TubeSelectionRuleNodalData, + VariableOffsetSelectionRuleElementalData, + VariableOffsetSelectionRuleNodalData, + VectorData, +) + +__all__ = [ + "AnalysisPlyElementalData", + "AnalysisPlyNodalData", + "BooleanSelectionRuleElementalData", + "BooleanSelectionRuleNodalData", + "CutOffSelectionRuleElementalData", + "CutOffSelectionRuleNodalData", + "CylindricalSelectionRuleElementalData", + "CylindricalSelectionRuleNodalData", + "ElementSetElementalData", + "ElementSetNodalData", + "GeometricalSelectionRuleElementalData", + "GeometricalSelectionRuleNodalData", + "ImportedSolidModelElementalData", + "ImportedSolidModelNodalData", + "MeshData", + "ModelElementalData", + "ModelingPlyElementalData", + "ModelingPlyNodalData", + "ModelNodalData", + "OrientedSelectionSetElementalData", + "OrientedSelectionSetNodalData", + "ParallelSelectionRuleElementalData", + "ParallelSelectionRuleNodalData", + "ProductionPlyElementalData", + "ProductionPlyNodalData", + "ScalarData", + "SolidElementSetElementalData", + "SolidElementSetNodalData", + "SolidModelElementalData", + "SolidModelNodalData", + "SphericalSelectionRuleElementalData", + "SphericalSelectionRuleNodalData", + "TriangleMesh", + "TubeSelectionRuleElementalData", + "TubeSelectionRuleNodalData", + "VariableOffsetSelectionRuleElementalData", + "VariableOffsetSelectionRuleNodalData", + "VectorData", +] diff --git a/tests/benchmarks/conftest.py b/tests/benchmarks/conftest.py index 8bf8445546..f89d753ef3 100644 --- a/tests/benchmarks/conftest.py +++ b/tests/benchmarks/conftest.py @@ -45,7 +45,7 @@ BENCHMARK_IMAGE_NAME = "pyacp-benchmark-runner" -def pytest_ignore_collect(collection_path, path, config): +def pytest_ignore_collect(collection_path, config): # The benchmarks can only be run on Linux, since the 'tc-netem' tool # used for manipulating network speeds is not available on Docker for # Windows / Mac. @@ -95,7 +95,7 @@ def launcher_configuration(request): license_server = request.config.getoption(LICENSE_SERVER_OPTION_KEY) return pyacp.DockerComposeLaunchConfig( - image_name_pyacp=BENCHMARK_IMAGE_NAME, + image_name_acp=BENCHMARK_IMAGE_NAME, image_name_filetransfer=image_name_filetransfer, compose_file=SOURCE_ROOT_DIR / "docker-compose" / "docker-compose-benchmark.yaml", license_server=license_server, @@ -129,7 +129,7 @@ def launch_benchmark_server(network_options): "PYACP_DELAY": f"{network_options.delay_ms}ms", "PYACP_RATE": f"{network_options.rate_kbit}kbit", } - acp = pyacp.launch_acp(config=conf, launch_mode=pyacp.LaunchMode.DOCKER_COMPOSE) # type: ignore + acp = pyacp.launch_acp(config=conf, launch_mode=pyacp.LaunchMode.DOCKER_COMPOSE) acp.wait(SERVER_STARTUP_TIMEOUT) return acp @@ -159,5 +159,5 @@ def network_options(request): @pytest.fixture -def grpc_server(_benchmark_servers, network_options): +def acp_instance(_benchmark_servers, network_options): return _benchmark_servers[network_options] diff --git a/tests/benchmarks/test_class40.py b/tests/benchmarks/test_class40.py index 504039b9d3..78e48ded3c 100644 --- a/tests/benchmarks/test_class40.py +++ b/tests/benchmarks/test_class40.py @@ -24,27 +24,20 @@ import ansys.acp.core as pyacp -from ..conftest import SOURCE_ROOT_DIR - -@pytest.mark.benchmark( - min_rounds=1, -) -def test_class40(benchmark, acp_instance): +@pytest.mark.benchmark(min_rounds=1) +def test_class40(benchmark, acp_instance, model_data_dir): """Benchmark for creating a composite lay-up for the Class40 model.""" - benchmark(create_class40, acp_instance) + class40_file = model_data_dir / "class40.cdb" + benchmark(create_class40, acp_instance, class40_file) -def create_class40(pyacp_client): +def create_class40(pyacp_client, cdb_file): """ Create a composite lay-up for the Class40 model. """ - examples_data_dir = SOURCE_ROOT_DIR / "examples" / "data" / "class40" - - cdb_file_path = pyacp_client.upload_file(local_path=str(examples_data_dir / "class40.cdb")) - model = pyacp_client.import_model( - path=cdb_file_path, format="ansys:cdb", unit_system=pyacp.UnitSystemType.MPA + path=cdb_file, format="ansys:cdb", unit_system=pyacp.UnitSystemType.MPA ) # Materials diff --git a/tests/conftest.py b/tests/conftest.py index 47183c3827..537f76b02d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -31,10 +31,11 @@ import docker from hypothesis import settings +from packaging.version import parse as parse_version import pytest from ansys.acp.core import ( - ACP, + ACPInstance, ConnectLaunchConfig, DirectLaunchConfig, DockerComposeLaunchConfig, @@ -42,7 +43,7 @@ launch_acp, ) from ansys.acp.core._server.common import ServerProtocol -from ansys.acp.core._typing_helper import PATH +from ansys.acp.core._utils.typing_helper import PATH from ansys.tools.local_product_launcher.config import set_config_for __all__ = [ @@ -81,7 +82,8 @@ BUILD_BENCHMARK_IMAGE_OPTION_KEY = "--build-benchmark-image" VALIDATE_BENCHMARKS_ONLY_OPTION_KEY = "--validate-benchmarks-only" SERVER_STARTUP_TIMEOUT = 30.0 -SERVER_STOP_TIMEOUT = 1.0 +SERVER_STOP_TIMEOUT = 2.0 +SERVER_CHECK_TIMEOUT = 2.0 pytest.register_assert_rewrite("common") @@ -189,7 +191,7 @@ def _configure_launcher(request: pytest.FixtureRequest) -> None: product_name="ACP", launch_mode=LaunchMode.DOCKER_COMPOSE, config=DockerComposeLaunchConfig( - image_name_pyacp=image_name, + image_name_acp=image_name, image_name_filetransfer=image_name_filetransfer, license_server=license_server, keep_volume=False, @@ -207,18 +209,18 @@ def model_data_dir() -> pathlib.Path: @pytest.fixture(scope="session") -def acp_instance(_configure_launcher) -> Generator[ACP[ServerProtocol], None, None]: +def acp_instance(_configure_launcher) -> Generator[ACPInstance[ServerProtocol], None, None]: """Provide the currently active gRPC server.""" yield launch_acp(timeout=SERVER_STARTUP_TIMEOUT) @pytest.fixture(autouse=True) def check_grpc_server_before_run( - acp_instance: ACP[ServerProtocol], + acp_instance: ACPInstance[ServerProtocol], ) -> Generator[None, None, None]: """Check if the server still responds before running each test, otherwise restart it.""" try: - acp_instance.wait(timeout=1.0) + acp_instance.wait(timeout=SERVER_CHECK_TIMEOUT) except RuntimeError: acp_instance.restart(stop_timeout=SERVER_STOP_TIMEOUT) acp_instance.wait(timeout=SERVER_STARTUP_TIMEOUT) @@ -237,13 +239,25 @@ def load_model_from_tempfile(model_data_dir, acp_instance): def inner(relative_file_path="minimal_complete_model_no_matml_link.acph5", format="acp:h5"): with tempfile.TemporaryDirectory() as tmp_dir: source_path = model_data_dir / relative_file_path + # Copy the file to a temporary directory, so the original file is never + # modified. This can happen for example when a geometry reload happens. + file_path = shutil.copy(source_path, tmp_dir) - if acp_instance.is_remote: - file_path = acp_instance.upload_file(source_path) - else: - # Copy the file to a temporary directory, so the original file is never - # modified. This can happen for example when a geometry reload happens. - file_path = shutil.copy(source_path, tmp_dir) + yield acp_instance.import_model(path=file_path, format=format) + + return inner + + +@pytest.fixture +def load_model_imported_plies_from_tempfile(model_data_dir, acp_instance): + @contextmanager + def inner(relative_file_path="minimal_model_imported_plies.acph5", format="acp:h5"): + with tempfile.TemporaryDirectory() as tmp_dir: + source_path = model_data_dir / relative_file_path + + # Copy the file to a temporary directory, so the original file is never + # modified. This can happen for example when a geometry reload happens. + file_path = shutil.copy(source_path, tmp_dir) yield acp_instance.import_model(path=file_path, format=format) @@ -254,9 +268,36 @@ def inner(relative_file_path="minimal_complete_model_no_matml_link.acph5", forma def load_cad_geometry(model_data_dir, acp_instance): @contextmanager def inner(model, relative_file_path="square_and_solid.stp"): - cad_file_path = acp_instance.upload_file(model_data_dir / relative_file_path) + cad_file_path_local = model_data_dir / relative_file_path + cad_file_path_remote = acp_instance.upload_file(cad_file_path_local) yield model.create_cad_geometry( - external_path=cad_file_path, + external_path=cad_file_path_remote, ) return inner + + +@pytest.fixture +def raises_before_version(acp_instance): + """Mark a test as expected to fail before a certain server version.""" + + @contextmanager + def inner(version: str): + if parse_version(acp_instance.server_version) < parse_version(version): + with pytest.raises(RuntimeError): + yield + else: + yield + + return inner + + +@pytest.fixture +def skip_before_version(acp_instance): + """Skip a test before a certain server version.""" + + def inner(version: str): + if parse_version(acp_instance.server_version) < parse_version(version): + pytest.skip(f"Test is not supported before version {version}") + + return inner diff --git a/examples/data/class40/class40.cdb b/tests/data/class40.cdb similarity index 100% rename from examples/data/class40/class40.cdb rename to tests/data/class40.cdb diff --git a/tests/data/minimal_complete_model.acph5 b/tests/data/minimal_complete_model.acph5 index b72ff55697..464dbd4ac6 100644 Binary files a/tests/data/minimal_complete_model.acph5 and b/tests/data/minimal_complete_model.acph5 differ diff --git a/tests/data/minimal_complete_model_no_matml_link.acph5 b/tests/data/minimal_complete_model_no_matml_link.acph5 index cbe03f3189..c426389652 100644 Binary files a/tests/data/minimal_complete_model_no_matml_link.acph5 and b/tests/data/minimal_complete_model_no_matml_link.acph5 differ diff --git a/tests/data/minimal_model_imported_plies.acph5 b/tests/data/minimal_model_imported_plies.acph5 new file mode 100644 index 0000000000..339cb88d83 Binary files /dev/null and b/tests/data/minimal_model_imported_plies.acph5 differ diff --git a/tests/unittests/common/linked_object_list_tester.py b/tests/unittests/common/linked_object_list_tester.py index cc08021472..c62c215089 100644 --- a/tests/unittests/common/linked_object_list_tester.py +++ b/tests/unittests/common/linked_object_list_tester.py @@ -22,10 +22,11 @@ from __future__ import annotations +from collections.abc import Callable from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from mypy_extensions import DefaultNamedArg, KwArg import pytest diff --git a/tests/unittests/common/tree_object_tester.py b/tests/unittests/common/tree_object_tester.py index 6ffe124907..2150423aa4 100644 --- a/tests/unittests/common/tree_object_tester.py +++ b/tests/unittests/common/tree_object_tester.py @@ -21,7 +21,7 @@ # SOFTWARE. from dataclasses import dataclass -from typing import Any, Optional +from typing import Any import pytest @@ -36,6 +36,14 @@ class PropertyWithConversion: converted_value: Any +@dataclass +class PropertyWithCustomComparison: + """A class to store a property which is compared using a custom function.""" + + initial_value: Any + comparison_function: Any + + @dataclass class ObjectPropertiesToTest: read_write: list[tuple[str, Any]] @@ -47,7 +55,7 @@ class ObjectPropertiesToTest: # If create_args exists the create method will use the create_args dictionaries # to create the object. The object # will be created for each dictionary in the list. - create_args: Optional[list[dict[str, Any]]] = None + create_args: list[dict[str, Any]] | None = None class TreeObjectTesterReadOnly: @@ -111,6 +119,14 @@ def test_collection_getitem_inexistent(collection_test_data): object_collection[INEXISTENT_ID] assert object_collection.get(INEXISTENT_ID) is None + @staticmethod + def test_parent_access(collection_test_data, parent_object): + """Test the parent access of the objects in the collection.""" + object_collection, _, object_ids = collection_test_data + ref_id = object_ids[0] + + assert object_collection[ref_id].parent is parent_object + class TreeObjectTester(TreeObjectTesterReadOnly): COLLECTION_NAME: str @@ -143,6 +159,9 @@ def create_and_check(init_args: dict[str, Any]): new_object = create_method(**init_args_final) for key, val in init_args.items(): + if isinstance(val, PropertyWithCustomComparison): + assert val.comparison_function(getattr(new_object, key), val.initial_value) + continue if isinstance(val, PropertyWithConversion): val = val.converted_value assert_allclose( @@ -168,13 +187,18 @@ def test_properties(tree_object, object_properties: ObjectPropertiesToTest): if isinstance(value, PropertyWithConversion): initial_value = value.initial_value converted_value = value.converted_value + elif isinstance(value, PropertyWithCustomComparison): + initial_value = converted_value = value.initial_value else: initial_value = converted_value = value setattr(tree_object, prop, initial_value) - assert_allclose( - actual=getattr(tree_object, prop), - desired=converted_value, - ) + if isinstance(value, PropertyWithCustomComparison): + assert value.comparison_function(getattr(tree_object, prop), converted_value) + else: + assert_allclose( + actual=getattr(tree_object, prop), + desired=converted_value, + ) for prop, value in object_properties.read_only: getattr( @@ -183,10 +207,11 @@ def test_properties(tree_object, object_properties: ObjectPropertiesToTest): with pytest.raises(AttributeError): setattr(tree_object, prop, value) + string_representation = str(tree_object) for prop, _ in object_properties.read_only + object_properties.read_write: - assert f"{prop}=" in str( - tree_object - ), f"{prop} not found in object string: {str(tree_object)}" + assert ( + f"{prop}=" in string_representation + ), f"{prop} not found in object string: {string_representation}" @staticmethod def test_collection_delitem(collection_test_data): @@ -199,6 +224,17 @@ def test_collection_delitem(collection_test_data): with pytest.raises(KeyError): object_collection[ref_id] + @staticmethod + def test_unstored_parent_access_raises(collection_test_data): + """Test that unstored objects raise an error when accessing the parent.""" + object_collection, _, object_ids = collection_test_data + ref_id = object_ids[0] + object = object_collection[ref_id].clone() + with pytest.raises(RuntimeError) as exc: + object.parent + assert "unstored" in str(exc.value) + assert "parent" in str(exc.value) + class NoLockedMixin(TreeObjectTester): @pytest.fixture diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py index 599e9c16dc..445080a1e4 100644 --- a/tests/unittests/helpers.py +++ b/tests/unittests/helpers.py @@ -20,14 +20,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Any, Optional, TypeVar +from typing import Any, TypeVar __all__ = ["check_property"] T = TypeVar("T") -def check_property(obj: Any, *, name: str, value: T, set_value: Optional[T] = None): +def check_property(obj: Any, *, name: str, value: T, set_value: T | None = None): assert hasattr(obj, name), f"Object '{obj}' has no property named '{name}'" assert ( getattr(obj, name) == value diff --git a/tests/unittests/test_acp_instance.py b/tests/unittests/test_acp_instance.py index f11c4d6855..22a7c7ade2 100644 --- a/tests/unittests/test_acp_instance.py +++ b/tests/unittests/test_acp_instance.py @@ -21,6 +21,15 @@ # SOFTWARE. +from contextlib import contextmanager +import os +import pathlib +import shutil +import tempfile + +import pytest + + def test_server_version(acp_instance): version = acp_instance.server_version assert isinstance(version, str) @@ -40,3 +49,29 @@ def test_models(acp_instance, load_model_from_tempfile): acp_instance.clear() assert acp_instance.models == () + + +@contextmanager +def change_cwd(new_cwd): + old_cwd = os.getcwd() + os.chdir(new_cwd) + try: + yield + finally: + os.chdir(old_cwd) + + +def test_import_from_differenct_cwd(acp_instance, model_data_dir): + if acp_instance.is_remote: + pytest.skip("Test is not relevant for remote instances") + with tempfile.TemporaryDirectory() as tmp_dir: + model_filename = "minimal_complete_model.acph5" + tmp_dir_path = pathlib.Path(tmp_dir) + shutil.copyfile(model_data_dir / model_filename, tmp_dir_path / model_filename) + with change_cwd(tmp_dir): + model = acp_instance.import_model(model_filename) + export_filename = "exported_model.acph5" + model.save(export_filename) + assert (tmp_dir_path / export_filename).exists() + # Check that the exported file does not exist on the original CWD + assert not pathlib.Path(export_filename).exists() diff --git a/tests/unittests/test_analysis_ply.py b/tests/unittests/test_analysis_ply.py index 005f351370..a3c56d2a00 100644 --- a/tests/unittests/test_analysis_ply.py +++ b/tests/unittests/test_analysis_ply.py @@ -20,9 +20,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from numpy.testing import assert_equal import pytest -from ansys.acp.core import AnalysisPlyElementalData, AnalysisPlyNodalData, FabricWithAngle, Model +from ansys.acp.core import FabricWithAngle, Model +from ansys.acp.core.mesh_data import AnalysisPlyElementalData, AnalysisPlyNodalData from .common.tree_object_tester import TreeObjectTesterReadOnly @@ -65,24 +67,33 @@ def properties_3_layers(self, model): "status": "UPTODATE", "material": first_material, "angle": 0.0, + "thickness": 0.0001, }, "P1L2__ModelingPly.1": { "status": "UPTODATE", "material": first_material, "angle": 10.0, + "thickness": 0.0001, }, "P1L3__ModelingPly.1": { "status": "UPTODATE", "material": first_material, "angle": 20.0, + "thickness": 0.0001, }, } + @staticmethod @pytest.fixture - def collection_test_data(self, model: Model): + def parent_object(model: Model): add_stackup_with_3_layers_to_modeling_ply(model) production_ply = get_first_modeling_ply(model).production_plies["ProductionPly"] + return production_ply + + @pytest.fixture + def collection_test_data(self, parent_object): + production_ply = parent_object object_collection = getattr(production_ply, self.COLLECTION_NAME) object_collection.values() object_names = ["P1L1__ModelingPly.1", "P1L2__ModelingPly.1", "P1L3__ModelingPly.1"] @@ -140,3 +151,27 @@ def test_mesh_data_existence(model: Model): assert isinstance(elemental_data, AnalysisPlyElementalData) nodal_data = analysis_ply.nodal_data assert isinstance(nodal_data, AnalysisPlyNodalData) + + +def test_meshes(model: Model, raises_before_version): + """ + Test that the mesh properties can be retrieved. + """ + analysis_ply = list(get_first_production_ply(model).analysis_plies.values())[0] + with raises_before_version("25.1"): + assert_equal(analysis_ply.mesh.element_labels, [1]) + with raises_before_version("25.1"): + assert_equal(analysis_ply.shell_mesh.element_labels, [1]) + with raises_before_version("25.1"): + assert_equal(analysis_ply.solid_mesh.element_labels, []) + with raises_before_version("25.1"): + model.create_solid_model( + element_sets=[model.element_sets["All_Elements"]], + ) + model.update() + with raises_before_version("25.1"): + assert_equal(analysis_ply.mesh.element_labels, [1, 2]) + with raises_before_version("25.1"): + assert_equal(analysis_ply.shell_mesh.element_labels, [1]) + with raises_before_version("25.1"): + assert_equal(analysis_ply.solid_mesh.element_labels, [2]) diff --git a/tests/unittests/test_boolean_selection_rule.py b/tests/unittests/test_boolean_selection_rule.py index b447e3b253..cfd1eb532e 100644 --- a/tests/unittests/test_boolean_selection_rule.py +++ b/tests/unittests/test_boolean_selection_rule.py @@ -22,11 +22,10 @@ import pytest -from ansys.acp.core import ( - BooleanOperationType, +from ansys.acp.core import BooleanOperationType, LinkedSelectionRule +from ansys.acp.core.mesh_data import ( BooleanSelectionRuleElementalData, BooleanSelectionRuleNodalData, - LinkedSelectionRule, ) from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -52,7 +51,7 @@ def default_properties(): return { "status": "NOTUPTODATE", "selection_rules": [], - "include_rule_type": True, + "include_rule": True, } CREATE_METHOD_NAME = "create_boolean_selection_rule" @@ -112,7 +111,7 @@ def object_properties(parent_object): ), ], ), - ("include_rule_type", False), + ("include_rule", False), ], read_only=[ ("id", "some_id"), diff --git a/tests/unittests/test_butt_joint_sequence.py b/tests/unittests/test_butt_joint_sequence.py new file mode 100644 index 0000000000..9608100dd4 --- /dev/null +++ b/tests/unittests/test_butt_joint_sequence.py @@ -0,0 +1,121 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ButtJointSequence, PrimaryPly + +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester +from .common.utils import AnyThing + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + ButtJointSequence._SUPPORTED_SINCE + ): + pytest.skip("ButtJointSequence is not supported on this version of the server.") + + +@pytest.fixture +def parent_model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def parent_object(parent_model): + return parent_model.modeling_groups["ModelingGroup.1"] + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_butt_joint_sequence() + + +class TestButtJointSequence(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "butt_joint_sequences" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "global_ply_nr": AnyThing(), + "primary_plies": [], + "secondary_plies": [], + } + + CREATE_METHOD_NAME = "create_butt_joint_sequence" + + @staticmethod + @pytest.fixture + def object_properties(parent_model): + mg1 = parent_model.create_modeling_group() + mg2 = parent_model.create_modeling_group() + mp1 = mg1.create_modeling_ply() + mp2 = mg1.create_modeling_ply() + return ObjectPropertiesToTest( + read_write=[ + ("name", "ButtJointSequence name"), + ("active", False), + ("global_ply_nr", 3), + ( + "primary_plies", + [ + PrimaryPly(sequence=mg1, level=1), + PrimaryPly(sequence=mp2, level=3), + ], + ), + ("secondary_plies", [mg2, mp1]), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) + + +def test_wrong_primary_ply_type_error_message(tree_object, parent_model): + butt_joint_sequence = tree_object + fabric = parent_model.create_fabric() + with pytest.raises(TypeError) as exc: + butt_joint_sequence.primary_plies = [fabric] + assert "PrimaryPly" in str(exc.value) + assert "Fabric" in str(exc.value) + + +def test_add_primary_ply(parent_object): + """Verify add method for primary plies.""" + modeling_ply_1 = parent_object.create_modeling_ply() + + butt_joint_sequence = parent_object.create_butt_joint_sequence() + butt_joint_sequence.add_primary_ply(modeling_ply_1) + assert butt_joint_sequence.primary_plies[-1].sequence == modeling_ply_1 + assert butt_joint_sequence.primary_plies[-1].level == 1 + modeling_ply_2 = modeling_ply_1.clone() + modeling_ply_2.store(parent=parent_object) + butt_joint_sequence.add_primary_ply(modeling_ply_2, level=3) + assert butt_joint_sequence.primary_plies[-1].sequence == modeling_ply_2 + assert butt_joint_sequence.primary_plies[-1].level == 3 diff --git a/tests/unittests/test_cad_component.py b/tests/unittests/test_cad_component.py index 19ecaf2392..8cce9b4ca2 100644 --- a/tests/unittests/test_cad_component.py +++ b/tests/unittests/test_cad_component.py @@ -32,7 +32,7 @@ def model(load_model_from_tempfile): @pytest.fixture -def cad_geometry(model, load_cad_geometry): +def parent_object(model, load_cad_geometry): with load_cad_geometry(model) as cad_geometry: yield cad_geometry @@ -41,6 +41,7 @@ class TestCADComponent(TreeObjectTesterReadOnly): COLLECTION_NAME = "cad_components" @pytest.fixture - def collection_test_data(self, model, cad_geometry): + def collection_test_data(self, model, parent_object): + cad_geometry = parent_object model.update() return cad_geometry.root_shapes, ["SOLID", "SHELL"], ["SOLID", "SHELL"] diff --git a/tests/unittests/test_cad_geometry.py b/tests/unittests/test_cad_geometry.py index 49ada9796c..271c1dd8bf 100644 --- a/tests/unittests/test_cad_geometry.py +++ b/tests/unittests/test_cad_geometry.py @@ -76,19 +76,22 @@ def object_properties(parent_object): ) @staticmethod - def test_refresh(load_cad_geometry, load_model_from_tempfile): + def test_refresh(load_model_from_tempfile, model_data_dir): """Test refreshing the geometry. Only tests that the call does not raise an exception. """ - with load_model_from_tempfile() as model, load_cad_geometry(model=model) as cad_geometry: - cad_geometry.refresh() + with load_model_from_tempfile() as model: + cad_geometry = model.create_cad_geometry() + local_cad_path = model_data_dir / "square_and_solid.stp" + assert local_cad_path.exists() + cad_geometry.refresh(local_cad_path) @staticmethod def test_refresh_inexistent_raises(tree_object): """Test refreshing the geometry from an inexistent file.""" - with pytest.raises(RuntimeError, match="source file `[^`]*` does not exist"): - tree_object.refresh() + with pytest.raises((FileNotFoundError, RuntimeError)): + tree_object.refresh("inexistent_file") @staticmethod def test_accessing_root_shapes(load_cad_geometry, load_model_from_tempfile): diff --git a/tests/unittests/test_cut_off_geometry.py b/tests/unittests/test_cut_off_geometry.py new file mode 100644 index 0000000000..3da4df9525 --- /dev/null +++ b/tests/unittests/test_cut_off_geometry.py @@ -0,0 +1,85 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import CutOffGeometry, CutOffGeometryOrientationType + +from .common.tree_object_tester import ObjectPropertiesToTest, TreeObjectTester, WithLockedMixin + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(CutOffGeometry._SUPPORTED_SINCE): + pytest.skip("CutOffGeometry is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture(params=["create_solid_model", "create_imported_solid_model"]) +def parent_object(model, request): + return getattr(model, request.param)() + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_cut_off_geometry() + + +class TestCutOffGeometry(WithLockedMixin, TreeObjectTester): + COLLECTION_NAME = "cut_off_geometries" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "cad_geometry": None, + "orientation_type": CutOffGeometryOrientationType.UP, + "relative_merge_tolerance": 0.1, + } + + CREATE_METHOD_NAME = "create_cut_off_geometry" + INITIAL_OBJECT_NAMES = tuple() + + @staticmethod + @pytest.fixture + def object_properties(model): + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_cut_off_geometry"), + ("active", False), + ("cad_geometry", model.create_virtual_geometry()), + ("orientation_type", CutOffGeometryOrientationType.DOWN), + ("relative_merge_tolerance", 0.1257), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) diff --git a/tests/unittests/test_cutoff_selection_rule.py b/tests/unittests/test_cut_off_selection_rule.py similarity index 70% rename from tests/unittests/test_cutoff_selection_rule.py rename to tests/unittests/test_cut_off_selection_rule.py index 79ccdeeb5d..3d996270e2 100644 --- a/tests/unittests/test_cutoff_selection_rule.py +++ b/tests/unittests/test_cut_off_selection_rule.py @@ -22,12 +22,8 @@ import pytest -from ansys.acp.core import ( - CutoffRuleType, - CutoffSelectionRuleElementalData, - CutoffSelectionRuleNodalData, - PlyCutoffType, -) +from ansys.acp.core import CutOffRuleType, PlyCutOffType +from ansys.acp.core.mesh_data import CutOffSelectionRuleElementalData, CutOffSelectionRuleNodalData from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -40,27 +36,27 @@ def parent_object(load_model_from_tempfile): @pytest.fixture def tree_object(parent_object): - return parent_object.create_cutoff_selection_rule() + return parent_object.create_cut_off_selection_rule() -class TestCutoffSelectionRule(NoLockedMixin, TreeObjectTester): - COLLECTION_NAME = "cutoff_selection_rules" +class TestCutOffSelectionRule(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "cut_off_selection_rules" @staticmethod @pytest.fixture def default_properties(): return { "status": "NOTUPTODATE", - "cutoff_rule_type": CutoffRuleType.GEOMETRY, - "cutoff_geometry": None, + "cut_off_rule_type": CutOffRuleType.GEOMETRY, + "cut_off_geometry": None, "taper_edge_set": None, "offset": 0.0, "angle": 0.0, - "ply_cutoff_type": PlyCutoffType.PRODUCTION_PLY_CUTOFF, + "ply_cut_off_type": PlyCutOffType.PRODUCTION_PLY_CUTOFF, "ply_tapering": False, } - CREATE_METHOD_NAME = "create_cutoff_selection_rule" + CREATE_METHOD_NAME = "create_cut_off_selection_rule" @staticmethod @pytest.fixture @@ -70,13 +66,13 @@ def object_properties(parent_object): edge_set = model.create_edge_set() return ObjectPropertiesToTest( read_write=[ - ("name", "Cutoff Selection Rule name"), - ("cutoff_rule_type", CutoffRuleType.TAPER), - ("cutoff_geometry", geometry), + ("name", "CutOff Selection Rule name"), + ("cut_off_rule_type", CutOffRuleType.TAPER), + ("cut_off_geometry", geometry), ("taper_edge_set", edge_set), ("offset", 1.2), ("angle", 2.3), - ("ply_cutoff_type", PlyCutoffType.ANALYSIS_PLY_CUTOFF), + ("ply_cut_off_type", PlyCutOffType.ANALYSIS_PLY_CUTOFF), ("ply_tapering", True), ], read_only=[ @@ -87,6 +83,6 @@ def object_properties(parent_object): def test_mesh_data(parent_object): - rule = parent_object.create_cutoff_selection_rule() - assert isinstance(rule.elemental_data, CutoffSelectionRuleElementalData) - assert isinstance(rule.nodal_data, CutoffSelectionRuleNodalData) + rule = parent_object.create_cut_off_selection_rule() + assert isinstance(rule.elemental_data, CutOffSelectionRuleElementalData) + assert isinstance(rule.nodal_data, CutOffSelectionRuleNodalData) diff --git a/tests/unittests/test_cylindrical_selection_rule.py b/tests/unittests/test_cylindrical_selection_rule.py index 7674d32c0a..867a2a776a 100644 --- a/tests/unittests/test_cylindrical_selection_rule.py +++ b/tests/unittests/test_cylindrical_selection_rule.py @@ -22,7 +22,10 @@ import pytest -from ansys.acp.core import CylindricalSelectionRuleElementalData, CylindricalSelectionRuleNodalData +from ansys.acp.core.mesh_data import ( + CylindricalSelectionRuleElementalData, + CylindricalSelectionRuleNodalData, +) from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -51,8 +54,8 @@ def default_properties(): "origin": (0.0, 0.0, 0.0), "direction": (0.0, 0.0, 1.0), "radius": 0.0, - "relative_rule_type": False, - "include_rule_type": True, + "relative_rule": False, + "include_rule": True, } CREATE_METHOD_NAME = "create_cylindrical_selection_rule" @@ -70,8 +73,8 @@ def object_properties(parent_object): ("origin", (1.0, 2.0, 3.0)), ("direction", (4.0, 5.0, 6.0)), ("radius", 7.0), - ("relative_rule_type", True), - ("include_rule_type", False), + ("relative_rule", True), + ("include_rule", False), ], read_only=[ ("id", "some_id"), diff --git a/tests/unittests/test_direct_launcher.py b/tests/unittests/test_direct_launcher.py new file mode 100644 index 0000000000..ee10c1c4f7 --- /dev/null +++ b/tests/unittests/test_direct_launcher.py @@ -0,0 +1,50 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pytest + +import ansys.acp.core as pyacp +from ansys.acp.core._server.direct import DirectLauncher + + +def test_inexistent_binary_error(): + """Check that the right error is raised when the binary does not exist.""" + with pytest.raises(FileNotFoundError) as exc: + pyacp.launch_acp( + launch_mode=pyacp.LaunchMode.DIRECT, + config=pyacp.DirectLaunchConfig(binary_path="inexistent_path"), + ) + assert "Binary not found" in str(exc.value) + + +def test_stop_after_failed_start(): + """Check that the .stop() method works after a failed start. + + This test is necessary because the '.stop()' method is called on teardown + even if the '.start()' method fails. + In the 'test_inexistent_binary_error' test, errors in '.stop()' are not + captured because they happen after the test ends. + """ + launcher = DirectLauncher(config=pyacp.DirectLaunchConfig(binary_path="inexistent_path")) + with pytest.raises(FileNotFoundError): + launcher.start() + launcher.stop() diff --git a/tests/unittests/test_edge_property_list.py b/tests/unittests/test_edge_property_list.py index 40cca5a226..3ca4919516 100644 --- a/tests/unittests/test_edge_property_list.py +++ b/tests/unittests/test_edge_property_list.py @@ -28,7 +28,7 @@ import pytest from ansys.acp.core import Fabric, Lamina, SubLaminate -from ansys.acp.core._typing_helper import PATH +from ansys.acp.core._utils.typing_helper import PATH @pytest.fixture @@ -79,10 +79,7 @@ def test_save_load_with_existing_entries( # WHEN: the model is saved and loaded with tempfile.TemporaryDirectory() as tmp_dir: - if not acp_instance.is_remote: - file_path: PATH = pathlib.Path(tmp_dir) / "model.acph5" - else: - file_path = "model.acph5" + file_path: PATH = pathlib.Path(tmp_dir) / "model.acph5" model.save(file_path) acp_instance.clear() model = acp_instance.import_model(path=file_path) diff --git a/tests/unittests/test_edge_property_types.py b/tests/unittests/test_edge_property_types.py new file mode 100644 index 0000000000..bd7052df14 --- /dev/null +++ b/tests/unittests/test_edge_property_types.py @@ -0,0 +1,83 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from pytest_cases import parametrize_with_cases + +from ansys.acp.core import ( + BooleanOperationType, + FabricWithAngle, + Lamina, + LinkedSelectionRule, + SubShape, + TaperEdge, +) + + +def case_fabric_with_angle(load_model_from_tempfile): + with load_model_from_tempfile() as model: + fabric = model.create_fabric() + yield FabricWithAngle(fabric=fabric, angle=12.3), ("fabric", "angle") + + +def case_linked_selection_rule(load_model_from_tempfile): + with load_model_from_tempfile() as model: + selection_rule = model.create_parallel_selection_rule() + yield LinkedSelectionRule( + selection_rule=selection_rule, + operation_type=BooleanOperationType.ADD, + template_rule=False, + parameter_1=1.0, + parameter_2=2.0, + ), ("selection_rule", "operation_type", "template_rule", "parameter_1", "parameter_2") + + +def case_taper_edge(load_model_from_tempfile): + with load_model_from_tempfile() as model: + edge_set = model.create_edge_set() + yield TaperEdge(edge_set=edge_set, angle=11.2, offset=0.6), ("edge_set", "angle", "offset") + + +def case_subshape(load_model_from_tempfile): + with load_model_from_tempfile() as model: + cad_geometry = model.create_cad_geometry() + yield SubShape(cad_geometry=cad_geometry, path="path/to/subshape"), ("cad_geometry", "path") + + +def case_lamina_fabric(load_model_from_tempfile): + with load_model_from_tempfile() as model: + material = model.create_fabric() + yield Lamina(material=material, angle=7.5), ("material", "angle") + + +def case_lamina_stackup(load_model_from_tempfile): + with load_model_from_tempfile() as model: + material = model.create_stackup() + yield Lamina(material=material, angle=7.5), ("material", "angle") + + +@parametrize_with_cases("edge_property_type_instance,attribute_names", cases=".", glob="*") +def test_clone(edge_property_type_instance, attribute_names): + cloned_instance = edge_property_type_instance.clone() + for attr_name in attribute_names: + assert getattr(cloned_instance, attr_name) == getattr( + edge_property_type_instance, attr_name + ) diff --git a/tests/unittests/test_element_set.py b/tests/unittests/test_element_set.py index b016caba55..4ce5308a63 100644 --- a/tests/unittests/test_element_set.py +++ b/tests/unittests/test_element_set.py @@ -66,3 +66,21 @@ def default_properties(): CREATE_METHOD_NAME = "create_element_set" INITIAL_OBJECT_NAMES = ("All_Elements",) + + +def test_clone_locked(parent_object, skip_before_version): + """Test that a locked element set can be correctly cloned. + + Regression test for #565: cloning and storing a locked element + set produces an empty element set. + The root cause for this issue was that the locked element set + did not expose their 'element_labels' in the API.ga + """ + skip_before_version("25.1") + + element_set = parent_object.element_sets["All_Elements"] + assert len(element_set.element_labels) > 0 + cloned_element_set = element_set.clone() + cloned_element_set.store(parent=parent_object) + assert not cloned_element_set.locked + assert len(cloned_element_set.element_labels) > 0 diff --git a/tests/unittests/test_extrusion_guide.py b/tests/unittests/test_extrusion_guide.py new file mode 100644 index 0000000000..9bad92e411 --- /dev/null +++ b/tests/unittests/test_extrusion_guide.py @@ -0,0 +1,151 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +import numpy.testing +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ExtrusionGuide, ExtrusionGuideType + +from .common.tree_object_tester import ObjectPropertiesToTest, TreeObjectTester, WithLockedMixin + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(ExtrusionGuide._SUPPORTED_SINCE): + pytest.skip("ExtrusionGuide is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def parent_object(model): + return model.create_solid_model() + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_extrusion_guide() + + +class TestExtrusionGuide(WithLockedMixin, TreeObjectTester): + COLLECTION_NAME = "extrusion_guides" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "edge_set": None, + "cad_geometry": None, + "direction": (0.0, 0.0, 1.0), + "radius": 0.0, + "depth": 0.0, + "use_curvature_correction": False, + "extrusion_guide_type": "by_direction", + } + + CREATE_METHOD_NAME = "create_extrusion_guide" + INITIAL_OBJECT_NAMES = tuple() + + @staticmethod + @pytest.fixture + def object_properties(model): + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_extrusion_guide"), + ("active", False), + ("edge_set", model.create_edge_set()), + ("extrusion_guide_type", ExtrusionGuideType.BY_DIRECTION), + ("cad_geometry", None), + ("direction", (2.0, 3.0, 4.0)), + ("radius", 1.5), + ("depth", 2.0), + ("use_curvature_correction", True), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) + + +def test_handling_of_extrusion_guide_type(model, parent_object, skip_before_version): + """Verify the handling of extrusion_guide_type. + + The backend determines the extrusion guide type based on the direction. + extrusion_guide_type = BY_DIRECTION if direction != 0., else + extrusion_guide_type = BY_GEOMETRY. + + In addition, the virtual geometry is None if + extrusion_guide_type = BY_DIRECTION. + """ + skip_before_version("25.1") + + virtual_cad = model.create_virtual_geometry(name="dummy") + + """ Case extrusion guide type = BY_DIRECTION """ + ex_by_direction = parent_object.create_extrusion_guide( + name="ExtrusionGuide", + direction=(0.0, 1.0, 1.0), + extrusion_guide_type=ExtrusionGuideType.BY_DIRECTION, + ) + assert ex_by_direction.extrusion_guide_type == ExtrusionGuideType.BY_DIRECTION + assert ex_by_direction.cad_geometry is None + numpy.testing.assert_allclose(ex_by_direction.direction, (0.0, 1.0, 1.0)) + # check that the user can modify the direction as long as the extrusion guide type is BY_DIRECTION + ex_by_direction.direction = (2.3, 2.0, 1.0) + # check that the user can change the extrusion guide type to BY_GEOMETRY + ex_by_direction.extrusion_guide_type = ExtrusionGuideType.BY_GEOMETRY + ex_by_direction.cad_geometry = virtual_cad + + """ Case extrusion guide type = BY_GEOMETRY """ + ex_by_geometry = parent_object.create_extrusion_guide( + name="ExtrusionGuide", + direction=(0.0, 0.0, 0.0), + extrusion_guide_type=ExtrusionGuideType.BY_GEOMETRY, + cad_geometry=virtual_cad, + ) + assert ex_by_geometry.extrusion_guide_type == ExtrusionGuideType.BY_GEOMETRY + assert ex_by_geometry.cad_geometry is not None and ex_by_geometry.cad_geometry.name == "dummy" + with pytest.raises(RuntimeError) as exc: + direction = ex_by_geometry.direction + assert "Cannot access direction if the extrusion guide type is not 'by_direction'!" in str( + exc.value + ) + + with pytest.raises(RuntimeError) as exc: + ex_by_geometry.direction = (0.0, 1.0, 1.0) + assert "Cannot set direction if extrusion guide type is not 'by_direction'!" in str(exc.value) + """ Case invalid initialization """ + with pytest.raises(RuntimeError) as exc: + ex_by_geometry = parent_object.create_extrusion_guide( + name="ExtrusionGuide", + direction=(0.0, 2.0, 1.0), + extrusion_guide_type=ExtrusionGuideType.BY_GEOMETRY, + cad_geometry=virtual_cad, + ) + assert "Cannot set direction if extrusion guide type is not 'by_direction'!" in str(exc.value) diff --git a/tests/unittests/test_fabric.py b/tests/unittests/test_fabric.py index d3e9d6ffe7..b4b8fa198a 100644 --- a/tests/unittests/test_fabric.py +++ b/tests/unittests/test_fabric.py @@ -20,9 +20,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from packaging.version import parse as parse_version import pytest -from ansys.acp.core import CutoffMaterialType, DrapingMaterialType, DropoffMaterialType +from ansys.acp.core import CutOffMaterialHandling, DrapingMaterialModel, DropOffMaterialHandling from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -43,44 +44,101 @@ class TestFabric(NoLockedMixin, TreeObjectTester): @staticmethod @pytest.fixture - def default_properties(): - return { - "status": "NOTUPTODATE", - "thickness": 0.0, - "area_price": 0.0, - "ignore_for_postprocessing": False, - "drop_off_material_handling": DropoffMaterialType.GLOBAL, - "cut_off_material_handling": CutoffMaterialType.COMPUTED, - "draping_material_model": DrapingMaterialType.WOVEN, - "draping_ud_coefficient": 0.0, - "material": None, - } + def default_properties(acp_instance): + if parse_version(acp_instance.server_version) < parse_version("25.1"): + return { + "status": "NOTUPTODATE", + "thickness": 0.0, + "area_price": 0.0, + "ignore_for_postprocessing": False, + "drop_off_material_handling": DropOffMaterialHandling.GLOBAL, + "cut_off_material_handling": CutOffMaterialHandling.COMPUTED, + "draping_material_model": DrapingMaterialModel.WOVEN, + "draping_ud_coefficient": 0.0, + "material": None, + } + else: + return { + "status": "NOTUPTODATE", + "thickness": 0.0, + "area_price": 0.0, + "ignore_for_postprocessing": False, + "drop_off_material_handling": DropOffMaterialHandling.GLOBAL, + "drop_off_material": None, + "cut_off_material_handling": CutOffMaterialHandling.COMPUTED, + "cut_off_material": None, + "draping_material_model": DrapingMaterialModel.WOVEN, + "draping_ud_coefficient": 0.0, + "material": None, + } CREATE_METHOD_NAME = "create_fabric" @staticmethod @pytest.fixture - def object_properties(parent_object): + def object_properties(parent_object, acp_instance): model = parent_object - material = model.create_material() - return ObjectPropertiesToTest( - read_write=[ - ("name", "Fabric name"), - ("thickness", 1e-6), - ("area_price", 5.98), - ("ignore_for_postprocessing", True), - ("drop_off_material_handling", DropoffMaterialType.CUSTOM), - ("cut_off_material_handling", CutoffMaterialType.GLOBAL), - ("draping_material_model", DrapingMaterialType.UD), - ("draping_ud_coefficient", 0.55), - ("material", material), - ("material", None), - ("material", material), - ], - read_only=[ - ("id", "some_id"), - ("status", "UPTODATE"), - ("area_weight", 0.0), - # ("draping_ud_coefficient", 4.32), # TODO: enable this check, see backend issue #778698 - ], - ) + material = model.create_material(name="Material") + cut_off_material = model.create_material(name="Cut-off Material") + drop_off_material = model.create_material(name="Drop-off Material") + if parse_version(acp_instance.server_version) < parse_version("25.1"): + return ObjectPropertiesToTest( + read_write=[ + ("name", "Fabric name"), + ("thickness", 1e-6), + ("area_price", 5.98), + ("ignore_for_postprocessing", True), + ("drop_off_material_handling", DropOffMaterialHandling.GLOBAL), + ("cut_off_material_handling", CutOffMaterialHandling.COMPUTED), + ("draping_material_model", DrapingMaterialModel.UD), + ("draping_ud_coefficient", 0.55), + ("material", material), + ("material", None), + ("material", material), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ("area_weight", 0.0), + # ("draping_ud_coefficient", 4.32), # TODO: enable this check, see backend issue #778698 + ], + ) + else: + return ObjectPropertiesToTest( + read_write=[ + ("name", "Fabric name"), + ("thickness", 1e-6), + ("area_price", 5.98), + ("ignore_for_postprocessing", True), + ("drop_off_material_handling", DropOffMaterialHandling.CUSTOM), + ("drop_off_material", drop_off_material), + ("cut_off_material_handling", CutOffMaterialHandling.CUSTOM), + ("cut_off_material", cut_off_material), + ("draping_material_model", DrapingMaterialModel.UD), + ("draping_ud_coefficient", 0.55), + ("material", material), + ("material", None), + ("material", material), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ("area_weight", 0.0), + ], + ) + + +@pytest.mark.parametrize("material_type", ["cut_off_material", "drop_off_material"]) +def test_solid_model_materials(parent_object, tree_object, acp_instance, material_type): + """Check that solid model materials are supported since 25.1.""" + tree_object.cut_off_material_handling = CutOffMaterialHandling.CUSTOM + tree_object.drop_off_material_handling = DropOffMaterialHandling.CUSTOM + if parse_version(acp_instance.server_version) < parse_version("25.1"): + with pytest.raises(RuntimeError) as exc: + setattr(tree_object, material_type, parent_object.create_material(name="Material")) + assert f"The property '{material_type}' is only editable since version" in str(exc.value) + else: + material = parent_object.create_material(name="new material") + setattr(tree_object, material_type, material) + sm_material = getattr(tree_object, material_type) + assert sm_material.id == material.id diff --git a/tests/unittests/test_field_definition.py b/tests/unittests/test_field_definition.py new file mode 100644 index 0000000000..1dd1dd8be2 --- /dev/null +++ b/tests/unittests/test_field_definition.py @@ -0,0 +1,115 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import FieldDefinition + +from .common.linked_object_list_tester import LinkedObjectListTestCase, LinkedObjectListTester +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(FieldDefinition._SUPPORTED_SINCE): + pytest.skip("FieldDefinition is not supported on this version of the server.") + + +@pytest.fixture +def parent_object(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_field_definition() + + +class TestFieldDefinition(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "field_definitions" + + @staticmethod + @pytest.fixture + def default_properties(acp_instance): + return { + "status": "NOTUPTODATE", + "active": True, + "scalar_field": None, + "scope_entities": tuple(), + "full_mapping": False, + } + + CREATE_METHOD_NAME = "create_field_definition" + + @staticmethod + @pytest.fixture + def object_properties(parent_object, acp_instance): + model = parent_object + lut_3D = model.create_lookup_table_3d(name="LUT 3D") + lut_col = lut_3D.create_column(name="Column 1") + el_set = model.create_element_set(name="my element set") + mg = model.create_modeling_group(name="my modeling group") + modeling_ply = mg.create_modeling_ply(name="my Modeling Ply") + oss = model.create_oriented_selection_set(name="my OSS") + + return ObjectPropertiesToTest( + read_write=[ + ("name", "FD name"), + ("active", False), + ("scalar_field", lut_col), + ("scope_entities", [el_set, modeling_ply, oss]), + ("full_mapping", True), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) + + +@pytest.fixture +def linked_object_case(tree_object, parent_object): + return LinkedObjectListTestCase( + parent_object=tree_object, + linked_attribute_name="scope_entities", + existing_linked_object_names=(), + linked_object_constructor=parent_object.create_element_set, + ) + + +linked_object_case_empty = linked_object_case + + +@pytest.fixture +def linked_object_case_nonempty(tree_object, parent_object): + tree_object.scope_entities = [parent_object.create_oriented_selection_set(name="OSS.1")] + return LinkedObjectListTestCase( + parent_object=tree_object, + linked_attribute_name="scope_entities", + existing_linked_object_names=("OSS.1",), + linked_object_constructor=parent_object.create_oriented_selection_set, + ) + + +class TestLinkedObjectLists(LinkedObjectListTester): + pass diff --git a/tests/unittests/test_geometrical_selection_rule.py b/tests/unittests/test_geometrical_selection_rule.py index 77ffbd85d0..1759b5a02c 100644 --- a/tests/unittests/test_geometrical_selection_rule.py +++ b/tests/unittests/test_geometrical_selection_rule.py @@ -22,8 +22,8 @@ import pytest -from ansys.acp.core import ( - GeometricalRuleType, +from ansys.acp.core import GeometricalRuleType +from ansys.acp.core.mesh_data import ( GeometricalSelectionRuleElementalData, GeometricalSelectionRuleNodalData, ) @@ -53,7 +53,7 @@ def default_properties(): "geometrical_rule_type": GeometricalRuleType.GEOMETRY, "geometry": None, "element_sets": [], - "include_rule_type": True, + "include_rule": True, "use_default_tolerances": True, "in_plane_capture_tolerance": 0.0, "negative_capture_tolerance": 0.0, @@ -74,7 +74,7 @@ def object_properties(parent_object): ("geometrical_rule_type", GeometricalRuleType.ELEMENT_SETS), ("geometry", geometry), ("element_sets", element_sets), - ("include_rule_type", False), + ("include_rule", False), ("use_default_tolerances", False), ("in_plane_capture_tolerance", 1.2), ("negative_capture_tolerance", 2.3), diff --git a/tests/unittests/test_imported_analysis_ply.py b/tests/unittests/test_imported_analysis_ply.py new file mode 100644 index 0000000000..5ef6a95cad --- /dev/null +++ b/tests/unittests/test_imported_analysis_ply.py @@ -0,0 +1,132 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ImportedAnalysisPly, Model + +from .common.tree_object_tester import TreeObjectTesterReadOnly + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + ImportedAnalysisPly._SUPPORTED_SINCE + ): + pytest.skip("ImportedAnalysisPly is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_imported_plies_from_tempfile): + with load_model_imported_plies_from_tempfile() as model: + yield model + + +def get_hdf5_imported_modeling_group(parent_model: Model): + return parent_model.imported_modeling_groups["by hdf5"] + + +def all_imported_analysis_plies(model: Model): + modeling_group = get_hdf5_imported_modeling_group(model) + imported_analysis_plies = [] + for imp in modeling_group.imported_modeling_plies.values(): + for ipp in imp.imported_production_plies.values(): + for iap in ipp.imported_analysis_plies.values(): + imported_analysis_plies.append(iap) + + return imported_analysis_plies + + +class TestImportedAnalysisPly(TreeObjectTesterReadOnly): + COLLECTION_NAME = "imported_analysis_plies" + + @staticmethod + @pytest.fixture + def parent_object(model: Model): + img = get_hdf5_imported_modeling_group(model) + return img.imported_modeling_plies["ud"].imported_production_plies["ImportedProductionPly"] + + @pytest.fixture + def collection_test_data(self, parent_object): + imported_production_ply = parent_object + object_collection = getattr(imported_production_ply, self.COLLECTION_NAME) + object_collection.values() + object_names = ["P1L1__ud"] + object_ids = ["P1L1__ud"] + + return object_collection, object_names, object_ids + + @pytest.fixture + def properties(self, model): + carbon_ud = model.materials["Epoxy Carbon UD (230 GPa) Prepreg"] + woven = model.materials["Epoxy Carbon Woven (230 GPa) Prepreg"] + return { + "P1L1__ud": { + "status": "UPTODATE", + "material": carbon_ud, + "angle": 45.0, + "thickness": 0.0015, + }, + "P1L1__woven": { + "status": "UPTODATE", + "material": woven, + "angle": 30.0, + "thickness": 0.001, + }, + "P1L1__ud 2": { + "status": "UPTODATE", + "material": carbon_ud, + "angle": 45.0, + "thickness": 0.0015, + }, + } + + def test_properties(self, model: Model, properties): + for ply in all_imported_analysis_plies(model): + ref_values = properties[ply.id] + for prop, value in ref_values.items(): + assert getattr(ply, prop) == value + + def test_after_update(self, model): + # Test that list of analysis plies stays up-to-date + # after update that removes analysis plies. + # Check that requesting properties on a removed analysis + # ply throws the expected error + + initial_imported_analysis_plies = all_imported_analysis_plies(model) + + modeling_group = get_hdf5_imported_modeling_group(model) + for imp in modeling_group.imported_modeling_plies.values(): + imp.active = False + model.update() + assert len(all_imported_analysis_plies(model)) == 0 + + for iap in initial_imported_analysis_plies: + with pytest.raises(LookupError, match="Entity not found") as ex: + _ = iap.status + + for imp in modeling_group.imported_modeling_plies.values(): + imp.active = True + + model.update() + assert len(all_imported_analysis_plies(model)) == 3 diff --git a/tests/unittests/test_imported_modeling_group.py b/tests/unittests/test_imported_modeling_group.py new file mode 100644 index 0000000000..b4cb994309 --- /dev/null +++ b/tests/unittests/test_imported_modeling_group.py @@ -0,0 +1,71 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ImportedModelingGroup + +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + ImportedModelingGroup._SUPPORTED_SINCE + ): + pytest.skip("ImportedModelingGroup is not supported on this version of the server.") + + +@pytest.fixture +def parent_object(load_model_imported_plies_from_tempfile): + with load_model_imported_plies_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_imported_modeling_group() + + +class TestImportedModelingGroup(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "imported_modeling_groups" + + @staticmethod + @pytest.fixture + def default_properties(): + return {} + + CREATE_METHOD_NAME = "create_imported_modeling_group" + + @staticmethod + @pytest.fixture + def object_properties(parent_object): + model = parent_object + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_name"), + ], + read_only=[ + ("id", "some_id"), + ], + ) diff --git a/tests/unittests/test_imported_modeling_ply.py b/tests/unittests/test_imported_modeling_ply.py new file mode 100644 index 0000000000..1ec31cf894 --- /dev/null +++ b/tests/unittests/test_imported_modeling_ply.py @@ -0,0 +1,157 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ( + ImportedModelingPly, + ImportedPlyDrapingType, + ImportedPlyOffsetType, + ImportedPlyThicknessType, + MeshImportType, + RosetteSelectionMethod, + ThicknessFieldType, +) + +from .common.linked_object_list_tester import LinkedObjectListTestCase, LinkedObjectListTester +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + ImportedModelingPly._SUPPORTED_SINCE + ): + pytest.skip("ImportedModelingPly is not supported on this version of the server.") + + +@pytest.fixture +def minimal_complete_model(load_model_imported_plies_from_tempfile): + with load_model_imported_plies_from_tempfile() as model: + yield model + + +@pytest.fixture +def parent_object(minimal_complete_model): + return minimal_complete_model.imported_modeling_groups["by hdf5"] + + +@pytest.fixture +def existing_imported_modeling_ply(minimal_complete_model): + return minimal_complete_model.imported_modeling_groups["by hdf5"].imported_modeling_plies["ud"] + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_imported_modeling_ply() + + +class TestImportedModelingPly(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "imported_modeling_plies" + CREATE_METHOD_NAME = "create_imported_modeling_ply" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "offset_type": ImportedPlyOffsetType.MIDDLE_OFFSET, + "mesh_import_type": MeshImportType.FROM_GEOMETRY, + "rosette_selection_method": "minimum_angle", + "rosettes": [], + "reference_direction_field": None, + "rotation_angle": 0.0, + "ply_material": None, + "ply_angle": 0.0, + "draping_type": ImportedPlyDrapingType.NO_DRAPING, + "draping_angle_1_field": None, + "draping_angle_2_field": None, + "thickness_type": ImportedPlyThicknessType.NOMINAL, + "thickness_field": None, + "thickness_field_type": ThicknessFieldType.ABSOLUTE_VALUES, + } + + @staticmethod + @pytest.fixture(params=[v.value for v in ImportedPlyOffsetType]) + def object_properties(request, minimal_complete_model): + ply_material = minimal_complete_model.create_fabric() + create_lut_method = getattr(minimal_complete_model, "create_lookup_table_1d") + lookup_table = create_lut_method() + column_1 = lookup_table.create_column() + column_2 = lookup_table.create_column() + rosette = minimal_complete_model.create_rosette() + virtual_geometry = minimal_complete_model.create_virtual_geometry() + + return ObjectPropertiesToTest( + read_write=[ + ("active", False), + ("offset_type", request.param), + ("mesh_import_type", MeshImportType.FROM_GEOMETRY), + ("mesh_geometry", virtual_geometry), + ("rosette_selection_method", RosetteSelectionMethod.MINIMUM_DISTANCE), + ("rosettes", [rosette]), + ("reference_direction_field", column_1), + ("rotation_angle", 67.2), + ("ply_material", ply_material), + ("ply_angle", 34.5), + ("draping_type", ImportedPlyDrapingType.TABULAR_VALUES), + ("draping_angle_1_field", column_1), + ("draping_angle_2_field", column_2), + ("thickness_type", ImportedPlyThicknessType.FROM_TABLE), + ("thickness_field", column_1), + ("thickness_field_type", ThicknessFieldType.RELATIVE_SCALING_FACTOR), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) + + +@pytest.fixture +def linked_object_case(tree_object, minimal_complete_model): + return LinkedObjectListTestCase( + parent_object=tree_object, + linked_attribute_name="rosettes", + existing_linked_object_names=(), + linked_object_constructor=minimal_complete_model.create_rosette, + ) + + +linked_object_case_empty = linked_object_case + + +@pytest.fixture +def linked_object_case_nonempty(tree_object, minimal_complete_model): + tree_object.rosettes = [minimal_complete_model.create_rosette(name="rosette 32")] + return LinkedObjectListTestCase( + parent_object=tree_object, + linked_attribute_name="rosettes", + existing_linked_object_names=("rosette 32",), + linked_object_constructor=minimal_complete_model.create_rosette, + ) + + +class TestLinkedObjectLists(LinkedObjectListTester): + pass diff --git a/tests/unittests/test_imported_production_ply.py b/tests/unittests/test_imported_production_ply.py new file mode 100644 index 0000000000..246aae3628 --- /dev/null +++ b/tests/unittests/test_imported_production_ply.py @@ -0,0 +1,138 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ImportedProductionPly, Model + +from .common.tree_object_tester import TreeObjectTesterReadOnly + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + ImportedProductionPly._SUPPORTED_SINCE + ): + pytest.skip("ImportedProductionPly is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_imported_plies_from_tempfile): + with load_model_imported_plies_from_tempfile() as model: + yield model + + +def get_hdf5_imported_modeling_group(parent_model: Model): + return parent_model.imported_modeling_groups["by hdf5"] + + +def all_imported_production_plies(model: Model): + modeling_group = get_hdf5_imported_modeling_group(model) + imported_production_plies = [] + for imp in modeling_group.imported_modeling_plies.values(): + for ipp in imp.imported_production_plies.values(): + imported_production_plies.append(ipp) + + return imported_production_plies + + +class TestImportedProductionPly(TreeObjectTesterReadOnly): + COLLECTION_NAME = "imported_production_plies" + + @staticmethod + @pytest.fixture + def parent_object(model: Model): + img = get_hdf5_imported_modeling_group(model) + return img.imported_modeling_plies["ud"] + + @pytest.fixture + def collection_test_data(self, parent_object): + imported_modeling_ply = parent_object + object_collection = getattr(imported_modeling_ply, self.COLLECTION_NAME) + object_collection.values() + object_names = ["P1__ud"] + object_ids = ["ImportedProductionPly"] + + return object_collection, object_names, object_ids + + @pytest.fixture + def properties(self, model): + fabric_ud = model.fabrics["ud_1.5mm"] + fabric_woven = model.fabrics["woven_1mm"] + return { + "ImportedProductionPly": { + "name": "P1__ud", + "status": "UPTODATE", + "material": fabric_ud, + "angle": 45.0, + "thickness": 0.0015, + }, + "ImportedProductionPly.2": { + "name": "P1__woven", + "status": "UPTODATE", + "material": fabric_woven, + "angle": 30.0, + "thickness": 0.001, + }, + "ImportedProductionPly.3": { + "name": "P1__ud 2", + "status": "UPTODATE", + "material": fabric_ud, + "angle": 45.0, + "thickness": 0.0015, + }, + } + + def test_properties(self, model: Model, properties): + for ply in all_imported_production_plies(model): + ref_values = properties[ply.id] + for prop, value in ref_values.items(): + assert getattr(ply, prop) == value + + def test_after_update(self, model): + # Test that list of analysis plies stays up-to-date + # after update that removes analysis plies. + # Check that requesting properties on a removed analysis + # ply throws the expected error + + initial_imported_production_plies = all_imported_production_plies(model) + + modeling_group = get_hdf5_imported_modeling_group(model) + for imp in modeling_group.imported_modeling_plies.values(): + imp.active = False + model.update() + assert len(all_imported_production_plies(model)) == 0 + + for iap in initial_imported_production_plies: + with pytest.raises(LookupError, match="Entity not found") as ex: + _ = iap.status + + for imp in modeling_group.imported_modeling_plies.values(): + imp.active = True + + model.update() + imported_production_plies = all_imported_production_plies(model) + assert len(imported_production_plies) == 3 + + for ipp in imported_production_plies: + assert len(ipp.imported_analysis_plies) == 1 diff --git a/tests/unittests/test_imported_solid_model.py b/tests/unittests/test_imported_solid_model.py new file mode 100644 index 0000000000..0080a57b9e --- /dev/null +++ b/tests/unittests/test_imported_solid_model.py @@ -0,0 +1,274 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +import pathlib +import tempfile + +from hypothesis import HealthCheck, assume, given, settings +import hypothesis.strategies as st +from packaging.version import parse as parse_version +import pytest + +import ansys.acp.core as pyacp + +from .common.tree_object_tester import ( + ObjectPropertiesToTest, + PropertyWithCustomComparison, + TreeObjectTester, + WithLockedMixin, +) + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + pyacp.ImportedSolidModel._SUPPORTED_SINCE + ): + pytest.skip("ImportedSolidModel is not supported on this version of the server.") + + +def compare_pb_object(given, expected): + if not isinstance(given, type(expected)): + return False + return given._pb_object == expected._pb_object + + +@pytest.fixture +def parent_object(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_imported_solid_model() + + +class TestImportedSolidModel(WithLockedMixin, TreeObjectTester): + COLLECTION_NAME = "imported_solid_models" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + } + + CREATE_METHOD_NAME = "create_imported_solid_model" + INITIAL_OBJECT_NAMES = tuple() + + @staticmethod + @pytest.fixture + def object_properties(parent_object): + model = parent_object + modeling_group = model.create_modeling_group() + + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_name"), + ("active", False), + ("format", pyacp.SolidModelImportFormat.ANSYS_H5), + ("unit_system", pyacp.UnitSystemType.SI), + ("external_path", "path/to/file"), + ("delete_bad_elements", False), + ("warping_limit", 0.6), + ("minimum_volume", 1.2), + ("cut_off_material", model.create_material()), + ( + "export_settings", + PropertyWithCustomComparison( + initial_value=pyacp.ImportedSolidModelExportSettings( + use_default_section_index=False, + section_index=2, + use_default_coordinate_system_index=False, + coordinate_system_index=3, + use_default_material_index=False, + material_index=4, + use_default_node_index=False, + node_index=5, + use_default_element_index=False, + element_index=6, + use_solsh_elements=True, + drop_hanging_nodes=False, + use_solid_model_prefix=False, + ), + comparison_function=compare_pb_object, + ), + ), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ("locked", True), + ], + ) + + +@pytest.fixture +def imported_solid_model_with_elements(acp_instance, parent_object): + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + with tempfile.TemporaryDirectory() as tmp_dir: + filename = pathlib.Path(tmp_dir) / "solid_model.h5" + solid_model.export(filename, format=pyacp.SolidModelExportFormat.ANSYS_H5) + assert filename.exists() + del model.solid_models[solid_model.id] + imported_solid_model = model.create_imported_solid_model( + external_path=acp_instance.upload_file(filename), + format=pyacp.SolidModelImportFormat.ANSYS_H5, + ) + imported_solid_model.create_layup_mapping_object( + shell_element_sets=[model.element_sets["All_Elements"]] + ) + model.update() + yield imported_solid_model + + +@pytest.mark.parametrize( + "format", + [ + "ansys:h5", + "ansys:cdb", + pyacp.SolidModelExportFormat.ANSYS_H5, + pyacp.SolidModelExportFormat.ANSYS_CDB, + ], +) +def test_export(imported_solid_model_with_elements, format): + """Check that the export to a file works.""" + with tempfile.TemporaryDirectory() as tmp_dir: + if format == "ansys:h5": + ext = ".h5" + else: + ext = ".cdb" + + out_path = pathlib.Path(tmp_dir) / f"out_file{ext}" + imported_solid_model_with_elements.export(path=out_path, format=format) + + assert out_path.exists() + assert out_path.stat().st_size > 0 + + +@given(invalid_format=st.text()) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_export_with_invalid_format_raises(imported_solid_model_with_elements, invalid_format): + """Check that the export to a file with an invalid format raises an exception.""" + assume(invalid_format not in ["ansys:h5", "ansys:cdb"]) + + with tempfile.TemporaryDirectory() as tmp_dir: + out_path = pathlib.Path(tmp_dir) / f"out_file.h5" + + with pytest.raises(ValueError): + imported_solid_model_with_elements.export(path=out_path, format=invalid_format) + + +@pytest.mark.parametrize("format", ["ansys:cdb", "step", "iges", "stl"]) +def test_skin_export(imported_solid_model_with_elements, format): + """Check that the skin export to a file works.""" + with tempfile.TemporaryDirectory() as tmp_dir: + ext = {"ansys:cdb": ".cdb", "step": ".stp", "iges": ".igs", "stl": ".stl"}[format] + + out_path = pathlib.Path(tmp_dir) / f"out_file{ext}" + + imported_solid_model_with_elements.export_skin(path=out_path, format=format) + + assert out_path.exists() + assert out_path.stat().st_size > 0 + + +@given(invalid_format=st.text()) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_skin_export_with_invalid_format_raises(imported_solid_model_with_elements, invalid_format): + """Check that the export to a file with an invalid format raises an exception.""" + assume(invalid_format not in ["ansys:cdb", "step", "iges", "stl"]) + + with tempfile.TemporaryDirectory() as tmp_dir: + out_path = pathlib.Path(tmp_dir) / f"out_file.h5" + + with pytest.raises(ValueError): + imported_solid_model_with_elements.export_skin(path=out_path, format=invalid_format) + + +def test_refresh(parent_object, acp_instance): + """Check that refreshing works. Does not check the result of the refresh.""" + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + with tempfile.TemporaryDirectory() as tmp_dir: + out_file_name = f"out_file.h5" + out_path = pathlib.Path(tmp_dir) / out_file_name + solid_model.export(path=out_path, format=pyacp.SolidModelExportFormat.ANSYS_H5) + + imported_solid_model = model.create_imported_solid_model( + external_path=acp_instance.upload_file(out_path), + format=pyacp.SolidModelImportFormat.ANSYS_H5, + ) + model.update() + imported_solid_model.refresh(out_path) + model.update() + + +@given(external_path=st.text()) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_refresh_inexistent_path(parent_object, external_path): + """Check that refreshing with an inexistent path raises an exception.""" + assume(not pathlib.Path(external_path).exists()) + model = parent_object + imported_solid_model = model.create_imported_solid_model() + with pytest.raises((OSError, ValueError, RuntimeError)): + imported_solid_model.refresh(external_path) + + +def test_import_initial_mesh(acp_instance, parent_object): + """Check that the 'import_initial_mesh' method works. Does not check the result.""" + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + with tempfile.TemporaryDirectory() as tmp_dir: + out_path_h5 = pathlib.Path(tmp_dir) / f"out_file.h5" + solid_model.export(path=out_path_h5, format=pyacp.SolidModelExportFormat.ANSYS_H5) + out_path_cdb = pathlib.Path(tmp_dir) / f"out_file.cdb" + solid_model.export(path=out_path_cdb, format=pyacp.SolidModelExportFormat.ANSYS_CDB) + + imported_solid_model = model.create_imported_solid_model( + external_path=acp_instance.upload_file(out_path_h5), + format=pyacp.SolidModelImportFormat.ANSYS_H5, + ) + imported_solid_model.import_initial_mesh() + assert imported_solid_model.solid_mesh is not None + assert imported_solid_model.solid_mesh.element_labels == (3,) + + # refresh from external source with the same format + imported_solid_model.refresh(out_path_h5) + imported_solid_model.import_initial_mesh() + + # refresh from external source where the format is different + imported_solid_model.refresh(out_path_cdb, format=pyacp.SolidModelImportFormat.ANSYS_CDB) + imported_solid_model.import_initial_mesh() + assert imported_solid_model.solid_mesh is not None + assert imported_solid_model.solid_mesh.element_labels == (3,) diff --git a/tests/unittests/test_interface_layer.py b/tests/unittests/test_interface_layer.py new file mode 100644 index 0000000000..8dd618e993 --- /dev/null +++ b/tests/unittests/test_interface_layer.py @@ -0,0 +1,84 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import InterfaceLayer + +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(InterfaceLayer._SUPPORTED_SINCE): + pytest.skip("InterfaceLayer is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def parent_object(model): + return list(model.modeling_groups.values())[0] + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_interface_layer() + + +class TestInterfaceLayer(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "interface_layers" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "oriented_selection_sets": [], + "open_area_sets": [], + } + + CREATE_METHOD_NAME = "create_interface_layer" + + @staticmethod + @pytest.fixture + def object_properties(model): + oriented_selection_sets = [model.create_oriented_selection_set() for _ in range(3)] + open_area_sets = [model.create_oriented_selection_set(), model.create_element_set()] + return ObjectPropertiesToTest( + read_write=[ + ("name", "Interface layer name"), + ("oriented_selection_sets", oriented_selection_sets), + ("open_area_sets", open_area_sets), + ("active", False), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) diff --git a/tests/unittests/test_layup_mapping_object.py b/tests/unittests/test_layup_mapping_object.py new file mode 100644 index 0000000000..a9cd3d115f --- /dev/null +++ b/tests/unittests/test_layup_mapping_object.py @@ -0,0 +1,147 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ( + BaseElementMaterialHandling, + ElementTechnology, + LayupMappingObject, + LayupMappingRosetteSelectionMethod, + ReinforcingBehavior, + StressStateType, +) + +from .common.tree_object_tester import ObjectPropertiesToTest, TreeObjectTester, WithLockedMixin + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + LayupMappingObject._SUPPORTED_SINCE + ): + pytest.skip("LayupMappingObject is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def parent_object(model): + return model.create_imported_solid_model() + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_layup_mapping_object() + + +class TestLayupMappingObject(WithLockedMixin, TreeObjectTester): + COLLECTION_NAME = "layup_mapping_objects" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "element_technology": ElementTechnology.LAYERED_ELEMENT, + "shell_element_sets": tuple(), + "use_imported_plies": False, + "select_all_plies": True, + "sequences": tuple(), + "entire_solid_mesh": True, + "solid_element_sets": tuple(), + "scale_ply_thicknesses": True, + "void_material": None, + "minimum_void_material_thickness": 1e-6, + "delete_lost_elements": True, + "filler_material": None, + "rosettes": tuple(), + "rosette_selection_method": LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, + "reinforcing_behavior": ReinforcingBehavior.TENSION_AND_COMPRESSION, + "base_element_material_handling": BaseElementMaterialHandling.REMOVE, + "stress_state": StressStateType.PLANE_STRESS_STATE_WITH_TRANSVERSE_SHEAR_AND_BENDING_STIFFNESS, + "base_material": None, + "base_element_rosettes": tuple(), + "base_element_rosette_selection_method": LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE, + } + + CREATE_METHOD_NAME = "create_layup_mapping_object" + INITIAL_OBJECT_NAMES = tuple() + + @staticmethod + @pytest.fixture + def object_properties(model): + modeling_group = model.create_modeling_group() + imported_modeling_group = model.create_imported_modeling_group() + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_layup_mapping_object"), + ("active", False), + ("element_technology", ElementTechnology.REINFORCING), + ("shell_element_sets", [model.create_element_set() for _ in range(3)]), + ("select_all_plies", False), + ( + "sequences", + [modeling_group.create_modeling_ply(), model.create_modeling_group()], + ), + ("use_imported_plies", True), + ( + "sequences", + [ + imported_modeling_group.create_imported_modeling_ply(), + model.create_imported_modeling_group(), + ], + ), + ("entire_solid_mesh", False), + # Note: solid_element_sets is not tested because we would need to + # create a fully-defined solid model + ("scale_ply_thicknesses", False), + ("void_material", model.create_material()), + ("minimum_void_material_thickness", 1e-3), + ("delete_lost_elements", False), + ("filler_material", model.create_material()), + ("rosettes", [model.create_rosette() for _ in range(3)]), + ( + "rosette_selection_method", + LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE_SUPERPOSED, + ), + ("reinforcing_behavior", ReinforcingBehavior.TENSION_ONLY), + ("base_element_material_handling", BaseElementMaterialHandling.RETAIN), + ("stress_state", StressStateType.PLANE_STRESS_STATE), + ("base_material", model.create_material()), + ("base_element_rosettes", [model.create_rosette() for _ in range(3)]), + ( + "base_element_rosette_selection_method", + LayupMappingRosetteSelectionMethod.MINIMUM_DISTANCE_SUPERPOSED, + ), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) diff --git a/tests/unittests/test_linked_object_list.py b/tests/unittests/test_linked_object_list.py new file mode 100644 index 0000000000..42e1bec2c2 --- /dev/null +++ b/tests/unittests/test_linked_object_list.py @@ -0,0 +1,134 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pytest + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def oriented_selection_set(model): + return list(model.oriented_selection_sets.values())[0] + + +def test_error_on_wrong_type(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test replaces the entire contents of a LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.rosettes = [model.create_element_set()] + assert "Rosette" in str(excinfo.value) + + +def test_error_on_wrong_type_polymorphic(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test replaces the entire contents of a polymorphic LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.selection_rules = [model.create_element_set()] + assert "SelectionRule" in str(excinfo.value) + + +def test_error_on_wrong_single_assignment(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test assigns to a single item in a LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.rosettes[0] = model.create_element_set() + assert "Rosette" in str(excinfo.value) + + +def test_error_on_wrong_single_assignment_polymorphic(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test assigns to a single item in a polymorphic LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.selection_rules[0] = model.create_element_set() + assert "SelectionRule" in str(excinfo.value) + + +def test_error_on_wrong_append(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test appends to a LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.rosettes.append(model.create_element_set()) + assert "Rosette" in str(excinfo.value) + + +def test_error_on_wrong_append_polymorphic(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test appends to a polymorphic LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.selection_rules.append(model.create_element_set()) + assert "SelectionRule" in str(excinfo.value) + + +def test_error_on_wrong_insert(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test inserts into a LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.rosettes.insert(0, model.create_element_set()) + assert "Rosette" in str(excinfo.value) + + +def test_error_on_wrong_insert_polymorphic(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test inserts into a polymorphic LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.selection_rules.insert(0, model.create_element_set()) + assert "SelectionRule" in str(excinfo.value) + + +def test_error_on_wrong_extend(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test extends a LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.rosettes.extend([model.create_element_set()]) + assert "Rosette" in str(excinfo.value) + + +def test_error_on_wrong_extend_polymorphic(model, oriented_selection_set): + """Test that the correct error is raised when assigning the wrong type. + + This test extends a polymorphic LinkedObjectList. + """ + with pytest.raises(TypeError) as excinfo: + oriented_selection_set.selection_rules.extend([model.create_element_set()]) + assert "SelectionRule" in str(excinfo.value) diff --git a/tests/unittests/test_lookup_table_column.py b/tests/unittests/test_lookup_table_column.py index fd4a5af4fc..ff895a37e9 100644 --- a/tests/unittests/test_lookup_table_column.py +++ b/tests/unittests/test_lookup_table_column.py @@ -26,10 +26,10 @@ import pytest from ansys.acp.core import ( - DimensionType, LookUpTable1DColumn, LookUpTable3DColumn, LookUpTableColumnValueType, + PhysicalDimension, ) from .common.tree_object_tester import ( @@ -115,7 +115,7 @@ class TestLookUpTableColumn(WithLockedMixin, TreeObjectTester): def default_properties(default_data): return { "value_type": LookUpTableColumnValueType.SCALAR, - "dimension_type": DimensionType.DIMENSIONLESS, + "physical_dimension": PhysicalDimension.DIMENSIONLESS, "data": default_data, } @@ -128,9 +128,9 @@ def object_properties(column_data, column_value_type): read_write=[ ("name", "some_name"), ("data", column_data), - ("dimension_type", DimensionType.TIME), - ("dimension_type", DimensionType.CURRENCY), - ("dimension_type", DimensionType.MASS), + ("physical_dimension", PhysicalDimension.TIME), + ("physical_dimension", PhysicalDimension.CURRENCY), + ("physical_dimension", PhysicalDimension.MASS), ], read_only=[ ("id", "some_id"), @@ -141,7 +141,7 @@ def object_properties(column_data, column_value_type): { "name": "some_name", "data": column_data, - "dimension_type": DimensionType.TIME, + "physical_dimension": PhysicalDimension.TIME, "value_type": column_value_type, } ], diff --git a/tests/unittests/test_material.py b/tests/unittests/test_material.py index a54ab375a3..e07956f0e6 100644 --- a/tests/unittests/test_material.py +++ b/tests/unittests/test_material.py @@ -20,7 +20,8 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from typing import Any, Callable +from collections.abc import Callable +from typing import Any import uuid from hypothesis import HealthCheck, given, settings diff --git a/tests/unittests/test_model.py b/tests/unittests/test_model.py index 70b3f7bbf2..29eeb7469e 100644 --- a/tests/unittests/test_model.py +++ b/tests/unittests/test_model.py @@ -29,9 +29,9 @@ import numpy as np import numpy.testing import pytest -import pyvista -from ansys.acp.core import ElementalDataType, VectorData +from ansys.acp.core import ElementalDataType, UnitSystemType +from ansys.acp.core.mesh_data import VectorData from .helpers import check_property @@ -41,9 +41,7 @@ def test_unittest(acp_instance, model_data_dir): Test basic properties of the model object """ input_file_path = model_data_dir / "ACP-Pre.h5" - remote_path = acp_instance.upload_file(input_file_path) - - model = acp_instance.import_model(name="kiteboard", path=remote_path, format="ansys:h5") + model = acp_instance.import_model(name="kiteboard", path=input_file_path, format="ansys:h5") # TODO: re-activate these tests when the respective features are implemented # assert model.unit_system.type == "mks" @@ -78,19 +76,11 @@ def test_unittest(acp_instance, model_data_dir): os.makedirs(working_dir) # model.solver.working_dir = str(working_dir) - if acp_instance.is_remote: - save_path: str | pathlib.Path = os.path.join( - os.path.dirname(remote_path), "test_model_serialization.acph5" - ) + with tempfile.TemporaryDirectory() as local_working_dir: + save_path = pathlib.Path(local_working_dir) / "test_model_serialization.acph5" model.save(save_path, save_cache=True) acp_instance.clear() model = acp_instance.import_model(path=save_path) - else: - with tempfile.TemporaryDirectory() as local_working_dir: - save_path = pathlib.Path(local_working_dir) / "test_model_serialization.acph5" - model.save(save_path, save_cache=True) - acp_instance.clear() - model = acp_instance.import_model(path=save_path) # TODO: re-activate these tests when the respective features are implemented # assert model.unit_system.type == "mks" @@ -128,31 +118,23 @@ def test_export_analysis_model(acp_instance, model_data_dir): are not checked. """ input_file_path = model_data_dir / "minimal_model_2.cdb" - remote_file_path = acp_instance.upload_file(input_file_path) - remote_workdir = remote_file_path.parent model = acp_instance.import_model( - name="minimal_model", path=remote_file_path, format="ansys:cdb", unit_system="mpa" + name="minimal_model", path=input_file_path, format="ansys:cdb", unit_system="mpa" ) - if acp_instance.is_remote: - out_file_path = remote_workdir / "out_file.cdb" - model.export_analysis_model(out_file_path) - with tempfile.TemporaryDirectory() as tmp_dir: - local_file_path = pathlib.Path(tmp_dir, "out_file.cdb") - acp_instance.download_file(out_file_path, local_file_path) - assert local_file_path.exists() - else: - with tempfile.TemporaryDirectory() as tmp_dir: - local_file_path = pathlib.Path(tmp_dir) / "out_file.cdb" - model.export_analysis_model(local_file_path) - assert local_file_path.exists() + with tempfile.TemporaryDirectory() as tmp_dir: + local_file_path = pathlib.Path(tmp_dir) / "out_file.cdb" + model.export_analysis_model(local_file_path) + assert local_file_path.exists() def test_string_representation(acp_instance, model_data_dir): input_file_path = model_data_dir / "ACP-Pre.h5" - remote_file_path = acp_instance.upload_file(input_file_path) model = acp_instance.import_model( - name="minimal_model", path=remote_file_path, format="ansys:cdb" + name="minimal_model", + path=input_file_path, + format="ansys:cdb", + unit_system=UnitSystemType.MKS, ) assert repr(model) == "" @@ -196,7 +178,7 @@ def test_elemental_data(minimal_complete_model): numpy.testing.assert_allclose(data.thickness.values, np.array([1e-4])) numpy.testing.assert_allclose(data.relative_thickness_correction.values, np.array([1.0])) numpy.testing.assert_allclose(data.area.values, np.array([9e4])) - numpy.testing.assert_allclose(data.price.values, np.array([0.0])) + # numpy.testing.assert_allclose(data.price.values, np.array([0.0])) # disabled due to issue #717. numpy.testing.assert_allclose(data.volume.values, np.array([9.0])) numpy.testing.assert_allclose(data.mass.values, np.array([7.065e-08])) numpy.testing.assert_allclose(data.offset.values, np.array([5e-5])) @@ -208,14 +190,20 @@ def test_nodal_data(minimal_complete_model): numpy.testing.assert_allclose(data.node_labels.values, np.array([1, 2, 3, 4])) +@pytest.mark.plotting def test_mesh_data_to_pyvista(minimal_complete_model): + import pyvista + pv_mesh = minimal_complete_model.mesh.to_pyvista() assert isinstance(pv_mesh, pyvista.core.pointset.UnstructuredGrid) assert pv_mesh.n_points == 4 assert pv_mesh.n_cells == 1 +@pytest.mark.plotting def test_elemental_data_to_pyvista(minimal_complete_model): + import pyvista + data = minimal_complete_model.elemental_data pv_mesh = data.get_pyvista_mesh(mesh=minimal_complete_model.mesh) assert isinstance(pv_mesh, pyvista.core.pointset.UnstructuredGrid) @@ -223,8 +211,11 @@ def test_elemental_data_to_pyvista(minimal_complete_model): assert pv_mesh.n_cells == 1 +@pytest.mark.plotting @pytest.mark.parametrize("component", [e.value for e in ElementalDataType]) def test_elemental_data_to_pyvista_with_component(minimal_complete_model, component): + import pyvista + data = minimal_complete_model.elemental_data if not hasattr(data, component): pytest.skip(f"Model elemental data does not contain component '{component}'") @@ -251,7 +242,7 @@ def test_regression_454(minimal_complete_model): assert not hasattr(minimal_complete_model, "store") -def test_modeling_ply_export(acp_instance, minimal_complete_model): +def test_modeling_ply_export(acp_instance, minimal_complete_model, raises_before_version): """ Test that the 'export_modeling_ply_geometries' method produces a file. The contents of the file are not checked. @@ -259,11 +250,155 @@ def test_modeling_ply_export(acp_instance, minimal_complete_model): out_filename = "modeling_ply_export.step" with tempfile.TemporaryDirectory() as tmp_dir: - local_file_path = pathlib.Path(tmp_dir) / out_filename - if acp_instance.is_remote: - out_file_path = pathlib.Path(out_filename) + out_file_path = pathlib.Path(tmp_dir) / out_filename + + with raises_before_version("25.1"): + minimal_complete_model.export_modeling_ply_geometries(out_file_path) + assert out_file_path.exists() + + +def test_parent_access_raises(minimal_complete_model): + with pytest.raises(RuntimeError) as exc: + minimal_complete_model.parent + assert "parent" in str(exc.value) + + +@pytest.mark.parametrize("unit_system", UnitSystemType) +def test_change_unit_system(minimal_complete_model, unit_system, raises_before_version): + assert minimal_complete_model.unit_system == UnitSystemType.MPA + + initial_node_coords = minimal_complete_model.mesh.node_coordinates + initial_minimum_analysis_ply_thickness = minimal_complete_model.minimum_analysis_ply_thickness + + conversion_factor_by_us = { + "mpa": 1.0, + "mks": 1e-3, + "cgs": 0.1, + "si": 1e-3, + "bin": 0.03937008, + "bft": 0.00328084, + "umks": 1e3, + } + + with raises_before_version("25.1"): + if unit_system in (UnitSystemType.UNDEFINED, UnitSystemType.FROM_FILE): + with pytest.raises(ValueError): + minimal_complete_model.unit_system = unit_system else: - out_file_path = local_file_path - minimal_complete_model.export_modeling_ply_geometries(out_file_path) - acp_instance.download_file(out_file_path, local_file_path) - assert local_file_path.exists() + minimal_complete_model.unit_system = unit_system + assert minimal_complete_model.unit_system == unit_system + minimal_complete_model.update() + + np.testing.assert_allclose( + minimal_complete_model.mesh.node_coordinates, + initial_node_coords * conversion_factor_by_us[unit_system.value], + ) + np.testing.assert_allclose( + minimal_complete_model.minimum_analysis_ply_thickness, + initial_minimum_analysis_ply_thickness * conversion_factor_by_us[unit_system.value], + ) + + +def test_material_export(minimal_complete_model): + """Check that the 'export_materials' method produces a file.""" + with tempfile.TemporaryDirectory() as tmp_dir: + export_path = pathlib.Path(tmp_dir) / "material_exported.xml" + minimal_complete_model.export_materials(export_path) + + assert export_path.exists() + assert os.stat(export_path).st_size > 0 + + +def test_material_import(minimal_complete_model, raises_before_version): + # GIVEN: a model, and a material XML file containing a material which is + # not present in the model + + with tempfile.TemporaryDirectory() as export_dir: + new_mat_id = "New Material" + export_file_path = pathlib.Path(export_dir) / "material_exported.xml" + mat = minimal_complete_model.materials["Structural Steel"].clone() + mat.name = new_mat_id + mat.store(parent=minimal_complete_model) + minimal_complete_model.export_materials(export_file_path) + del minimal_complete_model.materials[new_mat_id] + assert new_mat_id not in minimal_complete_model.materials + + with raises_before_version("25.1"): + # WHEN: the materials are imported + minimal_complete_model.import_materials(export_file_path) + + # THEN: the new material should be present in the model + assert new_mat_id in minimal_complete_model.materials + + +@pytest.mark.parametrize( + "layup_representation_3d", + [True, False], +) +@pytest.mark.parametrize( + "offset_type", + ["bottom_offset", "middle_offset", "top_offset"], +) +def test_hdf5_composite_cae_export( + acp_instance, + minimal_complete_model, + raises_before_version, + layup_representation_3d, + offset_type, +): + with raises_before_version("25.1"): + with tempfile.TemporaryDirectory() as tmp_dir: + export_file = pathlib.Path(tmp_dir) / "model_export.h5" + minimal_complete_model.export_hdf5_composite_cae( + export_file, + layup_representation_3d=layup_representation_3d, + offset_type=offset_type, + ) + + assert export_file.exists() + assert os.stat(export_file).st_size > 0 + + +def test_hdf5_composite_cae_export_with_scope( + acp_instance, minimal_complete_model, raises_before_version +): + with raises_before_version("25.1"): + with tempfile.TemporaryDirectory() as tmp_dir: + export_file = pathlib.Path(tmp_dir) / "model_export.h5" + minimal_complete_model.export_hdf5_composite_cae( + export_file, + all_elements=False, + element_sets=minimal_complete_model.element_sets.values(), + all_plies=False, + plies=minimal_complete_model.modeling_groups[ + "ModelingGroup.1" + ].modeling_plies.values(), + ) + + assert export_file.exists() + assert os.stat(export_file).st_size > 0 + + +@pytest.mark.parametrize( + "import_mode", + ["append", "overwrite"], +) +@pytest.mark.parametrize( + "projection_mode", + ["shell", "solid"], +) +def test_hdf5_composite_cae_export_import( + minimal_complete_model, + raises_before_version, + import_mode, + projection_mode, +): + with raises_before_version("25.1"): + with tempfile.TemporaryDirectory() as tmp_dir: + export_file = pathlib.Path(tmp_dir) / "model_export.h5" + minimal_complete_model.export_hdf5_composite_cae(export_file) + minimal_complete_model.import_hdf5_composite_cae( + export_file, + import_mode=import_mode, + projection_mode=projection_mode, + ) diff --git a/tests/unittests/test_modeling_ply.py b/tests/unittests/test_modeling_ply.py index c38f3dc9e7..84878e3425 100644 --- a/tests/unittests/test_modeling_ply.py +++ b/tests/unittests/test_modeling_ply.py @@ -23,11 +23,10 @@ import numpy as np import numpy.testing import pytest -import pyvista from ansys.acp.core import ( BooleanOperationType, - CutoffSelectionRule, + CutOffSelectionRule, DrapingType, ElementalDataType, Fabric, @@ -38,8 +37,8 @@ TaperEdge, ThicknessFieldType, ThicknessType, - VectorData, ) +from ansys.acp.core.mesh_data import VectorData from .common.linked_object_list_tester import LinkedObjectListTestCase, LinkedObjectListTester from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -64,6 +63,7 @@ def tree_object(parent_object): class TestModelingPly(NoLockedMixin, TreeObjectTester): COLLECTION_NAME = "modeling_plies" + CREATE_METHOD_NAME = "create_modeling_ply" @staticmethod @pytest.fixture @@ -75,7 +75,7 @@ def default_properties(): "ply_angle": 0.0, "active": True, "global_ply_nr": AnyThing(), - "draping": DrapingType.NO_DRAPING, + "draping_type": DrapingType.NO_DRAPING, "draping_seed_point": (0.0, 0.0, 0.0), "auto_draping_direction": True, "draping_direction": (1.0, 0.0, 0.0), @@ -91,8 +91,6 @@ def default_properties(): "taper_edges": [], } - CREATE_METHOD_NAME = "create_modeling_ply" - @staticmethod @pytest.fixture(params=["create_fabric", "create_stackup", "create_sublaminate"]) def object_properties(request, parent_model): @@ -150,7 +148,7 @@ def object_properties(request, parent_model): parameter_2=0.0, ), LinkedSelectionRule( - selection_rule=parent_model.create_cutoff_selection_rule(), + selection_rule=parent_model.create_cut_off_selection_rule(), operation_type=BooleanOperationType.INTERSECT, template_rule=True, parameter_1=1.2, @@ -343,7 +341,10 @@ def test_nodal_data(simple_modeling_ply): ) +@pytest.mark.plotting def test_elemental_data_to_pyvista(minimal_complete_model, simple_modeling_ply): + import pyvista + elemental_data = simple_modeling_ply.elemental_data pv_mesh = elemental_data.get_pyvista_mesh(mesh=minimal_complete_model.mesh) assert isinstance(pv_mesh, pyvista.core.pointset.UnstructuredGrid) @@ -351,10 +352,13 @@ def test_elemental_data_to_pyvista(minimal_complete_model, simple_modeling_ply): assert pv_mesh.n_cells == 1 +@pytest.mark.plotting @pytest.mark.parametrize("component", [e.value for e in ElementalDataType]) def test_elemental_data_to_pyvista_with_component( minimal_complete_model, simple_modeling_ply, component ): + import pyvista + data = simple_modeling_ply.elemental_data if not hasattr(data, component): pytest.skip(f"Modeling Ply elemental data does not contain component '{component}'") @@ -384,7 +388,10 @@ def test_elemental_data_to_pyvista_with_component( assert pv_mesh.n_cells == 1 +@pytest.mark.plotting def test_nodal_data_to_pyvista(minimal_complete_model, simple_modeling_ply): + import pyvista + data = simple_modeling_ply.nodal_data pv_mesh = data.get_pyvista_mesh(mesh=minimal_complete_model.mesh) assert isinstance(pv_mesh, pyvista.core.pointset.UnstructuredGrid) @@ -392,10 +399,13 @@ def test_nodal_data_to_pyvista(minimal_complete_model, simple_modeling_ply): assert pv_mesh.n_cells == 1 +@pytest.mark.plotting @pytest.mark.parametrize("component", [e.value for e in NodalDataType]) def test_nodal_data_to_pyvista_with_component( minimal_complete_model, simple_modeling_ply, component ): + import pyvista + data = simple_modeling_ply.nodal_data if not hasattr(data, component): pytest.skip(f"Modeling Ply nodal data does not contain component '{component}'") @@ -437,11 +447,21 @@ def test_linked_selection_rule_parameters(simple_modeling_ply, minimal_complete_ @pytest.mark.parametrize( "operation_type", [e for e in BooleanOperationType if e != BooleanOperationType.INTERSECT] ) -def test_linked_cutoff_selection_rule_operation_type(operation_type): - """Check that CutoffSelectionRule only allows INTERSECT operation type.""" +def test_linked_cut_off_selection_rule_operation_type(operation_type): + """Check that CutOffSelectionRule only allows INTERSECT operation type.""" with pytest.raises(ValueError) as exc: LinkedSelectionRule( - selection_rule=CutoffSelectionRule(), + selection_rule=CutOffSelectionRule(), operation_type=operation_type, ) assert "INTERSECT" in str(exc.value) + + +def test_taper_edge(parent_model): + edge_1 = parent_model.create_edge_set() + taper_edge = TaperEdge(edge_set=edge_1, angle=1, offset=2) + assert taper_edge != TaperEdge(edge_set=parent_model.create_edge_set(), angle=1, offset=2) + assert taper_edge != TaperEdge(edge_set=edge_1, angle=2, offset=2) + assert taper_edge != TaperEdge(edge_set=edge_1, angle=1, offset=3) + assert taper_edge == TaperEdge(edge_set=edge_1, angle=1, offset=2) + print(taper_edge) diff --git a/tests/unittests/test_object_permanence.py b/tests/unittests/test_object_permanence.py index c09f6b1e02..707ce70517 100644 --- a/tests/unittests/test_object_permanence.py +++ b/tests/unittests/test_object_permanence.py @@ -21,6 +21,7 @@ # SOFTWARE. import gc +import weakref import pytest @@ -43,23 +44,23 @@ def test_object_identity(model): assert mg_0 is mg_1 is mg_2 -def test_object_identity_after_deletion(model): +def test_object_deletion(model): """Check that objects are deleted when no longer referenced.""" key = list(model.modeling_groups.keys())[0] - # Immediately consume the objects via 'id' to ensure no references - # are kept by the test infrastructure. - id1 = id(model.modeling_groups[key]) + # Check the object is deleted when no longer referenced, after + # garbage collection. + ref = weakref.ref(model.modeling_groups[key]) + assert ref() is not None gc.collect() - id2 = id(model.modeling_groups[key]) - assert id1 != id2 + assert ref() is None - # test the inverse: keep a reference alive explicitly + # Test the inverse: keep a reference alive explicitly. The object + # should not be deleted. _ = model.modeling_groups[key] - id1 = id(model.modeling_groups[key]) + ref = weakref.ref(model.modeling_groups[key]) gc.collect() - id2 = id(model.modeling_groups[key]) - assert id1 == id2 + assert ref() is not None def test_unstored(): diff --git a/tests/unittests/test_parallel_selection_rule.py b/tests/unittests/test_parallel_selection_rule.py index 37845a34dc..2b3b327edc 100644 --- a/tests/unittests/test_parallel_selection_rule.py +++ b/tests/unittests/test_parallel_selection_rule.py @@ -22,8 +22,8 @@ import pytest -from ansys.acp.core import ( - LinkedSelectionRule, +from ansys.acp.core import LinkedSelectionRule +from ansys.acp.core.mesh_data import ( ParallelSelectionRuleElementalData, ParallelSelectionRuleNodalData, ) @@ -56,8 +56,8 @@ def default_properties(): "direction": (1.0, 0.0, 0.0), "lower_limit": 0.0, "upper_limit": 0.0, - "relative_rule_type": False, - "include_rule_type": True, + "relative_rule": False, + "include_rule": True, } CREATE_METHOD_NAME = "create_parallel_selection_rule" @@ -76,8 +76,8 @@ def object_properties(parent_object): ("direction", (4.0, 5.0, 6.0)), ("lower_limit", 7.0), ("upper_limit", 8.0), - ("relative_rule_type", True), - ("include_rule_type", False), + ("relative_rule", True), + ("include_rule", False), ], read_only=[ ("id", "some_id"), diff --git a/tests/unittests/test_plot_utils.py b/tests/unittests/test_plot_utils.py index daebe6ad88..e9d87de93c 100644 --- a/tests/unittests/test_plot_utils.py +++ b/tests/unittests/test_plot_utils.py @@ -20,11 +20,40 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import pytest +from pytest_cases import parametrize_with_cases + from ansys.acp.core._plotter import get_directions_plotter -from ansys.acp.core._utils.visualization import _replace_underscores_and_capitalize +from ansys.acp.core._utils.string_manipulation import replace_underscores_and_capitalize -def test_direction_plotter(acp_instance, load_model_from_tempfile): +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +def case_mesh_none_valid(): + return None + + +def case_model_mesh_valid(model): + return model.mesh + + +def case_other_mesh_valid(model, skip_before_version): + skip_before_version("25.1") + return model.element_sets["All_Elements"].mesh + + +def case_empty_mesh_invalid(model, skip_before_version): + skip_before_version("25.1") + return model.create_element_set().mesh + + +@pytest.mark.plotting +@parametrize_with_cases("mesh", cases=".", glob="*_valid") +def test_direction_plotter_valid_cases(model, mesh, load_model_from_tempfile): with load_model_from_tempfile() as model: modeling_ply = model.modeling_groups["ModelingGroup.1"].modeling_plies["ModelingPly.1"] analysis_ply = modeling_ply.production_plies["ProductionPly"].analysis_plies[ @@ -42,10 +71,37 @@ def test_direction_plotter(acp_instance, load_model_from_tempfile): ] plotter = get_directions_plotter( model=model, + mesh=mesh, components=components, ) for idx, data in enumerate(components): - assert plotter.legend.GetEntryString(idx) == _replace_underscores_and_capitalize( + assert plotter.legend.GetEntryString(idx) == replace_underscores_and_capitalize( data.component_name ) + + +@pytest.mark.plotting +@parametrize_with_cases("mesh", cases=".", glob="*_invalid") +def test_direction_plotter_invalid_cases(model, mesh, load_model_from_tempfile): + with load_model_from_tempfile() as model: + modeling_ply = model.modeling_groups["ModelingGroup.1"].modeling_plies["ModelingPly.1"] + analysis_ply = modeling_ply.production_plies["ProductionPly"].analysis_plies[ + "P1L1__ModelingPly.1" + ] + components = [ + analysis_ply.elemental_data.orientation, + analysis_ply.elemental_data.normal, + analysis_ply.elemental_data.reference_direction, + analysis_ply.elemental_data.fiber_direction, + analysis_ply.elemental_data.transverse_direction, + analysis_ply.elemental_data.draped_fiber_direction, + analysis_ply.elemental_data.draped_transverse_direction, + analysis_ply.elemental_data.material_1_direction, + ] + with pytest.raises(KeyError): + get_directions_plotter( + model=model, + mesh=mesh, + components=components, + ) diff --git a/tests/unittests/test_production_ply.py b/tests/unittests/test_production_ply.py index bf754fea79..1104ee7eb1 100644 --- a/tests/unittests/test_production_ply.py +++ b/tests/unittests/test_production_ply.py @@ -22,7 +22,8 @@ import pytest -from ansys.acp.core import Model, ModelingPly, ProductionPlyElementalData, ProductionPlyNodalData +from ansys.acp.core import Model, ModelingPly +from ansys.acp.core.mesh_data import ProductionPlyElementalData, ProductionPlyNodalData from .common.tree_object_tester import TreeObjectTesterReadOnly @@ -49,16 +50,19 @@ def properties_3_layers(self, parent_model: Model): "status": "UPTODATE", "material": first_fabric, "angle": 0.0, + "thickness": 0.0001, }, "ProductionPly.2": { "status": "UPTODATE", "material": first_fabric, "angle": 0.0, + "thickness": 0.0001, }, "ProductionPly.3": { "status": "UPTODATE", "material": first_fabric, "angle": 0.0, + "thickness": 0.0001, }, } diff --git a/tests/unittests/test_recursive_copy.py b/tests/unittests/test_recursive_copy.py new file mode 100644 index 0000000000..14bf0afdb7 --- /dev/null +++ b/tests/unittests/test_recursive_copy.py @@ -0,0 +1,308 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import numpy as np +import pytest + +from ansys.acp.core import FabricWithAngle, recursive_copy + + +@pytest.fixture +def minimal_complete_model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +def test_basic_recursive_copy(minimal_complete_model): + """Test copying a Modeling Ply and its linked objects.""" + # GIVEN: A Modeling Ply with linked objects + mg = minimal_complete_model.modeling_groups["ModelingGroup.1"] + mp = mg.modeling_plies["ModelingPly.1"] + + # WHEN: Recursively copying the Modeling Ply onto the same Modeling Group + new_objects = recursive_copy( + source_objects=[mp], + parent_mapping={mg: mg, minimal_complete_model: minimal_complete_model}, + linked_object_handling="copy", + ) + + # THEN: The expected new objects are created + assert len(new_objects) == 6 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "Global Coordinate System.2", + "All_Elements.2", + "Structural Steel.2", + "Fabric.2", + "OrientedSelectionSet.2", + "ModelingPly.2", + } + + +def test_basic_recursive_copy_keep_links(minimal_complete_model): + """Test copying a Modeling Ply without linked objects.""" + # GIVEN: A Modeling Ply with linked objects + mg = minimal_complete_model.modeling_groups["ModelingGroup.1"] + mp = mg.modeling_plies["ModelingPly.1"] + + # WHEN: Recursively copying the Modeling Ply onto the same Modeling Group, without linked objects + new_objects = recursive_copy( + source_objects=[mp], parent_mapping={mg: mg}, linked_object_handling="keep" + ) + + # THEN: The expected new objects are created, with the links kept + assert len(new_objects) == 1 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "ModelingPly.2", + } + assert list(new_objects.values())[0].ply_material.id == "Fabric.1" # type: ignore + + +def test_basic_recursive_copy_no_links(minimal_complete_model): + """Test copying a Modeling Ply without linked objects.""" + # GIVEN: A Modeling Ply with linked objects + mg = minimal_complete_model.modeling_groups["ModelingGroup.1"] + mp = mg.modeling_plies["ModelingPly.1"] + + # WHEN: Recursively copying the Modeling Ply onto the same Modeling Group, without linked objects + new_objects = recursive_copy( + source_objects=[mp], parent_mapping={mg: mg}, linked_object_handling="discard" + ) + + # THEN: The expected new objects are created, without links + assert len(new_objects) == 1 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "ModelingPly.2", + } + assert list(new_objects.values())[0].ply_material is None # type: ignore + + +def test_copy_all_objects(minimal_complete_model): + """Test copying all objects on a model onto itself.""" + # GIVEN: A simple model + model = minimal_complete_model + + # WHEN: Recursively copying, starting from the root of the model + new_objects = recursive_copy(source_objects=[model], parent_mapping={model: model}) + + # THEN: The expected new objects are created + # NOTE: This list may need to be updated when the model reference file + # is changed. + assert len(new_objects) == 8 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "Global Coordinate System.2", + "All_Elements.2", + "ns_edge.2", + "Structural Steel.2", + "Fabric.2", + "OrientedSelectionSet.2", + "ModelingGroup.2", + "ModelingPly.2", + } + + +def test_copy_to_different_model(minimal_complete_model, load_model_from_tempfile): + """Test copying all objects on a model onto a different model.""" + # GIVEN: Two models + model1 = minimal_complete_model + with load_model_from_tempfile() as model2: + + # WHEN: Recursively copying all objects from model1 to model2 + new_objects = recursive_copy( + source_objects=[model1], parent_mapping={model1: model2}, linked_object_handling="copy" + ) + + # THEN: The expected new objects are created + # NOTE: This list may need to be updated when the model reference file + # is changed. + assert len(new_objects) == 8 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "Global Coordinate System.2", + "All_Elements.2", + "ns_edge.2", + "Structural Steel.2", + "Fabric.2", + "OrientedSelectionSet.2", + "ModelingGroup.2", + "ModelingPly.2", + } + + +def test_copy_edge_property_list(minimal_complete_model): + """Test copying an object which has an Edge Property List.""" + # GIVEN: A simple model with a Stackup + model = minimal_complete_model + fabric1 = model.fabrics["Fabric.1"] + fabric2 = model.create_fabric(name="other_fabric", material=model.materials["Structural Steel"]) + stackup = model.create_stackup( + name="Stackup.1", + fabrics=[ + FabricWithAngle(fabric=fabric1, angle=0), + FabricWithAngle(fabric=fabric2, angle=90), + ], + ) + + # WHEN: Recursively copying the Stackup + new_objects = recursive_copy( + source_objects=[stackup], parent_mapping={model: model}, linked_object_handling="copy" + ) + + # THEN: + # - The stackup, fabrics, and materials are copied + # - The copied stackup links the new fabrics with the correct order and angles + assert len(new_objects) == 4 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "Stackup.2", + "Fabric.2", + "other_fabric.2", + "Structural Steel.2", + } + new_stackup = model.stackups["Stackup.2"] + linked_fabrics = new_stackup.fabrics + assert [fabric_with_angle.fabric.id for fabric_with_angle in linked_fabrics] == [ + "Fabric.2", + "other_fabric.2", + ] + assert [fabric_with_angle.angle for fabric_with_angle in linked_fabrics] == [0, 90] + + +def test_missing_parent_object_raises(minimal_complete_model): + """Test that an exception is raised if the parent_mapping is incomplete""" + + # GIVEN: A simple model + mg = minimal_complete_model.modeling_groups["ModelingGroup.1"] + mp = mg.modeling_plies["ModelingPly.1"] + + # WHEN: Recursively copying a Modeling Ply without providing the Model in + # the parent_mapping (needed due to links to e.g. the Global Coordinate System) + # THEN: An exception is raised + with pytest.raises(KeyError) as exc_info: + recursive_copy( + source_objects=[mp], + parent_mapping={mg: mg}, + linked_object_handling="copy", + ) + + msg = str(exc_info.value) + assert "Parent object" in msg + assert "parent_mapping" in msg + + +def test_copy_lookup_table_with_columns(minimal_complete_model): + """Test copying lookup tables with columns and their data. + + This case is special because LUT implement a check for the shape + of the data in their columns, which is determined by the "Location" + column. + """ + # GIVEN: a model which has objects with sub-attributes + # (here: a lookup table with columns) + model = minimal_complete_model + lut = model.create_lookup_table_1d() + lut.columns["Location"].data = [1.0, 2.0, 3.0] + lut.create_column(name="column1", data=[4.0, 5.0, 6.0]) + + # WHEN: recursively copying the lookup table + new_objects = recursive_copy(source_objects=[lut], parent_mapping={model: model}) + + # THEN: the expected new objects are created, but the sub-attributes are + # not explicitly copied + assert len(new_objects) == 2 + assert {obj.id for obj in new_objects.values()} == { # type: ignore + "LookUpTable1D.2", + "column1", + } + new_lut = model.lookup_tables_1d["LookUpTable1D.2"] + assert np.allclose(new_lut.columns["Location"].data, [1.0, 2.0, 3.0]) + assert np.allclose(new_lut.columns["column1"].data, [4.0, 5.0, 6.0]) + + +def test_inconsistent_source_model(minimal_complete_model, load_model_from_tempfile): + """Test that an exception is raised if the source objects are not all from the same model.""" + # GIVEN: Two models + model1 = minimal_complete_model + with load_model_from_tempfile() as model2: + # WHEN: Copying objects from different models + # THEN: An exception is raised + with pytest.raises(ValueError) as exc: + recursive_copy( + source_objects=[model1, model2], + parent_mapping={model1: model2}, + linked_object_handling="copy", + ) + assert "source_objects" in str(exc.value) + assert "'parent_mapping' keys" in str(exc.value) + assert "same model" in str(exc.value) + + +def test_inconsistent_source_model_2(minimal_complete_model, load_model_from_tempfile): + """Test that an exception is raised if the source objects are not all from the same model.""" + # GIVEN: Two models + model1 = minimal_complete_model + with load_model_from_tempfile() as model2: + # WHEN: Copying objects from different models + # THEN: An exception is raised + with pytest.raises(ValueError) as exc: + recursive_copy( + source_objects=[model1], + parent_mapping={model2: model2}, # parent_mapping keys are from the wrong model + linked_object_handling="copy", + ) + assert "source_objects" in str(exc.value) + assert "'parent_mapping' keys" in str(exc.value) + assert "same model" in str(exc.value) + + +def test_inconsistent_target_model(minimal_complete_model, load_model_from_tempfile): + """Test that an exception is raised if the target parents are not all from the same model.""" + # GIVEN: Two models + model1 = minimal_complete_model + mat = model1.materials["Structural Steel"] + with load_model_from_tempfile() as model2: + # WHEN: Copying objects from different models + # THEN: An exception is raised + with pytest.raises(ValueError) as exc: + recursive_copy( + source_objects=[model1], + parent_mapping={model1: model2, mat: mat}, + linked_object_handling="copy", + ) + assert "parent_mapping" in str(exc.value) + assert "'parent_mapping' values" in str(exc.value) + assert "same model" in str(exc.value) + + +def test_keep_links_across_models_raises(minimal_complete_model, load_model_from_tempfile): + """Test that an exception is raised when trying to keep links across models.""" + # GIVEN: Two models + model1 = minimal_complete_model + with load_model_from_tempfile() as model2: + # WHEN: Copying objects across models, with linked_object_handling="keep" + # THEN: An exception is raised + with pytest.raises(ValueError) as exc: + recursive_copy( + source_objects=[model1], + parent_mapping={model1: model2}, + linked_object_handling="keep", + ) + assert "linked_object_handling" in str(exc.value) + assert "keep" in str(exc.value) + assert "copy objects between models" in str(exc.value) diff --git a/tests/unittests/test_sampling_point.py b/tests/unittests/test_sampling_point.py new file mode 100644 index 0000000000..34a24c635c --- /dev/null +++ b/tests/unittests/test_sampling_point.py @@ -0,0 +1,84 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import SamplingPoint + +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(SamplingPoint._SUPPORTED_SINCE): + pytest.skip("InterfaceLayer is not supported on this version of the server.") + + +@pytest.fixture +def parent_object(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_sampling_point() + + +class TestSamplingPoint(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "sampling_points" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "point": (0.0, 0.0, 0.0), + "direction": (0.0, 0.0, 0.0), + "use_default_reference_direction": True, + "rosette": None, + "offset_is_middle": True, + "consider_coupling_effect": True, + } + + CREATE_METHOD_NAME = "create_sampling_point" + + @staticmethod + @pytest.fixture + def object_properties(parent_object): + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_name"), + ("point", (0.1, 0.2, 0.3)), + ("direction", (0.4, 0.5, 0.6)), + ("use_default_reference_direction", False), + ("rosette", parent_object.create_rosette()), + ("offset_is_middle", False), + ("consider_coupling_effect", False), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ("reference_direction", (0.1, 0.2, 0.3)), + ], + ) diff --git a/tests/unittests/test_section_cut.py b/tests/unittests/test_section_cut.py new file mode 100644 index 0000000000..864585ef3a --- /dev/null +++ b/tests/unittests/test_section_cut.py @@ -0,0 +1,111 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import math + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import ExtrusionType, IntersectionType, SectionCut, SectionCutType + +from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(SectionCut._SUPPORTED_SINCE): + pytest.skip("InterfaceLayer is not supported on this version of the server.") + + +@pytest.fixture +def parent_object(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_section_cut() + + +class TestSectionCut(NoLockedMixin, TreeObjectTester): + COLLECTION_NAME = "section_cuts" + CREATE_METHOD_NAME = "create_section_cut" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "locked": False, + "active": True, + "origin": (0, 0, 0), + "normal": (0, 0, 1), + "in_plane_reference_direction1": (1, 0, 0), + "scope_entire_model": True, + "scope_element_sets": [], + "extrusion_type": ExtrusionType.WIRE_FRAME, + "scale_factor": 1.0, + "core_scale_factor": 1.0, + "section_cut_type": SectionCutType.MODELING_PLY_WISE, + "intersection_type": IntersectionType.NORMAL_TO_SURFACE, + "use_default_tolerance": True, + "tolerance": 0.0, + "use_default_interpolation_settings": True, + "search_radius": 0.0, + "number_of_interpolation_points": 1, + } + + @staticmethod + @pytest.fixture + def object_properties(parent_object): + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_name"), + ("active", False), + ("origin", (0.1, 0.2, 0.3)), + ("normal", (0, 1.0 / math.sqrt(2), 1.0 / math.sqrt(2))), + ("in_plane_reference_direction1", (math.sqrt(1 / 3), math.sqrt(2 / 3), 0)), + ("scope_entire_model", False), + ( + "scope_element_sets", + [parent_object.create_element_set(), parent_object.create_element_set()], + ), + ("extrusion_type", ExtrusionType.SURFACE_NORMAL), + ("extrusion_type", ExtrusionType.SURFACE_SWEEP_BASED), + ("scale_factor", 1.5), + ("core_scale_factor", 12.3), + ("section_cut_type", SectionCutType.PRODUCTION_PLY_WISE), + ("section_cut_type", SectionCutType.ANALYSIS_PLY_WISE), + ("intersection_type", IntersectionType.IN_PLANE), + ("use_default_tolerance", False), + ("tolerance", 0.6), + ("use_default_interpolation_settings", False), + ("search_radius", 12.3), + ("number_of_interpolation_points", 5), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ("locked", True), + ], + ) diff --git a/tests/unittests/test_snap_to_geometry.py b/tests/unittests/test_snap_to_geometry.py new file mode 100644 index 0000000000..6a5f0627f4 --- /dev/null +++ b/tests/unittests/test_snap_to_geometry.py @@ -0,0 +1,85 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import SnapToGeometry, SnapToGeometryOrientationType + +from .common.tree_object_tester import ObjectPropertiesToTest, TreeObjectTester, WithLockedMixin + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(SnapToGeometry._SUPPORTED_SINCE): + pytest.skip("SnapToGeometry is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def parent_object(model): + return model.create_solid_model() + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_snap_to_geometry() + + +class TestSnapToGeometry(WithLockedMixin, TreeObjectTester): + COLLECTION_NAME = "snap_to_geometries" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + "active": True, + "orientation_type": SnapToGeometryOrientationType.TOP, + "cad_geometry": None, + "oriented_selection_set": None, + } + + CREATE_METHOD_NAME = "create_snap_to_geometry" + INITIAL_OBJECT_NAMES = tuple() + + @staticmethod + @pytest.fixture + def object_properties(model): + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_snap_to_geometry"), + ("active", False), + ("orientation_type", SnapToGeometryOrientationType.BOTTOM), + ("cad_geometry", model.create_virtual_geometry()), + ("oriented_selection_set", model.create_oriented_selection_set()), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ], + ) diff --git a/tests/unittests/test_solid_element_set.py b/tests/unittests/test_solid_element_set.py new file mode 100644 index 0000000000..c277288cdd --- /dev/null +++ b/tests/unittests/test_solid_element_set.py @@ -0,0 +1,94 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from packaging.version import parse as parse_version +import pytest + +from ansys.acp.core import Model, SolidElementSet + +from .common.tree_object_tester import TreeObjectTesterReadOnly + +DUMMY_SM_NAME = "dummy" +ESET_ALL_ELEMENTS = "All_Elements" + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version(SolidElementSet._SUPPORTED_SINCE): + pytest.skip("SolidElementSet is not supported on this version of the server.") + + +@pytest.fixture +def model(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +def add_solid_model_to_model(model: Model): + solid_model = model.create_solid_model( + name=DUMMY_SM_NAME, + element_sets=[model.element_sets[ESET_ALL_ELEMENTS]], + ) + return solid_model + + +class TestSolidElementSet(TreeObjectTesterReadOnly): + COLLECTION_NAME = "solid_element_sets" + + @staticmethod + @pytest.fixture + def parent_object(model: Model): + solid_model = add_solid_model_to_model(model) + # Solid model must be up-to-date to access the solid element sets + model.update() + return solid_model + + @pytest.fixture + def collection_test_data(self, parent_object): + solid_model = parent_object + object_collection = getattr(solid_model, self.COLLECTION_NAME) + object_collection.values() + object_names = [ESET_ALL_ELEMENTS] + object_ids = [ESET_ALL_ELEMENTS] + + return object_collection, object_names, object_ids + + @staticmethod + @pytest.fixture + def properties(): + return { + ESET_ALL_ELEMENTS: { + "status": "UPTODATE", + "locked": True, + "element_labels": (2,), + }, + } + + def test_properties(self, parent_object, properties): + + for solid_element_set in parent_object.solid_element_sets.values(): + ref_values = properties[solid_element_set.id] + for prop, value in ref_values.items(): + assert getattr(solid_element_set, prop) == value + + assert solid_element_set.solid_mesh is not None + assert solid_element_set.solid_mesh.element_labels == (2,) diff --git a/tests/unittests/test_solid_model.py b/tests/unittests/test_solid_model.py new file mode 100644 index 0000000000..d6cd0eaf01 --- /dev/null +++ b/tests/unittests/test_solid_model.py @@ -0,0 +1,389 @@ +# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pathlib +import tempfile + +from hypothesis import HealthCheck, assume, given, settings +import hypothesis.strategies as st +from packaging.version import parse as parse_version +import pytest + +import ansys.acp.core as pyacp + +from .common.tree_object_tester import ( + ObjectPropertiesToTest, + PropertyWithCustomComparison, + TreeObjectTester, + WithLockedMixin, +) + + +@pytest.fixture(autouse=True) +def skip_if_unsupported_version(acp_instance): + if parse_version(acp_instance.server_version) < parse_version( + pyacp.SolidModel._SUPPORTED_SINCE + ): + pytest.skip("SolidModel is not supported on this version of the server.") + + +def compare_pb_object(given, expected): + if not isinstance(given, type(expected)): + return False + return given._pb_object == expected._pb_object + + +@pytest.fixture +def parent_object(load_model_from_tempfile): + with load_model_from_tempfile() as model: + yield model + + +@pytest.fixture +def tree_object(parent_object): + return parent_object.create_solid_model() + + +class TestSolidModel(WithLockedMixin, TreeObjectTester): + COLLECTION_NAME = "solid_models" + + @staticmethod + @pytest.fixture + def default_properties(): + return { + "status": "NOTUPTODATE", + } + + CREATE_METHOD_NAME = "create_solid_model" + INITIAL_OBJECT_NAMES = tuple() + + @staticmethod + @pytest.fixture + def object_properties(parent_object): + model = parent_object + modeling_group = model.create_modeling_group() + + return ObjectPropertiesToTest( + read_write=[ + ("name", "new_name"), + ("active", False), + ( + "element_sets", + [model.create_element_set(), model.create_oriented_selection_set()], + ), + ("extrusion_method", pyacp.ExtrusionMethod.MONOLITHIC), + ("max_element_thickness", 12.3), + ("ply_group_pointers", [modeling_group.create_modeling_ply() for _ in range(3)]), + ("offset_direction_type", pyacp.SolidModelOffsetDirectionType.SURFACE_NORMAL), + ("skip_elements_without_plies", True), + ("drop_off_material", model.create_material()), + ("cut_off_material", model.create_material()), + ("delete_bad_elements", False), + ("warping_limit", 0.6), + ("minimum_volume", 1.2), + ( + "drop_off_settings", + PropertyWithCustomComparison( + initial_value=pyacp.DropOffSettings( + drop_off_type=pyacp.DropOffType.OUTSIDE_PLY, + disable_drop_offs_on_bottom=True, + drop_off_disabled_on_bottom_sets=[ + model.create_element_set(), + model.create_oriented_selection_set(), + ], + disable_drop_offs_on_top=True, + drop_off_disabled_on_top_sets=[ + model.create_oriented_selection_set(), + model.create_element_set(), + ], + connect_butt_joined_plies=False, + ), + comparison_function=compare_pb_object, + ), + ), + ( + "export_settings", + PropertyWithCustomComparison( + initial_value=pyacp.SolidModelExportSettings( + use_default_section_index=False, + section_index=2, + use_default_coordinate_system_index=False, + coordinate_system_index=3, + use_default_material_index=False, + material_index=4, + use_default_node_index=False, + node_index=5, + use_default_element_index=False, + element_index=6, + use_solsh_elements=True, + write_degenerated_elements=False, + drop_hanging_nodes=False, + use_solid_model_prefix=False, + transfer_all_sets=False, + transferred_element_sets=[model.create_element_set() for _ in range(2)], + transferred_edge_sets=[model.create_edge_set() for _ in range(3)], + ), + comparison_function=compare_pb_object, + ), + ), + ], + read_only=[ + ("id", "some_id"), + ("status", "UPTODATE"), + ("locked", True), + ], + ) + + +@given( + disable_drop_offs_on_bottom=st.booleans(), + disable_drop_offs_on_top=st.booleans(), + connect_butt_joined_plies=st.booleans(), +) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_drop_off_settings_on_init( + parent_object, disable_drop_offs_on_bottom, disable_drop_offs_on_top, connect_butt_joined_plies +): + """Check that the drop-off settings are correctly set when passed to the SolidModel constructor.""" + drop_off_disabled_on_bottom_sets = [ + parent_object.create_element_set(), + parent_object.create_oriented_selection_set(), + ] + drop_off_disabled_on_top_sets = [ + parent_object.create_oriented_selection_set(), + parent_object.create_element_set(), + ] + + drop_off_settings = pyacp.DropOffSettings( + disable_drop_offs_on_bottom=disable_drop_offs_on_bottom, + drop_off_disabled_on_bottom_sets=drop_off_disabled_on_bottom_sets, + disable_drop_offs_on_top=disable_drop_offs_on_top, + drop_off_disabled_on_top_sets=drop_off_disabled_on_top_sets, + connect_butt_joined_plies=connect_butt_joined_plies, + ) + + solid_model = parent_object.create_solid_model(drop_off_settings=drop_off_settings) + assert solid_model.drop_off_settings.disable_drop_offs_on_bottom == disable_drop_offs_on_bottom + assert solid_model.drop_off_settings.disable_drop_offs_on_top == disable_drop_offs_on_top + assert solid_model.drop_off_settings.connect_butt_joined_plies == connect_butt_joined_plies + assert ( + solid_model.drop_off_settings.drop_off_disabled_on_bottom_sets + == drop_off_disabled_on_bottom_sets + ) + assert ( + solid_model.drop_off_settings.drop_off_disabled_on_top_sets == drop_off_disabled_on_top_sets + ) + + +@given( + disable_drop_offs_on_bottom=st.booleans(), + disable_drop_offs_on_top=st.booleans(), + connect_butt_joined_plies=st.booleans(), +) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_drop_off_settings_assign( + parent_object, disable_drop_offs_on_bottom, disable_drop_offs_on_top, connect_butt_joined_plies +): + """Check that the drop-off settings are correctly set when assigned to the SolidModel.""" + drop_off_disabled_on_bottom_sets = [ + parent_object.create_element_set(), + parent_object.create_oriented_selection_set(), + ] + drop_off_disabled_on_top_sets = [ + parent_object.create_oriented_selection_set(), + parent_object.create_element_set(), + ] + + drop_off_settings = pyacp.DropOffSettings( + disable_drop_offs_on_bottom=disable_drop_offs_on_bottom, + drop_off_disabled_on_bottom_sets=drop_off_disabled_on_bottom_sets, + disable_drop_offs_on_top=disable_drop_offs_on_top, + drop_off_disabled_on_top_sets=drop_off_disabled_on_top_sets, + connect_butt_joined_plies=connect_butt_joined_plies, + ) + + solid_model = parent_object.create_solid_model() + solid_model.drop_off_settings = drop_off_settings + assert solid_model.drop_off_settings.disable_drop_offs_on_bottom == disable_drop_offs_on_bottom + assert solid_model.drop_off_settings.disable_drop_offs_on_top == disable_drop_offs_on_top + assert solid_model.drop_off_settings.connect_butt_joined_plies == connect_butt_joined_plies + assert ( + solid_model.drop_off_settings.drop_off_disabled_on_bottom_sets + == drop_off_disabled_on_bottom_sets + ) + assert ( + solid_model.drop_off_settings.drop_off_disabled_on_top_sets == drop_off_disabled_on_top_sets + ) + + +def test_drop_off_settings_assign_wrong_type(parent_object): + """Check that assigning the wrong type to the drop-off settings raises an exception.""" + + solid_model = parent_object.create_solid_model() + with pytest.raises(TypeError): + solid_model.drop_off_settings = "wrong_type" + + +def test_drop_off_settings_string_representation(parent_object): + """Check that the string representation of the drop-off settings is correct.""" + solid_model = parent_object.create_solid_model() + drop_off_disabled_on_bottom_sets = [ + parent_object.create_element_set(), + parent_object.create_oriented_selection_set(), + ] + drop_off_disabled_on_top_sets = [ + parent_object.create_oriented_selection_set(), + parent_object.create_element_set(), + ] + + drop_off_settings = pyacp.DropOffSettings( + drop_off_type=pyacp.DropOffType.OUTSIDE_PLY, + disable_drop_offs_on_bottom=True, + drop_off_disabled_on_bottom_sets=drop_off_disabled_on_bottom_sets, + disable_drop_offs_on_top=True, + drop_off_disabled_on_top_sets=drop_off_disabled_on_top_sets, + connect_butt_joined_plies=False, + ) + # When the drop-off settings are not yet linked to the server, the linked + # object lists cannot instantiate the objects. Therefore, the string representation + # will contain '' for the linked objects. + assert "" in str(drop_off_settings) + # Assign the drop-off settings to the solid model, to establish the + # link to the server + solid_model.drop_off_settings = drop_off_settings + str_repr = str(solid_model.drop_off_settings) + assert "DropOffSettings" in str_repr + for val in drop_off_disabled_on_bottom_sets + drop_off_disabled_on_top_sets: + assert repr(val) in str_repr + + +@pytest.mark.parametrize( + "format", + [ + "ansys:h5", + "ansys:cdb", + pyacp.SolidModelExportFormat.ANSYS_H5, + pyacp.SolidModelExportFormat.ANSYS_CDB, + ], +) +def test_solid_model_export(acp_instance, parent_object, format): + """Check that the export to a file works.""" + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + with tempfile.TemporaryDirectory() as tmp_dir: + if format == "ansys:h5": + ext = ".h5" + else: + ext = ".cdb" + + out_path = pathlib.Path(tmp_dir) / f"out_file{ext}" + solid_model.export(path=out_path, format=format) + + assert out_path.exists() + assert out_path.stat().st_size > 0 + + +@given(invalid_format=st.text()) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_export_with_invalid_format_raises(parent_object, invalid_format): + """Check that the export to a file with an invalid format raises an exception.""" + assume(invalid_format not in ["ansys:h5", "ansys:cdb"]) + + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + with tempfile.TemporaryDirectory() as tmp_dir: + out_file_name = f"out_file.h5" + out_path = pathlib.Path(tmp_dir) / out_file_name + + with pytest.raises(ValueError): + solid_model.export(path=out_path, format=invalid_format) + + +@pytest.mark.parametrize("format", ["ansys:cdb", "step", "iges", "stl"]) +def test_solid_model_skin_export(acp_instance, parent_object, format): + """Check that the skin export to a file works.""" + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + with tempfile.TemporaryDirectory() as tmp_dir: + ext = {"ansys:cdb": ".cdb", "step": ".stp", "iges": ".igs", "stl": ".stl"}[format] + out_path = pathlib.Path(tmp_dir) / f"out_file{ext}" + + solid_model.export_skin(path=out_path, format=format) + + assert out_path.exists() + assert out_path.stat().st_size > 0 + + +@given(invalid_format=st.text()) +@settings(suppress_health_check=[HealthCheck.function_scoped_fixture], deadline=None) +def test_skin_export_with_invalid_format_raises(parent_object, invalid_format): + """Check that the export to a file with an invalid format raises an exception.""" + assume(invalid_format not in ["ansys:cdb", "step", "iges", "stl"]) + + model = parent_object + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + with tempfile.TemporaryDirectory() as tmp_dir: + out_file_name = f"out_file.stp" + out_path = pathlib.Path(tmp_dir) / out_file_name + + with pytest.raises(ValueError): + solid_model.export_skin(path=out_path, format=invalid_format) + + +def test_elemental_data(parent_object): + """Check that the elemental data can be accessed.""" + model = parent_object + model.fabrics["Fabric.1"].thickness = 0.1 + + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + elemental_data = solid_model.elemental_data + empty_keys = [key for key, value in vars(elemental_data).items() if value is None] + assert not empty_keys, f"Keys with None values: {empty_keys}" + + +def test_nodal_data(parent_object): + """Check that the nodal data can be accessed.""" + model = parent_object + model.fabrics["Fabric.1"].thickness = 0.1 + + solid_model = model.create_solid_model() + solid_model.element_sets = [model.element_sets["All_Elements"]] + model.update() + + nodal_data = solid_model.nodal_data + empty_keys = [key for key, value in vars(nodal_data).items() if value is None] + assert not empty_keys, f"Keys with None values: {empty_keys}" diff --git a/tests/unittests/test_spherical_selection_rule.py b/tests/unittests/test_spherical_selection_rule.py index fa8212d93b..dc7043b7e7 100644 --- a/tests/unittests/test_spherical_selection_rule.py +++ b/tests/unittests/test_spherical_selection_rule.py @@ -22,7 +22,10 @@ import pytest -from ansys.acp.core import SphericalSelectionRuleElementalData, SphericalSelectionRuleNodalData +from ansys.acp.core.mesh_data import ( + SphericalSelectionRuleElementalData, + SphericalSelectionRuleNodalData, +) from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -50,8 +53,8 @@ def default_properties(): "rosette": None, "origin": (0.0, 0.0, 0.0), "radius": 0.0, - "relative_rule_type": False, - "include_rule_type": True, + "relative_rule": False, + "include_rule": True, } CREATE_METHOD_NAME = "create_spherical_selection_rule" @@ -68,8 +71,8 @@ def object_properties(parent_object): ("rosette", rosette), ("origin", (1.0, 2.0, 3.0)), ("radius", 4.0), - ("relative_rule_type", True), - ("include_rule_type", False), + ("relative_rule", True), + ("include_rule", False), ], read_only=[ ("id", "some_id"), diff --git a/tests/unittests/test_stackup.py b/tests/unittests/test_stackup.py index 04fdfb32e2..b28ced9271 100644 --- a/tests/unittests/test_stackup.py +++ b/tests/unittests/test_stackup.py @@ -23,9 +23,9 @@ import pytest from ansys.acp.core import ( - CutoffMaterialType, - DrapingMaterialType, - DropoffMaterialType, + CutOffMaterialHandling, + DrapingMaterialModel, + DropOffMaterialHandling, FabricWithAngle, SymmetryType, ) @@ -56,11 +56,11 @@ def default_properties(): "topdown": True, "fabrics": [], "symmetry": SymmetryType.NO_SYMMETRY, - "drop_off_material_handling": DropoffMaterialType.GLOBAL, + "drop_off_material_handling": DropOffMaterialHandling.GLOBAL, "drop_off_material": None, - "cut_off_material_handling": CutoffMaterialType.COMPUTED, + "cut_off_material_handling": CutOffMaterialHandling.COMPUTED, "cut_off_material": None, - "draping_material_model": DrapingMaterialType.WOVEN, + "draping_material_model": DrapingMaterialModel.WOVEN, "draping_ud_coefficient": 0.0, } @@ -86,11 +86,11 @@ def object_properties(parent_object): ], ), ("symmetry", SymmetryType.EVEN_SYMMETRY), - ("drop_off_material_handling", DropoffMaterialType.CUSTOM), + ("drop_off_material_handling", DropOffMaterialHandling.CUSTOM), ("drop_off_material", material), - ("cut_off_material_handling", CutoffMaterialType.CUSTOM), + ("cut_off_material_handling", CutOffMaterialHandling.CUSTOM), ("cut_off_material", material), - ("draping_material_model", DrapingMaterialType.UD), + ("draping_material_model", DrapingMaterialModel.UD), ("draping_ud_coefficient", 0.55), ], read_only=[ @@ -188,3 +188,11 @@ def test_add_fabric(parent_object): stackup.add_fabric(fabric2, angle=45.0) assert stackup.fabrics[-1].fabric == fabric2 assert stackup.fabrics[-1].angle == 45.0 + + +def test_fabric_wit_angle(parent_object): + fabric1 = parent_object.create_fabric() + fabric_with_angle = FabricWithAngle(fabric=fabric1, angle=45.0) + assert fabric_with_angle != FabricWithAngle(fabric=parent_object.create_fabric(), angle=45.0) + assert fabric_with_angle != FabricWithAngle(fabric=fabric1, angle=55.0) + assert fabric_with_angle == FabricWithAngle(fabric=fabric1, angle=45.0) diff --git a/tests/unittests/test_sublaminate.py b/tests/unittests/test_sublaminate.py index 67fc566c4d..6d7e7725f8 100644 --- a/tests/unittests/test_sublaminate.py +++ b/tests/unittests/test_sublaminate.py @@ -104,3 +104,17 @@ def test_add_lamina(parent_object): assert sublaminate.materials[1].angle == 0.0 assert sublaminate.materials[2].material == fabric1 assert sublaminate.materials[2].angle == -45.0 + + +def test_lamina(parent_object): + fabric1 = parent_object.create_fabric() + fabric1.material = parent_object.create_material() + stackup = parent_object.create_stackup() + stackup.add_fabric(fabric1, angle=30.0) + stackup.add_fabric(fabric1, angle=-30.0) + + lamina = Lamina(material=fabric1, angle=45.0) + assert lamina != Lamina(material=stackup, angle=45.0) + assert lamina != Lamina(material=fabric1, angle=-45.0) + assert lamina == Lamina(material=fabric1, angle=45.0) + print(lamina) diff --git a/tests/unittests/test_tree_printer.py b/tests/unittests/test_tree_printer.py index d80a00b401..d3ebe4d131 100644 --- a/tests/unittests/test_tree_printer.py +++ b/tests/unittests/test_tree_printer.py @@ -21,51 +21,49 @@ # SOFTWARE. import os +import textwrap + +from pytest_cases import parametrize_with_cases from ansys.acp.core import get_model_tree -def test_printed_model(acp_instance, model_data_dir): - """ - Test that model tree looks correct. - """ +def case_simple_model(acp_instance, model_data_dir): input_file_path = model_data_dir / "minimal_complete_model_no_matml_link.acph5" - remote_path = acp_instance.upload_file(input_file_path) + model = acp_instance.import_model(name="minimal_complete", path=input_file_path) + model.update() + return model, textwrap.dedent( + """\ + 'minimal_complete' + Materials + 'Structural Steel' + Fabrics + 'Fabric.1' + Element Sets + 'All_Elements' + Edge Sets + 'ns_edge' + Rosettes + 'Global Coordinate System' + Oriented Selection Sets + 'OrientedSelectionSet.1' + Modeling Groups + 'ModelingGroup.1' + Modeling Plies + 'ModelingPly.1' + Production Plies + 'P1__ModelingPly.1' + Analysis Plies + 'P1L1__ModelingPly.1' + """ + ) - model = acp_instance.import_model(name="minimal_complete", path=remote_path) - model.update() - tree = get_model_tree(model) +def case_more_objects(acp_instance, model_data_dir): + input_file_path = model_data_dir / "minimal_complete_model_no_matml_link.acph5" + model = acp_instance.import_model(name="minimal_complete", path=input_file_path) - assert ( - os.linesep + str(tree) - == """ -Model - Material Data - Materials - Structural Steel - Fabrics - Fabric.1 - Element Sets - All_Elements - Edge Sets - ns_edge - Geometry - Rosettes - Global Coordinate System - Lookup Tables - Selection Rules - Oriented Selection Sets - OrientedSelectionSet.1 - Modeling Groups - ModelingGroup.1 - ModelingPly.1 - ProductionPly - P1L1__ModelingPly.1 -""".replace( - "\n", os.linesep - ) - ) + model.update() model.create_edge_set() model.create_stackup() @@ -75,68 +73,77 @@ def test_printed_model(acp_instance, model_data_dir): model.create_parallel_selection_rule() model.create_cylindrical_selection_rule() model.create_tube_selection_rule() - model.create_cutoff_selection_rule() + model.create_cut_off_selection_rule() model.create_geometrical_selection_rule() model.create_boolean_selection_rule() model.create_lookup_table_1d() model.create_lookup_table_3d() model.create_sensor() + return model, textwrap.dedent( + """\ + 'minimal_complete' + Materials + 'Structural Steel' + Fabrics + 'Fabric.1' + Stackups + 'Stackup' + Sublaminates + 'SubLaminate' + Element Sets + 'All_Elements' + Edge Sets + 'ns_edge' + 'EdgeSet' + Cad Geometries + 'CADGeometry' + Virtual Geometries + 'VirtualGeometry' + Rosettes + 'Global Coordinate System' + Lookup Tables 1d + 'LookUpTable1D' + Columns + 'Location' + Lookup Tables 3d + 'LookUpTable3D' + Columns + 'Location' + Parallel Selection Rules + 'ParallelSelectionrule' + Cylindrical Selection Rules + 'CylindricalSelectionrule' + Tube Selection Rules + 'TubeSelectionrule' + Cut Off Selection Rules + 'CutOffSelectionrule' + Geometrical Selection Rules + 'GeometricalSelectionrule' + Boolean Selection Rules + 'BooleanSelectionrule' + Oriented Selection Sets + 'OrientedSelectionSet.1' + Modeling Groups + 'ModelingGroup.1' + Modeling Plies + 'ModelingPly.1' + Production Plies + 'P1__ModelingPly.1' + Analysis Plies + 'P1L1__ModelingPly.1' + Sensors + 'Sensor' + """ + ) + + +@parametrize_with_cases("model,expected", cases=".", glob="*") +def test_printed_model(model, expected): + """ + Test that model tree looks correct. + """ + tree = get_model_tree(model) - assert ( - os.linesep + str(tree) - == """ -Model - Material Data - Materials - Structural Steel - Fabrics - Fabric.1 - Stackups - Stackup - Sublaminates - SubLaminate - Element Sets - All_Elements - Edge Sets - ns_edge - EdgeSet - Geometry - Cad Geometries - CADGeometry - Virtual Geometries - VirtualGeometry - Rosettes - Global Coordinate System - Lookup Tables - Lookup Tables 1d - LookUpTable1D - Lookup Tables 3d - LookUpTable3D - Selection Rules - Parallel Selection Rules - ParallelSelectionrule - Cylindrical Selection Rules - CylindricalSelectionrule - Tube Selection Rules - TubeSelectionrule - Cutoff Selection Rules - CutoffSelectionrule - Geometrical Selection Rules - GeometricalSelectionrule - Boolean Selection Rules - BooleanSelectionrule - Oriented Selection Sets - OrientedSelectionSet.1 - Modeling Groups - ModelingGroup.1 - ModelingPly.1 - ProductionPly - P1L1__ModelingPly.1 - Sensors - Sensor -""".replace( - "\n", os.linesep - ) - ) + assert str(tree) == expected.replace("\n", os.linesep) diff --git a/tests/unittests/test_tube_selection_rule.py b/tests/unittests/test_tube_selection_rule.py index 92f7f9a0a9..0b08a7ad5c 100644 --- a/tests/unittests/test_tube_selection_rule.py +++ b/tests/unittests/test_tube_selection_rule.py @@ -22,7 +22,7 @@ import pytest -from ansys.acp.core import TubeSelectionRuleElementalData, TubeSelectionRuleNodalData +from ansys.acp.core.mesh_data import TubeSelectionRuleElementalData, TubeSelectionRuleNodalData from .common.tree_object_tester import NoLockedMixin, ObjectPropertiesToTest, TreeObjectTester @@ -49,7 +49,7 @@ def default_properties(): "edge_set": None, "outer_radius": 1.0, "inner_radius": 0.0, - "include_rule_type": True, + "include_rule": True, "extend_endings": False, "symmetrical_extension": True, "head": (0.0, 0.0, 0.0), @@ -70,7 +70,7 @@ def object_properties(parent_object): ("edge_set", edge_set), ("outer_radius", 1.3), ("inner_radius", 0.3), - ("include_rule_type", False), + ("include_rule", False), ("extend_endings", True), ("symmetrical_extension", False), ("head", (1.0, 2.0, 3.0)), diff --git a/tests/unittests/test_variable_offset_selection_rule.py b/tests/unittests/test_variable_offset_selection_rule.py index 7c8dce5473..c25f8f9d83 100644 --- a/tests/unittests/test_variable_offset_selection_rule.py +++ b/tests/unittests/test_variable_offset_selection_rule.py @@ -22,7 +22,7 @@ import pytest -from ansys.acp.core import ( +from ansys.acp.core.mesh_data import ( VariableOffsetSelectionRuleElementalData, VariableOffsetSelectionRuleNodalData, ) @@ -52,7 +52,7 @@ def default_properties(): "edge_set": None, "offsets": None, "angles": None, - "include_rule_type": True, + "include_rule": True, "use_offset_correction": False, "element_set": None, "inherit_from_lookup_table": True, @@ -78,7 +78,7 @@ def object_properties(parent_object): ("edge_set", edge_set), ("offsets", column_1), ("angles", column_2), - ("include_rule_type", False), + ("include_rule", False), ("use_offset_correction", True), ("element_set", element_set), ("inherit_from_lookup_table", False), diff --git a/tests/unittests/test_virtual_geometry.py b/tests/unittests/test_virtual_geometry.py index b606d3995f..3dd5231934 100644 --- a/tests/unittests/test_virtual_geometry.py +++ b/tests/unittests/test_virtual_geometry.py @@ -100,3 +100,13 @@ def test_virtual_geometry_no_or_invalid_links(parent_object, load_cad_geometry): cad_components=cad_geometry.root_shapes.values(), sub_shapes=[SubShape(cad_geometry=cad_geometry, path="some/path/to/shape")], ) + + +def test_sub_shape(parent_object, load_cad_geometry): + model = parent_object + with load_cad_geometry(model) as cad_geometry: + sub_shape = SubShape(cad_geometry=cad_geometry, path="some/path/to/shape") + with load_cad_geometry(model) as other_cad_geometry: + assert sub_shape != SubShape(cad_geometry=other_cad_geometry, path="some/path/to/shape") + assert sub_shape != SubShape(cad_geometry=cad_geometry, path="some/other/path/to/shape") + assert sub_shape == SubShape(cad_geometry=cad_geometry, path="some/path/to/shape") diff --git a/tests/unittests/test_workflow.py b/tests/unittests/test_workflow.py deleted file mode 100644 index e92ade4f61..0000000000 --- a/tests/unittests/test_workflow.py +++ /dev/null @@ -1,145 +0,0 @@ -# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates. -# SPDX-License-Identifier: MIT -# -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import pathlib -import shutil -import tempfile - -import pytest - -from ansys.acp.core import ACPWorkflow, UnitSystemType - - -@pytest.mark.parametrize("explict_temp_dir", [None, tempfile.TemporaryDirectory()]) -def test_workflow(acp_instance, model_data_dir, explict_temp_dir): - """Test that workflow can be initialized and files can be retrieved.""" - input_file_path = model_data_dir / "minimal_model_2.cdb" - - if explict_temp_dir is not None: - working_dir = pathlib.Path(explict_temp_dir.name) - else: - working_dir = None - - workflow = ACPWorkflow.from_cdb_or_dat_file( - acp=acp_instance, - cdb_or_dat_file_path=input_file_path, - local_working_directory=working_dir, - unit_system=UnitSystemType.MPA, - ) - workflow.model.update() - - cbd_path = workflow.get_local_cdb_file() - assert cbd_path == workflow.working_directory.path / f"{workflow.model.name}.cdb" - assert cbd_path.is_file() - - acph5_path = workflow.get_local_acph5_file() - assert acph5_path == workflow.working_directory.path / f"{workflow.model.name}.acph5" - assert acph5_path.is_file() - - materials_path = workflow.get_local_materials_file() - assert materials_path == workflow.working_directory.path / "materials.xml" - assert materials_path.is_file() - - composite_definitions = workflow.get_local_composite_definitions_file() - assert composite_definitions == workflow.working_directory.path / "ACPCompositeDefinitions.h5" - assert composite_definitions.is_file() - - -def test_reload_cad_geometry(acp_instance, model_data_dir, load_cad_geometry): - input_file_path = model_data_dir / "minimal_model_2.cdb" - - workflow = ACPWorkflow.from_cdb_or_dat_file( - acp=acp_instance, - cdb_or_dat_file_path=input_file_path, - unit_system=UnitSystemType.MPA, - ) - workflow.model.update() - - cad_geometry_file_path = model_data_dir / "square_and_solid.stp" - cad_geometry = workflow.add_cad_geometry_from_local_file(cad_geometry_file_path) - - with tempfile.TemporaryDirectory() as tempdir: - copied_path = pathlib.Path(shutil.copy(cad_geometry_file_path, tempdir)) - - workflow.refresh_cad_geometry_from_local_file(copied_path, cad_geometry) - if acp_instance.is_remote: - assert cad_geometry.external_path == str(copied_path.name) - else: - assert cad_geometry.external_path == str(copied_path) - - # Test that refresh works twice with the same local file. - workflow.refresh_cad_geometry_from_local_file(copied_path, cad_geometry) - - # Test error when local file does not exist. - pathlib.Path.unlink(copied_path) - with pytest.raises(FileNotFoundError) as excinfo: - workflow.refresh_cad_geometry_from_local_file(copied_path, cad_geometry) - assert "No such file or directory" in str(excinfo.value) - - -@pytest.mark.parametrize("unit_system", UnitSystemType) -def test_workflow_unit_system_dat(acp_instance, model_data_dir, unit_system): - """Test that workflow can be initialized and files can be retrieved.""" - - input_file_path = model_data_dir / "flat_plate_input.dat" - - if unit_system != UnitSystemType.UNDEFINED: - with pytest.raises(ValueError) as ex: - # Initializing a workflow with a defined unit system is not allowed - # if the input file does contain the unit system. - ACPWorkflow.from_cdb_or_dat_file( - acp=acp_instance, - cdb_or_dat_file_path=input_file_path, - unit_system=unit_system, - ) - else: - workflow = ACPWorkflow.from_cdb_or_dat_file( - acp=acp_instance, - cdb_or_dat_file_path=input_file_path, - unit_system=unit_system, - ) - # Unit system in the dat file is MKS - assert workflow.model.unit_system == UnitSystemType.MKS - - -@pytest.mark.parametrize("unit_system", UnitSystemType) -def test_workflow_unit_system_cdb(acp_instance, model_data_dir, unit_system): - """Test that workflow can be initialized and files can be retrieved.""" - - input_file_path = model_data_dir / "minimal_model_2.cdb" - - if unit_system == UnitSystemType.UNDEFINED: - with pytest.raises(ValueError) as ex: - # Initializing a workflow with an undefined unit system is not allowed - # if the input file does not contain the unit system. - ACPWorkflow.from_cdb_or_dat_file( - acp=acp_instance, - cdb_or_dat_file_path=input_file_path, - unit_system=unit_system, - ) - else: - workflow = ACPWorkflow.from_cdb_or_dat_file( - acp=acp_instance, - cdb_or_dat_file_path=input_file_path, - unit_system=unit_system, - ) - assert workflow.model.unit_system == unit_system diff --git a/type_checks/add_methods.py b/type_checks/add_methods.py index 57820446fa..7d06eb821c 100644 --- a/type_checks/add_methods.py +++ b/type_checks/add_methods.py @@ -1,4 +1,5 @@ -from typing import Callable, Union +from collections.abc import Callable +from typing import Union from mypy_extensions import Arg, DefaultNamedArg from typing_extensions import assert_type @@ -6,7 +7,7 @@ from ansys.acp.core import ( BooleanOperationType, BooleanSelectionRule, - CutoffSelectionRule, + CutOffSelectionRule, CylindricalSelectionRule, GeometricalSelectionRule, LinkedSelectionRule, @@ -29,7 +30,7 @@ Arg( Union[ BooleanSelectionRule, - CutoffSelectionRule, + CutOffSelectionRule, CylindricalSelectionRule, GeometricalSelectionRule, ParallelSelectionRule, diff --git a/type_checks/create_methods.py b/type_checks/create_methods.py index 44531d336f..e8815a15e8 100644 --- a/type_checks/create_methods.py +++ b/type_checks/create_methods.py @@ -1,4 +1,4 @@ -from typing import Callable +from collections.abc import Callable from mypy_extensions import DefaultNamedArg from typing_extensions import assert_type diff --git a/type_checks/plots.py b/type_checks/plots.py index 85c901f85e..721c9fe710 100644 --- a/type_checks/plots.py +++ b/type_checks/plots.py @@ -3,8 +3,8 @@ import numpy as np from typing_extensions import assert_type -from ansys.acp.core import Model, ScalarData, VectorData -from ansys.acp.core._tree_objects.modeling_ply import ModelingPlyElementalData +from ansys.acp.core import Model +from ansys.acp.core.mesh_data import ModelingPlyElementalData, ScalarData, VectorData model = Model()