procfunc 0.30.2__tar.gz → 0.33.0__tar.gz
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.
- {procfunc-0.30.2/src/procfunc.egg-info → procfunc-0.33.0}/PKG-INFO +4 -4
- {procfunc-0.30.2 → procfunc-0.33.0}/README.md +1 -2
- {procfunc-0.30.2 → procfunc-0.33.0}/pyproject.toml +9 -2
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/__init__.py +2 -1
- procfunc-0.33.0/src/procfunc/cli/__init__.py +3 -0
- procfunc-0.33.0/src/procfunc/cli/main.py +35 -0
- procfunc-0.33.0/src/procfunc/codegen/__init__.py +8 -0
- {procfunc-0.30.2/src/procfunc/transpiler → procfunc-0.33.0/src/procfunc/codegen}/codegen.py +103 -150
- {procfunc-0.30.2/src/procfunc/transpiler → procfunc-0.33.0/src/procfunc/codegen}/identifiers.py +10 -0
- procfunc-0.33.0/src/procfunc/codegen/repr.py +121 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/compute_graph/__init__.py +26 -0
- procfunc-0.33.0/src/procfunc/compute_graph/compute_graph.py +32 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/compute_graph/node.py +2 -17
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/compute_graph/operators_info.py +0 -2
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/compute_graph/util.py +26 -23
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/context.py +8 -7
- procfunc-0.30.2/src/procfunc/control.py → procfunc-0.33.0/src/procfunc/control/__init__.py +1 -1
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/__init__.py +8 -4
- procfunc-0.33.0/src/procfunc/nodes/color.py +345 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/compositor.py +238 -400
- procfunc-0.33.0/src/procfunc/nodes/execute/construct_nodes.py +268 -0
- procfunc-0.33.0/src/procfunc/nodes/execute/construct_operator.py +277 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/execute/construct_special_cases.py +125 -18
- procfunc-0.33.0/src/procfunc/nodes/execute/construct_standard.py +357 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/execute/execute.py +17 -218
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/execute/infer_runtime_data_type.py +70 -32
- procfunc-0.33.0/src/procfunc/nodes/execute/realize.py +225 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/execute/util.py +109 -8
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/func.py +227 -533
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/geo.py +584 -264
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/manifest.json +2710 -839
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/math.py +330 -143
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/shader.py +121 -940
- procfunc-0.33.0/src/procfunc/nodes/texture.py +720 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/nodes/types.py +5 -27
- procfunc-0.33.0/src/procfunc/nodes/util/bindings_util.py +427 -0
- {procfunc-0.30.2/src/procfunc/nodes → procfunc-0.33.0/src/procfunc/nodes/util}/bpy_node_info.py +90 -7
- {procfunc-0.30.2/src/procfunc/nodes → procfunc-0.33.0/src/procfunc/nodes/util}/node_function.py +13 -2
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/__init__.py +1 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/_util.py +1 -1
- procfunc-0.33.0/src/procfunc/ops/file.py +279 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/mesh.py +5 -1
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/modifier.py +1 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/primitives/mesh.py +2 -2
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/random.py +4 -2
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/tracer/__init__.py +2 -10
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/tracer/decorator.py +6 -9
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/tracer/patch.py +0 -17
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/tracer/proxy.py +1 -1
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/__init__.py +2 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/cleanup.py +4 -1
- procfunc-0.33.0/src/procfunc/transforms/convert.py +20 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/distribution.py +25 -26
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/infer_distribution.py +37 -7
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transpiler/__init__.py +1 -8
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transpiler/bpy_to_computegraph.py +161 -167
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transpiler/main.py +105 -73
- procfunc-0.33.0/src/procfunc/transpiler/parse_default_values.py +35 -0
- procfunc-0.33.0/src/procfunc/transpiler/parse_special_cases.py +141 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/types.py +21 -73
- procfunc-0.33.0/src/procfunc/util/bpy_data.py +39 -0
- procfunc-0.33.0/src/procfunc/util/camera.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/util/pytree.py +30 -24
- {procfunc-0.30.2 → procfunc-0.33.0/src/procfunc.egg-info}/PKG-INFO +4 -4
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc.egg-info/SOURCES.txt +28 -8
- procfunc-0.33.0/src/procfunc.egg-info/entry_points.txt +2 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc.egg-info/requires.txt +1 -0
- procfunc-0.33.0/tests/test_bpy_data_cleanup.py +50 -0
- procfunc-0.33.0/tests/test_cli_transpile.py +81 -0
- procfunc-0.30.2/tests/test_dedup_names.py → procfunc-0.33.0/tests/test_codegen.py +1 -1
- procfunc-0.33.0/tests/test_codegen_matrix.py +110 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/tests/test_compute_graph.py +25 -0
- procfunc-0.33.0/tests/test_node_function.py +28 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/tests/test_ops.py +60 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/tests/test_pytree.py +10 -0
- procfunc-0.33.0/tests/test_random.py +29 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/tests/test_trace.py +1 -1
- procfunc-0.33.0/tests/test_transforms.py +389 -0
- procfunc-0.30.2/src/procfunc/compute_graph/compute_graph.py +0 -115
- procfunc-0.30.2/src/procfunc/nodes/bindings_util.py +0 -196
- procfunc-0.30.2/src/procfunc/nodes/execute/construct_nodes.py +0 -571
- procfunc-0.30.2/src/procfunc/ops/file.py +0 -126
- procfunc-0.30.2/src/procfunc/transforms/convert.py +0 -20
- {procfunc-0.30.2 → procfunc-0.33.0}/LICENSE.md +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/setup.cfg +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/color.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/compute_graph/proxy.py +0 -0
- {procfunc-0.30.2/src/procfunc → procfunc-0.33.0/src/procfunc/nodes}/util/__init__.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/addons.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/attr.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/collection.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/curve.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/manifest.json +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/object.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/primitives/__init__.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/primitives/camera.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/primitives/curve.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/primitives/light.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/ops/uv.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/tracer/trace.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/extract_materials.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/parameters.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/transforms/util.py +0 -0
- /procfunc-0.30.2/src/procfunc/util/camera.py → /procfunc-0.33.0/src/procfunc/util/__init__.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/util/bpy_info.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/util/keyframe.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/util/log.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/util/manifest.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc/util/teardown.py +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc.egg-info/dependency_links.txt +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/src/procfunc.egg-info/top_level.txt +0 -0
- {procfunc-0.30.2 → procfunc-0.33.0}/tests/test_asset.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: procfunc
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.33.0
|
|
4
4
|
Summary: Function-Oriented Abstractions for Procedural 3D Generation in Python
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
Project-URL: Homepage, https://github.com/princeton-vl/procfunc
|
|
7
7
|
Project-URL: Repository, https://github.com/princeton-vl/procfunc
|
|
8
|
-
Requires-Python:
|
|
8
|
+
Requires-Python: <3.12,>=3.11
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE.md
|
|
11
11
|
Requires-Dist: bpy==4.2.0
|
|
@@ -19,13 +19,13 @@ Requires-Dist: ty; extra == "dev"
|
|
|
19
19
|
Provides-Extra: docs
|
|
20
20
|
Requires-Dist: sphinx; extra == "docs"
|
|
21
21
|
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
22
|
+
Requires-Dist: sphinx-argparse; extra == "docs"
|
|
22
23
|
Dynamic: license-file
|
|
23
24
|
|
|
24
25
|
# ProcFunc: Function-Oriented Abstractions for Procedural 3D Generation in Python
|
|
25
26
|
|
|
26
27
|
[**Documentation**](#documentation)
|
|
27
28
|
| [**Research Paper**](https://arxiv.org/abs/2604.26943)
|
|
28
|
-
| [**Documentation**](https://procfunc.readthedocs.io)
|
|
29
29
|
| [**Transpiling**](#transpile-a-blender-file-to-procfunc-code)
|
|
30
30
|
| [**Experiments**](#experiments)
|
|
31
31
|
| [**Contributing**](#contributing)
|
|
@@ -62,7 +62,7 @@ Please create Github Issues for any bugs or unclear interfaces!
|
|
|
62
62
|
Convert a Blender geometry node tree into procfunc Python code by downloading our example blend and executing the transpiler:
|
|
63
63
|
```bash
|
|
64
64
|
wget https://raw.githubusercontent.com/princeton-vl/procfunc/main/examples/transpile_simple_chair/simple_chair.blend
|
|
65
|
-
uv run
|
|
65
|
+
uv run procfunc transpile simple_chair.blend --node_trees simple_chair --output transpiled_code.py
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
See the expected output in [`examples/transpile_simple_chair/transpiled_code.py`](examples/transpile_simple_chair/transpiled_code.py).
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[**Documentation**](#documentation)
|
|
4
4
|
| [**Research Paper**](https://arxiv.org/abs/2604.26943)
|
|
5
|
-
| [**Documentation**](https://procfunc.readthedocs.io)
|
|
6
5
|
| [**Transpiling**](#transpile-a-blender-file-to-procfunc-code)
|
|
7
6
|
| [**Experiments**](#experiments)
|
|
8
7
|
| [**Contributing**](#contributing)
|
|
@@ -39,7 +38,7 @@ Please create Github Issues for any bugs or unclear interfaces!
|
|
|
39
38
|
Convert a Blender geometry node tree into procfunc Python code by downloading our example blend and executing the transpiler:
|
|
40
39
|
```bash
|
|
41
40
|
wget https://raw.githubusercontent.com/princeton-vl/procfunc/main/examples/transpile_simple_chair/simple_chair.blend
|
|
42
|
-
uv run
|
|
41
|
+
uv run procfunc transpile simple_chair.blend --node_trees simple_chair --output transpiled_code.py
|
|
43
42
|
```
|
|
44
43
|
|
|
45
44
|
See the expected output in [`examples/transpile_simple_chair/transpiled_code.py`](examples/transpile_simple_chair/transpiled_code.py).
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "procfunc"
|
|
7
|
-
|
|
7
|
+
dynamic = ["version"]
|
|
8
8
|
description = "Function-Oriented Abstractions for Procedural 3D Generation in Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
11
|
-
requires-python = ">=3.11"
|
|
11
|
+
requires-python = ">=3.11,<3.12"
|
|
12
12
|
dependencies = [
|
|
13
13
|
"bpy==4.2.0",
|
|
14
14
|
"numpy<2",
|
|
@@ -19,6 +19,9 @@ dependencies = [
|
|
|
19
19
|
Homepage = "https://github.com/princeton-vl/procfunc"
|
|
20
20
|
Repository = "https://github.com/princeton-vl/procfunc"
|
|
21
21
|
|
|
22
|
+
[project.scripts]
|
|
23
|
+
procfunc = "procfunc.cli:cli"
|
|
24
|
+
|
|
22
25
|
[project.optional-dependencies]
|
|
23
26
|
dev = [
|
|
24
27
|
"pytest",
|
|
@@ -29,8 +32,12 @@ dev = [
|
|
|
29
32
|
docs = [
|
|
30
33
|
"sphinx",
|
|
31
34
|
"sphinx-rtd-theme",
|
|
35
|
+
"sphinx-argparse",
|
|
32
36
|
]
|
|
33
37
|
|
|
38
|
+
[tool.setuptools.dynamic]
|
|
39
|
+
version = {attr = "procfunc.__version__"}
|
|
40
|
+
|
|
34
41
|
[tool.setuptools.packages.find]
|
|
35
42
|
where = ["src"]
|
|
36
43
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# ensure this gets imported first so that mathutils etc is available even if later modules dont import bpy
|
|
3
3
|
import bpy
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.33.0"
|
|
6
6
|
|
|
7
7
|
from numpy.random import Generator as RNG
|
|
8
8
|
|
|
@@ -41,6 +41,7 @@ from .types import (
|
|
|
41
41
|
LightObject,
|
|
42
42
|
LightProbeObject,
|
|
43
43
|
MetaObject,
|
|
44
|
+
PointCloudObject,
|
|
44
45
|
Material,
|
|
45
46
|
Texture,
|
|
46
47
|
Collection,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from procfunc.transpiler import main as transpiler_main
|
|
4
|
+
from procfunc.util.teardown import skip_teardown_on_exit
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_parser() -> argparse.ArgumentParser:
|
|
8
|
+
parser = argparse.ArgumentParser(prog="procfunc")
|
|
9
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
10
|
+
|
|
11
|
+
transpile_parser = subparsers.add_parser(
|
|
12
|
+
"transpile", help="Transpile a Blender file to procfunc Python code."
|
|
13
|
+
)
|
|
14
|
+
transpiler_main.add_transpile_arguments(transpile_parser)
|
|
15
|
+
|
|
16
|
+
return parser
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = get_parser()
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
if args.command == "transpile":
|
|
24
|
+
transpiler_main.run(args)
|
|
25
|
+
else:
|
|
26
|
+
parser.error(f"Unknown command: {args.command}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def cli():
|
|
30
|
+
with skip_teardown_on_exit():
|
|
31
|
+
main()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
cli()
|
|
@@ -5,19 +5,19 @@ import itertools
|
|
|
5
5
|
import logging
|
|
6
6
|
from collections import OrderedDict, defaultdict
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import Any, Callable, Generator
|
|
8
|
+
from typing import Any, Callable, Generator
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
|
|
12
12
|
import procfunc as pf
|
|
13
13
|
from procfunc import compute_graph as cg
|
|
14
|
+
from procfunc.codegen import identifiers
|
|
15
|
+
from procfunc.codegen.repr import repr_type, repr_value
|
|
14
16
|
from procfunc.compute_graph.operators_info import (
|
|
15
17
|
FUNCTIONS_TO_OPERATORS,
|
|
16
18
|
OPERATOR_TEMPLATES,
|
|
17
19
|
OperatorType,
|
|
18
20
|
)
|
|
19
|
-
from procfunc.nodes import types as nt
|
|
20
|
-
from procfunc.transpiler import identifiers
|
|
21
21
|
from procfunc.util import pytree
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
@@ -29,92 +29,6 @@ def indent_lines(lines: list[str], indent: str = INDENT) -> list[str]:
|
|
|
29
29
|
return [indent + line for line in lines]
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def _repr_type(x: Any) -> str:
|
|
33
|
-
# TODO: make the user pass in special resolutions for types, or else we will just do verbose types
|
|
34
|
-
|
|
35
|
-
if isinstance(x, str):
|
|
36
|
-
return x
|
|
37
|
-
|
|
38
|
-
if x.__name__ == "NoneType":
|
|
39
|
-
return "None"
|
|
40
|
-
|
|
41
|
-
origin = get_origin(x)
|
|
42
|
-
args = get_args(x)
|
|
43
|
-
|
|
44
|
-
if x.__name__ == "ProcNode":
|
|
45
|
-
if len(args) == 1:
|
|
46
|
-
return f"pf.ProcNode[{_repr_type(args[0])}]"
|
|
47
|
-
elif len(args) == 0:
|
|
48
|
-
return "pf.ProcNode"
|
|
49
|
-
else:
|
|
50
|
-
raise ValueError(f"Unsupported ProcNode type: {x} {args=}")
|
|
51
|
-
|
|
52
|
-
if hasattr(pf, x.__name__):
|
|
53
|
-
if len(args):
|
|
54
|
-
raise ValueError(f"procfunc type had unhandled annotations: {x} {args=}")
|
|
55
|
-
return f"pf.{x.__name__}"
|
|
56
|
-
|
|
57
|
-
if x.__module__ == "builtins":
|
|
58
|
-
return x.__name__
|
|
59
|
-
|
|
60
|
-
origin = get_origin(x)
|
|
61
|
-
args = get_args(x)
|
|
62
|
-
|
|
63
|
-
if origin is Union:
|
|
64
|
-
args_0 = get_args(args[0])
|
|
65
|
-
if get_origin(args[0]) is nt.ProcNode and args_0[0] is args[1]:
|
|
66
|
-
return f"t.SocketOrVal[{_repr_type(args_0[0])}]"
|
|
67
|
-
else:
|
|
68
|
-
return " | ".join([_repr_type(a) for a in args])
|
|
69
|
-
|
|
70
|
-
if getattr(x, "__module__", None) == "procfunc.nodes.types":
|
|
71
|
-
return f"t.{x.__name__}"
|
|
72
|
-
|
|
73
|
-
return x.__name__
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _repr_value(value: Any) -> str:
|
|
77
|
-
if hasattr(value, "__wrapped__"):
|
|
78
|
-
value = value.__wrapped__
|
|
79
|
-
|
|
80
|
-
if isinstance(value, cg.Proxy):
|
|
81
|
-
logger.warning(
|
|
82
|
-
f"Proxy object {value} should never appear as a raw value in codegen - "
|
|
83
|
-
f"its underlying node {value.node} was not resolved to a variable"
|
|
84
|
-
)
|
|
85
|
-
if isinstance(value, nt.ProcNode):
|
|
86
|
-
logger.warning(
|
|
87
|
-
f"Procnode object {value} should never be treated as a raw value in codegen"
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if isinstance(value, np.random.Generator):
|
|
91
|
-
return "np.random.default_rng()"
|
|
92
|
-
elif isinstance(value, type):
|
|
93
|
-
return _repr_type(value)
|
|
94
|
-
elif isinstance(value, np.ndarray):
|
|
95
|
-
nprepr = repr(value).replace("\n", "")
|
|
96
|
-
return f"np.{nprepr}"
|
|
97
|
-
elif isinstance(value, np.dtype):
|
|
98
|
-
return f"np.dtype('{value}')"
|
|
99
|
-
elif isinstance(value, (pf.Color, pf.Vector, pf.Euler, pf.Quaternion, pf.Matrix)):
|
|
100
|
-
x = tuple(round(x, 6) for x in value)
|
|
101
|
-
return f"pf.{value.__class__.__name__}({x})"
|
|
102
|
-
elif isinstance(value, enum.Enum):
|
|
103
|
-
return f"{type(value).__name__}.{value.name}"
|
|
104
|
-
elif isinstance(value, Path):
|
|
105
|
-
return f"Path({str(value)!r})"
|
|
106
|
-
elif dataclasses.is_dataclass(value) and not isinstance(value, type):
|
|
107
|
-
args_str = ", ".join(
|
|
108
|
-
f"{f.name}={_repr_value(getattr(value, f.name))}"
|
|
109
|
-
for f in dataclasses.fields(value)
|
|
110
|
-
)
|
|
111
|
-
return f"{type(value).__name__}({args_str})"
|
|
112
|
-
elif isinstance(value, list):
|
|
113
|
-
return f"[{', '.join([_repr_value(x) for x in value])}]"
|
|
114
|
-
else:
|
|
115
|
-
return repr(value)
|
|
116
|
-
|
|
117
|
-
|
|
118
32
|
def _repr_inp(
|
|
119
33
|
arg: Any,
|
|
120
34
|
scope_expressions: dict[int, str | list[str]],
|
|
@@ -127,7 +41,7 @@ def _repr_inp(
|
|
|
127
41
|
)
|
|
128
42
|
expr = scope_expressions[id(arg)]
|
|
129
43
|
else:
|
|
130
|
-
expr =
|
|
44
|
+
expr = repr_value(arg)
|
|
131
45
|
|
|
132
46
|
if isinstance(expr, list):
|
|
133
47
|
if len(expr) > 1:
|
|
@@ -184,7 +98,7 @@ def _repr_args(
|
|
|
184
98
|
|
|
185
99
|
argreprs = pytree.PyTree(args).map(lambda x: _repr_inp(x, scope_expressions))
|
|
186
100
|
argreprs = [
|
|
187
|
-
pytree.repr_tree_to_str(v, type_namer=
|
|
101
|
+
pytree.repr_tree_to_str(v, type_namer=repr_type)
|
|
188
102
|
for v in argreprs.unflatten_one_level()
|
|
189
103
|
]
|
|
190
104
|
|
|
@@ -194,7 +108,7 @@ def _repr_args(
|
|
|
194
108
|
.unflatten_one_level()
|
|
195
109
|
)
|
|
196
110
|
kwargreprs = {
|
|
197
|
-
k: pytree.repr_tree_to_str(v, type_namer=
|
|
111
|
+
k: pytree.repr_tree_to_str(v, type_namer=repr_type)
|
|
198
112
|
for k, v in kwargreprs.items()
|
|
199
113
|
}
|
|
200
114
|
|
|
@@ -222,11 +136,12 @@ def _repr_function_call(
|
|
|
222
136
|
node: cg.FunctionCallNode | cg.MethodCallNode | cg.SubgraphCallNode,
|
|
223
137
|
scope_expressions: dict[int, str | list[str]],
|
|
224
138
|
line_limit: int = 80,
|
|
139
|
+
func_str: str | None = None,
|
|
225
140
|
) -> list[str]:
|
|
226
141
|
match node:
|
|
227
142
|
case cg.FunctionCallNode():
|
|
228
143
|
func = node.func
|
|
229
|
-
func_str = scope_expressions[id(func)]
|
|
144
|
+
func_str = func_str or scope_expressions[id(func)]
|
|
230
145
|
case cg.MethodCallNode(args=(target, *_), method_name=method_name):
|
|
231
146
|
if not isinstance(target, cg.Node):
|
|
232
147
|
raise ValueError(f"Method call {node=} has non-node target {target=}")
|
|
@@ -261,22 +176,44 @@ def _repr_function_call(
|
|
|
261
176
|
return [f"{func_str}({', '.join(arg_reprs)})"]
|
|
262
177
|
|
|
263
178
|
|
|
264
|
-
def
|
|
179
|
+
def _operator_call_operands(
|
|
265
180
|
node: cg.FunctionCallNode,
|
|
181
|
+
template: str,
|
|
182
|
+
) -> list | None:
|
|
183
|
+
"""Operand values for rendering `node` in infix/operator form, or None to
|
|
184
|
+
decline it. The operator template only has slots for the operands, so any
|
|
185
|
+
extra argument beyond them (e.g. a non-default epsilon on func.equal) would
|
|
186
|
+
be silently dropped by the infix form - decline unless it merely restates
|
|
187
|
+
the signature default, in which case it is redundant and dropped."""
|
|
188
|
+
n_slots = template.count("{}")
|
|
189
|
+
try:
|
|
190
|
+
sig = inspect.signature(node.func)
|
|
191
|
+
bound = sig.bind(*node.args, **node.kwargs)
|
|
192
|
+
except (TypeError, ValueError):
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
operand_names = list(sig.parameters)[:n_slots]
|
|
196
|
+
if any(name not in bound.arguments for name in operand_names):
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
for name, value in bound.arguments.items():
|
|
200
|
+
if name in operand_names:
|
|
201
|
+
continue
|
|
202
|
+
if not _kwarg_matches_default(sig, name, value):
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
return [bound.arguments[name] for name in operand_names]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _repr_operator_call(
|
|
209
|
+
operands: list,
|
|
210
|
+
template: str,
|
|
266
211
|
scope_expressions: dict[int, str | list[str]],
|
|
267
212
|
) -> list[str]:
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
# Support both positional args and kwargs for operator templates
|
|
271
|
-
all_args = [
|
|
272
|
-
_repr_inp(v, scope_expressions, extra_parens=True) for v in node.args
|
|
273
|
-
] + [
|
|
274
|
-
_repr_inp(v, scope_expressions, extra_parens=True) for v in node.kwargs.values()
|
|
213
|
+
operand_reprs = [
|
|
214
|
+
_repr_inp(v, scope_expressions, extra_parens=True) for v in operands
|
|
275
215
|
]
|
|
276
|
-
|
|
277
|
-
operator_template = scope_expressions[id(node.func)]
|
|
278
|
-
assert isinstance(operator_template, str), operator_template
|
|
279
|
-
return [operator_template.format(*all_args)]
|
|
216
|
+
return [template.format(*operand_reprs)]
|
|
280
217
|
|
|
281
218
|
|
|
282
219
|
def _codegen_for_node(
|
|
@@ -293,7 +230,17 @@ def _codegen_for_node(
|
|
|
293
230
|
elif funcres == OperatorType.NOOP:
|
|
294
231
|
return [] # no code needed
|
|
295
232
|
elif "{}" in funcres:
|
|
296
|
-
|
|
233
|
+
operands = _operator_call_operands(node, funcres)
|
|
234
|
+
if operands is None:
|
|
235
|
+
# args the infix form cannot express: emit a named call.
|
|
236
|
+
# scope_expressions holds the operator template, so re-derive
|
|
237
|
+
# the callsite name (import already present via
|
|
238
|
+
# default_func_resolution_map)
|
|
239
|
+
_, callsite = _resolve_func(func)
|
|
240
|
+
return _repr_function_call(
|
|
241
|
+
node, scope_expressions, func_str=callsite
|
|
242
|
+
)
|
|
243
|
+
return _repr_operator_call(operands, funcres, scope_expressions)
|
|
297
244
|
else:
|
|
298
245
|
return _repr_function_call(node, scope_expressions)
|
|
299
246
|
case cg.MethodCallNode() if node.method_name == "__getitem__":
|
|
@@ -320,7 +267,7 @@ def _codegen_for_node(
|
|
|
320
267
|
)
|
|
321
268
|
return [f"{arg_expr}.{attribute_name}"]
|
|
322
269
|
case cg.ConstantNode(value=value):
|
|
323
|
-
return [
|
|
270
|
+
return [repr_value(value)]
|
|
324
271
|
case _:
|
|
325
272
|
raise TypeError(f"Unsupported {node=}")
|
|
326
273
|
|
|
@@ -353,17 +300,17 @@ def _codegen_graph_inputs(
|
|
|
353
300
|
|
|
354
301
|
known_value_type = node.metadata.get("known_value_type", None)
|
|
355
302
|
line = (
|
|
356
|
-
f"{name}: {
|
|
303
|
+
f"{name}: {repr_type(known_value_type)}"
|
|
357
304
|
if known_value_type is not None
|
|
358
305
|
else f"{name}"
|
|
359
306
|
)
|
|
360
307
|
|
|
361
308
|
if (default := node.kwargs.get("default_value")) is not None:
|
|
362
|
-
line += f" = {
|
|
309
|
+
line += f" = {repr_value(default)}"
|
|
363
310
|
|
|
364
311
|
args_lines.append(line + ",")
|
|
365
312
|
|
|
366
|
-
end_statement = "):" if typename is None else f") -> {typename}:
|
|
313
|
+
end_statement = "):" if typename is None else f") -> {typename}:"
|
|
367
314
|
|
|
368
315
|
return [f"def {func_name}("] + indent_lines(args_lines) + [end_statement]
|
|
369
316
|
|
|
@@ -379,7 +326,7 @@ def _codegen_namedtuple_def(outputs: pytree.PyTree):
|
|
|
379
326
|
if vt is None:
|
|
380
327
|
type_lines.append(f"{name}: Any")
|
|
381
328
|
else:
|
|
382
|
-
type_lines.append(f"{name}: {
|
|
329
|
+
type_lines.append(f"{name}: {repr_type(vt)}")
|
|
383
330
|
|
|
384
331
|
return [f"class {tupletype.__name__}(NamedTuple):"] + indent_lines(type_lines)
|
|
385
332
|
|
|
@@ -389,15 +336,17 @@ def _codegen_for_outputs(
|
|
|
389
336
|
scope_expressions: dict[int, str | list[str]],
|
|
390
337
|
) -> tuple[str | None, list[str], list[str]]:
|
|
391
338
|
if len(graph.outputs) == 0:
|
|
392
|
-
return
|
|
339
|
+
# Sink graphs (no return values) still need a body statement so the
|
|
340
|
+
# generated function parses; emit `pass` when nothing else fills it.
|
|
341
|
+
return None, [], ["pass"]
|
|
393
342
|
if len(graph.outputs) == 1:
|
|
394
343
|
single_output = next(graph.outputs.values())
|
|
395
344
|
vt = single_output.metadata.get("known_value_type", None)
|
|
396
|
-
type_name =
|
|
345
|
+
type_name = repr_type(vt) if vt is not None else None
|
|
397
346
|
return type_name, [], [f"return {_repr_inp(single_output, scope_expressions)}"]
|
|
398
347
|
|
|
399
348
|
graph_output_type = graph.outputs.toplevel_type()
|
|
400
|
-
type_name =
|
|
349
|
+
type_name = repr_type(graph_output_type)
|
|
401
350
|
|
|
402
351
|
is_pf_type = hasattr(pf, graph_output_type.__name__)
|
|
403
352
|
if is_pf_type:
|
|
@@ -416,7 +365,7 @@ def _codegen_for_outputs(
|
|
|
416
365
|
|
|
417
366
|
reprs_tree = graph.outputs.map(lambda node: _repr_inp(node, scope_expressions))
|
|
418
367
|
return_lines = [
|
|
419
|
-
f"return {pytree.repr_tree_to_str(reprs_tree, type_namer=
|
|
368
|
+
f"return {pytree.repr_tree_to_str(reprs_tree, type_namer=repr_type)}"
|
|
420
369
|
]
|
|
421
370
|
|
|
422
371
|
return type_name, type_def, return_lines
|
|
@@ -572,6 +521,7 @@ def _code_paragraphing_predicate(
|
|
|
572
521
|
def _codegen_for_assignment(
|
|
573
522
|
assign_varname: str,
|
|
574
523
|
node_code: list[str] | str,
|
|
524
|
+
node: cg.Node,
|
|
575
525
|
add_line_comments: bool,
|
|
576
526
|
) -> list[str]:
|
|
577
527
|
assert isinstance(assign_varname, str)
|
|
@@ -582,7 +532,7 @@ def _codegen_for_assignment(
|
|
|
582
532
|
else:
|
|
583
533
|
node_code = [f"{assign_varname} = {node_code}"]
|
|
584
534
|
if add_line_comments:
|
|
585
|
-
node_code[
|
|
535
|
+
node_code[-1] += f" # {str(node).replace(chr(10), ' ')}"
|
|
586
536
|
|
|
587
537
|
return node_code
|
|
588
538
|
|
|
@@ -662,7 +612,7 @@ def _codegen_for_graph(
|
|
|
662
612
|
continue
|
|
663
613
|
|
|
664
614
|
varname = expressions[id(node)]
|
|
665
|
-
node_code = _codegen_for_assignment(varname, node_code, add_line_comments)
|
|
615
|
+
node_code = _codegen_for_assignment(varname, node_code, node, add_line_comments)
|
|
666
616
|
code_lines.extend(node_code)
|
|
667
617
|
|
|
668
618
|
if last_varname.split("_")[0] != varname.split("_")[0]:
|
|
@@ -720,7 +670,7 @@ def default_func_resolution_map(
|
|
|
720
670
|
skip_funcs: set | None = None,
|
|
721
671
|
) -> tuple[dict[Any, str | OperatorType], list[str]]:
|
|
722
672
|
func_resolution = {}
|
|
723
|
-
import_lines =
|
|
673
|
+
import_lines = {"import procfunc as pf"}
|
|
724
674
|
|
|
725
675
|
for graph in cg.traverse_nested_graphs(toplevel_graph):
|
|
726
676
|
assert isinstance(graph, cg.ComputeGraph), graph
|
|
@@ -771,43 +721,46 @@ def graphs_to_python_functions(
|
|
|
771
721
|
np_linewidth = np.get_printoptions()["linewidth"]
|
|
772
722
|
np.set_printoptions(linewidth=100000)
|
|
773
723
|
|
|
774
|
-
|
|
724
|
+
try:
|
|
725
|
+
targets = _topo_sort_subgraphs(graph)
|
|
775
726
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
727
|
+
def _clean_graph_name(name: str) -> str:
|
|
728
|
+
for suffix in identifiers.NONDESCRIPTIVE_NODE_NAME_PARTS:
|
|
729
|
+
if name.endswith("_" + suffix):
|
|
730
|
+
name = name[: -(len(suffix) + 1)]
|
|
731
|
+
return name
|
|
781
732
|
|
|
782
|
-
|
|
783
|
-
|
|
733
|
+
for subgraph in cg.traverse_nested_graphs(graph):
|
|
734
|
+
subgraph.name = _clean_graph_name(subgraph.name)
|
|
784
735
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
scope_expressions = subgraph_names.copy()
|
|
791
|
-
for k, v in func_resolution.items():
|
|
792
|
-
if isinstance(v, OperatorType):
|
|
793
|
-
scope_expressions[id(k)] = OPERATOR_TEMPLATES[v]
|
|
794
|
-
else:
|
|
795
|
-
scope_expressions[id(k)] = v
|
|
796
|
-
|
|
797
|
-
lines_for_modules = []
|
|
798
|
-
for subgraph in targets:
|
|
799
|
-
func_name = subgraph_names[id(subgraph)]
|
|
800
|
-
result = _codegen_for_graph(
|
|
801
|
-
subgraph,
|
|
802
|
-
scope_expressions=scope_expressions.copy(),
|
|
803
|
-
as_maincall=(subgraph is graph and toplevel_as_maincall),
|
|
804
|
-
add_version_comment=add_version_comment,
|
|
805
|
-
add_line_comments=add_line_comments,
|
|
806
|
-
func_name=func_name,
|
|
736
|
+
subgraph_names = {
|
|
737
|
+
id(subgraph): subgraph.name for subgraph in cg.traverse_nested_graphs(graph)
|
|
738
|
+
}
|
|
739
|
+
subgraph_names = identifiers.dedup_names_with_suffix(
|
|
740
|
+
subgraph_names, separator="_"
|
|
807
741
|
)
|
|
808
|
-
lines_for_modules.append((subgraph_names[id(subgraph)], result))
|
|
809
742
|
|
|
810
|
-
|
|
743
|
+
scope_expressions = subgraph_names.copy()
|
|
744
|
+
for k, v in func_resolution.items():
|
|
745
|
+
if isinstance(v, OperatorType):
|
|
746
|
+
scope_expressions[id(k)] = OPERATOR_TEMPLATES[v]
|
|
747
|
+
else:
|
|
748
|
+
scope_expressions[id(k)] = v
|
|
749
|
+
|
|
750
|
+
lines_for_modules = []
|
|
751
|
+
for subgraph in targets:
|
|
752
|
+
func_name = subgraph_names[id(subgraph)]
|
|
753
|
+
result = _codegen_for_graph(
|
|
754
|
+
subgraph,
|
|
755
|
+
scope_expressions=scope_expressions.copy(),
|
|
756
|
+
as_maincall=(subgraph is graph and toplevel_as_maincall),
|
|
757
|
+
add_version_comment=add_version_comment,
|
|
758
|
+
add_line_comments=add_line_comments,
|
|
759
|
+
func_name=func_name,
|
|
760
|
+
)
|
|
761
|
+
lines_for_modules.append((subgraph_names[id(subgraph)], result))
|
|
762
|
+
finally:
|
|
763
|
+
np.set_printoptions(linewidth=np_linewidth)
|
|
811
764
|
|
|
812
765
|
return OrderedDict(lines_for_modules)
|
|
813
766
|
|
{procfunc-0.30.2/src/procfunc/transpiler → procfunc-0.33.0/src/procfunc/codegen}/identifiers.py
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import keyword
|
|
1
2
|
import logging
|
|
2
3
|
import re
|
|
3
4
|
from collections import Counter, defaultdict
|
|
@@ -36,6 +37,10 @@ def bpy_name_to_pythonid(name: str) -> str:
|
|
|
36
37
|
|
|
37
38
|
name = name.lower()
|
|
38
39
|
|
|
40
|
+
# drop anything that isn't valid in a python identifier (e.g. parentheses
|
|
41
|
+
# in socket names like "Joint ID (do not set)")
|
|
42
|
+
name = re.sub(r"[^0-9a-z_]", "_", name)
|
|
43
|
+
|
|
39
44
|
name = re.sub(r"_+", "_", name).strip("_")
|
|
40
45
|
|
|
41
46
|
# move number terms to end
|
|
@@ -46,6 +51,9 @@ def bpy_name_to_pythonid(name: str) -> str:
|
|
|
46
51
|
|
|
47
52
|
name = "_".join(parts)
|
|
48
53
|
|
|
54
|
+
if keyword.iskeyword(name):
|
|
55
|
+
name = name + "_"
|
|
56
|
+
|
|
49
57
|
return name
|
|
50
58
|
|
|
51
59
|
|
|
@@ -60,6 +68,8 @@ def is_valid_snake_identifier(name: str) -> bool:
|
|
|
60
68
|
return False
|
|
61
69
|
if name != name.lower():
|
|
62
70
|
return False # this is opinionated
|
|
71
|
+
if not name.isidentifier() or keyword.iskeyword(name):
|
|
72
|
+
return False
|
|
63
73
|
return True
|
|
64
74
|
|
|
65
75
|
|