proact-py 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ include src/*.c src/*.h
2
+ include Makefile
@@ -0,0 +1,32 @@
1
+ CC ?= gcc
2
+ CFLAGS = -Wall -Wextra -O2 -std=c11
3
+ SRCDIR = src
4
+ SOURCES = $(SRCDIR)/term.c $(SRCDIR)/parse.c $(SRCDIR)/unify.c $(SRCDIR)/eval.c $(SRCDIR)/main.c
5
+ LIB_SOURCES = $(SRCDIR)/term.c $(SRCDIR)/parse.c $(SRCDIR)/unify.c $(SRCDIR)/eval.c
6
+ BUILDDIR = build
7
+ TARGET = $(BUILDDIR)/proact
8
+
9
+ all: $(TARGET)
10
+
11
+ $(BUILDDIR):
12
+ mkdir -p $(BUILDDIR)
13
+
14
+ $(TARGET): $(SOURCES) $(SRCDIR)/proact.h | $(BUILDDIR)
15
+ $(CC) $(CFLAGS) -I$(SRCDIR) $(SOURCES) -o $(TARGET)
16
+
17
+ test: test/test_proact.c $(LIB_SOURCES) $(SRCDIR)/proact.h | $(BUILDDIR)
18
+ $(CC) $(CFLAGS) -I$(SRCDIR) $(LIB_SOURCES) test/test_proact.c -o $(BUILDDIR)/test_proact
19
+ ./$(BUILDDIR)/test_proact
20
+
21
+ coverage: test/test_proact.c $(LIB_SOURCES) $(SRCDIR)/proact.h | $(BUILDDIR)
22
+ $(CC) -Wall -Wextra -std=c11 -O0 --coverage -I$(SRCDIR) $(LIB_SOURCES) test/test_proact.c -o $(BUILDDIR)/test_proact_cov
23
+ ulimit -s unlimited && ./$(BUILDDIR)/test_proact_cov
24
+ lcov --capture --directory . --output-file $(BUILDDIR)/coverage.info --ignore-errors inconsistent
25
+ lcov --remove $(BUILDDIR)/coverage.info '*/test/*' --output-file $(BUILDDIR)/coverage.info --ignore-errors unused
26
+ lcov --list $(BUILDDIR)/coverage.info
27
+
28
+ clean:
29
+ rm -rf $(BUILDDIR)
30
+ rm -f $(SRCDIR)/*.gcda $(SRCDIR)/*.gcno
31
+
32
+ .PHONY: all test coverage clean
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: proact-py
3
+ Version: 0.1.0
4
+ Summary: Active logic meets Prolog — three-valued reasoning engine
5
+ License-Expression: MIT
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest; extra == "dev"
10
+
11
+ # Proact
12
+
13
+ Prolog has two truth values: succeed and fail. Proact adds a third: **cont** — *work/reasoning in progress*.
14
+
15
+ Every goal returns a status. Conjunction is a sequence. Disjunction is a selector. Backtracking only triggers on failure — `cont` and `done` cut the search. Between ticks, queries returning `cont` are automatically re-evaluated while the database persists, giving you a stateless tick loop for free.
16
+
17
+ Applications:
18
+
19
+ - **Reasoning under uncertainty.** The three values map onto epistemic states: *verified*, *undetermined*, *refuted*. A conjunction of checks naturally implements iterative deepening — work focuses on the earliest unresolved premise. Unary operators give you cognitive control: `demote` means "don't commit yet" (done→cont); `promote` means "don't give up yet" (fail→cont). This is relevant to validation layers for LLM reasoning, where forcing every check into true/false is lossy compression of the actual epistemic state.
20
+
21
+ - **Behavior modeling.** Reactive agents, planners, robot controllers — anything where logic meets time. Prolog gives you unification, backtracking, and pattern matching. Active logic adds temporal flow. You get behavior trees with the expressiveness of logic programming, without the trees.
22
+
23
+ ## Install
24
+
25
+ ```
26
+ pip install git+https://github.com/eelstork/proact.git
27
+ ```
28
+
29
+ Requires a C compiler (`gcc`, `clang`, or MSVC).
30
+
31
+ ```python
32
+ from proact import Proact
33
+
34
+ p = Proact()
35
+ p.add("mortal(X) :- human(X). human(socrates).")
36
+ print(p.query("mortal(socrates)")) # Result(status='done')
37
+ ```
38
+
39
+ ## Tutorials
40
+
41
+ - [Reasoning under uncertainty](tutorial/reasoning.md) — from predicate logic to three-valued epistemics
42
+ - [Behavior modeling](tutorial/behavior.md) — behavior trees via logic programming
43
+
44
+ ## Build
45
+
46
+ ```
47
+ make
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ ```
53
+ ./proact # REPL
54
+ ./proact file.pa # consult file, then REPL
55
+ ./proact file.pa -e "goal." # evaluate goal and exit
56
+ ./proact --max-ticks 100 -e "g." # limit tick count
57
+ ```
58
+
59
+ ## The Transform
60
+
61
+ ### Conjunction (`,`) — Active logic AND / Sequence
62
+
63
+ ```
64
+ fail cont done
65
+ fail fail fail fail
66
+ cont cont cont cont
67
+ done fail cont done
68
+ ```
69
+
70
+ If the left goal is `cont`, the whole conjunction is `cont` — don't evaluate the right side yet.
71
+
72
+ ### Disjunction (`;`) — Active logic OR / Selector
73
+
74
+ ```
75
+ fail cont done
76
+ fail fail cont done
77
+ cont cont cont cont
78
+ done done done done
79
+ ```
80
+
81
+ If the left goal is `done` or `cont`, don't try alternatives.
82
+
83
+ ### Clause resolution
84
+
85
+ Multiple clauses for the same predicate provide choice points. Backtracking occurs **only on fail** — if a clause body returns `cont` or `done`, stop searching.
86
+
87
+ ### Tick loop
88
+
89
+ Queries returning `cont` are automatically re-evaluated on the next tick with a fresh environment. The database (asserted facts) persists between ticks.
90
+
91
+ ## Built-ins
92
+
93
+ | Predicate | Status | Description |
94
+ |---|---|---|
95
+ | `done` / `true` | done | Always succeeds |
96
+ | `fail` / `false` | fail | Always fails |
97
+ | `cont` | cont | Always continues |
98
+ | `inv(G)` / `\+ G` | swap done/fail, keep cont | Inverter |
99
+ | `condone(G)` | fail→done | Forgive failure |
100
+ | `promote(G)` | fail→cont, cont→done | Optimistic |
101
+ | `demote(G)` | done→cont, cont→fail | Pessimistic |
102
+ | `par_any(A,B)` | max(A,B) | Lenient parallel |
103
+ | `par_all(A,B)` | min(A,B) | Strict parallel |
104
+ | `tick(N)` | done | Unify N with current tick |
105
+
106
+ Plus standard Prolog: `write/1`, `writeln/1`, `nl`, `assert/1`, `retract/1`, `is/2`, `=/2`, `\=/2`, `</2`, `>/2`, `>=/2`, `=</2`, `call/1`, `consult/1`.
107
+
108
+ ## Examples
109
+
110
+ ### Wait N ticks, then succeed
111
+
112
+ ```prolog
113
+ wait(N) :- tick(T), T >= N.
114
+ wait(N) :- tick(T), T < N, cont.
115
+ ```
116
+
117
+ ```
118
+ ?- wait(3), writeln(hello).
119
+ [tick 0] cont
120
+ [tick 1] cont
121
+ [tick 2] cont
122
+ hello
123
+ done.
124
+ ```
125
+
126
+ ### Counter (stateful, using assert/retract)
127
+
128
+ ```prolog
129
+ :- assert(count(0)).
130
+
131
+ count_to(Target) :- count(N), N >= Target.
132
+ count_to(Target) :-
133
+ count(N), N < Target,
134
+ retract(count(N)), N1 is N + 1, assert(count(N1)),
135
+ cont.
136
+ ```
137
+
138
+ ### Reactive behavior (condition-gated sequence)
139
+
140
+ ```prolog
141
+ :- assert(agent_pos(0)).
142
+
143
+ move_to(X) :- agent_pos(P), P =:= X.
144
+ move_to(X) :- agent_pos(P), P < X,
145
+ retract(agent_pos(P)), P1 is P + 1, assert(agent_pos(P1)), cont.
146
+
147
+ patrol :-
148
+ agent_pos(P), P < 3, move_to(3), cont.
149
+ patrol :-
150
+ agent_pos(P), P >= 3, P < 5, move_to(5), cont.
151
+ patrol :-
152
+ agent_pos(P), P >= 5.
153
+ ```
154
+
155
+ ### Reasoning under uncertainty
156
+
157
+ ```prolog
158
+ % reason.pa — three-valued validation for multi-step arguments
159
+
160
+ :- assert(known(premise_a)).
161
+ :- assert(investigation(premise_b, 2)).
162
+ :- assert(investigation(premise_c, 1)).
163
+
164
+ check(P) :- known(P).
165
+ check(P) :- known_false(P), fail.
166
+ check(P) :-
167
+ investigation(P, N), N > 0,
168
+ retract(investigation(P, N)), N1 is N - 1,
169
+ (N1 > 0 -> assert(investigation(P, N1)) ; assert(known(P))),
170
+ cont.
171
+
172
+ argument :- check(premise_a), check(premise_b), check(premise_c).
173
+ ```
174
+
175
+ The conjunction *is* the search strategy — it focuses work on the earliest unresolved premise and doesn't waste computation on steps whose preconditions haven't been met:
176
+
177
+ ```
178
+ [tick 0] a=done, b=cont → cont (c never reached)
179
+ [tick 1] a=done, b=done, c=cont → cont (work shifts to c)
180
+ [tick 2] a=done, b=done, c=done → done
181
+ ```
182
+
183
+ The epistemic operators give cognitive control over the validation:
184
+
185
+ ```prolog
186
+ cautious(X) :- demote(check(X)). % "verified but needs review" (done→cont)
187
+ keep_exploring(X) :- promote(check(X)). % "refuted but try harder" (fail→cont)
188
+ viable_theory :- par_any(hyp_a, hyp_b). % first hypothesis verified wins
189
+ ```
190
+
191
+ ## Relation to active-logic
192
+
193
+ This prototype implements the core operators from [activelogic-cs](https://github.com/active-logic/activelogic-cs) in a logic programming context:
194
+
195
+ | C# (activelogic-cs) | Proact | BT equivalent |
196
+ |---|---|---|
197
+ | `status && status` | `Goal, Goal` | Sequence |
198
+ | `status \|\| status` | `Goal ; Goal` | Selector |
199
+ | `+` (lenient) | `par_any(A, B)` | Parallel (any) |
200
+ | `*` (strict) | `par_all(A, B)` | Parallel (all) |
201
+ | `!status` | `inv(G)` | Inverter |
202
+ | `~status` | `condone(G)` | Condone |
203
+ | `+status` (unary) | `promote(G)` | Promoter |
204
+ | `-status` (unary) | `demote(G)` | Demoter |
205
+ | `cont` | `cont` | Running |
206
+
207
+ The key addition over Prolog is `cont` — a single built-in that bridges logic programming with reactive, tick-based behavior.
@@ -0,0 +1,197 @@
1
+ # Proact
2
+
3
+ Prolog has two truth values: succeed and fail. Proact adds a third: **cont** — *work/reasoning in progress*.
4
+
5
+ Every goal returns a status. Conjunction is a sequence. Disjunction is a selector. Backtracking only triggers on failure — `cont` and `done` cut the search. Between ticks, queries returning `cont` are automatically re-evaluated while the database persists, giving you a stateless tick loop for free.
6
+
7
+ Applications:
8
+
9
+ - **Reasoning under uncertainty.** The three values map onto epistemic states: *verified*, *undetermined*, *refuted*. A conjunction of checks naturally implements iterative deepening — work focuses on the earliest unresolved premise. Unary operators give you cognitive control: `demote` means "don't commit yet" (done→cont); `promote` means "don't give up yet" (fail→cont). This is relevant to validation layers for LLM reasoning, where forcing every check into true/false is lossy compression of the actual epistemic state.
10
+
11
+ - **Behavior modeling.** Reactive agents, planners, robot controllers — anything where logic meets time. Prolog gives you unification, backtracking, and pattern matching. Active logic adds temporal flow. You get behavior trees with the expressiveness of logic programming, without the trees.
12
+
13
+ ## Install
14
+
15
+ ```
16
+ pip install git+https://github.com/eelstork/proact.git
17
+ ```
18
+
19
+ Requires a C compiler (`gcc`, `clang`, or MSVC).
20
+
21
+ ```python
22
+ from proact import Proact
23
+
24
+ p = Proact()
25
+ p.add("mortal(X) :- human(X). human(socrates).")
26
+ print(p.query("mortal(socrates)")) # Result(status='done')
27
+ ```
28
+
29
+ ## Tutorials
30
+
31
+ - [Reasoning under uncertainty](tutorial/reasoning.md) — from predicate logic to three-valued epistemics
32
+ - [Behavior modeling](tutorial/behavior.md) — behavior trees via logic programming
33
+
34
+ ## Build
35
+
36
+ ```
37
+ make
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```
43
+ ./proact # REPL
44
+ ./proact file.pa # consult file, then REPL
45
+ ./proact file.pa -e "goal." # evaluate goal and exit
46
+ ./proact --max-ticks 100 -e "g." # limit tick count
47
+ ```
48
+
49
+ ## The Transform
50
+
51
+ ### Conjunction (`,`) — Active logic AND / Sequence
52
+
53
+ ```
54
+ fail cont done
55
+ fail fail fail fail
56
+ cont cont cont cont
57
+ done fail cont done
58
+ ```
59
+
60
+ If the left goal is `cont`, the whole conjunction is `cont` — don't evaluate the right side yet.
61
+
62
+ ### Disjunction (`;`) — Active logic OR / Selector
63
+
64
+ ```
65
+ fail cont done
66
+ fail fail cont done
67
+ cont cont cont cont
68
+ done done done done
69
+ ```
70
+
71
+ If the left goal is `done` or `cont`, don't try alternatives.
72
+
73
+ ### Clause resolution
74
+
75
+ Multiple clauses for the same predicate provide choice points. Backtracking occurs **only on fail** — if a clause body returns `cont` or `done`, stop searching.
76
+
77
+ ### Tick loop
78
+
79
+ Queries returning `cont` are automatically re-evaluated on the next tick with a fresh environment. The database (asserted facts) persists between ticks.
80
+
81
+ ## Built-ins
82
+
83
+ | Predicate | Status | Description |
84
+ |---|---|---|
85
+ | `done` / `true` | done | Always succeeds |
86
+ | `fail` / `false` | fail | Always fails |
87
+ | `cont` | cont | Always continues |
88
+ | `inv(G)` / `\+ G` | swap done/fail, keep cont | Inverter |
89
+ | `condone(G)` | fail→done | Forgive failure |
90
+ | `promote(G)` | fail→cont, cont→done | Optimistic |
91
+ | `demote(G)` | done→cont, cont→fail | Pessimistic |
92
+ | `par_any(A,B)` | max(A,B) | Lenient parallel |
93
+ | `par_all(A,B)` | min(A,B) | Strict parallel |
94
+ | `tick(N)` | done | Unify N with current tick |
95
+
96
+ Plus standard Prolog: `write/1`, `writeln/1`, `nl`, `assert/1`, `retract/1`, `is/2`, `=/2`, `\=/2`, `</2`, `>/2`, `>=/2`, `=</2`, `call/1`, `consult/1`.
97
+
98
+ ## Examples
99
+
100
+ ### Wait N ticks, then succeed
101
+
102
+ ```prolog
103
+ wait(N) :- tick(T), T >= N.
104
+ wait(N) :- tick(T), T < N, cont.
105
+ ```
106
+
107
+ ```
108
+ ?- wait(3), writeln(hello).
109
+ [tick 0] cont
110
+ [tick 1] cont
111
+ [tick 2] cont
112
+ hello
113
+ done.
114
+ ```
115
+
116
+ ### Counter (stateful, using assert/retract)
117
+
118
+ ```prolog
119
+ :- assert(count(0)).
120
+
121
+ count_to(Target) :- count(N), N >= Target.
122
+ count_to(Target) :-
123
+ count(N), N < Target,
124
+ retract(count(N)), N1 is N + 1, assert(count(N1)),
125
+ cont.
126
+ ```
127
+
128
+ ### Reactive behavior (condition-gated sequence)
129
+
130
+ ```prolog
131
+ :- assert(agent_pos(0)).
132
+
133
+ move_to(X) :- agent_pos(P), P =:= X.
134
+ move_to(X) :- agent_pos(P), P < X,
135
+ retract(agent_pos(P)), P1 is P + 1, assert(agent_pos(P1)), cont.
136
+
137
+ patrol :-
138
+ agent_pos(P), P < 3, move_to(3), cont.
139
+ patrol :-
140
+ agent_pos(P), P >= 3, P < 5, move_to(5), cont.
141
+ patrol :-
142
+ agent_pos(P), P >= 5.
143
+ ```
144
+
145
+ ### Reasoning under uncertainty
146
+
147
+ ```prolog
148
+ % reason.pa — three-valued validation for multi-step arguments
149
+
150
+ :- assert(known(premise_a)).
151
+ :- assert(investigation(premise_b, 2)).
152
+ :- assert(investigation(premise_c, 1)).
153
+
154
+ check(P) :- known(P).
155
+ check(P) :- known_false(P), fail.
156
+ check(P) :-
157
+ investigation(P, N), N > 0,
158
+ retract(investigation(P, N)), N1 is N - 1,
159
+ (N1 > 0 -> assert(investigation(P, N1)) ; assert(known(P))),
160
+ cont.
161
+
162
+ argument :- check(premise_a), check(premise_b), check(premise_c).
163
+ ```
164
+
165
+ The conjunction *is* the search strategy — it focuses work on the earliest unresolved premise and doesn't waste computation on steps whose preconditions haven't been met:
166
+
167
+ ```
168
+ [tick 0] a=done, b=cont → cont (c never reached)
169
+ [tick 1] a=done, b=done, c=cont → cont (work shifts to c)
170
+ [tick 2] a=done, b=done, c=done → done
171
+ ```
172
+
173
+ The epistemic operators give cognitive control over the validation:
174
+
175
+ ```prolog
176
+ cautious(X) :- demote(check(X)). % "verified but needs review" (done→cont)
177
+ keep_exploring(X) :- promote(check(X)). % "refuted but try harder" (fail→cont)
178
+ viable_theory :- par_any(hyp_a, hyp_b). % first hypothesis verified wins
179
+ ```
180
+
181
+ ## Relation to active-logic
182
+
183
+ This prototype implements the core operators from [activelogic-cs](https://github.com/active-logic/activelogic-cs) in a logic programming context:
184
+
185
+ | C# (activelogic-cs) | Proact | BT equivalent |
186
+ |---|---|---|
187
+ | `status && status` | `Goal, Goal` | Sequence |
188
+ | `status \|\| status` | `Goal ; Goal` | Selector |
189
+ | `+` (lenient) | `par_any(A, B)` | Parallel (any) |
190
+ | `*` (strict) | `par_all(A, B)` | Parallel (all) |
191
+ | `!status` | `inv(G)` | Inverter |
192
+ | `~status` | `condone(G)` | Condone |
193
+ | `+status` (unary) | `promote(G)` | Promoter |
194
+ | `-status` (unary) | `demote(G)` | Demoter |
195
+ | `cont` | `cont` | Running |
196
+
197
+ The key addition over Prolog is `cont` — a single built-in that bridges logic programming with reactive, tick-based behavior.
@@ -0,0 +1,5 @@
1
+ """Proact — active logic meets Prolog."""
2
+
3
+ from proact.engine import Proact, ProactError
4
+
5
+ __all__ = ["Proact", "ProactError"]
@@ -0,0 +1,158 @@
1
+ """Subprocess wrapper for the proact binary."""
2
+
3
+ import os
4
+ import re
5
+ import subprocess
6
+ import tempfile
7
+
8
+
9
+ class ProactError(Exception):
10
+ pass
11
+
12
+
13
+ def _find_binary():
14
+ """Find the proact binary — packaged, local build, or PATH."""
15
+ # 1. Bundled with pip install
16
+ pkg_dir = os.path.dirname(__file__)
17
+ for name in ("proact", "proact.exe"):
18
+ path = os.path.join(pkg_dir, name)
19
+ if os.path.isfile(path):
20
+ return path
21
+
22
+ # 2. Built locally (repo root/build/)
23
+ repo = os.path.dirname(pkg_dir)
24
+ for subdir in ("build", ""):
25
+ for name in ("proact", "proact.exe"):
26
+ path = os.path.join(repo, subdir, name)
27
+ if os.path.isfile(path):
28
+ return path
29
+
30
+ # 3. On PATH
31
+ from shutil import which
32
+ path = which("proact")
33
+ if path:
34
+ return path
35
+
36
+ raise ProactError(
37
+ "proact binary not found. "
38
+ "Run 'make' to build it or 'pip install .' to install the package."
39
+ )
40
+
41
+
42
+ class Proact:
43
+ """Three-valued reasoning engine.
44
+
45
+ Example::
46
+
47
+ p = Proact()
48
+ p.consult("rules.pa")
49
+ result = p.query("mortal(socrates)")
50
+ print(result.status) # 'done'
51
+
52
+ # Multi-tick evaluation
53
+ p.add("wait(3) :- tick(T), T >= 3.")
54
+ p.add("wait(3) :- tick(T), T < 3, cont.")
55
+ result = p.query("wait(3)", max_ticks=10)
56
+ print(result.ticks) # 4
57
+ """
58
+
59
+ def __init__(self, binary=None, max_ticks=1000, max_depth=512, trace=False):
60
+ self._binary = binary or _find_binary()
61
+ self._files = []
62
+ self._inline = []
63
+ self.max_ticks = max_ticks
64
+ self.max_depth = max_depth
65
+ self.trace = trace
66
+
67
+ def consult(self, path):
68
+ """Load a .pa file."""
69
+ if not os.path.isfile(path):
70
+ raise FileNotFoundError(f"No such file: {path}")
71
+ self._files.append(os.path.abspath(path))
72
+ return self
73
+
74
+ def add(self, source):
75
+ """Add clauses/directives from a string."""
76
+ self._inline.append(source)
77
+ return self
78
+
79
+ def query(self, goal, max_ticks=None):
80
+ """Evaluate a goal. Returns a Result."""
81
+ if not goal.endswith("."):
82
+ goal += "."
83
+
84
+ ticks = max_ticks or self.max_ticks
85
+
86
+ # Build a combined source file if we have inline clauses
87
+ tmp = None
88
+ files = list(self._files)
89
+
90
+ if self._inline:
91
+ tmp = tempfile.NamedTemporaryFile(
92
+ mode="w", suffix=".pa", delete=False
93
+ )
94
+ tmp.write("\n".join(self._inline) + "\n")
95
+ tmp.close()
96
+ files.append(tmp.name)
97
+
98
+ try:
99
+ cmd = [self._binary] + files + [
100
+ "--max-ticks", str(ticks),
101
+ "-q", # quiet — we parse output ourselves
102
+ "-e", goal,
103
+ ]
104
+ if self.trace:
105
+ cmd.append("--trace")
106
+
107
+ proc = subprocess.run(
108
+ cmd, capture_output=True, text=True, timeout=30,
109
+ )
110
+ return Result._parse(proc)
111
+ finally:
112
+ if tmp:
113
+ os.unlink(tmp.name)
114
+
115
+ def run(self, goal="main", max_ticks=None):
116
+ """Shorthand for query(goal, max_ticks)."""
117
+ return self.query(goal, max_ticks)
118
+
119
+ def reset(self):
120
+ """Clear all loaded files and inline clauses."""
121
+ self._files.clear()
122
+ self._inline.clear()
123
+ return self
124
+
125
+
126
+ class Result:
127
+ """Result of a proact query."""
128
+
129
+ __slots__ = ("status", "output", "returncode")
130
+
131
+ def __init__(self, status, output, returncode):
132
+ self.status = status
133
+ self.output = output
134
+ self.returncode = returncode
135
+
136
+ def _parse(proc):
137
+ stdout = proc.stdout or ""
138
+ stderr = proc.stderr or ""
139
+ output = (stdout + stderr).strip()
140
+
141
+ if proc.returncode == 0:
142
+ status = "done"
143
+ elif proc.returncode == 1:
144
+ status = "fail"
145
+ else:
146
+ status = "error"
147
+
148
+ return Result(status=status, output=output, returncode=proc.returncode)
149
+
150
+ @property
151
+ def ok(self):
152
+ return self.status == "done"
153
+
154
+ def __repr__(self):
155
+ return f"Result(status={self.status!r})"
156
+
157
+ def __bool__(self):
158
+ return self.ok
File without changes