中文 | English
predylogic
An embedded, composable, type-safe predicate logic engine for Python.
v0.x; breaking changes can land between minor versions.
Business rules rarely start out complex. You write one if. A few weeks later you add a branch. A quarter later that decision is spread across orders, fraud checks, and reporting, and nobody dares touch it. Changing one threshold means a code change, a review, and a deploy.
predylogic splits logic in two: what to check lives in code, how to combine it lives in data. Policies can be loaded from config, swapped at runtime, and every evaluation can be traced:
❌ AND
❌ is_safe
✅ OR
❌ is_high_value
✅ in_regions
Install
pip install predylogic
# or
uv add predylogic
Example
from typing import TypedDict
from predylogic import Registry
class Transaction(TypedDict):
amount: int
region: str
is_fraud_flagged: bool
txn = Registry[Transaction]("txn")
@txn.rule_def()
def is_high_value(ctx: Transaction, threshold: int = 1000) -> bool:
return ctx["amount"] >= threshold
@txn.rule_def()
def in_regions(ctx: Transaction, regions: list[str]) -> bool:
return ctx["region"] in regions
@txn.rule_def()
def is_safe(ctx: Transaction) -> bool:
return not ctx["is_fraud_flagged"]
# safe AND (high value OR in a target region)
policy = is_safe() & (is_high_value(2000) | in_regions(["US", "EU"]))
assert policy({"amount": 5000, "region": "JP", "is_fraud_flagged": False})
# inspect the reasoning
bad = {"amount": 500, "region": "US", "is_fraud_flagged": True}
trace = policy(bad, trace=True, short_circuit=False)
print(trace)
❌ AND
❌ is_safe
✅ OR
❌ is_high_value
✅ in_regions
trace=True switches the return value from bool to a result tree recording each node's verdict. short_circuit=False runs every branch so you see all the hits and misses at once — useful for compliance audits, debugging, or listing everything a user got wrong in one pass. The trace path is compiled separately, so leaving it off costs nothing.
Policies can also be loaded from JSON config and hot-reloaded at runtime without a restart. See Schema & Serde and Hot Reloading.
Why not X?
The common alternatives each cost something.
- Hardcoded
if/elseis the fastest to write, but logic and control flow get tangled — changing one threshold means a code change, a PR, and a redeploy; there is no runtime swap. - Untyped JSON/YAML looks flexible but nothing validates it: a wrong type or a reference to a rule that doesn't exist only blows up at runtime. Give it time and the YAML grows its own interpreter. Greenspun's tenth rule, again.
- A heavyweight rule engine like Drools or OPA is capable, but you stand up a separate runtime, DSL, and deploy pipeline. For a few dozen rules, that's overkill.
predylogic runs in-process — no JVM, no sidecar. Atomic rules are plain Python functions you can test in isolation. Config is validated against a schema, so type mismatches and unknown rule names surface at config time, not runtime.
Performance
On the default path (short-circuit on, Trace off) the predicate tree compiles to Python bytecode and is cached. Runtime overhead lands within 7% of native Python — close to a handwritten and / or. See ADR 002 for benchmarks.
Docs
Full guides, API reference, and design notes: nagato-yuzuru.github.io/predylogic
About the name
predy (adj.) Archaic British. Nautical.
- (of a ship) prepared or ready for sailing or action.
- to make the ship ready for battle (e.g., "predy the decks").
— Collins English Dictionary
predylogic takes its name from predy: logic that isn't hardcoded into the flow of control, but defined, cleared, and made "predy" for execution. It's also a nod to Predicate Logic.
Release files for predylogic 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| predylogic-0.1.1.tar.gz | 57.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| predylogic-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:91.2 kB
Release files / predylogic-0.1.1.tar.gz
| Download URL | predylogic-0.1.1.tar.gz |
|---|---|
| Size | 57.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
878182b161a1007ba844a9c98205cd1408391e712c05b374137bf6532384f9c5
|
|
BLAKE2b-256 checksum How to use checksums |
72a344e805e9c569c59347ce0468bdc8ad6348684c34fe882eab132774381a54
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / predylogic-0.1.1-py3-none-any.whl
| Download URL | predylogic-0.1.1-py3-none-any.whl |
|---|---|
| Size | 34.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a9a349fa5fd870a940b6010023f9c94b7af0d509f5380998e5bfe9285954b387
|
|
BLAKE2b-256 checksum How to use checksums |
ff37101c6d24927742d990afc6d1a54df3e7745961ba8e3c018e01b192e75110
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|