Skip to content

Commit 460ae34

Browse files
committed
Errors introduced
1 parent 70d66d2 commit 460ae34

18 files changed

Lines changed: 1191 additions & 87 deletions

.github/workflows/py-sdk-build-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ jobs:
119119
120120
publish_test:
121121
name: Publish to Test PyPI
122-
if: github.ref != 'refs/heads/main'
122+
if: ${{ github.ref_type == 'branch' && github.ref_name != 'main' }}
123123
needs: [build_wheels, build_sdist]
124124
runs-on: ubuntu-latest
125125
#environment: pypi

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ members = [
88
resolver = "2"
99

1010
[workspace.package]
11-
version = "0.3.0"
11+
version = "0.4.0"
1212
edition = "2021"
1313
license = "Apache-2.0"
1414
authors = ["Amit Saxena"]

examples/python/core-sdk/basic_refund.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
and blocks the call if the policy denies it.
99
"""
1010

11-
from actra import Actra
11+
from actra import Actra, ActraPolicyError
1212
from actra.runtime import ActraRuntime
1313

1414

@@ -121,4 +121,8 @@ def refund(amount: int):
121121

122122

123123
print("\n--- Blocked call ---")
124-
refund(amount=1500)
124+
try:
125+
refund(amount=1500)
126+
except ActraPolicyError as e:
127+
print("Refund blocked by policy")
128+
print("Rule:", e.matched_rule )
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""
2+
Build Action Example
3+
4+
This example demonstrates how to use `ActraRuntime.build_action`
5+
when evaluating actions outside of decorators
6+
7+
This pattern is useful for integrations where the application
8+
does not call a protected function, such as:
9+
10+
- API frameworks
11+
- message queues
12+
- MCP servers
13+
- background workers
14+
"""
15+
16+
from actra import Actra, ActraRuntime
17+
18+
19+
# ------------------------------------------------------------
20+
# 1. Schema
21+
# ------------------------------------------------------------
22+
23+
schema_yaml = """
24+
version: 1
25+
26+
actions:
27+
refund:
28+
fields:
29+
amount: number
30+
31+
actor:
32+
fields:
33+
role: string
34+
35+
snapshot:
36+
fields:
37+
fraud_flag: boolean
38+
"""
39+
40+
41+
# ------------------------------------------------------------
42+
# 2. Policy
43+
# ------------------------------------------------------------
44+
45+
policy_yaml = """
46+
version: 1
47+
48+
rules:
49+
- id: block_large_refund
50+
scope:
51+
action: refund
52+
when:
53+
subject:
54+
domain: action
55+
field: amount
56+
operator: greater_than
57+
value:
58+
literal: 1000
59+
effect: block
60+
"""
61+
62+
63+
# ------------------------------------------------------------
64+
# 3. Compile policy
65+
# ------------------------------------------------------------
66+
67+
policy = Actra.from_strings(schema_yaml, policy_yaml)
68+
runtime = ActraRuntime(policy)
69+
70+
71+
# ------------------------------------------------------------
72+
# 4. Register resolvers
73+
# ------------------------------------------------------------
74+
75+
runtime.set_actor_resolver(lambda ctx: {"role": "support"})
76+
runtime.set_snapshot_resolver(lambda ctx: {"fraud_flag": False})
77+
78+
79+
# ------------------------------------------------------------
80+
# 5. Example external input
81+
# ------------------------------------------------------------
82+
# Imagine this comes from an API request or message queue
83+
84+
request_data = {
85+
"amount": 200
86+
}
87+
88+
89+
# ------------------------------------------------------------
90+
# 6. Define a handler signature
91+
# ------------------------------------------------------------
92+
# build_action uses the function signature to determine which
93+
# fields are valid action parameters.
94+
#
95+
# The function is NOT executed. It is only used for introspection.
96+
97+
def fake_handler(amount):
98+
pass
99+
100+
101+
action = runtime.build_action(
102+
func=fake_handler,
103+
action_type="refund",
104+
args=(),
105+
kwargs=request_data,
106+
ctx=None
107+
)
108+
109+
110+
# ------------------------------------------------------------
111+
# 7. Evaluate decision
112+
# ------------------------------------------------------------
113+
114+
decision = runtime.evaluate(action)
115+
116+
print("Decision:", decision)

examples/python/core-sdk/custom_action_builder.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
map directly to policy fields.
99
"""
1010

11-
from actra import Actra
11+
from actra import Actra, ActraPolicyError
1212
from actra.runtime import ActraRuntime
1313

1414

@@ -79,6 +79,7 @@
7979
def build_refund_action(action_type, args, kwargs, ctx):
8080
"""
8181
Convert application inputs into the policy action object.
82+
Only fields defined in the policy schema should be included
8283
"""
8384

8485
return {
@@ -104,4 +105,9 @@ def refund(amount: int, currency: str):
104105
refund(amount=200, currency="USD")
105106

106107
print("\nBlocked call")
107-
refund(amount=1500, currency="USD")
108+
109+
try:
110+
refund(amount=1500, currency="USD")
111+
except ActraPolicyError as e:
112+
print("Refund blocked by policy")
113+
print("Rule:", e.matched_rule)

examples/python/core-sdk/custom_actor_snapshot_resolvers.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,22 @@
22
Custom Actor and Snapshot Resolvers Example
33
44
Demonstrates how runtime context can be used to dynamically
5-
resolve actor and system state.
5+
resolve actor identity and external system state.
6+
7+
Resolvers allow applications to supply policy inputs
8+
from runtime context such as:
9+
10+
- authenticated user identity
11+
- request metadata
12+
- system state
13+
614
"""
715

8-
from actra import Actra, ActraRuntime
16+
from actra import Actra, ActraRuntime, ActraPolicyError
917

18+
# ------------------------------------------------------------
19+
# 1. Schema
20+
# ------------------------------------------------------------
1021

1122
schema_yaml = """
1223
version: 1
@@ -25,6 +36,9 @@
2536
fraud_flag: boolean
2637
"""
2738

39+
# ------------------------------------------------------------
40+
# 2. Policy
41+
# ------------------------------------------------------------
2842

2943
policy_yaml = """
3044
version: 1
@@ -50,31 +64,46 @@
5064
effect: block
5165
"""
5266

67+
# ------------------------------------------------------------
68+
# 3. Compile policy
69+
# ------------------------------------------------------------
5370

5471
policy = Actra.from_strings(schema_yaml, policy_yaml)
5572
runtime = ActraRuntime(policy)
5673

57-
58-
# Example request context
74+
# ------------------------------------------------------------
75+
# 4. Example request context
76+
# ------------------------------------------------------------
5977
class RequestContext:
6078
def __init__(self, role, fraud_flag):
6179
self.role = role
6280
self.fraud_flag = fraud_flag
6381

6482

65-
# Actor resolver
83+
# ------------------------------------------------------------
84+
# 5. Register resolvers
85+
# ------------------------------------------------------------
86+
87+
# Actor resolver extracts identity information
88+
6689
runtime.set_actor_resolver(
6790
lambda ctx: {"role": ctx.role}
6891
)
6992

70-
# Snapshot resolver
93+
# Snapshot resolver extracts system state
7194
runtime.set_snapshot_resolver(
7295
lambda ctx: {"fraud_flag": ctx.fraud_flag}
7396
)
7497

98+
# ------------------------------------------------------------
99+
# 6. Create runtime context
100+
# ------------------------------------------------------------
75101

76102
ctx = RequestContext(role="support", fraud_flag=False)
77103

104+
# ------------------------------------------------------------
105+
# 8. Build action
106+
# ------------------------------------------------------------
78107

79108
action = runtime.build_action(
80109
action_type="refund",
@@ -83,6 +112,9 @@ def __init__(self, role, fraud_flag):
83112
ctx=ctx
84113
)
85114

86-
decision = runtime.evaluate(action, ctx)
115+
# ------------------------------------------------------------
116+
# 9. Evaluate policy
117+
# ------------------------------------------------------------
87118

119+
decision = runtime.evaluate(action, ctx)
88120
print(decision)

examples/python/core-sdk/fields_filtering.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22
Field Filtering Example
33
44
This example demonstrates how to restrict which parameters
5-
become part of the Actra action object.
5+
become part of the Actra action object
66
7-
This is useful when functions contain additional arguments
8-
that should not be exposed to the policy engine.
7+
Field filtering is useful when functions contain additional
8+
arguments that should not be exposed to the policy engine
9+
10+
In this example the function accepts both `amount` and
11+
`currency`, but the policy schema only defines the `amount`
12+
field. The `fields` parameter ensures only the allowed fields
13+
are included in the policy action
914
"""
1015

11-
from actra import Actra
16+
from actra import Actra, ActraPolicyError
1217
from actra.runtime import ActraRuntime
1318

1419

@@ -85,4 +90,8 @@ def refund(amount: int, currency: str):
8590
refund(amount=200, currency="USD")
8691

8792
print("\nBlocked call")
88-
refund(amount=1500, currency="USD")
93+
try:
94+
refund(amount=1500, currency="USD")
95+
except ActraPolicyError as e:
96+
print("Refund blocked by policy")
97+
print("Rule:", e.matched_rule)

examples/python/core-sdk/manual_runtime_evaluation.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,10 @@
8787
# - message queues
8888
#
8989

90-
action = runtime.build_action(
91-
action_type="refund",
92-
args=(),
93-
kwargs={"amount": 200},
94-
ctx=None
95-
)
90+
action = {
91+
"type": "refund",
92+
"amount": 200
93+
}
9694

9795
# ------------------------------------------------------------
9896
# 7. Evaluate the action
@@ -107,12 +105,10 @@
107105
# 8. Blocked example
108106
# ------------------------------------------------------------
109107

110-
blocked_action = runtime.build_action(
111-
action_type="refund",
112-
args=(),
113-
kwargs={"amount": 1500},
114-
ctx=None
115-
)
108+
blocked_action = {
109+
"type": "refund",
110+
"amount": 1500
111+
}
116112

117113
blocked_result = runtime.evaluate(blocked_action)
118114

examples/python/core-sdk/multiple_runtimes.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
Each runtime can enforce a different policy.
88
"""
99

10-
from actra import Actra
10+
from actra import Actra, ActraPolicyError
1111
from actra.runtime import ActraRuntime
1212

1313

@@ -156,8 +156,9 @@ def admin_refund(amount: int):
156156

157157
try:
158158
support_refund(amount=5000)
159-
except PermissionError as e:
160-
print(e)
159+
except ActraPolicyError as e:
160+
print("Support refund blocked")
161+
print("Rule:",e.matched_rule)
161162

162163

163164
print("\nAdmin runtime")
@@ -166,5 +167,6 @@ def admin_refund(amount: int):
166167

167168
try:
168169
admin_refund(amount=20000)
169-
except PermissionError as e:
170-
print(e)
170+
except ActraPolicyError as e:
171+
print("Admin refund blocked")
172+
print("Rule:",e.matched_rule)

0 commit comments

Comments
 (0)