diff --git a/doc/architecture_improvements.md b/doc/architecture_improvements.md index caea066..a6cbcab 100644 --- a/doc/architecture_improvements.md +++ b/doc/architecture_improvements.md @@ -761,14 +761,6 @@ class LazyModelCollector: if self._steps is None: self._steps = self._discover_steps() return self._steps - - @lru_cache(maxsize=128) - def evaluate_guard(self, step_name: str) -> bool: - """Evaluate guard with caching.""" - guard = self._guards.get(f"guard_{step_name}") - if guard is None: - return True - return guard(self.model) ``` ### 6.2 Algorithm Optimizations diff --git a/license_to_commit.sh b/license_to_commit.sh old mode 100644 new mode 100755 diff --git a/pyosmo/decorators.py b/pyosmo/decorators.py index 60233e6..935e665 100644 --- a/pyosmo/decorators.py +++ b/pyosmo/decorators.py @@ -101,21 +101,21 @@ def can_process(self) -> bool: # Inline guard - return a decorator that attaches the guard to the step guard_func = step_name_or_func - def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + def inline_decorator(func: Callable[..., Any]) -> Callable[..., Any]: func._osmo_guard_inline = guard_func # type: ignore[attr-defined] func._osmo_guard_invert = invert # type: ignore[attr-defined] return func - return decorator + return inline_decorator # Named guard - def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + def named_decorator(func: Callable[..., Any]) -> Callable[..., Any]: func._osmo_guard = True # type: ignore[attr-defined] func._osmo_guard_for = step_name_or_func # type: ignore[attr-defined] func._osmo_guard_invert = invert # type: ignore[attr-defined] return func - return decorator + return named_decorator def pre(step_name: str) -> Callable[[F], F]: diff --git a/pyosmo/model.py b/pyosmo/model.py index cd32837..d60a7d6 100644 --- a/pyosmo/model.py +++ b/pyosmo/model.py @@ -108,10 +108,13 @@ def _find_decorator_guard(self) -> Optional['ModelFunction']: """Find a guard method decorated with @guard("step_name") for this step.""" for attr_name in dir(self.object_instance): method = getattr(self.object_instance, attr_name) - if callable(method) and hasattr(method, '_osmo_guard') and hasattr(method, '_osmo_guard_for'): - # Check if this guard is for our step - if method._osmo_guard_for == self.name: - return ModelFunction(attr_name, self.object_instance) + if ( + callable(method) + and hasattr(method, '_osmo_guard') + and hasattr(method, '_osmo_guard_for') + and method._osmo_guard_for == self.name + ): + return ModelFunction(attr_name, self.object_instance) return None @property diff --git a/pyosmo/tests/test_decorators.py b/pyosmo/tests/test_decorators.py index 1a59473..66f199a 100644 --- a/pyosmo/tests/test_decorators.py +++ b/pyosmo/tests/test_decorators.py @@ -162,7 +162,7 @@ def __init__(self): def process(self): self.process_called = True - @guard("process") + @guard('process') def can_process(self) -> bool: return self.ready @@ -207,7 +207,8 @@ def other_action(self): model = InlineGuardModel() osmo = Osmo(model) - osmo.test_end_condition = Length(10) + osmo.seed = 42 + osmo.test_end_condition = Length(20) osmo.run() @@ -227,7 +228,7 @@ def __init__(self): def action(self): self.action_called = True - @guard("action", invert=True) + @guard('action', invert=True) def is_blocked(self) -> bool: return self.blocked @@ -262,11 +263,11 @@ def login(self): def checkout(self): pass - @guard("login") + @guard('login') def can_login(self) -> bool: return not self.logged_in - @guard("checkout") + @guard('checkout') def can_checkout(self) -> bool: return self.logged_in and self.has_items @@ -305,7 +306,7 @@ def __init__(self): def action_a(self): pass - @guard("action_a") + @guard('action_a') def guard_for_a(self) -> bool: return self.flag_a diff --git a/pyproject.toml b/pyproject.toml index 6e1233f..732e555 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=45", "wheel", "setuptools-scm[toml]>=6.2"] +requires = ["setuptools>=77.0.0", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -8,7 +8,7 @@ version = "0.2.2" description = "pyosmo - a model-based testing tool" readme = "README.md" requires-python = ">=3.11" -license = {text = "MIT"} +license = "MIT" authors = [ {name = "Olli-Pekka Puolitaival", email = "oopee1@gmail.com"}, ] @@ -18,7 +18,6 @@ maintainers = [ classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -27,13 +26,13 @@ classifiers = [ ] dependencies = [ "click", - "mypy>=1.18.2", - "pytest>=9.0.0", ] [project.optional-dependencies] dev = [ "pytest>=7.0", + "mypy>=1.18.2", + "pytest>=9.0.0", "hypothesis>=6.0", "ruff>=0.1.0", "mypy>=1.0", diff --git a/uv.lock b/uv.lock index d8ceedc..932178a 100644 --- a/uv.lock +++ b/uv.lock @@ -129,12 +129,10 @@ wheels = [ [[package]] name = "pyosmo" -version = "0.1.3" +version = "0.2.2" source = { editable = "." } dependencies = [ { name = "click" }, - { name = "mypy" }, - { name = "pytest" }, ] [package.optional-dependencies] @@ -149,10 +147,10 @@ dev = [ requires-dist = [ { name = "click" }, { name = "hypothesis", marker = "extra == 'dev'", specifier = ">=6.0" }, - { name = "mypy", specifier = ">=1.18.2" }, { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.0" }, - { name = "pytest", specifier = ">=9.0.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.18.2" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.0" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, ] provides-extras = ["dev"]