blender-pyutils 2025.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env python3
2
+ """Package stub."""
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env python3
2
+ """Invoke the Blender executable."""
3
+
4
+ import functools
5
+ import logging
6
+ import pathlib
7
+ import shutil
8
+ import subprocess
9
+ import tempfile
10
+ import textwrap
11
+ import tomllib
12
+
13
+ import tomli_w
14
+ from xdg.BaseDirectory import xdg_config_home
15
+
16
+ from .common import ENCODING
17
+ from .exceptions import BlenderError, PipError
18
+ from .utils import run
19
+
20
+ LOGGER = logging.getLogger(__name__)
21
+
22
+
23
+ class BlenderWrapper:
24
+ """
25
+ Simple wrapper around the Blender command-line executable.
26
+ """
27
+
28
+ def __init__(self, exe="blender", ext_dir=None):
29
+ """
30
+ Args:
31
+ exe:
32
+ The blender command to execute.
33
+
34
+ ext_dir:
35
+ The path to the directory with the extension to validate and
36
+ build.
37
+ """
38
+ self.exe = str(exe)
39
+ if ext_dir is None:
40
+ ext_dir = pathlib.Path.cwd()
41
+ else:
42
+ ext_dir = pathlib.Path(ext_dir).resolve()
43
+ self.ext_dir = ext_dir
44
+
45
+ def run_blender(self, args, **kwargs):
46
+ """
47
+ Invoke blender.
48
+
49
+ Args:
50
+ Arguments to pass to the blender command.
51
+
52
+ **kwargs:
53
+ Keyword arguments passed through to subprocess.run().
54
+
55
+ Returns:
56
+ The return value of subprocess.run()
57
+ """
58
+ return run((self.exe, *args), **kwargs)
59
+
60
+ @functools.cached_property
61
+ def info(self):
62
+ """
63
+ A dict with information about Blender and its embedded Python
64
+ environment. It contains the following keys:
65
+ Blender version:
66
+ The Blender version.
67
+
68
+ Python version:
69
+ The Python version.
70
+
71
+ Python executable:
72
+ The path to the Python executable.
73
+ """
74
+ delim = "###"
75
+ blver = "Blender version"
76
+ blmoddir = "Blender module directory"
77
+ pyver = "Python version"
78
+ pyexe = "Python executable"
79
+ code = textwrap.dedent(
80
+ f"""\
81
+ import sys
82
+ import bpy
83
+ print(f'{blver}{delim}{{bpy.app.version_string}}')
84
+ ver = sys.version_info
85
+ print(f'{pyver}{delim}{{ver.major}}.{{ver.minor}}.{{ver.micro}}')
86
+ print(f'{pyexe}{delim}{{sys.executable}}')
87
+ bpy.ops.wm.quit_blender()
88
+ """
89
+ )
90
+ cmd = ["--background", "--python-expr", code]
91
+ try:
92
+ response = self.run_blender(cmd, stdout=subprocess.PIPE)
93
+ except subprocess.CalledProcessError as err:
94
+ raise BlenderError(
95
+ f"Failed to determine Blender's Python version: {err}"
96
+ ) from err
97
+ info = {}
98
+ for line in response.stdout.decode(ENCODING).splitlines():
99
+ try:
100
+ key, value = line.strip().split(delim, 1)
101
+ except ValueError:
102
+ continue
103
+ value = value.strip()
104
+ if key == pyexe:
105
+ value = pathlib.Path(value).resolve()
106
+ info[key] = value
107
+
108
+ bldir = ".".join(info[blver].split(".", 2)[:2])
109
+ info[blmoddir] = (
110
+ pathlib.Path(xdg_config_home) / "blender" / bldir / "scripts/modules"
111
+ )
112
+
113
+ return info
114
+
115
+ def run_python(self, args, **kwargs):
116
+ """
117
+ Invoke Blender's Python interpreter.
118
+
119
+ Args:
120
+ Arguments to pass to the python command.
121
+
122
+ **kwargs:
123
+ Keyword arguments passed through to subprocess.run().
124
+
125
+ Returns:
126
+ The return value of subprocess.run()
127
+ """
128
+ return run((self.info["Python executable"], *args), **kwargs)
129
+
130
+ def validate(self):
131
+ """
132
+ Validate the extension.
133
+ """
134
+ try:
135
+ self.run_blender(["--command", "extension", "validate"])
136
+ except subprocess.CalledProcessError as err:
137
+ raise BlenderError(f"Failed to validate the extension: {err}") from err
138
+ LOGGER.info("The extension has been validated.")
139
+
140
+ def download_wheel_deps(self, wheels_dir="wheels"):
141
+ """
142
+ Download the wheels of all external dependencies to the local wheel
143
+ directory and update the manifest to point to them.
144
+ """
145
+ wheels_dir = self.ext_dir / wheels_dir
146
+ wheels_dir.mkdir(parents=True, exist_ok=True)
147
+ req_file = self.ext_dir / "requirements.txt"
148
+ LOGGER.info("Downloading wheels...")
149
+ try:
150
+ self.run_python(
151
+ (
152
+ "-m",
153
+ "pip",
154
+ "wheel",
155
+ "-w",
156
+ wheels_dir,
157
+ "-r",
158
+ req_file,
159
+ )
160
+ )
161
+ except subprocess.CalledProcessError as err:
162
+ raise PipError(f"Failed to download dependency wheels: {err}") from err
163
+
164
+ manifest_file = self.ext_dir / "blender_manifest.toml"
165
+ LOGGER.info("Loading %s", manifest_file)
166
+ with manifest_file.open("rb") as handle:
167
+ manifest = tomllib.load(handle)
168
+ manifest["wheels"] = sorted(
169
+ str(p.relative_to(manifest_file.parent)) for p in wheels_dir.glob("*.whl")
170
+ )
171
+
172
+ # Write the updated manifest to a temporary file to avoid truncating the
173
+ # original on error. The temporary file is moved into place on
174
+ # success.
175
+ manifest_tmp_file = manifest_file.with_suffix(".tmp.toml")
176
+ with manifest_tmp_file.open("wb") as handle:
177
+ tomli_w.dump(manifest, handle)
178
+ LOGGER.info("Saving updated wheels to %s", manifest_file)
179
+ shutil.move(manifest_tmp_file, manifest_file)
180
+
181
+ def build(self):
182
+ """
183
+ Build the extension (i.e. the .zip file).
184
+ """
185
+ try:
186
+ self.run_blender(["--command", "extension", "build"])
187
+ except subprocess.CalledProcessError as err:
188
+ raise BlenderError(f"Failed to build the extension: {err}") from err
189
+ LOGGER.info("The extension has been built.")
190
+
191
+ def pip(self, args, path=None, uv=False):
192
+ """
193
+ Manage Python packages in Blender's module directory with pip or uv.
194
+
195
+ Args:
196
+ args:
197
+ Arguments to pass through to the pip command.
198
+
199
+ path:
200
+ The target directory. If None, the default location will be
201
+ used.
202
+
203
+ uv:
204
+ If True, use "uv pip" instead of pip.
205
+ """
206
+ if path is None:
207
+ path = self.info["Blender module directory"]
208
+ else:
209
+ path = pathlib.Path(path).resolve()
210
+ LOGGER.info("Pip target directory: %s", path)
211
+ LOGGER.info("Creating temporary Python virtual environment.")
212
+ with tempfile.TemporaryDirectory() as tmp_dir:
213
+ tmp_dir = pathlib.Path(tmp_dir)
214
+ venv_dir = tmp_dir / "venv"
215
+ self.run_python(("-m", "venv", venv_dir))
216
+ py_exe = venv_dir / "bin/python"
217
+ LOGGER.info("Ensuring pip in the virtual environment.")
218
+ run([py_exe, "-m", "ensurepip", "-U"])
219
+ run([py_exe, "-m", "pip", "install", "-U", "pip"])
220
+ if uv:
221
+ LOGGER.info("Installing uv in virtual environment.")
222
+ run((py_exe, "-m", "pip", "install", "-U", "uv"))
223
+ cmd = ("uv", "pip")
224
+ else:
225
+ cmd = ("pip",)
226
+ try:
227
+ run((py_exe, "-m", *cmd, *args, "--target", path))
228
+ except subprocess.CalledProcessError as err:
229
+ raise PipError(f"Failed to run pip command: {err}") from err
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env python3
2
+ """Common constants."""
3
+
4
+
5
+ ENCODING = "utf-8"
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python
2
+ """Custom exceptions."""
3
+
4
+
5
+ class BlenderPythonUtilsError(Exception):
6
+ """Base class for custom exceptions raised by this module."""
7
+
8
+
9
+ class SubprocessError(BlenderPythonUtilsError):
10
+ """Errors raised by subprocess calls."""
11
+
12
+
13
+ class BlenderError(SubprocessError):
14
+ """Errors raised when invoking the Blender executable."""
15
+
16
+
17
+ class PipError(SubprocessError):
18
+ """Errors raised when invoking pip."""
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env python3
2
+ """Utility script for validating and packaging Blender extensions."""
3
+
4
+ import argparse
5
+ import logging
6
+ import pathlib
7
+ import sys
8
+
9
+ from .blender_wrapper import BlenderWrapper
10
+ from .exceptions import BlenderPythonUtilsError
11
+
12
+ LOGGER = logging.getLogger(__name__)
13
+
14
+
15
+ def configure_logging(level=logging.INFO):
16
+ """
17
+ Configure logging.
18
+
19
+ Args:
20
+ level:
21
+ The logging level.
22
+ """
23
+ logging.basicConfig(
24
+ style="{",
25
+ format="[{asctime}] {levelname} {message}",
26
+ datefmt="%Y-%m-%d %H:%M:%S",
27
+ level=level,
28
+ )
29
+
30
+
31
+ class CommandRunner:
32
+ """
33
+ Run selected commands. The only real purpose of this class is to avoid
34
+ redundant subprocess calls to retrieve Blender information.
35
+ """
36
+
37
+ def __init__(self):
38
+ self.blender = BlenderWrapper()
39
+
40
+ def info(self, _pargs):
41
+ """
42
+ Print Blender information.
43
+ """
44
+ for key, value in sorted(self.blender.info.items()):
45
+ print(f"{key}: {value}")
46
+
47
+ def build(self, pargs):
48
+ """
49
+ Validate and build an extension. If a requirements.txt file is found in the
50
+ extension directory then the wheels for its dependencies will be downloaded
51
+ to the wheels directory and the manifest will be updated to include them.
52
+ """
53
+ bld = self.blender
54
+ bld.ext_dir = pargs.path
55
+ bld.download_wheel_deps()
56
+ bld.validate()
57
+ bld.build()
58
+
59
+ def pip(self, pargs):
60
+ """
61
+ Install Python packages to Blender's module directory. Additional
62
+ arguments as passed through to pip.
63
+ """
64
+ self.blender.pip(pargs.pip_args, path=pargs.path, uv=pargs.uv)
65
+
66
+
67
+ def main(args=None):
68
+ """
69
+ Main function.
70
+
71
+ Args:
72
+ args:
73
+ Passed through to ArgumentParser.parse_args().
74
+ """
75
+ cmd_runner = CommandRunner()
76
+
77
+ parser = argparse.ArgumentParser(description=__doc__)
78
+ subparsers = parser.add_subparsers(required=True)
79
+
80
+ parser_info = subparsers.add_parser("info", description=cmd_runner.info.__doc__)
81
+ parser_info.set_defaults(func=cmd_runner.info)
82
+
83
+ parser_build = subparsers.add_parser("build", description=cmd_runner.build.__doc__)
84
+ parser_build.add_argument(
85
+ "-p",
86
+ "--path",
87
+ type=pathlib.Path,
88
+ help=(
89
+ "The path to the extension's root directory. "
90
+ "If not given, the current working directory is assumed."
91
+ ),
92
+ )
93
+ parser_build.set_defaults(func=cmd_runner.build)
94
+
95
+ parser_pip = subparsers.add_parser("pip", description=cmd_runner.pip.__doc__)
96
+ parser_pip.add_argument(
97
+ "--path",
98
+ type=pathlib.Path,
99
+ help=(
100
+ "Installation directory for Python packages. "
101
+ "If not given, the default Blender module directory will be used. "
102
+ 'Use the "info" command to show the path to the module directory.'
103
+ ),
104
+ )
105
+ parser_pip.add_argument(
106
+ "--uv", action="store_true", help='Use "uv pip" instead of pip.'
107
+ )
108
+ parser_pip.add_argument(
109
+ "pip_args",
110
+ nargs="+",
111
+ help=(
112
+ "Arguments to pass through to pip. "
113
+ 'Precede these arguments with "--" if any of them begin with "-".'
114
+ ),
115
+ metavar="<PIP ARG>",
116
+ )
117
+ parser_pip.set_defaults(func=cmd_runner.pip)
118
+
119
+ pargs = parser.parse_args(args)
120
+ pargs.func(pargs)
121
+
122
+
123
+ def run_main(*args, **kwargs):
124
+ """
125
+ Run the main function with exception handling.
126
+
127
+ Args:
128
+ *args:
129
+ Positional arguments passed through to main().
130
+
131
+ **kwargs:
132
+ Keyword arguments passed through to main().
133
+ """
134
+ configure_logging()
135
+ try:
136
+ main(*args, **kwargs)
137
+ except BlenderPythonUtilsError as err:
138
+ sys.exit(err)
139
+
140
+
141
+ if __name__ == "__main__":
142
+ run_main()
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python3
2
+ """Utility functions."""
3
+
4
+
5
+ import logging
6
+ import subprocess
7
+
8
+ LOGGER = logging.getLogger(__name__)
9
+
10
+
11
+ def run(cmd, **kwargs):
12
+ """
13
+ Run a command with subprocess.run.
14
+
15
+ Args:
16
+ cmd:
17
+ The command to run.
18
+
19
+ **kwargs:
20
+ Keyword arguments passed through to subprocess.run().
21
+
22
+ Returns:
23
+ The return value of subprocess.run().
24
+ """
25
+ cmd = [str(w) for w in cmd]
26
+ LOGGER.debug("Running command: %s", cmd)
27
+ kwargs["check"] = kwargs.get("check", True)
28
+ # pylint: disable=subprocess-run-check
29
+ return subprocess.run(cmd, **kwargs)
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python3
2
+ '''
3
+ Version file automatically generated by setuptools-scm via hatch-vcs.
4
+ https://pypi.org/project/setuptools-scm/
5
+ https://github.com/ofek/hatch-vcs
6
+ Do not track this file and do not edit it manually.
7
+ '''
8
+ VERSION = '2025.2'
9
+ VERSION_TUPLE = (2025, 2)
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: blender-pyutils
3
+ Version: 2025.2
4
+ Summary: Simple utilities for building and installing Python extensions for Blender.
5
+ Project-URL: Homepage, https://gitlab.inria.fr/jrye/blender-pyutils
6
+ Project-URL: Source, https://gitlab.inria.fr/jrye/blender-pyutils.git
7
+ Project-URL: Documentation, https://jrye.gitlabpages.inria.fr/blender-pyutils
8
+ Project-URL: Issues, https://gitlab.inria.fr/jrye/blender-pyutils/issues
9
+ Author-email: Jan-Michael Rye <jan-michael.rye@inria.fr>
10
+ License: MIT License
11
+
12
+ Copyright (c) 2025, Inria
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+ License-File: LICENSE.txt
20
+ Classifier: License :: OSI Approved :: MIT License
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Programming Language :: Python :: 3
23
+ Requires-Python: >=3.7
24
+ Requires-Dist: pyxdg
25
+ Requires-Dist: tomli-w
26
+ Description-Content-Type: text/markdown
27
+
28
+ [insert: badges gitlab]: #
29
+
30
+ [![Hatch](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) [![Latest Release](https://gitlab.inria.fr/jrye/blender-pyutils/-/badges/release.svg)](https://gitlab.inria.fr/jrye/blender-pyutils/-/tags) [![License](https://img.shields.io/badge/license-MIT-9400d3.svg)](https://spdx.org/licenses/MIT.html) [![Pipeline Status](https://gitlab.inria.fr/jrye/blender-pyutils/badges/main/pipeline.svg)](https://gitlab.inria.fr/jrye/blender-pyutils/-/commits/main) [![Pylint](https://gitlab.inria.fr/jrye/blender-pyutils/-/jobs/artifacts/main/raw/pylint/pylint.svg?job=pylint)](https://gitlab.inria.fr/jrye/blender-pyutils/-/jobs/artifacts/main/raw/pylint/pylint.txt?job=pylint)
31
+
32
+ [/insert: badges gitlab]: #
33
+
34
+ # Synopsis
35
+
36
+ This is a small Python package that installs a command-line utility to facilitate extension installation in [Blender](https://www.blender.org/). It currently provides the following functionality:
37
+
38
+
39
+ * Print information about Blender and its internal Python environment.
40
+ * Validate and build Blender extensions, including their dependency wheels.
41
+ * Install Python packages directly into Blender's modules directory.
42
+
43
+ ## Links
44
+
45
+ [insert: links 2]: #
46
+
47
+ ### GitLab
48
+
49
+ * [Homepage](https://gitlab.inria.fr/jrye/blender-pyutils)
50
+ * [Source](https://gitlab.inria.fr/jrye/blender-pyutils.git)
51
+ * [Issues](https://gitlab.inria.fr/jrye/blender-pyutils/issues)
52
+ * [Documentation](https://jrye.gitlabpages.inria.fr/blender-pyutils)
53
+ * [GitLab package registry](https://gitlab.inria.fr/jrye/blender-pyutils/-/packages)
54
+
55
+ ### Other Repositories
56
+
57
+ * [Software Heritage](https://archive.softwareheritage.org/browse/origin/?origin_url=https%3A//gitlab.inria.fr/jrye/blender-pyutils.git)
58
+
59
+ [/insert: links 2]: #
60
+
61
+
62
+
63
+ # Usage
64
+
65
+ Install this package, preferably in a Python virtual environment.
66
+
67
+ ~~~sh
68
+ # Create the virtual environment.
69
+ python3 -m venv
70
+
71
+ # Activate it.
72
+ source venv/bin/activate
73
+
74
+ # Ensure that pip is installed and up-to-date.
75
+ python3 -m ensure pip
76
+ pip install -U pip
77
+
78
+ # Install this package.
79
+ pip install -U Blender Python Utils
80
+ ~~~
81
+
82
+ ## blender-pytuils Executable
83
+
84
+ The package will install the `blender-pyutils` command that accepts different subcommands as arguments.
85
+
86
+ [insert: command_output: blender-pyutils --help]: #
87
+
88
+ ~~~
89
+ usage: blender-pyutils [-h] {info,build,pip} ...
90
+
91
+ Utility script for validating and packaging Blender extensions.
92
+
93
+ positional arguments:
94
+ {info,build,pip}
95
+
96
+ options:
97
+ -h, --help show this help message and exit
98
+
99
+ ~~~
100
+
101
+ [/insert: command_output: blender-pyutils --help]: #
102
+
103
+ ### `info` Subcommand
104
+
105
+ The `info` subcommand will print information about Blender's version, module directory path and configured Python executable.
106
+
107
+ [insert: command_output: blender-pyutils info --help]: #
108
+
109
+ ~~~
110
+ usage: blender-pyutils info [-h]
111
+
112
+ Print Blender information.
113
+
114
+ options:
115
+ -h, --help show this help message and exit
116
+
117
+ ~~~
118
+
119
+ [/insert: command_output: blender-pyutils info --help]: #
120
+
121
+
122
+ ### `build` Subcommand
123
+
124
+ The `build` subommand will validate and build a Blender extension. If a `requirements.txt` file is found in the extension directory then it will also download the dependency wheels to the `wheels` subdirectory and add them to the `blender_manifest.toml`.
125
+
126
+ [insert: command_output: blender-pyutils build --help]: #
127
+
128
+ ~~~
129
+ usage: blender-pyutils build [-h] [-p PATH]
130
+
131
+ Validate and build an extension. If a requirements.txt file is found in the
132
+ extension directory then the wheels for its dependencies will be downloaded to
133
+ the wheels directory and the manifest will be updated to include them.
134
+
135
+ options:
136
+ -h, --help show this help message and exit
137
+ -p, --path PATH The path to the extension's root directory. If not given,
138
+ the current working directory is assumed.
139
+
140
+ ~~~
141
+
142
+ [/insert: command_output: blender-pyutils build --help]: #
143
+
144
+ ### `pip` Subcommand
145
+
146
+ The `pip` subcommand will install Python packages directly to Blender's modules directory. It accepts the same commands as `pip`.
147
+
148
+ ~~~sh
149
+ # Example: install scipy using pip
150
+ blender-pyutils pip install scipy
151
+
152
+ # Example: install scipy using uv
153
+ blender-pyutils pip --uv install scipy
154
+
155
+ # Example: use "--" to pass through options to pip, such as a requirements.txt file
156
+ blender-pyutils pip -- install -U -r requirements.txt
157
+ ~~~
158
+
159
+ [insert: command_output: blender-pyutils pip --help]: #
160
+
161
+ ~~~
162
+ usage: blender-pyutils pip [-h] [--path PATH] [--uv] <PIP ARG> [<PIP ARG> ...]
163
+
164
+ Install Python packages to Blender's module directory. Additional arguments as
165
+ passed through to pip.
166
+
167
+ positional arguments:
168
+ <PIP ARG> Arguments to pass through to pip. Precede these arguments with
169
+ "--" if any of them begin with "-".
170
+
171
+ options:
172
+ -h, --help show this help message and exit
173
+ --path PATH Installation directory for Python packages. If not given, the
174
+ default Blender module directory will be used. Use the "info"
175
+ command to show the path to the module directory.
176
+ --uv Use "uv pip" instead of pip.
177
+
178
+ ~~~
179
+
180
+ [/insert: command_output: blender-pyutils pip --help]: #
181
+
182
+ #### Caveats
183
+
184
+ * At the time or writing, Blender does not recognize packages installed with [pip's editable option (-e/--editiable)](https://pip.pypa.io/en/stable/cli/pip_install/).
185
+ * Packages must be installed before launching Blender.
186
+
187
+
188
+ #### Legacy Script
189
+
190
+ This repository originally only provided [blender-pip_install.sh](blender-pip_install.sh) as a wrapper around `pip install` and `uv pip install`. It has been superceded by `blender-pyutils pip` and users should migrate accordingly.
191
+
@@ -0,0 +1,12 @@
1
+ blender_pyutils/__init__.py,sha256=NX3VQ0ZDvrZ_aLmHCQaSPLL38yArwPpRDoC5Cyb-k7g,43
2
+ blender_pyutils/blender_wrapper.py,sha256=TENBESp6acgEcYf_G_99TkDd7c_lD184YVxpU0CxEnc,7509
3
+ blender_pyutils/common.py,sha256=hjVPhkcEvbtbIJbyM5P0yqqTc5GAi1dnsBYb2Re-sTY,68
4
+ blender_pyutils/exceptions.py,sha256=L4ytXmhq_faO2h2aqLA8wXxxbL1cWmGHcP7Ue9Rijv0,431
5
+ blender_pyutils/main.py,sha256=BJsptA4I-ewYeKnNsUEVOJjwYMt3jvAItHijkGkcHVM,3822
6
+ blender_pyutils/utils.py,sha256=ljuFuXEV8s2BzX2R8sTTKh_WS5ifUw0RbR8td-LmHzI,612
7
+ blender_pyutils/version.py,sha256=MyfNAMQbnFV-0l7nVy1e9fhTwBwxxmPyDXqo0BVw5a4,273
8
+ blender_pyutils-2025.2.dist-info/METADATA,sha256=21H2yeoNElOyxl3niT76yS5QQYOtLDkhsHnWW6DNB-Q,7462
9
+ blender_pyutils-2025.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ blender_pyutils-2025.2.dist-info/entry_points.txt,sha256=Wa1uzefC5LtB5KrQg0lMDHT8Tv8rNUPz8SwgjcHgxfg,66
11
+ blender_pyutils-2025.2.dist-info/licenses/LICENSE.txt,sha256=vmijgBBvVdO2IJ1lTOrkBE98NnHVf7Ek6JA9Uc9do0g,1063
12
+ blender_pyutils-2025.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ blender-pyutils = blender_pyutils.main:run_main
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025, Inria
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.