├── src └── mcstructure │ ├── py.typed │ └── __init__.py ├── logo.png ├── docs ├── api.md ├── changelog.md ├── installation.md ├── contributing.md ├── index.md ├── Makefile ├── make.bat ├── conf.py └── quickstart.md ├── samples ├── barrel.mcstructure ├── cmdblock.mcstructure ├── dirt_house.mcstructure ├── scarecrow.mcstructure ├── large_nether.mcstructure ├── waterlogged.mcstructure ├── caged_villager.mcstructure └── armor_stand_steve_head.mcstructure ├── examples ├── out │ ├── area.mcstructure │ ├── diagonal.mcstructure │ ├── simple.mcstructure │ ├── custom_entity.mcstructure │ ├── large_nether.mcstructure │ └── waterlogged.mcstructure ├── copystruct.py ├── simple.py ├── waterlogged.py ├── diagonal.py ├── read_and_display.py ├── area.py └── custom_entity.py ├── .github ├── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ └── black.yaml ├── .readthedocs.yaml ├── tests ├── test_block.py └── test_structure.py ├── LICENSE.txt ├── pyproject.toml ├── scripts └── packer.py ├── README_CN.md ├── README_DE.md ├── README.md ├── .gitignore └── poetry.lock /src/mcstructure/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/logo.png -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # API Refernce 2 | 3 | ```{eval-rst} 4 | .. automodule:: mcstructure 5 | ``` 6 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The changelog will be documented as soon as the stable version is released. 4 | -------------------------------------------------------------------------------- /samples/barrel.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/barrel.mcstructure -------------------------------------------------------------------------------- /samples/cmdblock.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/cmdblock.mcstructure -------------------------------------------------------------------------------- /examples/out/area.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/examples/out/area.mcstructure -------------------------------------------------------------------------------- /samples/dirt_house.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/dirt_house.mcstructure -------------------------------------------------------------------------------- /samples/scarecrow.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/scarecrow.mcstructure -------------------------------------------------------------------------------- /examples/out/diagonal.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/examples/out/diagonal.mcstructure -------------------------------------------------------------------------------- /examples/out/simple.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/examples/out/simple.mcstructure -------------------------------------------------------------------------------- /samples/large_nether.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/large_nether.mcstructure -------------------------------------------------------------------------------- /samples/waterlogged.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/waterlogged.mcstructure -------------------------------------------------------------------------------- /samples/caged_villager.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/caged_villager.mcstructure -------------------------------------------------------------------------------- /examples/out/custom_entity.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/examples/out/custom_entity.mcstructure -------------------------------------------------------------------------------- /examples/out/large_nether.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/examples/out/large_nether.mcstructure -------------------------------------------------------------------------------- /examples/out/waterlogged.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/examples/out/waterlogged.mcstructure -------------------------------------------------------------------------------- /samples/armor_stand_steve_head.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phoenixr-codes/mcstructure/HEAD/samples/armor_stand_steve_head.mcstructure -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | *mcstructure* can be installed/updated with *pip*: 4 | 5 | ```bash 6 | pip install -U mcstructure 7 | ``` 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/black.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: psf/black@stable 11 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcomed. Feel free to 4 | [submit an issue](https://github.com/phoenixr-codes/mcstructure/issues/new/choose) 5 | or help with [already existing issues](https://github.com/phoenixr-codes/mcstructure/issues). 6 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | 4 | build: 5 | os: ubuntu-20.04 6 | tools: 7 | python: "3.13" 8 | 9 | python: 10 | install: 11 | - method: pip 12 | path: . 13 | extra_requirements: 14 | - docs 15 | 16 | sphinx: 17 | configuration: docs/conf.py 18 | -------------------------------------------------------------------------------- /examples/copystruct.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Structure 2 | from pathlib import Path 3 | 4 | here = Path(__file__).parent 5 | 6 | with here.joinpath("../samples/large_nether.mcstructure").open("rb") as f: 7 | struct = Structure.load(f) 8 | 9 | with here.joinpath("out/large_nether.mcstructure").open("wb") as f: 10 | struct.dump(f) 11 | -------------------------------------------------------------------------------- /examples/simple.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Block, Structure 2 | from pathlib import Path 3 | 4 | here = Path(__file__).parent 5 | 6 | struct = Structure( 7 | (2, 2, 2), # Size of the Structure 声明结构大小,注意这是大小,其坐标从0开始 8 | Block("minecraft:air"), # pre-fill blocks 预填充方块 9 | ) 10 | 11 | # fill blocks 填充方块 12 | struct.set_block((1, 1, 1), Block("minecraft:iron_block")) 13 | 14 | # write into file 写入文件 15 | with here.joinpath("out/simple.mcstructure").open("wb") as f: 16 | struct.dump(f) 17 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # mcstructure 2 | 3 | ```{include} ../README.md 4 | :start-after: 5 | :end-before: 6 | ``` 7 | 8 | ```{toctree} 9 | --- 10 | caption: Introduction 11 | maxdepth: 2 12 | --- 13 | installation 14 | quickstart 15 | ``` 16 | 17 | ```{toctree} 18 | --- 19 | caption: Content 20 | maxdepth: 2 21 | --- 22 | api 23 | ``` 24 | 25 | ```{toctree} 26 | --- 27 | caption: Development 28 | maxdepth: 2 29 | --- 30 | contributing 31 | changelog 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /examples/waterlogged.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Block, Structure 2 | import nbtx 3 | from pathlib import Path 4 | 5 | here = Path(__file__).parent 6 | 7 | struct = Structure((2, 2, 2), Block("minecraft:air")) 8 | 9 | struct.set_block( 10 | (0, 0, 0), 11 | Block( 12 | "minecraft:birch_stairs", 13 | upside_down_bit=False, 14 | weirdo_direction=2, 15 | waterlogged=True 16 | ) 17 | ) 18 | 19 | with here.joinpath("out/waterlogged.mcstructure").open("wb") as f: 20 | struct.dump(f) 21 | -------------------------------------------------------------------------------- /examples/diagonal.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Block, Structure 2 | from pathlib import Path 3 | 4 | here = Path(__file__).parent 5 | 6 | BLOCK = Block("minecraft:grass") 7 | 8 | struct = Structure((10, 10, 10), Block("minecraft:air")) 9 | ( 10 | struct.set_block((0, 0, 0), BLOCK) 11 | .set_block((1, 1, 1), BLOCK) 12 | .set_block((2, 2, 2), BLOCK) 13 | .set_block((3, 3, 3), BLOCK) 14 | .set_block((4, 4, 4), BLOCK) 15 | .set_block((5, 5, 5), BLOCK) 16 | ) 17 | 18 | with here.joinpath("out/diagonal.mcstructure").open("wb") as f: 19 | struct.dump(f) 20 | -------------------------------------------------------------------------------- /examples/read_and_display.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Structure 2 | from pathlib import Path 3 | 4 | here = Path(__file__).parent 5 | 6 | with here.joinpath("../samples/dirt_house.mcstructure").open("rb") as f: 7 | struct = Structure.load(f) 8 | print("Dirt House") 9 | print(struct) 10 | 11 | with here.joinpath("../samples/waterlogged.mcstructure").open("rb") as f: 12 | struct = Structure.load(f) 13 | print("Waterlogged Stairs") 14 | print(struct.palette) 15 | 16 | with here.joinpath("../samples/cmdblock.mcstructure").open("rb") as f: 17 | struct = Structure.load(f) 18 | print("Command Block") 19 | print(struct.palette) 20 | -------------------------------------------------------------------------------- /examples/area.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Block, Structure 2 | from pathlib import Path 3 | 4 | here = Path(__file__).parent 5 | 6 | struct = Structure( 7 | (6, 6, 6), # Size of the Structure 声明结构大小,注意这是大小,其坐标从0开始 8 | Block("minecraft:air"), # pre-fill blocks 预填充方块 9 | ) 10 | 11 | # fill blocks 填充方块 12 | struct.set_blocks((1, 1, 1), (4, 1, 4), Block("minecraft:iron_block")) 13 | 14 | # display stuffs in it 显示一些东西 15 | print(struct.get_structure()) 16 | print(struct._get_str_array(with_namespace=False, with_states=True)) 17 | 18 | # write into file 写入文件 19 | with here.joinpath("out/area.mcstructure").open("wb") as f: 20 | struct.dump(f) 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /tests/test_block.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Block 2 | 3 | 4 | def test_stringify() -> None: 5 | b = Block("minecraft:wool", color="red") 6 | assert b.stringify() == 'minecraft:wool ["color"="red"]' 7 | assert b.stringify(with_namespace=False) == 'wool ["color"="red"]' 8 | assert b.stringify(with_states=False) == "minecraft:wool" 9 | assert b.stringify(with_namespace=False, with_states=False) == "wool" 10 | 11 | b = Block("minecraft:dispenser", triggered_bit=True) 12 | assert ( 13 | b.stringify(with_namespace=False, with_states=True) 14 | == 'dispenser ["triggered_bit"=true]' 15 | ) 16 | 17 | b = Block("minecraft:jigsaw", rotation=12) 18 | assert ( 19 | b.stringify(with_namespace=False, with_states=True) == 'jigsaw ["rotation"=12]' 20 | ) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jonas da Silva 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "setuptools-scm"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "mcstructure" 7 | version = "0.0.1b6" 8 | description = "Read and write Minecraft .mcstructure files." 9 | dependencies = [ 10 | "numpy==2.3.0", 11 | "nbtx==0.4.1", 12 | ] 13 | requires-python = ">=3.13" 14 | readme = "README.md" 15 | license = {text = "MIT"} 16 | classifiers = [ 17 | "Development Status :: 4 - Beta", 18 | "Intended Audience :: Developers", 19 | "Intended Audience :: Education", 20 | "License :: OSI Approved :: MIT License", 21 | "Natural Language :: English", 22 | "Programming Language :: Python :: 3", 23 | "Topic :: Games/Entertainment", 24 | "Typing :: Typed", 25 | ] 26 | keywords = [ 27 | "mcstructure", 28 | "Minecraft", 29 | ] 30 | 31 | [project.optional-dependencies] 32 | dev = [ 33 | "pytest>=8", 34 | "mypy==1.16.1", 35 | "black==25.1.0", 36 | ] 37 | docs = [ 38 | "furo", 39 | "myst-parser>=2", 40 | "Sphinx>=7", 41 | "sphinx-copybutton>=0", 42 | ] 43 | 44 | [tool.mypy] 45 | strict = true 46 | 47 | [project.urls] 48 | "Documentation" = "https://mcstructure.readthedocs.io/en/latest/" 49 | "Source Code" = "https://github.com/phoenixr-codes/mcstructure/" 50 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #:========================================== 2 | # Sphinx Documentation Builder Configuration 3 | #:========================================== 4 | 5 | 6 | ##################### 7 | # Project Information 8 | 9 | project = "mcstructure" 10 | copyright = "2023, Jonas da Silva" 11 | author = "Jonas da Silva" 12 | release = "0.0.1b5" 13 | 14 | 15 | ############### 16 | # Configuration 17 | 18 | extensions = [ 19 | "sphinx.ext.autodoc", 20 | "sphinx.ext.intersphinx", 21 | "sphinx.ext.napoleon", 22 | "myst_parser", 23 | "sphinx_copybutton", 24 | ] 25 | 26 | ########################### 27 | # Intersphinx Configuration 28 | 29 | intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} 30 | 31 | ####################### 32 | # Autodoc Configuration 33 | 34 | autodoc_default_options = dict.fromkeys( 35 | """ 36 | members 37 | inherited-members 38 | undoc-members 39 | """.split(), 40 | True, 41 | ) 42 | autodoc_member_order = "bysource" 43 | autodoc_typehints = "both" 44 | autoclass_content = "both" 45 | 46 | 47 | ############ 48 | # Find Files 49 | 50 | templates_path = ["_templates"] 51 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 52 | 53 | 54 | ########################### 55 | # HTML Output Configuration 56 | 57 | html_theme = "furo" 58 | html_logo = "../logo.png" 59 | html_favicon = "../logo.png" 60 | html_static_path = ["_static"] 61 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | ## Loading/Initializing a Structure 4 | 5 | You can load a pre-existing structure by using the {py:meth}`mcstructure.Structure.load` method. 6 | 7 | ```python 8 | from mcstructure import Structure 9 | 10 | with open("house.mcstructure", "rb") as f: 11 | struct = Structure.load(f) 12 | ``` 13 | 14 | Alternatively, create a new empty structure: 15 | 16 | ```python 17 | from mcstructure import Block, Structure 18 | 19 | struct = Structure( 20 | (12, 20, 5), # size of the structure 21 | Block("minecraft:wool", color="red") # fill the structure with red wool 22 | ) 23 | ``` 24 | 25 | 26 | ## Inspecting a Structure 27 | 28 | ```python 29 | struct.size # The size of the structure 30 | struct.get_block(1, 1, 1) # Get the block at (1, 1, 1) 31 | struct.get_structure() # Get the numpy array representing the structure 32 | ``` 33 | 34 | 35 | ## Viewing a Structure 36 | 37 | You can simply print the structure object: 38 | 39 | ```python 40 | print(struct) 41 | ``` 42 | 43 | 44 | ## Modifying a Structure 45 | 46 | Simple modifications such as placing blocks: 47 | 48 | ```python 49 | from mcstructure import Block 50 | 51 | struct.set_block((1, 1, 1), Block("minecraft:wool", color="red")) # Places a red wool block at (1, 1, 1) 52 | struct.set_blocks((1, 1, 1), (5, 5, 5), Block("minecraft:wool", color="red")) # Places red wool blocks from (1, 1, 1) to (5, 5, 5) 53 | ``` 54 | 55 | Advanced modifications can be achieved with [numpy](https://numpy.org/doc/stable/index.html). 56 | A structure object consists of an array of integers representing IDs. Each ID is associated 57 | with a {py:class}`mcstructure.Block` in a list. 58 | 59 | ```python 60 | import numpy as np 61 | 62 | np.rot90(struct.structure) 63 | struct.structure = np.transpose(struct.structure) 64 | ``` 65 | 66 | 67 | ## Saving a Structure 68 | 69 | ```python 70 | with open("mansion.mcstructure", "wb") as f: 71 | struct.dump(f) 72 | ``` 73 | -------------------------------------------------------------------------------- /tests/test_structure.py: -------------------------------------------------------------------------------- 1 | from mcstructure import has_suitable_size, STRUCTURE_MAX_SIZE, Structure, Block 2 | import pytest 3 | 4 | 5 | def test_oversized() -> None: 6 | assert has_suitable_size(STRUCTURE_MAX_SIZE) 7 | assert not has_suitable_size((65, 0, 0)) 8 | 9 | 10 | def test_resize_larger() -> None: 11 | dirt = Block("minecraft:dirt") 12 | air = Block("minecraft:air") 13 | struct = Structure((2, 2, 2), fill=dirt) 14 | struct.resize((4, 4, 4), air) 15 | assert struct.get_block((3, 3, 3)) == air 16 | assert struct.get_block((0, 0, 0)) == dirt 17 | assert struct.get_block((1, 1, 1)) == dirt 18 | assert struct.get_block((2, 2, 2)) == air 19 | 20 | 21 | def test_resize_smaller() -> None: 22 | dirt = Block("minecraft:dirt") 23 | struct = Structure((4, 4, 4), fill=dirt) 24 | struct.resize((2, 2, 2)) 25 | with pytest.raises(IndexError): 26 | assert struct.get_block((3, 3, 3)) is None 27 | with pytest.raises(IndexError): 28 | assert struct.get_block((2, 2, 2)) is None 29 | assert struct.get_block((1, 1, 1)) == dirt 30 | assert struct.get_block((0, 0, 0)) == dirt 31 | 32 | 33 | def test_combine() -> None: 34 | dirt = Block("minecraft:dirt") 35 | air = Block("minecraft:air") 36 | void = Block("minecraft:structure_void") 37 | struct_a = Structure((1, 2, 2), fill=air) 38 | struct_b = Structure((1, 2, 2), fill=dirt) 39 | struct_c = struct_a.combine(struct_b, (0, 1, 1)) 40 | 41 | # Check the combined structure 42 | assert struct_c.get_block((0, 0, 0)) == air 43 | assert struct_c.get_block((0, 0, 1)) == air 44 | assert struct_c.get_block((0, 0, 2)) == void 45 | assert struct_c.get_block((0, 1, 0)) == air 46 | assert struct_c.get_block((0, 1, 1)) == dirt 47 | assert struct_c.get_block((0, 1, 2)) == dirt 48 | assert struct_c.get_block((0, 2, 0)) == void 49 | assert struct_c.get_block((0, 2, 1)) == dirt 50 | assert struct_c.get_block((0, 2, 2)) == dirt 51 | -------------------------------------------------------------------------------- /scripts/packer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Packs mcstructure files into a usable MCBE add-on. 3 | """ 4 | 5 | from datetime import datetime 6 | import json 7 | from pathlib import Path 8 | from uuid import uuid4 9 | from tempfile import TemporaryDirectory 10 | import zipfile 11 | 12 | here = Path(__file__).parent 13 | root = here.parent 14 | logo = root / "logo.png" 15 | structures = root / "examples/out" 16 | 17 | 18 | def main() -> None: 19 | timestamp = datetime.now().strftime("%d.%m %H:%M") 20 | name = f"mcstructure pack - {timestamp}" 21 | manifest = { 22 | "format_version": 2, 23 | "header": { 24 | "name": name, 25 | "description": "github.com/phoenixr-codes/mcstructure", 26 | "uuid": str(uuid4()), 27 | "version": [1, 0, 0], 28 | "min_engine_version": [1, 21, 0], 29 | }, 30 | "modules": [{"type": "data", "uuid": str(uuid4()), "version": [1, 0, 0]}], 31 | "metadata": {"product_type": "addon"}, 32 | } 33 | with TemporaryDirectory() as temp_dir_str: 34 | temp_dir = Path(temp_dir_str) 35 | with (temp_dir / "manifest.json").open("w") as f: 36 | json.dump(manifest, f) 37 | with (temp_dir / "pack_icon.png").open("wb") as f: 38 | f.write(logo.read_bytes()) 39 | structures_dir = temp_dir / "structures" 40 | structures_dir.mkdir() 41 | for structure_path in structures.glob("*.mcstructure"): 42 | destination = structures_dir / structure_path.name 43 | destination.write_bytes(structure_path.read_bytes()) 44 | print(f"adding {structure_path}") 45 | destination = (root / name.replace(".", "_")).with_suffix(".mcpack") 46 | with zipfile.ZipFile(destination, "w") as archive: 47 | for source_path in temp_dir.glob("**/*"): 48 | print(f"archiving {source_path}") 49 | archive.write(source_path, arcname=source_path.relative_to(temp_dir)) 50 | print(f"bundled add-on at {destination}") 51 | 52 | 53 | if __name__ == "__main__": 54 | main() 55 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 |

2 | mcstructure logo 7 |

mcstructure

8 |

9 | 《我的世界》.mcstructure 文件的读写操作库 10 |

11 |

12 | 13 | 14 | 🌍 此介绍文件亦可见于以下语种: 15 | 16 | * [🇬🇧 英文](./README.md) 17 | * [🇩🇪 德文](./README_DE.md) *(未及时更新)* 18 | 19 | 21 |

22 | 23 | [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge)](https://github.com/psf/black) 24 | [![Documentation Status](https://readthedocs.org/projects/mcstructure/badge/?style=for-the-badge&version=latest)](https://mcstructure.readthedocs.io/en/latest/?badge=latest) 25 | [![PyPI](https://img.shields.io/pypi/v/mcstructure?style=for-the-badge)](https://pypi.org/project/mcstructure) 26 | 27 | _在整个项目中(且更官方地是在“大一统更新”("Better Together Update")之后,名词《我的世界》("Minecraft")所指代的均为基岩版("Bedrock Edition")。_ 28 | 29 | _此库中的所有特性也是仅仅针对基岩版的。_ 30 | 31 | > [!WARNING] 32 | > **请注意** 33 | > 此项目目前仍属于 **BETA** 版本,因此部分特性可能并未启用或在未经示警的情况下频繁更改。 34 | 35 | 36 | 37 | 此库可以让您以代码实现对 *《我的世界》* 结构文件的创建与编辑。 38 | 您能够凭此而将您自己的结构存储为 `.mcstructure` 文件,因而可以使之用于行为包中,或者发展出更厉害的用途。 39 | 40 | 当然,通过此库您也可以通过此库来读取(read)这些在游戏中通过*结构方块*保存的结构文件,从而获取(identify)其中存储之方块与实体之类。 41 | 42 | 43 | 44 | 下载安装 45 | ------------ 46 | 47 | ```console 48 | pip install mcstructure 49 | ``` 50 | 51 | 52 | 基本用法 53 | ----------- 54 | 55 | 1. 写入结构文件 56 | 57 | ```python 58 | # 导入库 59 | from mcstructure import Block, Structure 60 | 61 | # 实例化对象 Structure 62 | struct = Structure( 63 | (7, 7, 7), # 声明结构大小 64 | Block("minecraft:wool", color = "red") # 预填充方块 65 | ) 66 | 67 | # 设定方块 68 | (struct 69 | .set_block((1, 1, 1), Block("minecraft:grass")) 70 | .set_block((2, 2, 2), Block("minecraft:grass")) 71 | .set_block((3, 3, 3), Block("minecraft:grass")) 72 | .set_block((4, 4, 4), Block("minecraft:grass")) 73 | .set_block((5, 5, 5), Block("minecraft:grass")) 74 | .set_block((6, 6, 6), Block("minecraft:grass")) 75 | ) 76 | 77 | # 写入文件 78 | with open("house.mcstructure", "wb") as f: 79 | struct.dump(f) 80 | 81 | ``` 82 | 83 | 2. 读取结构文件 84 | 85 | ```python 86 | with open("house.mcstructure", "rb") as f: 87 | struct = Structure.load(f) 88 | 89 | ``` 90 | 91 | 妙用链接 92 | ------------ 93 | 94 | * 📖 [此项目之文档](https://mcstructure.readthedocs.io/en/latest/) 95 | * 📁 [此项目之源码](https://github.com/phoenixr-codes/mcstructure) 96 | * 🐍 [PyPI](https://pypi.org/project/mcstructure/) 97 | 98 | ### 其他资源 99 | 100 | * 👋 [结构方块的简介](https://learn.microsoft.com/en-us/minecraft/creator/documents/introductiontostructureblocks) 101 | * 📖 [基岩版维基](https://wiki.bedrock.dev/nbt/mcstructure.html#file-format) 102 | _译注:文件结构文档已经被我翻译了,详见[我的译本](https://gitee.com/TriM-Organization/mcstructure/blob/main/docs/mcstructure%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84.md)_ 103 | -------------------------------------------- 104 | 105 | NOT AN OFFICIAL MINECRAFT PRODUCT. 106 | NOT APPROVED BY OR ASSOCIATED WITH MOJANG. 107 | 108 | 此项目并非一个官方 《我的世界》(*Minecraft*)项目 109 | 110 | 此项目不隶属或关联于 Mojang Studios 111 | -------------------------------------------------------------------------------- /README_DE.md: -------------------------------------------------------------------------------- 1 |

2 | mcstructure logo 7 |

mcstructure

8 |

9 | Lesen und Schreiben von Minecraft .mcstructure-Dateien. 10 |

11 |

12 | 13 | 🌍 Diese README-Datei ist auch in den folgenden 14 | Sprachen verfügbar: 15 | 16 | * [🇨🇳 Chinesisch](./README_CN.md) 17 | * [🇬🇧 Englisch](./README.md) 18 | 19 |

20 | 21 | [![Code-Stil](https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge)](https://github.com/psf/black) 22 | [![Dokumentationsstatus](https://readthedocs.org/projects/mcstructure/badge/?style=for-the-badge&version=latest)](https://mcstructure.readthedocs.io/en/latest/?badge=latest) 23 | [![PyPI](https://img.shields.io/pypi/v/mcstructure?style=for-the-badge)](https://pypi.org/project/mcstructure) 24 | 25 | _Im gesamten Projekt (und offiziell seit dem 26 | "Better Together Update") ist mit "Minecraft" 27 | die Version gemeint, welche auch als "Bedrock 28 | Edition" bekannt ist._ 29 | 30 | _Features dieser Bibliothek sind nur in der 31 | oben genannten Edition von Minecraft nützlich._ 32 | 33 | > [!WARNING] 34 | > Dieses Projekt ist momentan in der BETA Version. 35 | > Die meisten Features sind somit instabil. 36 | 37 | Diese Bibliothek ermöglicht es innerhalb eines 38 | Programmes Minecraft Strukturen zu editieren. 39 | Diese können dann als ``.mcstructure``-Datei 40 | gespeichert werden und beispielsweise in einem 41 | Verhaltenspaket genutzt werden. 42 | 43 | Es ist auch möglich, Blöcke und Entitäten zu 44 | identifizieren, welche mit einem Konstruktionsblock 45 | innerhalb des Spiels gespeichert wurden. 46 | 47 | 48 | Installation 49 | ------------ 50 | 51 | ```console 52 | pip install mcstructure 53 | ``` 54 | 55 | 56 | Demonstration 57 | ------------- 58 | 59 | ```python 60 | from mcstructure import Block, Structure 61 | 62 | struct = Structure( 63 | (7, 7, 7), 64 | Block("minecraft:wool", color = "red") 65 | ) 66 | 67 | (struct 68 | .set_block((1, 1, 1), Block("minecraft:grass")) 69 | .set_block((2, 2, 2), Block("minecraft:grass")) 70 | .set_block((3, 3, 3), Block("minecraft:grass")) 71 | .set_block((4, 4, 4), Block("minecraft:grass")) 72 | .set_block((5, 5, 5), Block("minecraft:grass")) 73 | .set_block((6, 6, 6), Block("minecraft:grass")) 74 | ) 75 | 76 | with open("house.mcstructure", "wb") as f: 77 | struct.dump(f) 78 | ``` 79 | 80 | ```python 81 | with open("house.mcstructure", "rb") as f: 82 | struct = Structure.load(f) 83 | ``` 84 | 85 | 86 | Nützliche Links 87 | --------------- 88 | 89 | * [📖 Dokumentation](https://mcstructure.readthedocs.io/en/latest/) 90 | * [📁 Quellcode](https://github.com/phoenixr-codes/mcstructure) 91 | * [🐍 PyPI](https://pypi.org/project/mcstructure/) 92 | 93 | ### Externe Resourcen 94 | 95 | * [👋 Einführung zu Konstruktionsblöcken](https://learn.microsoft.com/en-us/minecraft/creator/documents/introductiontostructureblocks) 96 | * [📖 Bedrock Wiki](https://wiki.bedrock.dev/nbt/mcstructure.html#file-format) 97 | 98 | 99 | -------------------------------------------- 100 | 101 | NOT AN OFFICIAL MINECRAFT PRODUCT. 102 | NOT APPROVED BY OR ASSOCIATED WITH MOJANG. 103 | 104 | KEIN OFFIZIELLES MINECRAFT PRODUKT. 105 | NICHT VON MOJANG GENEHMIGT ODER MIT MOJANG 106 | ASSOZIIERT. 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | mcstructure logo 7 |

mcstructure

8 |

9 | Read and write Minecraft .mcstructure files. 10 |

11 |

12 | 13 | 🌍 This README is also available in the following 14 | languages: 15 | 16 | * 🇨🇳 [Chinese](./README_CN.md) 17 | * 🇩🇪 [German](./README_DE.md) 18 | 19 | 20 | 22 |

23 | 24 | [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge)](https://github.com/psf/black) 25 | [![Documentation Status](https://readthedocs.org/projects/mcstructure/badge/?style=for-the-badge&version=latest)](https://mcstructure.readthedocs.io/en/latest/?badge=latest) 26 | [![PyPI](https://img.shields.io/pypi/v/mcstructure?style=for-the-badge)](https://pypi.org/project/mcstructure) 27 | 28 | _In the entire project ([and officially since 29 | the "Better Together Update"](https://www.minecraft.net/de-de/article/all-news-e3)) the term 30 | "Minecraft" refers to the edition of Minecraft 31 | that is also known as the "Bedrock Edition"._ 32 | 33 | _Features that this library provides are only 34 | useful for the above named edition of Minecraft._ 35 | 36 | > [!WARNING] 37 | > This project is currently in the **BETA** version. Some 38 | > features may not work as expected and might change without backwards compability or deprecation warnings. 39 | 40 | 41 | 42 | This library lets you programmatically create 43 | and edit Minecraft structures. You are able to 44 | save these as ``.mcstructure`` files and for 45 | example use them in behavior packs. 46 | 47 | You may as well read them to identify blocks and 48 | and entities that were saved with a Structure 49 | Block in-game. 50 | 51 | 52 | 53 | Installation 54 | ------------ 55 | 56 | ```console 57 | pip install mcstructure 58 | ``` 59 | 60 | 61 | Basic Usage 62 | ----------- 63 | 64 | ```python 65 | from mcstructure import Block, Structure 66 | 67 | struct = Structure( 68 | (7, 7, 7), 69 | Block("minecraft:wool", color = "red") 70 | ) 71 | 72 | (struct 73 | .set_block((1, 1, 1), Block("minecraft:grass")) 74 | .set_block((2, 2, 2), Block("minecraft:grass")) 75 | .set_block((3, 3, 3), Block("minecraft:grass")) 76 | .set_block((4, 4, 4), Block("minecraft:grass")) 77 | .set_block((5, 5, 5), Block("minecraft:grass")) 78 | .set_block((6, 6, 6), Block("minecraft:grass")) 79 | ) 80 | 81 | with open("house.mcstructure", "wb") as f: 82 | struct.dump(f) 83 | ``` 84 | 85 | ```python 86 | with open("house.mcstructure", "rb") as f: 87 | struct = Structure.load(f) 88 | ``` 89 | 90 | 91 | References 92 | ---------- 93 | 94 | * 📖 [Documentation](https://mcstructure.readthedocs.io/en/latest/) 95 | * 📁 [Source Code](https://github.com/phoenixr-codes/mcstructure) 96 | * 🐍 [PyPI](https://pypi.org/project/mcstructure/) 97 | 98 | ### External Resources 99 | 100 | * 👋 [Introduction to Structure Blocks](https://learn.microsoft.com/en-us/minecraft/creator/documents/introductiontostructureblocks) 101 | * 📖 [Bedrock Wiki](https://wiki.bedrock.dev/nbt/mcstructure.html#file-format) 102 | 103 | 104 | -------------------------------------------- 105 | 106 | NOT AN OFFICIAL MINECRAFT PRODUCT. 107 | NOT APPROVED BY OR ASSOCIATED WITH MOJANG. 108 | -------------------------------------------------------------------------------- /examples/custom_entity.py: -------------------------------------------------------------------------------- 1 | from mcstructure import Block, Structure 2 | import nbtx 3 | from pathlib import Path 4 | 5 | here = Path(__file__).parent 6 | 7 | struct = Structure((2, 2, 2), Block("minecraft:air")) 8 | 9 | struct.add_entity( 10 | nbtx.TagCompound( 11 | "", 12 | [ 13 | nbtx.TagList( 14 | "Armor", 15 | child_id=nbtx.TagCompound.id(), 16 | value=[ 17 | # head slot 18 | nbtx.TagCompound( 19 | "", 20 | [ 21 | nbtx.TagCompound("Block", [ 22 | nbtx.TagString("name", "minecraft:player_head"), 23 | nbtx.TagCompound("states", [ 24 | nbtx.TagInt("facing_direction", 0), 25 | ]), 26 | nbtx.TagInt("version", 18168865), 27 | ]), 28 | nbtx.TagByte("Count", 1), 29 | nbtx.TagShort("Damage", 0), 30 | nbtx.TagString("Name", "minecraft:player_head"), 31 | nbtx.TagByte("WasPickedUp", 0), 32 | ] 33 | ), 34 | 35 | # chest slot 36 | nbtx.TagCompound( 37 | "", 38 | [ 39 | nbtx.TagByte("Count", 0), 40 | nbtx.TagShort("Damage", 0), 41 | nbtx.TagString("Name", ""), 42 | nbtx.TagByte("WasPickedUp", 0), 43 | ] 44 | ), 45 | 46 | # legs slot 47 | nbtx.TagCompound( 48 | "", 49 | [ 50 | nbtx.TagByte("Count", 0), 51 | nbtx.TagShort("Damage", 0), 52 | nbtx.TagString("Name", ""), 53 | nbtx.TagByte("WasPickedUp", 0), 54 | ] 55 | ), 56 | 57 | # feet slot 58 | nbtx.TagCompound( 59 | "", 60 | [ 61 | nbtx.TagByte("Count", 0), 62 | nbtx.TagShort("Damage", 0), 63 | nbtx.TagString("Name", ""), 64 | nbtx.TagByte("WasPickedUp", 0), 65 | ] 66 | ), 67 | ] 68 | ), 69 | nbtx.TagList( 70 | "Attributes", 71 | child_id=nbtx.TagCompound.id(), 72 | value=[] 73 | ), 74 | nbtx.TagList( 75 | "Pos", 76 | child_id=nbtx.TagFloat.id(), 77 | value=[ 78 | nbtx.TagFloat("", 0), 79 | nbtx.TagFloat("", 0), 80 | nbtx.TagFloat("", 0), 81 | ] 82 | ), 83 | nbtx.TagList( 84 | "Rotation", 85 | child_id=nbtx.TagFloat.id(), 86 | value=[ 87 | nbtx.TagFloat("", -90.), 88 | nbtx.TagFloat("", 0.), 89 | ] 90 | ), 91 | nbtx.TagList( 92 | "definitions", 93 | child_id=nbtx.TagString.id(), 94 | value=[ 95 | nbtx.TagString("", "+minecraft:armor_stand"), 96 | ] 97 | ), 98 | nbtx.TagString("identifier", "minecraft:armor_stand"), 99 | ] 100 | ) 101 | ) 102 | 103 | with here.joinpath("out/custom_entity.mcstructure").open("wb") as f: 104 | struct.dump(f) 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # artifacts 2 | *.mcpack 3 | *.mcaddon 4 | 5 | # editors & IDEs 6 | .vscode 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | share/python-wheels/ 31 | *.egg-info/ 32 | .installed.cfg 33 | *.egg 34 | MANIFEST 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | *.py,cover 57 | .hypothesis/ 58 | .pytest_cache/ 59 | cover/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | db.sqlite3-journal 70 | 71 | # Flask stuff: 72 | instance/ 73 | .webassets-cache 74 | 75 | # Scrapy stuff: 76 | .scrapy 77 | 78 | # Sphinx documentation 79 | docs/_build/ 80 | 81 | # PyBuilder 82 | .pybuilder/ 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | # For a library or package, you might want to ignore these files since the code is 94 | # intended to run in multiple environments; otherwise, check them in: 95 | # .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # poetry 105 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 106 | # This is especially recommended for binary packages to ensure reproducibility, and is more 107 | # commonly ignored for libraries. 108 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 109 | #poetry.lock 110 | 111 | # pdm 112 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 113 | #pdm.lock 114 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 115 | # in version control. 116 | # https://pdm.fming.dev/#use-with-ide 117 | .pdm.toml 118 | 119 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 120 | __pypackages__/ 121 | 122 | # Celery stuff 123 | celerybeat-schedule 124 | celerybeat.pid 125 | 126 | # SageMath parsed files 127 | *.sage.py 128 | 129 | # Environments 130 | .env 131 | .venv 132 | env/ 133 | venv/ 134 | ENV/ 135 | env.bak/ 136 | venv.bak/ 137 | 138 | # Spyder project settings 139 | .spyderproject 140 | .spyproject 141 | 142 | # Rope project settings 143 | .ropeproject 144 | 145 | # mkdocs documentation 146 | /site 147 | 148 | # mypy 149 | .mypy_cache/ 150 | .dmypy.json 151 | dmypy.json 152 | 153 | # Pyre type checker 154 | .pyre/ 155 | 156 | # pytype static type analyzer 157 | .pytype/ 158 | 159 | # Cython debug symbols 160 | cython_debug/ 161 | 162 | # PyCharm 163 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 164 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 165 | # and can be added to the global gitignore or merged into this file. For a more nuclear 166 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 167 | #.idea/ 168 | -------------------------------------------------------------------------------- /src/mcstructure/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Read and write Minecraft ``.mcstructure`` files. 3 | """ 4 | 5 | from __future__ import annotations 6 | 7 | from collections.abc import Sequence 8 | from dataclasses import dataclass, field 9 | from functools import partial 10 | from itertools import repeat 11 | from typing import Any, BinaryIO, Tuple, Self, cast 12 | 13 | import numpy as np 14 | from numpy.typing import NDArray 15 | import nbtx 16 | 17 | 18 | Coordinate = Tuple[int, int, int] 19 | BlockStateValue = str | bool | int 20 | 21 | 22 | COMPABILITY_VERSION: int = 17959425 23 | """ 24 | The compability version for a block. The four bytes making up this 25 | integer determine the game version number. For example, ``17879555`` is 26 | ``01 10 D2 03`` in hex meaning ``1.16.210.03``. 27 | """ 28 | 29 | STRUCTURE_MAX_SIZE: tuple[int, int, int] = (64, 384, 64) 30 | """ 31 | The maximum size a structure can have. This does not apply for 32 | structures created externally and thus is not affect structures 33 | created with this library. 34 | """ 35 | 36 | 37 | def _get_deep(obj: Any, *keys: Any, default: Any = None) -> Any: 38 | for key in keys: 39 | try: 40 | obj = obj[key] 41 | except (KeyError, ValueError): 42 | return default 43 | return obj 44 | 45 | 46 | def is_valid_structure_name(name: str, *, with_prefix: bool = False) -> bool: 47 | """ 48 | Validates the structure name. 49 | 50 | .. seealso: https://minecraft.fandom.com/wiki/Structure_Block 51 | 52 | Parameters 53 | ---------- 54 | name 55 | The name of the structure. 56 | 57 | with_prefix 58 | Whether to take the prefix (e.g. ``mystructure:``) 59 | into account. 60 | """ 61 | if with_prefix: 62 | name = name.replace(":", "", 1) 63 | 64 | return all((char.isalnum() and char in "-_") for char in name) 65 | 66 | 67 | def has_suitable_size(size: tuple[int, int, int]) -> bool: 68 | """ 69 | Returns ``False`` if ``size`` is greater than the size of 70 | structures that can be created within Minecraft. 71 | 72 | .. seealso:: :const:`STRUCTURE_MAX_SIZE` 73 | """ 74 | return all(map(lambda n: n[0] <= n[1], zip(size, STRUCTURE_MAX_SIZE))) 75 | 76 | 77 | @dataclass(init=False, slots=True) 78 | class Block: 79 | """ 80 | Attributes 81 | ---------- 82 | name 83 | The name of the block. 84 | 85 | states 86 | The states of the block. 87 | 88 | Examples 89 | -------- 90 | .. code-block:: 91 | 92 | Block("minecraft:beehive", honey_level=4) 93 | Block("minecraft:grass") 94 | 95 | """ 96 | 97 | identifier: str 98 | states: dict[str, BlockStateValue] 99 | waterlogged: bool = field(kw_only=True, default=False) 100 | block_entity_data: Sequence[nbtx.Tag[Any, Any]] | None = field(kw_only=True, default=None) 101 | tick_queue_data: nbtx.Tag[Any, Any] | None = field(kw_only=True, default=None) 102 | 103 | def __init__( 104 | self, 105 | identifier: str, 106 | *, 107 | waterlogged: bool = False, 108 | block_entity_data: Sequence[nbtx.Tag[Any, Any]] | None = None, 109 | tick_queue_data: nbtx.Tag[Any, Any] | None = None, # TODO: this can be turned into a Python type and then converted into NBT 110 | **states: BlockStateValue 111 | ): 112 | """ 113 | Parameters 114 | ---------- 115 | identifier 116 | The identifier of the block (e.g. "minecraft:wool"). 117 | 118 | states 119 | The block states such as ``color`` or ``stone_type``. 120 | This varies by every block. 121 | 122 | .. seealso:: https://learn.microsoft.com/en-us/minecraft/creator/reference/content/blockreference/examples/blockstateslist 123 | 124 | waterlogged 125 | Whether this block is waterlogged. 126 | 127 | block_entity_data 128 | Block entity data like the content of a container. 129 | 130 | tick_queue_data 131 | 132 | """ 133 | self.identifier = identifier 134 | self.states = states 135 | self.waterlogged = waterlogged 136 | self.block_entity_data = block_entity_data 137 | self.tick_queue_data = tick_queue_data 138 | 139 | def __str__(self) -> str: 140 | return self.stringify() 141 | 142 | def stringify( 143 | self, 144 | *, 145 | with_namespace: bool = True, 146 | with_states: bool = True, 147 | ) -> str: 148 | """Returns a human-readable representation of the block. 149 | 150 | This format matches the one used for commands like `setblock`. 151 | 152 | Parameters 153 | ---------- 154 | with_namespace 155 | Whether to include the block's namespace. 156 | 157 | with_states 158 | Whether to include the block's states. 159 | """ 160 | result = "" 161 | if with_namespace and (ns := self.namespace) is not None: 162 | result += ns + ":" 163 | result += self.name 164 | if with_states: 165 | result += " [" 166 | for key, value in self.states.items(): 167 | result += f'"{key}"=' 168 | if isinstance(value, str): 169 | result += f'"{value}"' 170 | elif isinstance(value, bool): 171 | result += str(value).lower() 172 | elif isinstance(value, int): 173 | result += str(value) 174 | else: 175 | raise ValueError("block state value must be str, bool or int") 176 | result += "]" 177 | return result 178 | 179 | @property 180 | def namespace_and_name(self) -> tuple[str | None, str]: 181 | """The namespace and the name of the block. 182 | 183 | Examples 184 | -------- 185 | .. code-block:: python 186 | 187 | >>> from mcstructure import Block 188 | >>> 189 | >>> block = Block("minecraft:wool", color="red") 190 | >>> block.namespace_and_name 191 | ("minecraft", "wool") 192 | >>> 193 | >>> block = Block("foobar") 194 | >>> block.namespace_and_name 195 | (None, "foobar") 196 | 197 | """ 198 | if ":" in self.identifier: 199 | ns, name = self.identifier.split(":", 1) 200 | return ns, name 201 | 202 | return (None, self.identifier) 203 | 204 | @property 205 | def name(self) -> str: 206 | """The name of the block. 207 | 208 | Examples 209 | -------- 210 | .. code-block:: python 211 | 212 | >>> from mcstructure import Block 213 | >>> 214 | >>> block = Block("minecraft:wool", color="red") 215 | >>> block.name 216 | "wool" 217 | >>> 218 | >>> block = Block("foobar") 219 | >>> block.name 220 | "foobar" 221 | 222 | """ 223 | return self.namespace_and_name[1] 224 | 225 | @property 226 | def namespace(self) -> str | None: 227 | """The namespace of the block. 228 | 229 | Examples 230 | -------- 231 | .. code-block:: python 232 | 233 | >>> from mcstructure import Block 234 | >>> 235 | >>> block = Block("minecraft:wool", color="red") 236 | >>> block.namespace 237 | "minecraft" 238 | >>> 239 | >>> block = Block("foobar") 240 | >>> (block.namespace,) 241 | (None,) 242 | 243 | """ 244 | return self.namespace_and_name[0] 245 | 246 | _WATER = Block("minecraft:water", liquid_depth=0) 247 | 248 | class Structure: 249 | """Class representing a Minecraft structure that consists of blocks and entities. 250 | 251 | Attributes 252 | ---------- 253 | structure 254 | The numpy array representing the blocks in the structure. Each entry is an ID 255 | associated with a block present in :meth:`palette`. 256 | 257 | entities 258 | A list of entities the structure contains. 259 | 260 | _size 261 | Size of the structure stored internally. 262 | 263 | _palette 264 | Internal list of blocks that are used within the structure. The position of 265 | the block in the list represents its ID. Modification of this list should be 266 | done with caution and if you do so consider modifying :attr:`structure` 267 | appropriately. Instead use methods like :meth:`set_block`. 268 | """ 269 | 270 | def __init__( 271 | self, 272 | size: tuple[int, int, int], 273 | fill: Block | None = Block("minecraft:air"), 274 | ) -> None: 275 | """ 276 | Parameters 277 | ---------- 278 | size 279 | The size of the structure. 280 | 281 | fill 282 | Fill the structure with this block at 283 | creation of a new structure object. 284 | 285 | If this is set to ``None`` the structure 286 | is filled with "Structure Void" blocks. 287 | 288 | ``'minecraft:air'`` is used as default. 289 | """ 290 | self.structure: NDArray[np.intc] 291 | self.entities: list[nbtx.Tag[Any, Any]] = [] 292 | 293 | self._size = size 294 | self._palette: list[Block] = [] 295 | 296 | if fill is None: 297 | self.structure = np.full(size, -1, dtype=np.intc) 298 | 299 | else: 300 | self.structure = np.zeros(size, dtype=np.intc) 301 | self._palette.append(fill) 302 | 303 | @classmethod 304 | def load(cls, file: BinaryIO) -> Self: 305 | """ 306 | Loads a structure from a file. 307 | 308 | Examples 309 | -------- 310 | .. code-block:: python 311 | 312 | from mcstructure import Structure 313 | 314 | with open("house.mcstructure", "rb") as f: 315 | struct = Structure.load(f) 316 | 317 | Parameters 318 | ---------- 319 | file 320 | File object to read. 321 | """ 322 | nbt_root = nbtx.load(file, endianness="little") 323 | assert isinstance(nbt_root, nbtx.TagCompound) 324 | nbt_body_data = nbt_root 325 | nbt_body = nbt_root.as_python() 326 | 327 | size: tuple[int, int, int] = tuple(nbt_body["size"]) 328 | 329 | struct = cls(size, None) 330 | 331 | struct.structure = np.array( 332 | [x for x in nbt_body["structure"]["block_indices"][0]], 333 | dtype=np.intc, 334 | ).reshape(size) 335 | 336 | struct.entities = nbt_body["structure"]["entities"] 337 | 338 | waterlogged_mask: list[bool] = [value != -1 for value in nbt_body["structure"]["block_indices"][1]] 339 | 340 | nbt_structure = nbt_body_data["structure"] 341 | assert isinstance(nbt_structure, nbtx.TagCompound) 342 | nbt_palette = nbt_structure["palette"] 343 | assert isinstance(nbt_palette, nbtx.TagCompound) 344 | nbt_default_palette = nbt_palette["default"] 345 | assert isinstance(nbt_default_palette, nbtx.TagCompound) 346 | block_position_data = nbt_default_palette["block_position_data"] 347 | 348 | for (index, block) in enumerate(nbt_body["structure"]["palette"]["default"]["block_palette"]): 349 | maybe_block_entity_data = _get_deep(block_position_data, str(index), "block_entity_data") 350 | if maybe_block_entity_data is None: 351 | block_entity_data = None 352 | else: 353 | block_entity_data = maybe_block_entity_data.value 354 | 355 | struct._palette.append( 356 | Block( 357 | block["name"], 358 | **(block["states"]), 359 | waterlogged=waterlogged_mask[index], 360 | block_entity_data=block_entity_data, 361 | tick_queue_data=_get_deep(block_position_data, str(index), "tick_queue_data"), 362 | ) 363 | ) 364 | 365 | return struct 366 | 367 | @property 368 | def size(self) -> tuple[int, int, int]: 369 | """The size of the structure.""" 370 | return self._size 371 | 372 | @property 373 | def palette(self) -> list[Block]: 374 | """A copy of the palette.""" 375 | palette = self._palette.copy() 376 | return palette 377 | 378 | def __repr__(self) -> str: 379 | return repr(self._get_str_array()) 380 | 381 | def __str__(self) -> str: 382 | return str(self._get_str_array()) 383 | 384 | def _get_str_array( 385 | self, *, with_namespace: bool = False, with_states: bool = False 386 | ) -> NDArray[Any]: 387 | """ 388 | Returns a numpy array where each entry is a 389 | readable string of the corresponding block. 390 | 391 | Parameters 392 | ---------- 393 | with_namespace 394 | Adds the namespace to the string if present. 395 | 396 | with_states 397 | Adds the block states to the string if present. 398 | """ 399 | arr = self.get_structure().copy() 400 | vec = np.vectorize( 401 | partial( 402 | Block.stringify, with_namespace=with_namespace, with_states=with_states 403 | ) 404 | ) 405 | return cast(NDArray[Any], vec(arr)) 406 | 407 | def _add_block_to_palette(self, block: Block | None) -> int: 408 | """ 409 | Adds a block to the palette. 410 | 411 | Parameters 412 | ---------- 413 | block 414 | The block to add. If this is set to ``None`` 415 | "Structure Void" will be used. 416 | 417 | Returns 418 | ------- 419 | The position of the block in the palette. This is 420 | ``-1`` when ``None`` is used as ``block``. 421 | """ 422 | if block is None: 423 | return -1 424 | 425 | if (index := self._block_index(block, ignore_waterlogged=True, ignore_block_entity_data=True, ignore_tick_queue_data=True)) is not None: 426 | return index 427 | 428 | self._palette.append(block) 429 | return len(self._palette) - 1 430 | 431 | def _block_index(self, block: Block, *, ignore_states: bool = False, ignore_waterlogged: bool = False, ignore_block_entity_data: bool = False, ignore_tick_queue_data: bool = False) -> int | None: 432 | for (index, palette_block) in enumerate(self._palette): 433 | if block.namespace_and_name != palette_block.namespace_and_name: 434 | continue 435 | if not ignore_states and block.states != palette_block.states: 436 | continue 437 | if not ignore_waterlogged and block.waterlogged != palette_block.waterlogged: 438 | continue 439 | if not ignore_block_entity_data and block.block_entity_data != palette_block.block_entity_data: 440 | continue 441 | if not ignore_tick_queue_data and block.tick_queue_data != palette_block.tick_queue_data: 442 | continue 443 | return index 444 | return None 445 | 446 | def _water_index(self) -> int: 447 | """ 448 | Returns the index of water with liquid depth 0. 449 | 450 | If water is not present in the palette, then water will be added to the 451 | palette. 452 | """ 453 | for (index, block) in enumerate(self._palette): 454 | if block == _WATER: 455 | return index 456 | return self._add_block_to_palette(_WATER) 457 | 458 | def get_structure(self) -> NDArray[Any]: 459 | """ 460 | Returns the structure as a numpy array filled 461 | with the corresponding block objects. 462 | """ 463 | arr = np.full( 464 | self.structure.shape, Block("minecraft:structure_void"), dtype=object 465 | ) 466 | for key, block in enumerate(self._palette): 467 | arr[self.structure == key] = block 468 | return arr 469 | 470 | def as_nbt(self) -> nbtx.TagCompound[Any, Any]: 471 | """ 472 | Serialize the structure as a NBT data. 473 | 474 | Examples 475 | -------- 476 | .. code-block:: python 477 | 478 | from mcstructure import Structure 479 | 480 | struct = Structure((5, 5, 5), None) 481 | nbt = struct.as_nbt() 482 | print(nbt.pretty()) 483 | """ 484 | return nbtx.TagCompound[Any, Any]( 485 | name="", 486 | value=[ 487 | nbtx.TagInt(name="format_version", value=1), 488 | nbtx.TagList( 489 | name="size", 490 | child_id=nbtx.TagInt.id(), 491 | value=[ 492 | nbtx.TagInt("", self._size[0]), 493 | nbtx.TagInt("", self._size[1]), 494 | nbtx.TagInt("", self._size[2]), 495 | ], 496 | ), 497 | nbtx.TagCompound( 498 | name="structure", 499 | value=[ 500 | nbtx.TagList( 501 | name="block_indices", 502 | child_id=nbtx.TagList.id(), 503 | value=[ 504 | nbtx.TagList( 505 | name="", 506 | child_id=nbtx.TagInt.id(), 507 | value=[ 508 | nbtx.TagInt("", i.item()) 509 | for i in self.structure.flatten() 510 | ], 511 | ), 512 | nbtx.TagList( 513 | name="", 514 | child_id=nbtx.TagInt.id(), 515 | value=[ 516 | nbtx.TagInt( 517 | "", 518 | self._water_index() if self._palette[i.item()].waterlogged else -1 519 | ) 520 | for i in self.structure.flatten() 521 | ], 522 | ), 523 | ], 524 | ), 525 | nbtx.TagList( 526 | name="entities", 527 | child_id=nbtx.TagCompound.id(), 528 | value=self.entities, 529 | ), 530 | nbtx.TagCompound( 531 | name="palette", 532 | value=[ 533 | nbtx.TagCompound( 534 | name="default", 535 | value=[ 536 | nbtx.TagList( 537 | name="block_palette", 538 | child_id=nbtx.TagCompound.id(), 539 | value=[ 540 | nbtx.TagCompound( 541 | name="", 542 | value=[ 543 | nbtx.TagString( 544 | name="name", 545 | value=block.identifier, 546 | ), 547 | nbtx.TagCompound( 548 | name="states", 549 | value=[ 550 | ( 551 | nbtx.TagInt( 552 | state_name, 553 | state_value, 554 | ) 555 | if isinstance( 556 | state_value, int 557 | ) 558 | else ( 559 | nbtx.TagString( 560 | state_name, 561 | state_value, 562 | ) 563 | if isinstance( 564 | state_value, 565 | str, 566 | ) 567 | else nbtx.TagByte( 568 | state_name, 569 | state_value, 570 | ) 571 | ) 572 | ) 573 | for state_name, state_value in block.states.items() 574 | ], 575 | ), 576 | nbtx.TagInt( 577 | name="version", 578 | value=COMPABILITY_VERSION, 579 | ), 580 | ], 581 | ) 582 | for block in self._palette 583 | ], 584 | ), 585 | nbtx.TagCompound( 586 | name="block_position_data", value=[ 587 | nbtx.TagCompound( 588 | name=str(index), 589 | value=[ 590 | *([nbtx.TagCompound( 591 | name="block_entity_data", 592 | value=block.block_entity_data 593 | )] if block.block_entity_data is not None else []), 594 | *([nbtx.TagList( 595 | name="tick_queue_data", 596 | child_id=nbtx.TagCompound.id(), 597 | value=[] # TODO 598 | )] if block.tick_queue_data is not None else []) 599 | ] 600 | ) 601 | for (index, block) in enumerate(self._palette) 602 | if block.block_entity_data is not None or block.tick_queue_data is not None 603 | ] 604 | ), 605 | ], 606 | ) 607 | ], 608 | ), 609 | ], 610 | ), 611 | nbtx.TagList( 612 | name="structure_world_origin", 613 | value=[ 614 | nbtx.TagInt(name="", value=0), 615 | nbtx.TagInt(name="", value=0), 616 | nbtx.TagInt(name="", value=0), 617 | ], 618 | child_id=nbtx.TagInt.id(), 619 | ), 620 | ], 621 | ) 622 | 623 | def dump(self, file: BinaryIO) -> None: 624 | """ 625 | Serialize the structure as a NBT file. 626 | 627 | Examples 628 | -------- 629 | .. code-block:: python 630 | 631 | from mcstructure import Structure 632 | 633 | struct = Structure((5, 5, 5), None) 634 | with open("house.mcstructure", "wb") as f: 635 | struct.dump(f) 636 | 637 | Parameters 638 | ---------- 639 | file 640 | File object to write to. 641 | """ 642 | nbt = self.as_nbt() 643 | nbtx.dump(nbt, file, endianness="little") 644 | 645 | def get_block(self, coordinate: Coordinate) -> Block | None: 646 | """ 647 | Returns the block in a specific position. 648 | 649 | Parameters 650 | ---------- 651 | coordinate 652 | The coordinte of the block. 653 | """ 654 | x, y, z = coordinate 655 | return ( 656 | self._palette[self.structure[x, y, z]] 657 | if self.structure[x, y, z] >= 0 658 | else Block("minecraft:structure_void") 659 | ) 660 | 661 | def add_entity(self, nbt_data: nbtx.Tag[Any, Any]) -> Self: 662 | """ 663 | Adds an entity to the structure. 664 | 665 | Parameters 666 | ---------- 667 | nbt_data 668 | The NBT data of the entity to add. 669 | The best approach would be to export a structure from the game, 670 | display the NBT data and use that as a reference. 671 | """ 672 | self.entities.append(nbt_data) 673 | return self 674 | 675 | def set_block( 676 | self, 677 | coordinate: Coordinate, 678 | block: Block | None, 679 | ) -> Self: 680 | """ 681 | Places a block in the structure. 682 | 683 | Parameters 684 | ---------- 685 | coordinate 686 | Relative coordinates of the block's position. 687 | 688 | block 689 | The block to place. If this is set to ``None`` 690 | "Structure Void" blocks will be used. 691 | """ 692 | x, y, z = coordinate 693 | 694 | ident = self._add_block_to_palette(block) 695 | 696 | self.structure[x, y, z] = ident 697 | return self 698 | 699 | def set_blocks( 700 | self, 701 | from_coordinate: Coordinate, 702 | to_coordinate: Coordinate, 703 | block: Block, 704 | ) -> Self: 705 | """ 706 | Fills an area in the structure with blocks. 707 | 708 | Notes 709 | ----- 710 | Both start and end points are included. 711 | 712 | Parameters 713 | ---------- 714 | from_coordinate 715 | Relative coordinates of the start edge. 716 | 717 | to_coordinate 718 | Relative coordinates of the end edge. 719 | 720 | block 721 | The block to place. If this is set to ``None`` 722 | "Structure Void" blocks will be used to fill. 723 | """ 724 | fx, fy, fz = from_coordinate 725 | tx, ty, tz = to_coordinate 726 | 727 | ident = self._add_block_to_palette(block) 728 | self.structure[fx : tx + 1, fy : ty + 1, fz : tz + 1] = np.array( 729 | [ 730 | [ 731 | [ident for k in range(abs(fz - tz) + 1)] 732 | for j in range(abs(fy - ty) + 1) 733 | ] 734 | for i in range(abs(fx - tx) + 1) 735 | ], 736 | dtype=np.intc, 737 | ).reshape([abs(i) + 1 for i in (fx - tx, fy - ty, fz - tz)]) 738 | return self 739 | 740 | def resize( 741 | self, size: tuple[int, int, int], fill: Block | None = Block("minecraft:air") 742 | ) -> Self: 743 | """ 744 | Resizes the structure. 745 | 746 | This function erases blocks that are out of bounds and fill newly 747 | created space with ``fill`` (default is air). 748 | 749 | Parameters 750 | ---------- 751 | size 752 | The new size of the structure. 753 | 754 | fill 755 | The block to fill newly created space with. If this is set to 756 | ``None`` (default) then "Structure Void" blocks will be used. 757 | """ 758 | ident = self._add_block_to_palette(fill) 759 | self._size = size 760 | 761 | if self.structure.shape == size: 762 | # No need to resize 763 | return self 764 | 765 | # Create new structure filled with the fill block identifier 766 | if fill is not None: 767 | new_structure = np.full(size, ident, dtype=np.intc) 768 | else: 769 | new_structure = np.full(size, -1, dtype=np.intc) # Fill Structure void 770 | 771 | # Calculate the overlap region (what we can copy from old to new) 772 | old_size = self.structure.shape 773 | copy_size = tuple(min(old_size[i], size[i]) for i in range(3)) 774 | 775 | # Copy the existing Blocks that fits into the new size 776 | if all(s > 0 for s in copy_size): 777 | new_structure[: copy_size[0], : copy_size[1], : copy_size[2]] = ( 778 | self.structure[: copy_size[0], : copy_size[1], : copy_size[2]] 779 | ) 780 | 781 | self.structure = new_structure 782 | return self 783 | 784 | def combine(self, other: Structure, position: Coordinate = (0, 0, 0)) -> Structure: 785 | """ 786 | Combines another structure into this structure. 787 | 788 | Parameters 789 | ---------- 790 | other 791 | The other structure to combine with. 792 | 793 | position 794 | The position to place the other structure at. 795 | 796 | note: This position is relative to the 797 | current structure's origin. ( no negative coordinates allowed ) 798 | 799 | Returns 800 | ------- 801 | Structure 802 | The combined structure. 803 | """ 804 | 805 | ox, oy, oz = position 806 | 807 | if ox < 0 or oy < 0 or oz < 0: 808 | raise ValueError("Negative coordinates are not allowed.") 809 | 810 | # Calculate the new size needed to accommodate both structures 811 | end_pos = (ox + other._size[0], oy + other._size[1], oz + other._size[2]) 812 | new_size = ( 813 | max(self._size[0], end_pos[0]), 814 | max(self._size[1], end_pos[1]), 815 | max(self._size[2], end_pos[2]), 816 | ) 817 | 818 | combined = Structure(new_size, None) 819 | 820 | # Copy the current structure's palette 821 | combined._palette = self._palette.copy() 822 | 823 | # Copy the current structure 824 | combined.structure[: self._size[0], : self._size[1], : self._size[2]] = ( 825 | self.structure 826 | ) 827 | 828 | # Create mapping from other's palette indices to combined palette indices 829 | other_to_combined_mapping = { 830 | other_idx: combined._add_block_to_palette(block) 831 | for other_idx, block in enumerate(other._palette) 832 | } 833 | 834 | # Remap the entire other structure using vectorized operations 835 | remapped_structure = other.structure.copy() 836 | 837 | # For non-structure-void blocks, apply the palette mapping 838 | for other_idx, combined_idx in other_to_combined_mapping.items(): 839 | block_mask = other.structure == other_idx 840 | remapped_structure[block_mask] = combined_idx 841 | 842 | # Overlay the remapped structure onto the combined structure 843 | combined.structure[ox:, oy:, oz:] = remapped_structure[:, :, :] 844 | 845 | return combined 846 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "alabaster" 5 | version = "1.0.0" 6 | description = "A light, configurable Sphinx theme" 7 | optional = true 8 | python-versions = ">=3.10" 9 | groups = ["main"] 10 | markers = "extra == \"docs\"" 11 | files = [ 12 | {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, 13 | {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, 14 | ] 15 | 16 | [[package]] 17 | name = "babel" 18 | version = "2.17.0" 19 | description = "Internationalization utilities" 20 | optional = true 21 | python-versions = ">=3.8" 22 | groups = ["main"] 23 | markers = "extra == \"docs\"" 24 | files = [ 25 | {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, 26 | {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, 27 | ] 28 | 29 | [package.extras] 30 | dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] 31 | 32 | [[package]] 33 | name = "beautifulsoup4" 34 | version = "4.13.4" 35 | description = "Screen-scraping library" 36 | optional = true 37 | python-versions = ">=3.7.0" 38 | groups = ["main"] 39 | markers = "extra == \"docs\"" 40 | files = [ 41 | {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, 42 | {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, 43 | ] 44 | 45 | [package.dependencies] 46 | soupsieve = ">1.2" 47 | typing-extensions = ">=4.0.0" 48 | 49 | [package.extras] 50 | cchardet = ["cchardet"] 51 | chardet = ["chardet"] 52 | charset-normalizer = ["charset-normalizer"] 53 | html5lib = ["html5lib"] 54 | lxml = ["lxml"] 55 | 56 | [[package]] 57 | name = "black" 58 | version = "25.1.0" 59 | description = "The uncompromising code formatter." 60 | optional = true 61 | python-versions = ">=3.9" 62 | groups = ["main"] 63 | markers = "extra == \"dev\"" 64 | files = [ 65 | {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, 66 | {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, 67 | {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, 68 | {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, 69 | {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, 70 | {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, 71 | {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, 72 | {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, 73 | {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, 74 | {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, 75 | {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, 76 | {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, 77 | {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, 78 | {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, 79 | {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, 80 | {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, 81 | {file = "black-25.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0"}, 82 | {file = "black-25.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f"}, 83 | {file = "black-25.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e"}, 84 | {file = "black-25.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355"}, 85 | {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, 86 | {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, 87 | ] 88 | 89 | [package.dependencies] 90 | click = ">=8.0.0" 91 | mypy-extensions = ">=0.4.3" 92 | packaging = ">=22.0" 93 | pathspec = ">=0.9.0" 94 | platformdirs = ">=2" 95 | 96 | [package.extras] 97 | colorama = ["colorama (>=0.4.3)"] 98 | d = ["aiohttp (>=3.10)"] 99 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 100 | uvloop = ["uvloop (>=0.15.2)"] 101 | 102 | [[package]] 103 | name = "certifi" 104 | version = "2025.6.15" 105 | description = "Python package for providing Mozilla's CA Bundle." 106 | optional = true 107 | python-versions = ">=3.7" 108 | groups = ["main"] 109 | markers = "extra == \"docs\"" 110 | files = [ 111 | {file = "certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057"}, 112 | {file = "certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b"}, 113 | ] 114 | 115 | [[package]] 116 | name = "charset-normalizer" 117 | version = "3.4.2" 118 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 119 | optional = true 120 | python-versions = ">=3.7" 121 | groups = ["main"] 122 | markers = "extra == \"docs\"" 123 | files = [ 124 | {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, 125 | {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, 126 | {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, 127 | {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, 128 | {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, 129 | {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, 130 | {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, 131 | {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, 132 | {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, 133 | {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, 134 | {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, 135 | {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, 136 | {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, 137 | {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, 138 | {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, 139 | {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, 140 | {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, 141 | {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, 142 | {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, 143 | {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, 144 | {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, 145 | {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, 146 | {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, 147 | {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, 148 | {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, 149 | {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, 150 | {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, 151 | {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, 152 | {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, 153 | {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, 154 | {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, 155 | {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, 156 | {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, 157 | {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, 158 | {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, 159 | {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, 160 | {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, 161 | {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, 162 | {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, 163 | {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, 164 | {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, 165 | {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, 166 | {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, 167 | {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, 168 | {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, 169 | {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, 170 | {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, 171 | {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, 172 | {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, 173 | {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, 174 | {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, 175 | {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, 176 | {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, 177 | {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, 178 | {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, 179 | {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, 180 | {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, 181 | {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, 182 | {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, 183 | {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, 184 | {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, 185 | {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, 186 | {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, 187 | {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, 188 | {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, 189 | {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, 190 | {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, 191 | {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, 192 | {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, 193 | {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, 194 | {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, 195 | {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, 196 | {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, 197 | {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, 198 | {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, 199 | {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, 200 | {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, 201 | {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, 202 | {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, 203 | {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, 204 | {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, 205 | {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, 206 | {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, 207 | {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, 208 | {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, 209 | {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, 210 | {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, 211 | {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, 212 | {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, 213 | {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, 214 | {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, 215 | {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, 216 | ] 217 | 218 | [[package]] 219 | name = "click" 220 | version = "8.2.1" 221 | description = "Composable command line interface toolkit" 222 | optional = true 223 | python-versions = ">=3.10" 224 | groups = ["main"] 225 | markers = "extra == \"dev\"" 226 | files = [ 227 | {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, 228 | {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, 229 | ] 230 | 231 | [package.dependencies] 232 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 233 | 234 | [[package]] 235 | name = "colorama" 236 | version = "0.4.6" 237 | description = "Cross-platform colored terminal text." 238 | optional = true 239 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 240 | groups = ["main"] 241 | markers = "sys_platform == \"win32\" and (extra == \"dev\" or extra == \"docs\") or extra == \"dev\" and platform_system == \"Windows\"" 242 | files = [ 243 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 244 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 245 | ] 246 | 247 | [[package]] 248 | name = "docutils" 249 | version = "0.21.2" 250 | description = "Docutils -- Python Documentation Utilities" 251 | optional = true 252 | python-versions = ">=3.9" 253 | groups = ["main"] 254 | markers = "extra == \"docs\"" 255 | files = [ 256 | {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, 257 | {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, 258 | ] 259 | 260 | [[package]] 261 | name = "furo" 262 | version = "2024.8.6" 263 | description = "A clean customisable Sphinx documentation theme." 264 | optional = true 265 | python-versions = ">=3.8" 266 | groups = ["main"] 267 | markers = "extra == \"docs\"" 268 | files = [ 269 | {file = "furo-2024.8.6-py3-none-any.whl", hash = "sha256:6cd97c58b47813d3619e63e9081169880fbe331f0ca883c871ff1f3f11814f5c"}, 270 | {file = "furo-2024.8.6.tar.gz", hash = "sha256:b63e4cee8abfc3136d3bc03a3d45a76a850bada4d6374d24c1716b0e01394a01"}, 271 | ] 272 | 273 | [package.dependencies] 274 | beautifulsoup4 = "*" 275 | pygments = ">=2.7" 276 | sphinx = ">=6.0,<9.0" 277 | sphinx-basic-ng = ">=1.0.0.beta2" 278 | 279 | [[package]] 280 | name = "idna" 281 | version = "3.10" 282 | description = "Internationalized Domain Names in Applications (IDNA)" 283 | optional = true 284 | python-versions = ">=3.6" 285 | groups = ["main"] 286 | markers = "extra == \"docs\"" 287 | files = [ 288 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 289 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 290 | ] 291 | 292 | [package.extras] 293 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] 294 | 295 | [[package]] 296 | name = "imagesize" 297 | version = "1.4.1" 298 | description = "Getting image size from png/jpeg/jpeg2000/gif file" 299 | optional = true 300 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 301 | groups = ["main"] 302 | markers = "extra == \"docs\"" 303 | files = [ 304 | {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, 305 | {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, 306 | ] 307 | 308 | [[package]] 309 | name = "iniconfig" 310 | version = "2.1.0" 311 | description = "brain-dead simple config-ini parsing" 312 | optional = true 313 | python-versions = ">=3.8" 314 | groups = ["main"] 315 | markers = "extra == \"dev\"" 316 | files = [ 317 | {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, 318 | {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, 319 | ] 320 | 321 | [[package]] 322 | name = "jinja2" 323 | version = "3.1.6" 324 | description = "A very fast and expressive template engine." 325 | optional = true 326 | python-versions = ">=3.7" 327 | groups = ["main"] 328 | markers = "extra == \"docs\"" 329 | files = [ 330 | {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, 331 | {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, 332 | ] 333 | 334 | [package.dependencies] 335 | MarkupSafe = ">=2.0" 336 | 337 | [package.extras] 338 | i18n = ["Babel (>=2.7)"] 339 | 340 | [[package]] 341 | name = "markdown-it-py" 342 | version = "3.0.0" 343 | description = "Python port of markdown-it. Markdown parsing, done right!" 344 | optional = true 345 | python-versions = ">=3.8" 346 | groups = ["main"] 347 | markers = "extra == \"docs\"" 348 | files = [ 349 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 350 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 351 | ] 352 | 353 | [package.dependencies] 354 | mdurl = ">=0.1,<1.0" 355 | 356 | [package.extras] 357 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 358 | code-style = ["pre-commit (>=3.0,<4.0)"] 359 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 360 | linkify = ["linkify-it-py (>=1,<3)"] 361 | plugins = ["mdit-py-plugins"] 362 | profiling = ["gprof2dot"] 363 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 364 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 365 | 366 | [[package]] 367 | name = "markupsafe" 368 | version = "3.0.2" 369 | description = "Safely add untrusted strings to HTML/XML markup." 370 | optional = true 371 | python-versions = ">=3.9" 372 | groups = ["main"] 373 | markers = "extra == \"docs\"" 374 | files = [ 375 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, 376 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, 377 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, 378 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, 379 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, 380 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, 381 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, 382 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, 383 | {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, 384 | {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, 385 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, 386 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, 387 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, 388 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, 389 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, 390 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, 391 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, 392 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, 393 | {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, 394 | {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, 395 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, 396 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, 397 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, 398 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, 399 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, 400 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, 401 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, 402 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, 403 | {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, 404 | {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, 405 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, 406 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, 407 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, 408 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, 409 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, 410 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, 411 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, 412 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, 413 | {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, 414 | {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, 415 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, 416 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, 417 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, 418 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, 419 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, 420 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, 421 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, 422 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, 423 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, 424 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, 425 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, 426 | {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, 427 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, 428 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, 429 | {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, 430 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, 431 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, 432 | {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, 433 | {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, 434 | {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, 435 | {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, 436 | ] 437 | 438 | [[package]] 439 | name = "mdit-py-plugins" 440 | version = "0.4.2" 441 | description = "Collection of plugins for markdown-it-py" 442 | optional = true 443 | python-versions = ">=3.8" 444 | groups = ["main"] 445 | markers = "extra == \"docs\"" 446 | files = [ 447 | {file = "mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636"}, 448 | {file = "mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5"}, 449 | ] 450 | 451 | [package.dependencies] 452 | markdown-it-py = ">=1.0.0,<4.0.0" 453 | 454 | [package.extras] 455 | code-style = ["pre-commit"] 456 | rtd = ["myst-parser", "sphinx-book-theme"] 457 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 458 | 459 | [[package]] 460 | name = "mdurl" 461 | version = "0.1.2" 462 | description = "Markdown URL utilities" 463 | optional = true 464 | python-versions = ">=3.7" 465 | groups = ["main"] 466 | markers = "extra == \"docs\"" 467 | files = [ 468 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 469 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 470 | ] 471 | 472 | [[package]] 473 | name = "mypy" 474 | version = "1.16.1" 475 | description = "Optional static typing for Python" 476 | optional = true 477 | python-versions = ">=3.9" 478 | groups = ["main"] 479 | markers = "extra == \"dev\"" 480 | files = [ 481 | {file = "mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a"}, 482 | {file = "mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72"}, 483 | {file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea"}, 484 | {file = "mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574"}, 485 | {file = "mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d"}, 486 | {file = "mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6"}, 487 | {file = "mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc"}, 488 | {file = "mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782"}, 489 | {file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507"}, 490 | {file = "mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca"}, 491 | {file = "mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4"}, 492 | {file = "mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6"}, 493 | {file = "mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d"}, 494 | {file = "mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9"}, 495 | {file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79"}, 496 | {file = "mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15"}, 497 | {file = "mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd"}, 498 | {file = "mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b"}, 499 | {file = "mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438"}, 500 | {file = "mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536"}, 501 | {file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f"}, 502 | {file = "mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359"}, 503 | {file = "mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be"}, 504 | {file = "mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee"}, 505 | {file = "mypy-1.16.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7fc688329af6a287567f45cc1cefb9db662defeb14625213a5b7da6e692e2069"}, 506 | {file = "mypy-1.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e198ab3f55924c03ead626ff424cad1732d0d391478dfbf7bb97b34602395da"}, 507 | {file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09aa4f91ada245f0a45dbc47e548fd94e0dd5a8433e0114917dc3b526912a30c"}, 508 | {file = "mypy-1.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13c7cd5b1cb2909aa318a90fd1b7e31f17c50b242953e7dd58345b2a814f6383"}, 509 | {file = "mypy-1.16.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:58e07fb958bc5d752a280da0e890c538f1515b79a65757bbdc54252ba82e0b40"}, 510 | {file = "mypy-1.16.1-cp39-cp39-win_amd64.whl", hash = "sha256:f895078594d918f93337a505f8add9bd654d1a24962b4c6ed9390e12531eb31b"}, 511 | {file = "mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37"}, 512 | {file = "mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab"}, 513 | ] 514 | 515 | [package.dependencies] 516 | mypy_extensions = ">=1.0.0" 517 | pathspec = ">=0.9.0" 518 | typing_extensions = ">=4.6.0" 519 | 520 | [package.extras] 521 | dmypy = ["psutil (>=4.0)"] 522 | faster-cache = ["orjson"] 523 | install-types = ["pip"] 524 | mypyc = ["setuptools (>=50)"] 525 | reports = ["lxml"] 526 | 527 | [[package]] 528 | name = "mypy-extensions" 529 | version = "1.1.0" 530 | description = "Type system extensions for programs checked with the mypy type checker." 531 | optional = true 532 | python-versions = ">=3.8" 533 | groups = ["main"] 534 | markers = "extra == \"dev\"" 535 | files = [ 536 | {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, 537 | {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, 538 | ] 539 | 540 | [[package]] 541 | name = "myst-parser" 542 | version = "4.0.1" 543 | description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," 544 | optional = true 545 | python-versions = ">=3.10" 546 | groups = ["main"] 547 | markers = "extra == \"docs\"" 548 | files = [ 549 | {file = "myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d"}, 550 | {file = "myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4"}, 551 | ] 552 | 553 | [package.dependencies] 554 | docutils = ">=0.19,<0.22" 555 | jinja2 = "*" 556 | markdown-it-py = ">=3.0,<4.0" 557 | mdit-py-plugins = ">=0.4.1,<1.0" 558 | pyyaml = "*" 559 | sphinx = ">=7,<9" 560 | 561 | [package.extras] 562 | code-style = ["pre-commit (>=4.0,<5.0)"] 563 | linkify = ["linkify-it-py (>=2.0,<3.0)"] 564 | rtd = ["ipython", "sphinx (>=7)", "sphinx-autodoc2 (>=0.5.0,<0.6.0)", "sphinx-book-theme (>=1.1,<2.0)", "sphinx-copybutton", "sphinx-design", "sphinx-pyscript", "sphinx-tippy (>=0.4.3)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.9.0,<0.10.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] 565 | testing = ["beautifulsoup4", "coverage[toml]", "defusedxml", "pygments (<2.19)", "pytest (>=8,<9)", "pytest-cov", "pytest-param-files (>=0.6.0,<0.7.0)", "pytest-regressions", "sphinx-pytest"] 566 | testing-docutils = ["pygments", "pytest (>=8,<9)", "pytest-param-files (>=0.6.0,<0.7.0)"] 567 | 568 | [[package]] 569 | name = "nbtx" 570 | version = "0.4.1" 571 | description = "Minecraft NBT parser" 572 | optional = false 573 | python-versions = ">=3.13" 574 | groups = ["main"] 575 | files = [ 576 | {file = "nbtx-0.4.1-py3-none-any.whl", hash = "sha256:cea2abc9126ab5788cef5898fbb4e83396429dcb4437b4ba9417c8a7c07be755"}, 577 | {file = "nbtx-0.4.1.tar.gz", hash = "sha256:4cff89779883ef639a71ca31c39b56bca4f238b4f1bebcd9527c871eda22955b"}, 578 | ] 579 | 580 | [package.extras] 581 | dev = ["codespell (==2.4.1)", "coverage (==7.10.6)", "docstr-coverage (==2.3.2)", "isort (==6.0.1)", "mypy (==1.17.1)", "nox (==2025.5.1)", "pytest (==8.4.1)", "ruff (==0.12.12)"] 582 | docs = ["pdoc (==15.0.4)"] 583 | 584 | [[package]] 585 | name = "numpy" 586 | version = "2.3.0" 587 | description = "Fundamental package for array computing in Python" 588 | optional = false 589 | python-versions = ">=3.11" 590 | groups = ["main"] 591 | files = [ 592 | {file = "numpy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3c9fdde0fa18afa1099d6257eb82890ea4f3102847e692193b54e00312a9ae9"}, 593 | {file = "numpy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46d16f72c2192da7b83984aa5455baee640e33a9f1e61e656f29adf55e406c2b"}, 594 | {file = "numpy-2.3.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a0be278be9307c4ab06b788f2a077f05e180aea817b3e41cebbd5aaf7bd85ed3"}, 595 | {file = "numpy-2.3.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:99224862d1412d2562248d4710126355d3a8db7672170a39d6909ac47687a8a4"}, 596 | {file = "numpy-2.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2393a914db64b0ead0ab80c962e42d09d5f385802006a6c87835acb1f58adb96"}, 597 | {file = "numpy-2.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7729c8008d55e80784bd113787ce876ca117185c579c0d626f59b87d433ea779"}, 598 | {file = "numpy-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06d4fb37a8d383b769281714897420c5cc3545c79dc427df57fc9b852ee0bf58"}, 599 | {file = "numpy-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c39ec392b5db5088259c68250e342612db82dc80ce044cf16496cf14cf6bc6f8"}, 600 | {file = "numpy-2.3.0-cp311-cp311-win32.whl", hash = "sha256:ee9d3ee70d62827bc91f3ea5eee33153212c41f639918550ac0475e3588da59f"}, 601 | {file = "numpy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c55b6a860b0eb44d42341438b03513cf3879cb3617afb749ad49307e164edd"}, 602 | {file = "numpy-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:2e6a1409eee0cb0316cb64640a49a49ca44deb1a537e6b1121dc7c458a1299a8"}, 603 | {file = "numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba"}, 604 | {file = "numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e"}, 605 | {file = "numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2"}, 606 | {file = "numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459"}, 607 | {file = "numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a"}, 608 | {file = "numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a"}, 609 | {file = "numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67"}, 610 | {file = "numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc"}, 611 | {file = "numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570"}, 612 | {file = "numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd"}, 613 | {file = "numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea"}, 614 | {file = "numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a"}, 615 | {file = "numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959"}, 616 | {file = "numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe"}, 617 | {file = "numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb"}, 618 | {file = "numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0"}, 619 | {file = "numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f"}, 620 | {file = "numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8"}, 621 | {file = "numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270"}, 622 | {file = "numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f"}, 623 | {file = "numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5"}, 624 | {file = "numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e"}, 625 | {file = "numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8"}, 626 | {file = "numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3"}, 627 | {file = "numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f"}, 628 | {file = "numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808"}, 629 | {file = "numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8"}, 630 | {file = "numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad"}, 631 | {file = "numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b"}, 632 | {file = "numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555"}, 633 | {file = "numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61"}, 634 | {file = "numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb"}, 635 | {file = "numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944"}, 636 | {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80b46117c7359de8167cc00a2c7d823bdd505e8c7727ae0871025a86d668283b"}, 637 | {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:5814a0f43e70c061f47abd5857d120179609ddc32a613138cbb6c4e9e2dbdda5"}, 638 | {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ef6c1e88fd6b81ac6d215ed71dc8cd027e54d4bf1d2682d362449097156267a2"}, 639 | {file = "numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33a5a12a45bb82d9997e2c0b12adae97507ad7c347546190a18ff14c28bbca12"}, 640 | {file = "numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:54dfc8681c1906d239e95ab1508d0a533c4a9505e52ee2d71a5472b04437ef97"}, 641 | {file = "numpy-2.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e017a8a251ff4d18d71f139e28bdc7c31edba7a507f72b1414ed902cbe48c74d"}, 642 | {file = "numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6"}, 643 | ] 644 | 645 | [[package]] 646 | name = "packaging" 647 | version = "25.0" 648 | description = "Core utilities for Python packages" 649 | optional = true 650 | python-versions = ">=3.8" 651 | groups = ["main"] 652 | markers = "extra == \"dev\" or extra == \"docs\"" 653 | files = [ 654 | {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, 655 | {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, 656 | ] 657 | 658 | [[package]] 659 | name = "pathspec" 660 | version = "0.12.1" 661 | description = "Utility library for gitignore style pattern matching of file paths." 662 | optional = true 663 | python-versions = ">=3.8" 664 | groups = ["main"] 665 | markers = "extra == \"dev\"" 666 | files = [ 667 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 668 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 669 | ] 670 | 671 | [[package]] 672 | name = "platformdirs" 673 | version = "4.3.8" 674 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 675 | optional = true 676 | python-versions = ">=3.9" 677 | groups = ["main"] 678 | markers = "extra == \"dev\"" 679 | files = [ 680 | {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, 681 | {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, 682 | ] 683 | 684 | [package.extras] 685 | docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] 686 | test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] 687 | type = ["mypy (>=1.14.1)"] 688 | 689 | [[package]] 690 | name = "pluggy" 691 | version = "1.6.0" 692 | description = "plugin and hook calling mechanisms for python" 693 | optional = true 694 | python-versions = ">=3.9" 695 | groups = ["main"] 696 | markers = "extra == \"dev\"" 697 | files = [ 698 | {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, 699 | {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, 700 | ] 701 | 702 | [package.extras] 703 | dev = ["pre-commit", "tox"] 704 | testing = ["coverage", "pytest", "pytest-benchmark"] 705 | 706 | [[package]] 707 | name = "pygments" 708 | version = "2.19.2" 709 | description = "Pygments is a syntax highlighting package written in Python." 710 | optional = true 711 | python-versions = ">=3.8" 712 | groups = ["main"] 713 | markers = "extra == \"dev\" or extra == \"docs\"" 714 | files = [ 715 | {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, 716 | {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, 717 | ] 718 | 719 | [package.extras] 720 | windows-terminal = ["colorama (>=0.4.6)"] 721 | 722 | [[package]] 723 | name = "pytest" 724 | version = "8.4.1" 725 | description = "pytest: simple powerful testing with Python" 726 | optional = true 727 | python-versions = ">=3.9" 728 | groups = ["main"] 729 | markers = "extra == \"dev\"" 730 | files = [ 731 | {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, 732 | {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, 733 | ] 734 | 735 | [package.dependencies] 736 | colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} 737 | iniconfig = ">=1" 738 | packaging = ">=20" 739 | pluggy = ">=1.5,<2" 740 | pygments = ">=2.7.2" 741 | 742 | [package.extras] 743 | dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] 744 | 745 | [[package]] 746 | name = "pyyaml" 747 | version = "6.0.2" 748 | description = "YAML parser and emitter for Python" 749 | optional = true 750 | python-versions = ">=3.8" 751 | groups = ["main"] 752 | markers = "extra == \"docs\"" 753 | files = [ 754 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, 755 | {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, 756 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, 757 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, 758 | {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, 759 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, 760 | {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, 761 | {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, 762 | {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, 763 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, 764 | {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, 765 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, 766 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, 767 | {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, 768 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, 769 | {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, 770 | {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, 771 | {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, 772 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, 773 | {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, 774 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, 775 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, 776 | {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, 777 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, 778 | {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, 779 | {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, 780 | {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, 781 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, 782 | {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, 783 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, 784 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, 785 | {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, 786 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, 787 | {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, 788 | {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, 789 | {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, 790 | {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, 791 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, 792 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, 793 | {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, 794 | {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, 795 | {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, 796 | {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, 797 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, 798 | {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, 799 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, 800 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, 801 | {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, 802 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, 803 | {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, 804 | {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, 805 | {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, 806 | {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, 807 | ] 808 | 809 | [[package]] 810 | name = "requests" 811 | version = "2.32.4" 812 | description = "Python HTTP for Humans." 813 | optional = true 814 | python-versions = ">=3.8" 815 | groups = ["main"] 816 | markers = "extra == \"docs\"" 817 | files = [ 818 | {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, 819 | {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, 820 | ] 821 | 822 | [package.dependencies] 823 | certifi = ">=2017.4.17" 824 | charset_normalizer = ">=2,<4" 825 | idna = ">=2.5,<4" 826 | urllib3 = ">=1.21.1,<3" 827 | 828 | [package.extras] 829 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 830 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] 831 | 832 | [[package]] 833 | name = "roman-numerals-py" 834 | version = "3.1.0" 835 | description = "Manipulate well-formed Roman numerals" 836 | optional = true 837 | python-versions = ">=3.9" 838 | groups = ["main"] 839 | markers = "extra == \"docs\"" 840 | files = [ 841 | {file = "roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c"}, 842 | {file = "roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d"}, 843 | ] 844 | 845 | [package.extras] 846 | lint = ["mypy (==1.15.0)", "pyright (==1.1.394)", "ruff (==0.9.7)"] 847 | test = ["pytest (>=8)"] 848 | 849 | [[package]] 850 | name = "snowballstemmer" 851 | version = "3.0.1" 852 | description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." 853 | optional = true 854 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" 855 | groups = ["main"] 856 | markers = "extra == \"docs\"" 857 | files = [ 858 | {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, 859 | {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, 860 | ] 861 | 862 | [[package]] 863 | name = "soupsieve" 864 | version = "2.7" 865 | description = "A modern CSS selector implementation for Beautiful Soup." 866 | optional = true 867 | python-versions = ">=3.8" 868 | groups = ["main"] 869 | markers = "extra == \"docs\"" 870 | files = [ 871 | {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, 872 | {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, 873 | ] 874 | 875 | [[package]] 876 | name = "sphinx" 877 | version = "8.2.3" 878 | description = "Python documentation generator" 879 | optional = true 880 | python-versions = ">=3.11" 881 | groups = ["main"] 882 | markers = "extra == \"docs\"" 883 | files = [ 884 | {file = "sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3"}, 885 | {file = "sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348"}, 886 | ] 887 | 888 | [package.dependencies] 889 | alabaster = ">=0.7.14" 890 | babel = ">=2.13" 891 | colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} 892 | docutils = ">=0.20,<0.22" 893 | imagesize = ">=1.3" 894 | Jinja2 = ">=3.1" 895 | packaging = ">=23.0" 896 | Pygments = ">=2.17" 897 | requests = ">=2.30.0" 898 | roman-numerals-py = ">=1.0.0" 899 | snowballstemmer = ">=2.2" 900 | sphinxcontrib-applehelp = ">=1.0.7" 901 | sphinxcontrib-devhelp = ">=1.0.6" 902 | sphinxcontrib-htmlhelp = ">=2.0.6" 903 | sphinxcontrib-jsmath = ">=1.0.1" 904 | sphinxcontrib-qthelp = ">=1.0.6" 905 | sphinxcontrib-serializinghtml = ">=1.1.9" 906 | 907 | [package.extras] 908 | docs = ["sphinxcontrib-websupport"] 909 | lint = ["betterproto (==2.0.0b6)", "mypy (==1.15.0)", "pypi-attestations (==0.0.21)", "pyright (==1.1.395)", "pytest (>=8.0)", "ruff (==0.9.9)", "sphinx-lint (>=0.9)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.19.0.20250219)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241128)", "types-requests (==2.32.0.20241016)", "types-urllib3 (==1.26.25.14)"] 910 | test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "pytest-xdist[psutil] (>=3.4)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] 911 | 912 | [[package]] 913 | name = "sphinx-basic-ng" 914 | version = "1.0.0b2" 915 | description = "A modern skeleton for Sphinx themes." 916 | optional = true 917 | python-versions = ">=3.7" 918 | groups = ["main"] 919 | markers = "extra == \"docs\"" 920 | files = [ 921 | {file = "sphinx_basic_ng-1.0.0b2-py3-none-any.whl", hash = "sha256:eb09aedbabfb650607e9b4b68c9d240b90b1e1be221d6ad71d61c52e29f7932b"}, 922 | {file = "sphinx_basic_ng-1.0.0b2.tar.gz", hash = "sha256:9ec55a47c90c8c002b5960c57492ec3021f5193cb26cebc2dc4ea226848651c9"}, 923 | ] 924 | 925 | [package.dependencies] 926 | sphinx = ">=4.0" 927 | 928 | [package.extras] 929 | docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] 930 | 931 | [[package]] 932 | name = "sphinx-copybutton" 933 | version = "0.5.2" 934 | description = "Add a copy button to each of your code cells." 935 | optional = true 936 | python-versions = ">=3.7" 937 | groups = ["main"] 938 | markers = "extra == \"docs\"" 939 | files = [ 940 | {file = "sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd"}, 941 | {file = "sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e"}, 942 | ] 943 | 944 | [package.dependencies] 945 | sphinx = ">=1.8" 946 | 947 | [package.extras] 948 | code-style = ["pre-commit (==2.12.1)"] 949 | rtd = ["ipython", "myst-nb", "sphinx", "sphinx-book-theme", "sphinx-examples"] 950 | 951 | [[package]] 952 | name = "sphinxcontrib-applehelp" 953 | version = "2.0.0" 954 | description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" 955 | optional = true 956 | python-versions = ">=3.9" 957 | groups = ["main"] 958 | markers = "extra == \"docs\"" 959 | files = [ 960 | {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, 961 | {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, 962 | ] 963 | 964 | [package.extras] 965 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 966 | standalone = ["Sphinx (>=5)"] 967 | test = ["pytest"] 968 | 969 | [[package]] 970 | name = "sphinxcontrib-devhelp" 971 | version = "2.0.0" 972 | description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" 973 | optional = true 974 | python-versions = ">=3.9" 975 | groups = ["main"] 976 | markers = "extra == \"docs\"" 977 | files = [ 978 | {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, 979 | {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, 980 | ] 981 | 982 | [package.extras] 983 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 984 | standalone = ["Sphinx (>=5)"] 985 | test = ["pytest"] 986 | 987 | [[package]] 988 | name = "sphinxcontrib-htmlhelp" 989 | version = "2.1.0" 990 | description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" 991 | optional = true 992 | python-versions = ">=3.9" 993 | groups = ["main"] 994 | markers = "extra == \"docs\"" 995 | files = [ 996 | {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, 997 | {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, 998 | ] 999 | 1000 | [package.extras] 1001 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1002 | standalone = ["Sphinx (>=5)"] 1003 | test = ["html5lib", "pytest"] 1004 | 1005 | [[package]] 1006 | name = "sphinxcontrib-jsmath" 1007 | version = "1.0.1" 1008 | description = "A sphinx extension which renders display math in HTML via JavaScript" 1009 | optional = true 1010 | python-versions = ">=3.5" 1011 | groups = ["main"] 1012 | markers = "extra == \"docs\"" 1013 | files = [ 1014 | {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, 1015 | {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, 1016 | ] 1017 | 1018 | [package.extras] 1019 | test = ["flake8", "mypy", "pytest"] 1020 | 1021 | [[package]] 1022 | name = "sphinxcontrib-qthelp" 1023 | version = "2.0.0" 1024 | description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" 1025 | optional = true 1026 | python-versions = ">=3.9" 1027 | groups = ["main"] 1028 | markers = "extra == \"docs\"" 1029 | files = [ 1030 | {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, 1031 | {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, 1032 | ] 1033 | 1034 | [package.extras] 1035 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1036 | standalone = ["Sphinx (>=5)"] 1037 | test = ["defusedxml (>=0.7.1)", "pytest"] 1038 | 1039 | [[package]] 1040 | name = "sphinxcontrib-serializinghtml" 1041 | version = "2.0.0" 1042 | description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" 1043 | optional = true 1044 | python-versions = ">=3.9" 1045 | groups = ["main"] 1046 | markers = "extra == \"docs\"" 1047 | files = [ 1048 | {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, 1049 | {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, 1050 | ] 1051 | 1052 | [package.extras] 1053 | lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] 1054 | standalone = ["Sphinx (>=5)"] 1055 | test = ["pytest"] 1056 | 1057 | [[package]] 1058 | name = "typing-extensions" 1059 | version = "4.14.1" 1060 | description = "Backported and Experimental Type Hints for Python 3.9+" 1061 | optional = true 1062 | python-versions = ">=3.9" 1063 | groups = ["main"] 1064 | markers = "extra == \"dev\" or extra == \"docs\"" 1065 | files = [ 1066 | {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, 1067 | {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, 1068 | ] 1069 | 1070 | [[package]] 1071 | name = "urllib3" 1072 | version = "2.5.0" 1073 | description = "HTTP library with thread-safe connection pooling, file post, and more." 1074 | optional = true 1075 | python-versions = ">=3.9" 1076 | groups = ["main"] 1077 | markers = "extra == \"docs\"" 1078 | files = [ 1079 | {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, 1080 | {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, 1081 | ] 1082 | 1083 | [package.extras] 1084 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] 1085 | h2 = ["h2 (>=4,<5)"] 1086 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] 1087 | zstd = ["zstandard (>=0.18.0)"] 1088 | 1089 | [extras] 1090 | dev = ["black", "mypy", "pytest"] 1091 | docs = ["Sphinx", "furo", "myst-parser", "sphinx-copybutton"] 1092 | 1093 | [metadata] 1094 | lock-version = "2.1" 1095 | python-versions = ">=3.13" 1096 | content-hash = "cc4318c524b06119a71b66467cc3bdae263897318d8bbd48a37cfc3b524f06eb" 1097 | --------------------------------------------------------------------------------