refactor: fix audit findings — predicate mixin, runtime-config mixin, migration DDL#163
Merged
Merged
Conversation
AndPredicate / OrPredicate / NotPredicate each imported _resolve_outcome inside the evaluate() method body. The cycle they were defending against doesn't exist at runtime — evaluate.py only imports core under TYPE_CHECKING — so hoist the import to module scope.
30 tests covering the boolean combinators (And/Or/Not short-circuit semantics and Defer/Cancel pass-through), fail-closed evaluation + metric recording, JSON and string-DSL round-trips, recipe behaviour (after/before/in_time_window/payload_matches/env_var_truthy), the callable adapter, custom predicate registration, and three queue integration tests for enqueue-time cancel/defer.
The predicate state (six instance dicts plus PredicateMetrics) and the three gating methods (_apply_enqueue_predicate, _apply_dispatch_predicate, _reenqueue_after_defer) plus the inspection / registration API (list_predicates, predicate_for, register_predicate) all moved to a new QueuePredicateMixin. Event-emission boilerplate was lifted into small helpers on the mixin to remove the four repeated _emit_event(...) blocks. app.py drops from 901 to 619 LOC; the mixin owns ~280 LOC of cohesive predicate logic.
register_type, set_queue_rate_limit, and set_queue_concurrency are runtime configuration knobs, not decorator surface. Moving them to a dedicated QueueRuntimeConfigMixin pulls 64 LOC out of decorators.py (597 -> 533) and gives the three queue-tuning APIs a coherent home adjacent to the existing QueueSettingsMixin (dashboard key/value store).
Pull ~750 LOC of duplicated CREATE TABLE / CREATE INDEX / ALTER TABLE DDL out of sqlite/mod.rs and postgres/mod.rs into a new shared module crates/taskito-core/src/storage/diesel_common/migrations.rs. A Dialect struct holds the per-backend type substitutions (BLOB vs BYTEA, INTEGER vs BIGINT, REAL vs DOUBLE PRECISION, boolean defaults, and the IF NOT EXISTS prefix on ALTER). Each backend now exposes a short driver that walks create_tables / create_indexes / alter_statements; per-backend migration_alter() retains the dialect- specific idempotency behaviour (SQLite swallows 'duplicate column', Postgres uses IF NOT EXISTS). sqlite/mod.rs: 502 -> 126 LOC; postgres/mod.rs: 508 -> 130 LOC. All cargo check / clippy / test combinations green; full Python suite still passes (592 / 9 skipped).
Bumps the four workspace crates (taskito-core, taskito-python, taskito-workflows, taskito-async), the pyproject.toml project version, and the importlib.metadata fallback in py_src/taskito/__init__.py. Adds a 0.12.2 changelog entry summarising the audit-driven refactors landing in this release: predicate inline-import fix, QueuePredicateMixin and QueueRuntimeConfigMixin extractions, shared migration DDL via diesel_common/migrations.rs, and the new tests/core/test_predicates.py unit + integration suite.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
AndPredicate/OrPredicate/NotPredicate.evaluate(). The cycle they defended against doesn't exist at runtime (evaluate.pyonly importscoreunderTYPE_CHECKING), so the import hoists cleanly to module scope.tests/core/test_predicates.pywith 30 focused tests: combinator short-circuit semantics, fail-closed evaluation + metrics, JSON/string-DSL round-trips, built-in recipe behaviour, custom registration, and three queue integration tests.app.py(901 -> 619) into a newQueuePredicateMixin. Owns predicate state init,list_predicates/predicate_for/register_predicate, and the enqueue/dispatch gating methods. Repeated_emit_event(...)blocks consolidated into small helpers.register_type,set_queue_rate_limit,set_queue_concurrencyout ofQueueDecoratorMixin(597 -> 533) into a newQueueRuntimeConfigMixin. Decorator surface stays focused; runtime knobs get their own home.run_migrations()DDL via a newdiesel_common/migrations.rs.sqlite/mod.rsdrops 502 -> 126 LOC;postgres/mod.rsdrops 508 -> 130 LOC. ADialectstruct holds the per-backend type substitutions; per-backendmigration_alterretains dialect-specific idempotency.Net LOC impact
py_src/taskito/app.pypy_src/taskito/mixins/decorators.pycrates/taskito-core/src/storage/sqlite/mod.rscrates/taskito-core/src/storage/postgres/mod.rsmixins/predicates.pymixins/runtime_config.pydiesel_common/migrations.rstests/core/test_predicates.pyNet: existing files -1,100 LOC, new well-scoped files +1,167 LOC, with the duplication eliminated and one new test surface added.
Test plan
cargo check --workspace(default,+postgres,+redis,workflows,native-async) — all greencargo test --workspace --features workflows— all greencargo test --workspace --features postgres,workflows— all greencargo clippy --workspace --features workflows -- -D warnings— cleancargo fmt --all -- --check— cleanuv run maturin develop+uv run python -m pytest tests/— 592 passed, 9 skipped (no regressions, +30 new predicate tests)uv run ruff check py_src/ tests/— cleanuv run mypy py_src/taskito/ tests/ --no-incremental— clean (201 files)