Restructure: consolidate everything inside testium/ package
Move src/lib/ → src/testium/runtime/ (internal plumbing)
Move src/testium/libs/ → src/testium/api/ (public SDK for test scripts)
Move src/py_func/ → src/testium/py_func/ (Python subprocess)
Move src/lua_func/ → src/testium/lua_func/ (Lua subprocess data)
The package now ships as a single coherent unit instead of four sibling
top-level packages (testium, lib, py_func, lua_func) — pip install
gives a clean site-packages/testium/ with no namespace pollution; .lua
files travel with the wheel via package_data; the wheel installs
cleanly and `testium -b` runs end-to-end including py_func subprocesses
and entry-point exporter plugins.
Naming:
- runtime/ (internal, no API guarantees) clearer than lib/
- api/ (public SDK consumed as `import api.testium as tm`) clearer than libs/
Imports updated en masse: from lib. → from runtime. and from libs. →
from api., plus the importlib.import_module("libs.*") strings in
test_item_console.py and test_item_runtime_plot.py. Test/example
scripts (helper_lib.py, parallel.py, post_execution.py) and the
fake_exporter test suite migrated too.
paths.py: subproc_path() now returns testium_path() — both point at
the testium package directory since the subprocesses live inside.
pyproject.toml: removed exclude=["lua_func", "py_func"] (no longer
needed), added package-data for testium.lua_func/*.lua, removed the
license classifier (PEP 639 conflict with license expression).
Subprocess isolation contract: py_func/ and lua_func/ may only import
runtime/ and their own modules — never interpreter/, main_win/, api/,
or testium/. Enforced by test/validation/items/isolation/ which runs a
py_func that statically scans subprocess source files for forbidden
imports. The contract holds today; the test prevents future drift.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
def RetreiveData(console_name):
|
||||
print("--------------- retrieving data ---------------")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
def RetreiveData(console_name):
|
||||
print("--------------- retrieving data ---------------")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import libs.testium as libtm
|
||||
import api.testium as libtm
|
||||
|
||||
|
||||
def check_os(expected_os):
|
||||
|
||||
65
test/validation/items/isolation/check_isolation.py
Normal file
65
test/validation/items/isolation/check_isolation.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Static check that py_func/ and lua_func/ subprocess code does not depend
|
||||
on testium internals. The contract is:
|
||||
|
||||
py_func/*.py may import: py_func.*, runtime.*, plus stdlib/3rd-party
|
||||
lua_func/*.lua may require: lua_func/<own files>, plus lua stdlib
|
||||
|
||||
Forbidden top-level modules: interpreter, main_win, api, testium.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import os
|
||||
import re
|
||||
|
||||
FORBIDDEN_PY = {"interpreter", "main_win", "api", "testium"}
|
||||
FORBIDDEN_LUA = {"interpreter", "main_win", "api", "testium"}
|
||||
|
||||
|
||||
def _collect_py_imports(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
tree = ast.parse(f.read(), filename=path)
|
||||
out = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
for n in node.names:
|
||||
out.add(n.name.split(".")[0])
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
if node.level == 0 and node.module:
|
||||
out.add(node.module.split(".")[0])
|
||||
return out
|
||||
|
||||
|
||||
def _collect_lua_requires(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
return {m.split(".")[0] for m in re.findall(r'require\s*\(?\s*["\']([^"\']+)["\']', text)}
|
||||
|
||||
|
||||
def check_isolation(testium_dir):
|
||||
failures = []
|
||||
|
||||
py_dir = os.path.join(testium_dir, "py_func")
|
||||
for root, _, files in os.walk(py_dir):
|
||||
for f in files:
|
||||
if not f.endswith(".py"):
|
||||
continue
|
||||
p = os.path.join(root, f)
|
||||
leaks = _collect_py_imports(p) & FORBIDDEN_PY
|
||||
if leaks:
|
||||
failures.append(f"py_func/{os.path.relpath(p, py_dir)} leaks: {sorted(leaks)}")
|
||||
|
||||
lua_dir = os.path.join(testium_dir, "lua_func")
|
||||
for root, _, files in os.walk(lua_dir):
|
||||
for f in files:
|
||||
if not f.endswith(".lua"):
|
||||
continue
|
||||
p = os.path.join(root, f)
|
||||
leaks = _collect_lua_requires(p) & FORBIDDEN_LUA
|
||||
if leaks:
|
||||
failures.append(f"lua_func/{os.path.relpath(p, lua_dir)} leaks: {sorted(leaks)}")
|
||||
|
||||
if failures:
|
||||
for line in failures:
|
||||
print(f" - {line}")
|
||||
return False
|
||||
return True
|
||||
1
test/validation/items/isolation/param.yaml
Normal file
1
test/validation/items/isolation/param.yaml
Normal file
@@ -0,0 +1 @@
|
||||
no_param: Null
|
||||
8
test/validation/items/isolation/test.tum
Normal file
8
test/validation/items/isolation/test.tum
Normal file
@@ -0,0 +1,8 @@
|
||||
- py_func:
|
||||
name: py_func/lua_func do not depend on testium internals
|
||||
file: $(test_path)$(psep)check_isolation.py
|
||||
func_name: check_isolation
|
||||
key: $(test)_PASS
|
||||
param:
|
||||
- $(testium_path)
|
||||
expected_result: True
|
||||
@@ -1,5 +1,5 @@
|
||||
import time
|
||||
import libs.testium as tm
|
||||
import api.testium as tm
|
||||
|
||||
|
||||
def sleep_func(duration):
|
||||
|
||||
Reference in New Issue
Block a user