procfunc 0.35.0__tar.gz → 0.35.1__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.
Files changed (108) hide show
  1. {procfunc-0.35.0/src/procfunc.egg-info → procfunc-0.35.1}/PKG-INFO +7 -4
  2. {procfunc-0.35.0 → procfunc-0.35.1}/pyproject.toml +7 -3
  3. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/__init__.py +1 -1
  4. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/context.py +9 -0
  5. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/types.py +10 -3
  6. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transpiler/bpy_to_computegraph.py +3 -1
  7. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/manifest.py +2 -1
  8. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/teardown.py +15 -1
  9. {procfunc-0.35.0 → procfunc-0.35.1/src/procfunc.egg-info}/PKG-INFO +7 -4
  10. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc.egg-info/SOURCES.txt +1 -0
  11. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc.egg-info/requires.txt +7 -3
  12. procfunc-0.35.1/tests/test_teardown.py +51 -0
  13. {procfunc-0.35.0 → procfunc-0.35.1}/LICENSE.md +0 -0
  14. {procfunc-0.35.0 → procfunc-0.35.1}/README.md +0 -0
  15. {procfunc-0.35.0 → procfunc-0.35.1}/setup.cfg +0 -0
  16. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/cli/__init__.py +0 -0
  17. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/cli/main.py +0 -0
  18. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/codegen/__init__.py +0 -0
  19. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/codegen/codegen.py +0 -0
  20. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/codegen/identifiers.py +0 -0
  21. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/codegen/repr.py +0 -0
  22. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/color.py +0 -0
  23. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/compute_graph/__init__.py +0 -0
  24. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/compute_graph/compute_graph.py +0 -0
  25. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/compute_graph/node.py +0 -0
  26. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/compute_graph/operators_info.py +0 -0
  27. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/compute_graph/proxy.py +0 -0
  28. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/compute_graph/util.py +0 -0
  29. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/control/__init__.py +0 -0
  30. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/__init__.py +0 -0
  31. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/color.py +0 -0
  32. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/compositor.py +0 -0
  33. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/construct_nodes.py +0 -0
  34. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/construct_operator.py +0 -0
  35. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/construct_special_cases.py +0 -0
  36. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/construct_standard.py +0 -0
  37. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/execute.py +0 -0
  38. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/infer_runtime_data_type.py +0 -0
  39. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/realize.py +0 -0
  40. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/execute/util.py +0 -0
  41. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/func.py +0 -0
  42. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/geo.py +0 -0
  43. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/manifest.json +0 -0
  44. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/math.py +0 -0
  45. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/shader.py +0 -0
  46. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/texture.py +0 -0
  47. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/util/__init__.py +0 -0
  48. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/util/bindings_util.py +0 -0
  49. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/util/bpy_node_info.py +0 -0
  50. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/nodes/util/node_function.py +0 -0
  51. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/__init__.py +0 -0
  52. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/_util.py +0 -0
  53. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/addons.py +0 -0
  54. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/attr.py +0 -0
  55. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/collection.py +0 -0
  56. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/curve.py +0 -0
  57. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/file.py +0 -0
  58. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/manifest.json +0 -0
  59. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/mesh.py +0 -0
  60. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/modifier.py +0 -0
  61. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/object.py +0 -0
  62. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/primitives/__init__.py +0 -0
  63. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/primitives/camera.py +0 -0
  64. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/primitives/curve.py +0 -0
  65. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/primitives/light.py +0 -0
  66. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/primitives/mesh.py +0 -0
  67. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/ops/uv.py +0 -0
  68. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/random.py +0 -0
  69. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/tracer/__init__.py +0 -0
  70. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/tracer/decorator.py +0 -0
  71. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/tracer/patch.py +0 -0
  72. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/tracer/proxy.py +0 -0
  73. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/tracer/trace.py +0 -0
  74. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/__init__.py +0 -0
  75. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/cleanup.py +0 -0
  76. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/convert.py +0 -0
  77. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/distribution.py +0 -0
  78. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/extract_materials.py +0 -0
  79. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/infer_distribution.py +0 -0
  80. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/parameters.py +0 -0
  81. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transforms/util.py +0 -0
  82. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transpiler/__init__.py +0 -0
  83. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transpiler/main.py +0 -0
  84. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transpiler/parse_default_values.py +0 -0
  85. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/transpiler/parse_special_cases.py +0 -0
  86. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/types.py +0 -0
  87. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/__init__.py +0 -0
  88. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/bpy_data.py +0 -0
  89. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/bpy_info.py +0 -0
  90. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/camera.py +0 -0
  91. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/keyframe.py +0 -0
  92. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/log.py +0 -0
  93. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc/util/pytree.py +0 -0
  94. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc.egg-info/dependency_links.txt +0 -0
  95. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc.egg-info/entry_points.txt +0 -0
  96. {procfunc-0.35.0 → procfunc-0.35.1}/src/procfunc.egg-info/top_level.txt +0 -0
  97. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_asset.py +0 -0
  98. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_bpy_data_cleanup.py +0 -0
  99. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_cli_transpile.py +0 -0
  100. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_codegen.py +0 -0
  101. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_codegen_matrix.py +0 -0
  102. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_compute_graph.py +0 -0
  103. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_node_function.py +0 -0
  104. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_ops.py +0 -0
  105. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_pytree.py +0 -0
  106. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_random.py +0 -0
  107. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_trace.py +0 -0
  108. {procfunc-0.35.0 → procfunc-0.35.1}/tests/test_transforms.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: procfunc
3
- Version: 0.35.0
3
+ Version: 0.35.1
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
@@ -9,15 +9,18 @@ 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
12
- Requires-Dist: numpy<2
13
- Requires-Dist: pandas
12
+ Requires-Dist: numpy<2,>=1.23.5
13
+ Requires-Dist: pandas>=1.5
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest<10,>=7.2; extra == "test"
14
16
  Provides-Extra: dev
15
- Requires-Dist: pytest; extra == "dev"
17
+ Requires-Dist: pytest<10,>=7.2; extra == "dev"
16
18
  Requires-Dist: pytest-xdist; extra == "dev"
17
19
  Requires-Dist: ruff<0.16,>=0.15; extra == "dev"
18
20
  Requires-Dist: ty; extra == "dev"
19
21
  Requires-Dist: imageio; extra == "dev"
20
22
  Requires-Dist: matplotlib; extra == "dev"
23
+ Requires-Dist: coverage; extra == "dev"
21
24
  Provides-Extra: docs
22
25
  Requires-Dist: sphinx; extra == "docs"
23
26
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
@@ -11,8 +11,8 @@ license = "BSD-3-Clause"
11
11
  requires-python = ">=3.11,<3.12"
12
12
  dependencies = [
13
13
  "bpy==4.2.0",
14
- "numpy<2",
15
- "pandas",
14
+ "numpy>=1.23.5,<2",
15
+ "pandas>=1.5",
16
16
  ]
17
17
 
18
18
  [project.urls]
@@ -23,13 +23,17 @@ Repository = "https://github.com/princeton-vl/procfunc"
23
23
  procfunc = "procfunc.cli:cli"
24
24
 
25
25
  [project.optional-dependencies]
26
+ test = [
27
+ "pytest>=7.2,<10",
28
+ ]
26
29
  dev = [
27
- "pytest",
30
+ "pytest>=7.2,<10",
28
31
  "pytest-xdist",
29
32
  "ruff>=0.15,<0.16",
30
33
  "ty",
31
34
  "imageio",
32
35
  "matplotlib",
36
+ "coverage",
33
37
  ]
34
38
  docs = [
35
39
  "sphinx",
@@ -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.35.0"
5
+ __version__ = "0.35.1"
6
6
 
7
7
  from numpy.random import Generator as RNG
8
8
 
@@ -19,6 +19,13 @@ class ProcfuncContext:
19
19
  'ignore' silently returns an empty mesh, 'warn' logs a warning, 'throw' raises an error.
20
20
  """
21
21
 
22
+ record_node_definitions: bool = False
23
+ """
24
+ Record the user-space file/line/function that constructed each ProcNode, used only to
25
+ enrich node-instantiation error messages. Walks the stack once per node, which can
26
+ dominate build time in node-heavy callers, so off by default.
27
+ """
28
+
22
29
  def __post_init__(self):
23
30
  """Initialize computed fields after dataclass creation."""
24
31
  if self.num_cpu_cores <= 0:
@@ -44,6 +51,8 @@ globals = ProcfuncContext(
44
51
  num_cpu_cores=int(os.environ.get("PROCFUNC_NUM_CPU_CORES", 0)),
45
52
  warn_mode_empty_geonodes=_warn_mode_empty_geonodes, # type: ignore[invalid-assignment]
46
53
  current_trace_level=None,
54
+ record_node_definitions=os.environ.get("PROCFUNC_RECORD_NODE_DEFINITIONS", "0")
55
+ == "1",
47
56
  )
48
57
 
49
58
 
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from typing import Any, Generic, TypeVar, Union
6
6
 
7
7
  from procfunc import compute_graph as cg
8
+ from procfunc import context
8
9
  from procfunc import types as pt
9
10
  from procfunc.compute_graph.operators_info import (
10
11
  OPERATORS_TO_FUNCTIONS,
@@ -79,7 +80,8 @@ class ProcNode(Generic[T]):
79
80
  f"these should have been unwrapped to cg.Node {node.args} {node.kwargs}"
80
81
  )
81
82
 
82
- self._node.metadata["definition"] = _node_definition_metadata()
83
+ if context.globals.record_node_definitions:
84
+ self._node.metadata["definition"] = _node_definition_metadata()
83
85
 
84
86
  def astype(self, dtype: type) -> "ProcNode":
85
87
  node = copy.copy(self._node)
@@ -114,7 +116,8 @@ class ProcNode(Generic[T]):
114
116
  )
115
117
 
116
118
  node = cg.ProceduralNode(node_type=node_type, attrs=attrs, kwargs=inputs)
117
- node.metadata["definition"] = _node_definition_metadata()
119
+ if context.globals.record_node_definitions:
120
+ node.metadata["definition"] = _node_definition_metadata()
118
121
 
119
122
  return cls(node=node)
120
123
 
@@ -262,7 +265,11 @@ def node_definition_context_message(node: cg.Node):
262
265
  metadata = object.__getattribute__(node, "metadata")
263
266
  lineno_metadata = metadata.get("definition", None)
264
267
  if lineno_metadata is None:
265
- return ""
268
+ return (
269
+ " (set PROCFUNC_RECORD_NODE_DEFINITIONS=1 or "
270
+ "override_globals(record_node_definitions=True) to see the "
271
+ "file/line that defined this node) "
272
+ )
266
273
  file, lineno, procfunc_name = lineno_metadata
267
274
  return f" {procfunc_name}() call on {file}:{lineno} "
268
275
 
@@ -1196,9 +1196,11 @@ def parse_geo_modifier(
1196
1196
  pf.nodes.to_mesh_object, args=(node_curr,), kwargs={}
1197
1197
  )
1198
1198
  case 1, _:
1199
+ (geometry_getattr,) = geo_output_getattrs.values()
1199
1200
  return cg.FunctionCallNode(
1200
1201
  pf.nodes.to_mesh_object_with_attributes,
1201
- kwargs={**geo_output_getattrs, **attribute_output_getattrs},
1202
+ args=(geometry_getattr,),
1203
+ kwargs={"attributes": attribute_output_getattrs},
1202
1204
  )
1203
1205
  case _, _:
1204
1206
  return cg.FunctionCallNode(
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from typing import Any, Callable
7
7
 
8
8
  import pandas as pd
9
+ from pandas.api.types import is_string_dtype
9
10
 
10
11
  logger = logging.getLogger(__name__)
11
12
 
@@ -46,7 +47,7 @@ def filter_manifest(
46
47
  patterns = [patterns]
47
48
 
48
49
  before_count = len(manifest)
49
- if manifest[column].dtype == "object":
50
+ if is_string_dtype(manifest[column].dtype):
50
51
  mask = pd.Series([False] * len(manifest), index=manifest.index)
51
52
  for pattern in patterns:
52
53
  if isinstance(pattern, str):
@@ -2,7 +2,8 @@
2
2
 
3
3
  bpy's cleanup segfaults (SIGSEGV / exit code 139) during normal Python
4
4
  shutdown. ``os._exit`` skips Python teardown and atexit handlers, avoiding
5
- the crash while preserving the real exit code.
5
+ the crash while preserving the real exit code. Anything the process would
6
+ otherwise have written on its way out is flushed here first.
6
7
  """
7
8
 
8
9
  import os
@@ -11,7 +12,20 @@ import traceback
11
12
  from contextlib import contextmanager
12
13
 
13
14
 
15
+ def _save_coverage() -> None:
16
+ # coverage.py saves once the traced process returns, which os._exit never does
17
+ try:
18
+ import coverage
19
+
20
+ cov = coverage.Coverage.current()
21
+ if cov is not None:
22
+ cov.save()
23
+ except Exception:
24
+ traceback.print_exc()
25
+
26
+
14
27
  def exit_skipping_teardown(code: int = 0):
28
+ _save_coverage()
15
29
  sys.stdout.flush()
16
30
  sys.stderr.flush()
17
31
  os._exit(code)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: procfunc
3
- Version: 0.35.0
3
+ Version: 0.35.1
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
@@ -9,15 +9,18 @@ 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
12
- Requires-Dist: numpy<2
13
- Requires-Dist: pandas
12
+ Requires-Dist: numpy<2,>=1.23.5
13
+ Requires-Dist: pandas>=1.5
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest<10,>=7.2; extra == "test"
14
16
  Provides-Extra: dev
15
- Requires-Dist: pytest; extra == "dev"
17
+ Requires-Dist: pytest<10,>=7.2; extra == "dev"
16
18
  Requires-Dist: pytest-xdist; extra == "dev"
17
19
  Requires-Dist: ruff<0.16,>=0.15; extra == "dev"
18
20
  Requires-Dist: ty; extra == "dev"
19
21
  Requires-Dist: imageio; extra == "dev"
20
22
  Requires-Dist: matplotlib; extra == "dev"
23
+ Requires-Dist: coverage; extra == "dev"
21
24
  Provides-Extra: docs
22
25
  Requires-Dist: sphinx; extra == "docs"
23
26
  Requires-Dist: sphinx-rtd-theme; extra == "docs"
@@ -101,5 +101,6 @@ tests/test_node_function.py
101
101
  tests/test_ops.py
102
102
  tests/test_pytree.py
103
103
  tests/test_random.py
104
+ tests/test_teardown.py
104
105
  tests/test_trace.py
105
106
  tests/test_transforms.py
@@ -1,16 +1,20 @@
1
1
  bpy==4.2.0
2
- numpy<2
3
- pandas
2
+ numpy<2,>=1.23.5
3
+ pandas>=1.5
4
4
 
5
5
  [dev]
6
- pytest
6
+ pytest<10,>=7.2
7
7
  pytest-xdist
8
8
  ruff<0.16,>=0.15
9
9
  ty
10
10
  imageio
11
11
  matplotlib
12
+ coverage
12
13
 
13
14
  [docs]
14
15
  sphinx
15
16
  sphinx-rtd-theme
16
17
  sphinx-argparse
18
+
19
+ [test]
20
+ pytest<10,>=7.2
@@ -0,0 +1,51 @@
1
+ import os
2
+ import subprocess
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ _REPO = Path(__file__).resolve().parents[1]
9
+
10
+
11
+ def write_entrypoint(tmp_path: Path, body: str) -> Path:
12
+ script = tmp_path / "entrypoint.py"
13
+ script.write_text(
14
+ "from procfunc.util.teardown import skip_teardown_on_exit\n\n" + body
15
+ )
16
+ return script
17
+
18
+
19
+ def run(cmd: list[str], tmp_path: Path) -> subprocess.CompletedProcess:
20
+ return subprocess.run(
21
+ cmd,
22
+ cwd=_REPO,
23
+ env={**os.environ, "COVERAGE_FILE": str(tmp_path / "data")},
24
+ capture_output=True,
25
+ text=True,
26
+ )
27
+
28
+
29
+ def test_coverage_data_saved_before_exit(tmp_path):
30
+ pytest.importorskip("coverage")
31
+ script = write_entrypoint(
32
+ tmp_path, "with skip_teardown_on_exit():\n print('ran')\n"
33
+ )
34
+
35
+ proc = run(
36
+ [sys.executable, "-m", "coverage", "run", "--parallel-mode", str(script)],
37
+ tmp_path,
38
+ )
39
+
40
+ assert proc.returncode == 0, proc.stderr
41
+ assert sorted(tmp_path.glob("data.*")), proc.stderr
42
+
43
+
44
+ def test_exit_code_preserved_without_coverage(tmp_path):
45
+ script = write_entrypoint(
46
+ tmp_path, "with skip_teardown_on_exit():\n raise SystemExit(3)\n"
47
+ )
48
+
49
+ proc = run([sys.executable, str(script)], tmp_path)
50
+
51
+ assert proc.returncode == 3, proc.stderr
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes