tenzir-test 0.12.0__py3-none-any.whl → 0.13.1__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.
tenzir_test/cli.py CHANGED
@@ -51,6 +51,15 @@ def _normalize_exit_code(value: object) -> int:
51
51
  type=click.Path(path_type=Path, dir_okay=False, writable=False, resolve_path=False),
52
52
  help="Path to the tenzir-node executable.",
53
53
  )
54
+ @click.option(
55
+ "package_dirs",
56
+ "--package-dirs",
57
+ multiple=True,
58
+ help=(
59
+ "Comma-separated list of package directories to load (repeatable). "
60
+ "These only control package visibility; test selection still follows the usual --root/args."
61
+ ),
62
+ )
54
63
  @click.argument(
55
64
  "tests",
56
65
  nargs=-1,
@@ -137,6 +146,7 @@ def cli(
137
146
  root: Path | None,
138
147
  tenzir_binary: Path | None,
139
148
  tenzir_node_binary: Path | None,
149
+ package_dirs: tuple[str, ...],
140
150
  tests: tuple[Path, ...],
141
151
  update: bool,
142
152
  debug: bool,
@@ -155,6 +165,14 @@ def cli(
155
165
  ) -> int:
156
166
  """Execute tenzir-test scenarios."""
157
167
 
168
+ package_paths: list[Path] = []
169
+ for entry in package_dirs:
170
+ for piece in entry.split(","):
171
+ piece = piece.strip()
172
+ if not piece:
173
+ continue
174
+ package_paths.append(Path(piece))
175
+
158
176
  jobs_source = ctx.get_parameter_source("jobs")
159
177
  jobs_overridden = jobs_source is not click.core.ParameterSource.DEFAULT
160
178
 
@@ -163,6 +181,7 @@ def cli(
163
181
  root=root,
164
182
  tenzir_binary=tenzir_binary,
165
183
  tenzir_node_binary=tenzir_node_binary,
184
+ package_dirs=package_paths,
166
185
  tests=list(tests),
167
186
  update=update,
168
187
  debug=debug,
tenzir_test/config.py CHANGED
@@ -41,7 +41,7 @@ def discover_settings(
41
41
  ) -> Settings:
42
42
  """Produce harness settings by combining CLI overrides with environment defaults."""
43
43
 
44
- environment = dict(env or os.environ)
44
+ environment = dict(os.environ if env is None else env)
45
45
 
46
46
  chosen_root = root or environment.get("TENZIR_TEST_ROOT") or Path.cwd()
47
47
  root_path = Path(chosen_root).resolve()
@@ -6,6 +6,7 @@ import atexit
6
6
  import functools
7
7
  import logging
8
8
  import os
9
+ import select
9
10
  import shlex
10
11
  import subprocess
11
12
  import tempfile
@@ -26,6 +27,37 @@ from . import (
26
27
  if TYPE_CHECKING:
27
28
  from . import FixtureContext
28
29
 
30
+ _STDERR_READ_TIMEOUT = 0.5 # seconds to wait for stderr data
31
+
32
+
33
+ def _read_available_stderr(process: subprocess.Popen[str]) -> str:
34
+ """Read any available stderr output without blocking.
35
+
36
+ Uses select() to check if data is available before reading. This allows
37
+ capturing diagnostic output even when the process is still running but
38
+ has written error messages to stderr.
39
+ """
40
+ if not process.stderr:
41
+ return ""
42
+ try:
43
+ fd = process.stderr.fileno()
44
+ readable, _, _ = select.select([fd], [], [], _STDERR_READ_TIMEOUT)
45
+ if not readable:
46
+ return ""
47
+ # Read available data in chunks to avoid blocking on partial reads.
48
+ chunks: list[str] = []
49
+ while True:
50
+ ready, _, _ = select.select([fd], [], [], 0)
51
+ if not ready:
52
+ break
53
+ chunk = os.read(fd, 4096).decode("utf-8", errors="replace")
54
+ if not chunk:
55
+ break
56
+ chunks.append(chunk)
57
+ return "".join(chunks).strip()
58
+ except Exception:
59
+ return ""
60
+
29
61
 
30
62
  def _terminate_process(process: subprocess.Popen[str]) -> None:
31
63
  """Terminate the spawned node process and ensure its group is gone."""
@@ -138,16 +170,47 @@ def node() -> Iterator[dict[str, str]]:
138
170
  raise RuntimeError("TENZIR_NODE_BINARY must be configured for the node fixture")
139
171
 
140
172
  env = context.env.copy()
141
- config_args = [arg for arg in context.config_args if not arg.startswith("--config=")]
173
+ # Extract and filter config arguments: we handle --config and --package-dirs separately.
174
+ config_args: list[str] = []
175
+ config_package_dirs: list[str] = []
176
+ for arg in context.config_args:
177
+ if arg.startswith("--config="):
178
+ continue
179
+ if arg.startswith("--package-dirs="):
180
+ value = arg.split("=", 1)[1]
181
+ config_package_dirs.extend(entry.strip() for entry in value.split(",") if entry.strip())
182
+ continue
183
+ config_args.append(arg)
142
184
  node_config = env.get("TENZIR_NODE_CONFIG")
143
185
  if node_config:
144
186
  config_args.append(f"--config={node_config}")
145
- package_root = env.get("TENZIR_PACKAGE_ROOT")
146
187
  package_args: list[str] = []
188
+ package_root = env.get("TENZIR_PACKAGE_ROOT")
189
+ package_dirs: list[str] = []
147
190
  if package_root:
148
- package_arg = f"--package-dirs={package_root}"
149
- if package_arg not in config_args:
150
- package_args.append(package_arg)
191
+ package_dirs.append(package_root)
192
+
193
+ extra_package_dirs = env.get("TENZIR_PACKAGE_DIRS")
194
+ if extra_package_dirs:
195
+ package_dirs.extend(
196
+ [entry.strip() for entry in extra_package_dirs.split(",") if entry.strip()]
197
+ )
198
+
199
+ # Include package directories from config_args as well.
200
+ package_dirs.extend(config_package_dirs)
201
+
202
+ # Deduplicate while preserving order by using resolved paths as keys.
203
+ seen: set[str] = set()
204
+ unique_dirs: list[str] = []
205
+ for entry in package_dirs:
206
+ resolved = str(Path(entry).expanduser().resolve(strict=False))
207
+ if resolved not in seen:
208
+ seen.add(resolved)
209
+ unique_dirs.append(entry)
210
+ package_dirs = unique_dirs
211
+
212
+ if package_dirs:
213
+ package_args.append(f"--package-dirs={','.join(package_dirs)}")
151
214
  temp_dir = _ensure_temp_dir(context)
152
215
  key = id(context)
153
216
 
@@ -213,7 +276,21 @@ def node() -> Iterator[dict[str, str]]:
213
276
  endpoint = process.stdout.readline().strip()
214
277
 
215
278
  if not endpoint:
216
- raise RuntimeError("failed to obtain endpoint from tenzir-node")
279
+ # Collect diagnostic information to help debug startup failures.
280
+ diagnostics: list[str] = []
281
+ returncode = process.poll()
282
+ if returncode is not None:
283
+ diagnostics.append(f"exit code {returncode}")
284
+ # Try to read stderr output using non-blocking I/O. This captures
285
+ # diagnostics even when the process is still running (e.g., when it
286
+ # hangs after writing an error message but before exiting).
287
+ stderr_output = _read_available_stderr(process)
288
+ if stderr_output:
289
+ diagnostics.append(f"stderr:\n{stderr_output}")
290
+ detail = (
291
+ "; ".join(diagnostics) if diagnostics else "no additional diagnostics available"
292
+ )
293
+ raise RuntimeError(f"failed to obtain endpoint from tenzir-node ({detail})")
217
294
 
218
295
  fixture_env = {
219
296
  "TENZIR_NODE_CLIENT_ENDPOINT": endpoint,
tenzir_test/run.py CHANGED
@@ -128,6 +128,7 @@ class ExecutionMode(enum.Enum):
128
128
 
129
129
  PROJECT = "project"
130
130
  PACKAGE = "package"
131
+ LIBRARY = "library"
131
132
 
132
133
 
133
134
  class HarnessMode(enum.Enum):
@@ -146,6 +147,18 @@ class ColorMode(enum.Enum):
146
147
  NEVER = "never"
147
148
 
148
149
 
150
+ def _is_library_root(path: Path) -> bool:
151
+ """Return True when the directory looks like a library of packages."""
152
+
153
+ if packages.is_package_dir(path):
154
+ return False
155
+ try:
156
+ entries = list(packages.iter_package_dirs(path))
157
+ except OSError:
158
+ return False
159
+ return bool(entries)
160
+
161
+
149
162
  def detect_execution_mode(root: Path) -> tuple[ExecutionMode, Path | None]:
150
163
  """Return the execution mode and detected package root for `root`."""
151
164
 
@@ -156,6 +169,9 @@ def detect_execution_mode(root: Path) -> tuple[ExecutionMode, Path | None]:
156
169
  if parent is not None and packages.is_package_dir(parent):
157
170
  return ExecutionMode.PACKAGE, parent
158
171
 
172
+ if _is_library_root(root):
173
+ return ExecutionMode.LIBRARY, None
174
+
159
175
  return ExecutionMode.PROJECT, None
160
176
 
161
177
 
@@ -746,6 +762,23 @@ _CLI_LOGGER.setLevel(logging.DEBUG if _debug_logging else logging.WARNING)
746
762
  _DIRECTORY_CONFIG_CACHE: dict[Path, "_DirectoryConfig"] = {}
747
763
 
748
764
  _DISCOVERY_ENABLED = False
765
+ _CLI_PACKAGES: list[Path] = []
766
+
767
+
768
+ def _expand_package_dirs(path: Path) -> list[str]:
769
+ """Normalize a package dir hint; if it contains packages, return those."""
770
+
771
+ resolved = path.expanduser().resolve()
772
+ if packages.is_package_dir(resolved):
773
+ return [str(resolved)]
774
+ expanded: list[str] = []
775
+ try:
776
+ for pkg in packages.iter_package_dirs(resolved):
777
+ expanded.append(str(pkg.resolve()))
778
+ except OSError as exc:
779
+ _CLI_LOGGER.debug("failed to expand package dir %s: %s", path, exc)
780
+ return [str(resolved)]
781
+ return expanded or [str(resolved)]
749
782
 
750
783
 
751
784
  def _set_discovery_logging(enabled: bool) -> None:
@@ -753,6 +786,29 @@ def _set_discovery_logging(enabled: bool) -> None:
753
786
  _DISCOVERY_ENABLED = enabled
754
787
 
755
788
 
789
+ def _set_cli_packages(package_paths: list[Path]) -> None:
790
+ global _CLI_PACKAGES
791
+ _CLI_PACKAGES = [path.resolve() for path in package_paths]
792
+
793
+
794
+ def _get_cli_packages() -> list[Path]:
795
+ return list(_CLI_PACKAGES)
796
+
797
+
798
+ def _deduplicate_package_dirs(candidates: list[str]) -> list[str]:
799
+ """Remove duplicate package directories while preserving order."""
800
+
801
+ seen: set[str] = set()
802
+ result: list[str] = []
803
+ for candidate in candidates:
804
+ normalized = str(Path(candidate).expanduser().resolve(strict=False))
805
+ if normalized in seen:
806
+ continue
807
+ seen.add(normalized)
808
+ result.append(candidate)
809
+ return result
810
+
811
+
756
812
  def _print_discovery_message(message: str) -> None:
757
813
  if _CLI_LOGGER.isEnabledFor(logging.DEBUG):
758
814
  _CLI_LOGGER.debug(message)
@@ -768,6 +824,7 @@ class ProjectMarker(enum.Enum):
768
824
  TESTS_DIRECTORY = "tests_directory"
769
825
  TEST_CONFIG = "test_config"
770
826
  TEST_SUITE_DIRECTORY = "test_suite_directory"
827
+ LIBRARY_ROOT = "library_root"
771
828
 
772
829
 
773
830
  @dataclasses.dataclass(frozen=True, slots=True)
@@ -790,6 +847,7 @@ _PRIMARY_PROJECT_MARKERS = {
790
847
  ProjectMarker.TESTS_DIRECTORY,
791
848
  ProjectMarker.TEST_CONFIG,
792
849
  ProjectMarker.TEST_SUITE_DIRECTORY,
850
+ ProjectMarker.LIBRARY_ROOT,
793
851
  }
794
852
 
795
853
 
@@ -819,6 +877,12 @@ def _describe_project_root(path: Path) -> ProjectSignature | None:
819
877
  if resolved.name == "tests" and resolved.is_dir():
820
878
  markers.add(ProjectMarker.TEST_SUITE_DIRECTORY)
821
879
 
880
+ try:
881
+ if _is_library_root(resolved):
882
+ markers.add(ProjectMarker.LIBRARY_ROOT)
883
+ except OSError:
884
+ pass
885
+
822
886
  if not markers:
823
887
  return None
824
888
 
@@ -1117,12 +1181,15 @@ def _default_test_config() -> TestConfig:
1117
1181
  "inputs": None,
1118
1182
  "retry": 1,
1119
1183
  "suite": None,
1184
+ "package_dirs": tuple(),
1120
1185
  }
1121
1186
 
1122
1187
 
1123
1188
  def _canonical_config_key(key: str) -> str:
1124
1189
  if key == "fixture":
1125
1190
  return "fixtures"
1191
+ if key in {"package_dirs", "package-dirs"}:
1192
+ return "package_dirs"
1126
1193
  return key
1127
1194
 
1128
1195
 
@@ -1224,6 +1291,50 @@ def _normalize_inputs_value(
1224
1291
  return None
1225
1292
 
1226
1293
 
1294
+ def _normalize_package_dirs_value(
1295
+ value: typing.Any,
1296
+ *,
1297
+ location: Path | str,
1298
+ line_number: int | None = None,
1299
+ ) -> tuple[str, ...]:
1300
+ if value is None:
1301
+ return tuple()
1302
+ if not isinstance(value, (list, tuple)):
1303
+ _raise_config_error(
1304
+ location,
1305
+ f"Invalid value for 'package-dirs', expected list of strings, got '{value}'",
1306
+ line_number,
1307
+ )
1308
+ return tuple()
1309
+ base_dir = _extract_location_path(location).parent
1310
+ normalized: list[str] = []
1311
+ for entry in value:
1312
+ if not isinstance(entry, (str, os.PathLike)):
1313
+ _raise_config_error(
1314
+ location,
1315
+ f"Invalid package-dirs entry '{entry}', expected string",
1316
+ line_number,
1317
+ )
1318
+ continue
1319
+ raw = os.fspath(entry).strip()
1320
+ if not raw:
1321
+ _raise_config_error(
1322
+ location,
1323
+ "Invalid package-dirs entry: must be non-empty string",
1324
+ line_number,
1325
+ )
1326
+ continue
1327
+ path = Path(raw)
1328
+ if not path.is_absolute():
1329
+ path = base_dir / path
1330
+ try:
1331
+ path = path.resolve()
1332
+ except OSError:
1333
+ path = path
1334
+ normalized.append(str(path))
1335
+ return tuple(normalized)
1336
+
1337
+
1227
1338
  def _assign_config_option(
1228
1339
  config: TestConfig,
1229
1340
  key: str,
@@ -1234,7 +1345,16 @@ def _assign_config_option(
1234
1345
  origin: ConfigOrigin,
1235
1346
  ) -> None:
1236
1347
  canonical = _canonical_config_key(key)
1237
- valid_keys: set[str] = {"error", "timeout", "runner", "skip", "fixtures", "inputs", "retry"}
1348
+ valid_keys: set[str] = {
1349
+ "error",
1350
+ "timeout",
1351
+ "runner",
1352
+ "skip",
1353
+ "fixtures",
1354
+ "inputs",
1355
+ "retry",
1356
+ "package_dirs",
1357
+ }
1238
1358
  if origin == "directory":
1239
1359
  valid_keys.add("suite")
1240
1360
  if canonical not in valid_keys:
@@ -1321,6 +1441,17 @@ def _assign_config_option(
1321
1441
  value, location=location, line_number=line_number
1322
1442
  )
1323
1443
  return
1444
+ if canonical == "package_dirs":
1445
+ if origin != "directory":
1446
+ _raise_config_error(
1447
+ location,
1448
+ "'package-dirs' can only be specified in directory-level test.yaml files",
1449
+ line_number,
1450
+ )
1451
+ config[canonical] = _normalize_package_dirs_value(
1452
+ value, location=location, line_number=line_number
1453
+ )
1454
+ return
1324
1455
  if canonical == "retry":
1325
1456
  if origin == "frontmatter" and isinstance(config.get("suite"), str):
1326
1457
  _raise_config_error(
@@ -2710,14 +2841,27 @@ def run_simple_test(
2710
2841
  expect_error = bool(test_config.get("error", False))
2711
2842
  passthrough_mode = is_passthrough_enabled()
2712
2843
 
2844
+ config_package_dirs = cast(tuple[str, ...], test_config.get("package_dirs", tuple()))
2845
+ additional_package_dirs: list[str] = []
2846
+ for entry in config_package_dirs:
2847
+ additional_package_dirs.extend(_expand_package_dirs(Path(entry)))
2848
+
2713
2849
  package_root = packages.find_package_root(test)
2714
2850
  package_args: list[str] = []
2851
+ package_dir_candidates: list[str] = []
2715
2852
  if package_root is not None:
2716
2853
  env["TENZIR_PACKAGE_ROOT"] = str(package_root)
2717
2854
  package_tests_root = package_root / "tests"
2718
2855
  if inputs_override is None:
2719
2856
  env["TENZIR_INPUTS"] = str(package_tests_root / "inputs")
2720
- package_args.append(f"--package-dirs={package_root}")
2857
+ package_dir_candidates.append(str(package_root))
2858
+ package_dir_candidates.extend(additional_package_dirs)
2859
+ for cli_path in _get_cli_packages():
2860
+ package_dir_candidates.extend(_expand_package_dirs(cli_path))
2861
+ if package_dir_candidates:
2862
+ merged_dirs = _deduplicate_package_dirs(package_dir_candidates)
2863
+ env["TENZIR_PACKAGE_DIRS"] = ",".join(merged_dirs)
2864
+ package_args.append(f"--package-dirs={','.join(merged_dirs)}")
2721
2865
 
2722
2866
  context_token = fixtures_impl.push_context(
2723
2867
  fixtures_impl.FixtureContext(
@@ -2975,12 +3119,24 @@ class Worker:
2975
3119
  raise RuntimeError(f"failed to parse suite config for {primary_test}: {exc}") from exc
2976
3120
  inputs_override = typing.cast(str | None, primary_config.get("inputs"))
2977
3121
  env, config_args = get_test_env_and_config_args(primary_test, inputs=inputs_override)
3122
+ config_package_dirs = cast(tuple[str, ...], primary_config.get("package_dirs", tuple()))
3123
+ additional_package_dirs: list[str] = []
3124
+ for entry in config_package_dirs:
3125
+ additional_package_dirs.extend(_expand_package_dirs(Path(entry)))
2978
3126
  package_root = packages.find_package_root(primary_test)
3127
+ package_dir_candidates: list[str] = []
2979
3128
  if package_root is not None:
2980
3129
  env["TENZIR_PACKAGE_ROOT"] = str(package_root)
2981
3130
  if inputs_override is None:
2982
3131
  env["TENZIR_INPUTS"] = str((package_root / "tests" / "inputs"))
2983
- _apply_fixture_env(env, suite_item.fixtures)
3132
+ package_dir_candidates.append(str(package_root))
3133
+ package_dir_candidates.extend(additional_package_dirs)
3134
+ for cli_path in _get_cli_packages():
3135
+ package_dir_candidates.extend(_expand_package_dirs(cli_path))
3136
+ if package_dir_candidates:
3137
+ merged_dirs = _deduplicate_package_dirs(package_dir_candidates)
3138
+ env["TENZIR_PACKAGE_DIRS"] = ",".join(merged_dirs)
3139
+ config_args = list(config_args) + [f"--package-dirs={','.join(merged_dirs)}"]
2984
3140
  context_token = fixtures_impl.push_context(
2985
3141
  fixtures_impl.FixtureContext(
2986
3142
  test=primary_test,
@@ -3152,6 +3308,7 @@ def run_cli(
3152
3308
  root: Path | None,
3153
3309
  tenzir_binary: Path | None,
3154
3310
  tenzir_node_binary: Path | None,
3311
+ package_dirs: Sequence[Path] | None = None,
3155
3312
  tests: Sequence[Path],
3156
3313
  update: bool,
3157
3314
  debug: bool,
@@ -3236,6 +3393,7 @@ def run_cli(
3236
3393
  tenzir_node_binary=tenzir_node_binary,
3237
3394
  )
3238
3395
  apply_settings(settings)
3396
+ _set_cli_packages(list(package_dirs or []))
3239
3397
  selected_tests = list(tests)
3240
3398
 
3241
3399
  plan: ExecutionPlan | None = None
@@ -106,11 +106,16 @@ class DiffRunner(TqlRunner):
106
106
  fixture_api.pop_context(context_token)
107
107
  run_mod.cleanup_test_tmp_dir(env.get(run_mod.TEST_TMP_ENV_VAR))
108
108
 
109
+ # Strip the ROOT prefix from paths in output to make them relative,
110
+ # consistent with run_simple_test behavior.
111
+ root_bytes = str(run_mod.ROOT).encode() + b"/"
112
+ unoptimized_stdout = unoptimized.stdout.replace(root_bytes, b"")
113
+ optimized_stdout = optimized.stdout.replace(root_bytes, b"")
109
114
  diff_chunks = list(
110
115
  difflib.diff_bytes(
111
116
  difflib.unified_diff,
112
- unoptimized.stdout.splitlines(keepends=True),
113
- optimized.stdout.splitlines(keepends=True),
117
+ unoptimized_stdout.splitlines(keepends=True),
118
+ optimized_stdout.splitlines(keepends=True),
114
119
  n=2**31 - 1,
115
120
  )
116
121
  )[3:]
@@ -118,7 +123,7 @@ class DiffRunner(TqlRunner):
118
123
  diff_bytes = b"".join(diff_chunks)
119
124
  else:
120
125
  diff_bytes = b"".join(
121
- b" " + line for line in unoptimized.stdout.splitlines(keepends=True)
126
+ b" " + line for line in unoptimized_stdout.splitlines(keepends=True)
122
127
  )
123
128
  ref_path = test.with_suffix(".diff")
124
129
  if update:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tenzir-test
3
- Version: 0.12.0
3
+ Version: 0.13.1
4
4
  Summary: Reusable test execution framework extracted from the Tenzir repository.
5
5
  Project-URL: Homepage, https://github.com/tenzir/test
6
6
  Project-URL: Repository, https://github.com/tenzir/test
@@ -63,18 +63,18 @@ for an end-to-end walkthrough of writing tests.
63
63
  We also provide a dense [reference](https://docs.tenzir.com/reference/test) that
64
64
  explains concepts, configuration, multi-project execution, and CLI details.
65
65
 
66
- ## 🧑‍💻 Development
67
-
68
- Contributor workflows, quality gates, and release procedures live in
69
- [`DEVELOPMENT.md`](DEVELOPMENT.md). Follow that guide when you work on the
70
- project locally.
71
-
72
66
  ## 🗞️ Releases
73
67
 
74
68
  New versions are published to PyPI through trusted publishing when a GitHub
75
69
  release is created. Review the latest release notes on GitHub for details about
76
70
  what's new.
77
71
 
72
+ ## 🤝 Contributing
73
+
74
+ Want to contribute? We're all-in on agentic coding with [Claude
75
+ Code](https://claude.ai/code)! The repo comes pre-configured with our [custom
76
+ plugins](https://github.com/tenzir/claude-plugins)—just clone and start hacking.
77
+
78
78
  ## 📜 License
79
79
 
80
80
  `tenzir-test` is available under the Apache License, Version 2.0. See
@@ -1,29 +1,28 @@
1
1
  tenzir_test/__init__.py,sha256=k7V6Pbjaa8SAy6t4KnaauHTyfnyVEwc1VGtH823MANU,1181
2
2
  tenzir_test/_python_runner.py,sha256=LmghMIolsNEC2wUyJdv1h_cefOxTxET1IACrw-_hHuY,2900
3
- tenzir_test/checks.py,sha256=VhZjU1TExqWzA1KcaW1xOGICpqb_G43AezrJIzw09eM,653
4
- tenzir_test/cli.py,sha256=kDatxC4drfjAKnYIYFQmjhPJX45KYbm03pX44iqFgOk,5967
5
- tenzir_test/config.py,sha256=q1_VEXuxL-xsGlnooeGvXxx9cMw652UEB9a1mPzZIQs,1680
3
+ tenzir_test/cli.py,sha256=JJNCaF-vZSql6kvPGG4JwO9Z8XIwQWe_5bKuBiuGjLE,6553
4
+ tenzir_test/config.py,sha256=bVuMJlEvevZxCmvzTJ4bs4SkYLNGZtxcCDYhtC-0sp8,1697
6
5
  tenzir_test/packages.py,sha256=cTCQdGjCS1XmuKyiwh0ew-z9tHn6J-xZ6nvBP-hU8bc,948
7
6
  tenzir_test/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- tenzir_test/run.py,sha256=LE23W07n6O8TnUnkXJArvfwvLI1HYcIu8WCV4lPW70o,125110
7
+ tenzir_test/run.py,sha256=3uq2r3hqa-COrK3f-8UAO4jtD1i5-FgPn5UadspNfqA,130445
9
8
  tenzir_test/engine/__init__.py,sha256=5APwy90YDm7rmL_qCZfToAcfbQthcZ8yV2_ExXKqaqE,110
10
9
  tenzir_test/engine/operations.py,sha256=OCYjuMHyMAaay4s08u2Sl7oE-PmgeXumylp7R8GYIH4,950
11
10
  tenzir_test/engine/registry.py,sha256=LXCr6TGlv1sR1m1eboTk7SrbS2IVErc3PqUuHxGA2xk,594
12
11
  tenzir_test/engine/state.py,sha256=Ez-Q27dL5oNqiJE0A4P2OP0p6i7aYKzH4ZyVWNgEezs,889
13
12
  tenzir_test/engine/worker.py,sha256=WwIkx1m_ANNveQjNisy5-qpbUZl_DfDXxVIfdxABKjc,140
14
13
  tenzir_test/fixtures/__init__.py,sha256=PdkN334btlqagY-4wAwsCyfhHE8kd_RLNoVM_ULii_I,18384
15
- tenzir_test/fixtures/node.py,sha256=rLEzNff78r048KZmOanzGBCNg-OuJu9lQoKgGCArths,8197
14
+ tenzir_test/fixtures/node.py,sha256=Y3YqWeqYz-mg3B5V0wIthk55zVOd4JS37zlTdHzlA6c,11193
16
15
  tenzir_test/runners/__init__.py,sha256=M3p-TsDp231Dy58miDb467bA1kLYzgpa0pqVr_KP1ro,4616
17
16
  tenzir_test/runners/_utils.py,sha256=BWv7UEPGa01l4tGTCg5i_22NblIyRw8vjk_5NIf1x_c,467
18
17
  tenzir_test/runners/custom_python_fixture_runner.py,sha256=uXbSf18xTttf51k2EbjWf8AqN2R7TfpfSCkLyU5HJx4,7284
19
- tenzir_test/runners/diff_runner.py,sha256=ah1hr1vvD6BON2PZz61mxwioRFIzHFuaAbJ0DjDSqG4,5151
18
+ tenzir_test/runners/diff_runner.py,sha256=WIEry_-4R4VIlA9HON5kl2ipyy1TKzS4jRc2wPl6r5c,5476
20
19
  tenzir_test/runners/ext_runner.py,sha256=sKL9Mw_ksVVBWnrdIJR2WS5ueVnLKuNYYWZ22FTZIPo,730
21
20
  tenzir_test/runners/runner.py,sha256=LtlD8huQOSmD7RyYDnKeCuI4Y6vhxGXMKsHA2qgfWN0,989
22
21
  tenzir_test/runners/shell_runner.py,sha256=OuofgHeZN2FaO6xRI3uyqstLBymc6rmWC4HAnSn91AE,6068
23
22
  tenzir_test/runners/tenzir_runner.py,sha256=464FFYS_mh6l-ehccc-S8cIUO1MxdapwQL5X3PmMkMI,1006
24
23
  tenzir_test/runners/tql_runner.py,sha256=2ZLMf3TIKwcOvaOFrVvvhzK-EcWmGOUZxKkbSoByyQA,248
25
- tenzir_test-0.12.0.dist-info/METADATA,sha256=VjTW82AKGkRswsZB-0w5wsCWY7MPIrIzZEREINlgZrI,3008
26
- tenzir_test-0.12.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- tenzir_test-0.12.0.dist-info/entry_points.txt,sha256=q0eD9RQ_9eMPYvFNpBElo55HQYeaPgLfe9YhLsNwl10,93
28
- tenzir_test-0.12.0.dist-info/licenses/LICENSE,sha256=ajMbpcBiSTXI8Rr4t17pvowV-On8DktghfZKxY_A22Q,10750
29
- tenzir_test-0.12.0.dist-info/RECORD,,
24
+ tenzir_test-0.13.1.dist-info/METADATA,sha256=WB95n9cgGgb6T9SraMuXw3ymn3ujtM5qEIg3zm3fTXo,3066
25
+ tenzir_test-0.13.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
26
+ tenzir_test-0.13.1.dist-info/entry_points.txt,sha256=l8DJgiEVrjScdTTo613cZ3PKodOmqrUVIbz-3awfV8w,53
27
+ tenzir_test-0.13.1.dist-info/licenses/LICENSE,sha256=ajMbpcBiSTXI8Rr4t17pvowV-On8DktghfZKxY_A22Q,10750
28
+ tenzir_test-0.13.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,3 +1,2 @@
1
1
  [console_scripts]
2
- check-release = tenzir_test.checks:main
3
2
  tenzir-test = tenzir_test.cli:main
tenzir_test/checks.py DELETED
@@ -1,31 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import shlex
4
- import subprocess
5
- from typing import Sequence
6
-
7
- _COMMANDS: Sequence[Sequence[str]] = (
8
- ("ruff", "check"),
9
- ("ruff", "format", "--check"),
10
- ("mypy",),
11
- ("pytest",),
12
- ("uv", "build"),
13
- )
14
-
15
-
16
- def _run(command: Sequence[str]) -> None:
17
- printable = " ".join(shlex.quote(part) for part in command)
18
- print(f"> {printable}")
19
- result = subprocess.run(command, check=False)
20
- if result.returncode != 0:
21
- raise SystemExit(result.returncode)
22
-
23
-
24
- def main() -> int:
25
- for command in _COMMANDS:
26
- _run(command)
27
- return 0
28
-
29
-
30
- if __name__ == "__main__":
31
- raise SystemExit(main())