reeval-python 0.14.0__tar.gz → 0.16.1__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.
Files changed (81) hide show
  1. {reeval_python-0.14.0 → reeval_python-0.16.1}/PKG-INFO +49 -4
  2. {reeval_python-0.14.0 → reeval_python-0.16.1}/README.md +48 -3
  3. {reeval_python-0.14.0 → reeval_python-0.16.1}/pyproject.toml +1 -1
  4. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/__init__.py +3 -3
  5. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/_wire.py +251 -124
  6. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/aio.py +1 -9
  7. reeval_python-0.16.1/src/reeval_python/binaries.json +55 -0
  8. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/checks.py +8 -1
  9. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/cli.py +24 -7
  10. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/context.py +39 -7
  11. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/declare.py +222 -13
  12. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/errors.py +1 -1
  13. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/expect.py +51 -23
  14. reeval_python-0.16.1/src/reeval_python/options.py +86 -0
  15. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/runner.py +8 -4
  16. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/wire.py +7 -3
  17. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/expect-fail.eval.py +1 -1
  18. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/expect-fail.sync.eval.py +1 -1
  19. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/expect.eval.py +2 -1
  20. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/expect.sync.eval.py +2 -1
  21. reeval_python-0.16.1/tests/context/judge.eval.py +14 -0
  22. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/judge.sync.eval.py +3 -3
  23. reeval_python-0.16.1/tests/declare/best-of.eval.py +24 -0
  24. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/compare-questions.eval.py +10 -3
  25. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/questions-attempts.eval.py +22 -5
  26. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/questions-group.eval.py +8 -3
  27. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/questions-state.eval.py +25 -6
  28. reeval_python-0.16.1/tests/declare/revision.eval.py +20 -0
  29. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/stdio_host.py +13 -3
  30. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_api.py +58 -1
  31. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_checks.py +26 -5
  32. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_cli.py +18 -0
  33. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_conformance_context.py +12 -1
  34. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_conformance_declare.py +8 -2
  35. reeval_python-0.16.1/tests/test_load.py +96 -0
  36. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_runner_attempts.py +50 -9
  37. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_runner_checks.py +2 -1
  38. reeval_python-0.16.1/tests/test_runner_json.py +92 -0
  39. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_runner_sdk.py +19 -3
  40. reeval_python-0.16.1/tests/test_source.py +191 -0
  41. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_types.py +4 -4
  42. {reeval_python-0.14.0 → reeval_python-0.16.1}/uv.lock +1 -1
  43. reeval_python-0.14.0/src/reeval_python/binaries.json +0 -55
  44. reeval_python-0.14.0/src/reeval_python/options.py +0 -54
  45. reeval_python-0.14.0/tests/context/judge.eval.py +0 -10
  46. {reeval_python-0.14.0 → reeval_python-0.16.1}/.gitignore +0 -0
  47. {reeval_python-0.14.0 → reeval_python-0.16.1}/CHANGELOG.md +0 -0
  48. {reeval_python-0.14.0 → reeval_python-0.16.1}/LICENSE +0 -0
  49. {reeval_python-0.14.0 → reeval_python-0.16.1}/package.json +0 -0
  50. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/__main__.py +0 -0
  51. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/environment.py +0 -0
  52. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/py.typed +0 -0
  53. {reeval_python-0.14.0 → reeval_python-0.16.1}/src/reeval_python/types.py +0 -0
  54. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/abort.eval.py +0 -0
  55. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/abort.sync.eval.py +0 -0
  56. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/exec-retries.eval.py +0 -0
  57. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/exec-retries.sync.eval.py +0 -0
  58. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/exec.eval.py +0 -0
  59. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/exec.sync.eval.py +0 -0
  60. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/files.eval.py +0 -0
  61. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/files.sync.eval.py +0 -0
  62. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/record.async.eval.py +0 -0
  63. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/record.eval.py +0 -0
  64. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/send.eval.py +0 -0
  65. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/send.sync.eval.py +0 -0
  66. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/sync-async.async.eval.py +0 -0
  67. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/context/sync-async.eval.py +0 -0
  68. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/compare-variants.eval.py +0 -0
  69. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/each-families.eval.py +0 -0
  70. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/egress-cascade.eval.py +0 -0
  71. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/environment-extend.eval.py +0 -0
  72. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/environment-plugin-marketplace.eval.py +0 -0
  73. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/environment-plugins.eval.py +0 -0
  74. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/groups-skip-todo.eval.py +0 -0
  75. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/declare/tags.eval.py +0 -0
  76. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/runner_process.py +0 -0
  77. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_patterns.py +0 -0
  78. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_python_wheels.py +0 -0
  79. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_runner_isolation.py +0 -0
  80. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_runner_misuse.py +0 -0
  81. {reeval_python-0.14.0 → reeval_python-0.16.1}/tests/test_runner_stdio.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: reeval-python
3
- Version: 0.14.0
3
+ Version: 0.16.1
4
4
  Summary: Python authoring SDK for reeval (*.eval.py eval files)
5
5
  Project-URL: Homepage, https://reeval.ai
6
6
  Project-URL: Repository, https://github.com/invertase/reeval
@@ -73,7 +73,7 @@ from reeval_python import SyncAttemptContext, evaluate, expect
73
73
  def lands(c: SyncAttemptContext) -> None:
74
74
  c.send("Create greet.py that prints hi")
75
75
  assert c.exec("test -f greet.py").ok
76
- expect(c.judge("Does greet.py print hi?")).to_pass()
76
+ expect(c.judge("Does greet.py print hi?", contents=["greet.py"])).to_pass()
77
77
  ```
78
78
 
79
79
  Plain `assert` is first-class, as in pytest: each one in an eval file is a check in the run
@@ -83,9 +83,47 @@ as in Python, and is recorded with its message and both sides of a comparison as
83
83
  (`greet.py exists: assert 1 == 2`). An `AssertionError` the body catches isn't a failed check, so
84
84
  a retry loop can assert; one the escaping error carries (each failure in a `TaskGroup`'s
85
85
  `ExceptionGroup`, or one an error was raised from or while handling) is. Use typed results (`ExecResult.ok`, `Verdict.passed`, `Turn.text`, …) and `c.file(path)`
86
- for quick checks. Keep `expect` for reeval matchers (`to_have_file`, `to_succeed`, soft checks,
86
+ for quick checks. Keep `expect` for reeval matchers (`to_have_file`, `to_succeed`, checks that carry on,
87
87
  labels) — Playwright-style.
88
88
 
89
+ #### Checks carry on; `expect.require` is a gate
90
+
91
+ A failed `expect` is recorded and the body carries on, so every later check and judge still runs;
92
+ the attempt fails when the body ends. `expect.require` raises `ReevalAssertionError` instead, for a
93
+ gate the rest depends on:
94
+
95
+ ```python
96
+ c.send("Add a sign-up page")
97
+ expect.require(c.exec("npm run build"), "builds").to_succeed() # stops here if the build fails
98
+ expect(c.exec("npm test"), "tests pass").to_succeed() # recorded, carries on
99
+ ```
100
+
101
+ reeval records a failed `expect.require` before the error reaches the body. One the body catches
102
+ still fails the attempt when the body ends, and its error says the eval caught it. A plain
103
+ `assert` the body catches records nothing, as in Python.
104
+
105
+ | The body catches | Recorded | Fails the attempt |
106
+ |---|---|---|
107
+ | a failed `expect.require(…)` | a failed check | yes, when the body ends |
108
+ | a failed `assert` | nothing | no |
109
+ | neither (it escapes) | a failed check | yes |
110
+
111
+ So retry with `expect.poll`, which retries the matcher and records only its final outcome, or with
112
+ a plain `assert` or `if` in the loop, never `expect.require` in `try`/`except`:
113
+
114
+ ```python
115
+ # Retries the command until it succeeds, for up to 10 seconds: one check.
116
+ expect.poll(lambda: c.exec("curl -sf localhost:3000"), timeout=10).to_succeed()
117
+
118
+ # A plain assert in a retry loop: only the try that held is recorded.
119
+ for _ in range(3):
120
+ try:
121
+ assert c.exec("test -f ready.txt").ok
122
+ break
123
+ except AssertionError:
124
+ time.sleep(1)
125
+ ```
126
+
89
127
  Matchers that take a pattern (`to_have_file`, `to_have_run`, `to_have_searched`, `to_have_fetched`,
90
128
  `to_have_called_tool`, `to_have_edited`, `to_have_changed`) take a `str` or a compiled `re.Pattern`,
91
129
  `str` or `bytes`, with any flags. Python matches it with `pattern.search`, so anything `re` accepts
@@ -101,12 +139,19 @@ Use snake_case on the context (`read_file`, `write_file`, `file`) and on matcher
101
139
  `to_pass`, `to_equal`, `to_contain`, …). Matcher failures raise `ReevalAssertionError`.
102
140
 
103
141
  Options are typed (`EvalOptions`, and `JudgeOptions` for `c.judge(…)`), so pyright refuses a
104
- misspelt key such as `runz`. They take reeval's keys in snake_case at any depth (`min_lift`,
142
+ misspelled key such as `runz`. They take reeval's keys in snake_case at any depth (`min_lift`,
105
143
  `delay_ms`); the keys of `env`, `headers` and the other maps of your own keep their spelling. Every
106
144
  duration is seconds or a `timedelta`: `{"timeout": 90}`, `docker(start_timeout=timedelta(minutes=2))`,
107
145
  `expect.poll(…, timeout=10)`. `docker()`, `fake_harness()`, `fake_judge()` and `plugin.*` return
108
146
  values to pass as options, not data to edit.
109
147
 
148
+ `c.judge` takes the inputs the judge reads as keywords, and needs at least one: `diff=True`,
149
+ `contents=["src/**"]`, `output=True` with `on=result`, and so on. With none it raises `TypeError`.
150
+
151
+ ```python
152
+ expect(c.judge("Is it valid JSON?", on=result, output=True)).to_pass()
153
+ ```
154
+
110
155
  ### `def` and `async def` bodies
111
156
 
112
157
  A `def` body gets a blocking context: `c.exec(…)` runs the command and returns its `ExecResult`,
@@ -49,7 +49,7 @@ from reeval_python import SyncAttemptContext, evaluate, expect
49
49
  def lands(c: SyncAttemptContext) -> None:
50
50
  c.send("Create greet.py that prints hi")
51
51
  assert c.exec("test -f greet.py").ok
52
- expect(c.judge("Does greet.py print hi?")).to_pass()
52
+ expect(c.judge("Does greet.py print hi?", contents=["greet.py"])).to_pass()
53
53
  ```
54
54
 
55
55
  Plain `assert` is first-class, as in pytest: each one in an eval file is a check in the run
@@ -59,9 +59,47 @@ as in Python, and is recorded with its message and both sides of a comparison as
59
59
  (`greet.py exists: assert 1 == 2`). An `AssertionError` the body catches isn't a failed check, so
60
60
  a retry loop can assert; one the escaping error carries (each failure in a `TaskGroup`'s
61
61
  `ExceptionGroup`, or one an error was raised from or while handling) is. Use typed results (`ExecResult.ok`, `Verdict.passed`, `Turn.text`, …) and `c.file(path)`
62
- for quick checks. Keep `expect` for reeval matchers (`to_have_file`, `to_succeed`, soft checks,
62
+ for quick checks. Keep `expect` for reeval matchers (`to_have_file`, `to_succeed`, checks that carry on,
63
63
  labels) — Playwright-style.
64
64
 
65
+ #### Checks carry on; `expect.require` is a gate
66
+
67
+ A failed `expect` is recorded and the body carries on, so every later check and judge still runs;
68
+ the attempt fails when the body ends. `expect.require` raises `ReevalAssertionError` instead, for a
69
+ gate the rest depends on:
70
+
71
+ ```python
72
+ c.send("Add a sign-up page")
73
+ expect.require(c.exec("npm run build"), "builds").to_succeed() # stops here if the build fails
74
+ expect(c.exec("npm test"), "tests pass").to_succeed() # recorded, carries on
75
+ ```
76
+
77
+ reeval records a failed `expect.require` before the error reaches the body. One the body catches
78
+ still fails the attempt when the body ends, and its error says the eval caught it. A plain
79
+ `assert` the body catches records nothing, as in Python.
80
+
81
+ | The body catches | Recorded | Fails the attempt |
82
+ |---|---|---|
83
+ | a failed `expect.require(…)` | a failed check | yes, when the body ends |
84
+ | a failed `assert` | nothing | no |
85
+ | neither (it escapes) | a failed check | yes |
86
+
87
+ So retry with `expect.poll`, which retries the matcher and records only its final outcome, or with
88
+ a plain `assert` or `if` in the loop, never `expect.require` in `try`/`except`:
89
+
90
+ ```python
91
+ # Retries the command until it succeeds, for up to 10 seconds: one check.
92
+ expect.poll(lambda: c.exec("curl -sf localhost:3000"), timeout=10).to_succeed()
93
+
94
+ # A plain assert in a retry loop: only the try that held is recorded.
95
+ for _ in range(3):
96
+ try:
97
+ assert c.exec("test -f ready.txt").ok
98
+ break
99
+ except AssertionError:
100
+ time.sleep(1)
101
+ ```
102
+
65
103
  Matchers that take a pattern (`to_have_file`, `to_have_run`, `to_have_searched`, `to_have_fetched`,
66
104
  `to_have_called_tool`, `to_have_edited`, `to_have_changed`) take a `str` or a compiled `re.Pattern`,
67
105
  `str` or `bytes`, with any flags. Python matches it with `pattern.search`, so anything `re` accepts
@@ -77,12 +115,19 @@ Use snake_case on the context (`read_file`, `write_file`, `file`) and on matcher
77
115
  `to_pass`, `to_equal`, `to_contain`, …). Matcher failures raise `ReevalAssertionError`.
78
116
 
79
117
  Options are typed (`EvalOptions`, and `JudgeOptions` for `c.judge(…)`), so pyright refuses a
80
- misspelt key such as `runz`. They take reeval's keys in snake_case at any depth (`min_lift`,
118
+ misspelled key such as `runz`. They take reeval's keys in snake_case at any depth (`min_lift`,
81
119
  `delay_ms`); the keys of `env`, `headers` and the other maps of your own keep their spelling. Every
82
120
  duration is seconds or a `timedelta`: `{"timeout": 90}`, `docker(start_timeout=timedelta(minutes=2))`,
83
121
  `expect.poll(…, timeout=10)`. `docker()`, `fake_harness()`, `fake_judge()` and `plugin.*` return
84
122
  values to pass as options, not data to edit.
85
123
 
124
+ `c.judge` takes the inputs the judge reads as keywords, and needs at least one: `diff=True`,
125
+ `contents=["src/**"]`, `output=True` with `on=result`, and so on. With none it raises `TypeError`.
126
+
127
+ ```python
128
+ expect(c.judge("Is it valid JSON?", on=result, output=True)).to_pass()
129
+ ```
130
+
86
131
  ### `def` and `async def` bodies
87
132
 
88
133
  A `def` body gets a blocking context: `c.exec(…)` runs the command and returns its `ExecResult`,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "reeval-python"
3
- version = "0.14.0"
3
+ version = "0.16.1"
4
4
  description = "Python authoring SDK for reeval (*.eval.py eval files)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -17,14 +17,15 @@ from reeval_python.environment import (
17
17
  plugin,
18
18
  )
19
19
  from reeval_python.errors import ReevalAssertionError, ReevalCallError
20
- from reeval_python.expect import Expectation, expect, soft
21
- from reeval_python.options import EvalOptions, JudgeDefaults, JudgeOptions
20
+ from reeval_python.expect import Expectation, expect
21
+ from reeval_python.options import BestOf, EvalOptions, JudgeDefaults, JudgeOptions
22
22
  from reeval_python.types import BackgroundHandle, Diff, ExecResult, FileChange, Turn, Verdict
23
23
 
24
24
  __all__ = [
25
25
  "DEFAULT_EGRESS_ALLOW",
26
26
  "AttemptContext",
27
27
  "BackgroundHandle",
28
+ "BestOf",
28
29
  "Diff",
29
30
  "DockerSandbox",
30
31
  "Environment",
@@ -52,6 +53,5 @@ __all__ = [
52
53
  "fake_judge",
53
54
  "group",
54
55
  "plugin",
55
- "soft",
56
56
  "todo",
57
57
  ]