invar-tools 1.17.18__py3-none-any.whl → 1.17.20__py3-none-any.whl

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.
invar/shell/testing.py CHANGED
@@ -119,6 +119,7 @@ def run_doctests_on_files(
119
119
  verbose: bool = False,
120
120
  timeout: int = 60,
121
121
  collect_coverage: bool = False,
122
+ cwd: Path | None = None,
122
123
  ) -> Result[dict, str]:
123
124
  """
124
125
  Run doctests on a list of Python files.
@@ -143,17 +144,16 @@ def run_doctests_on_files(
143
144
  parts = f.parts
144
145
  # Check for consecutive "templates/examples" or ".invar/examples"
145
146
  for i in range(len(parts) - 1):
146
- if (parts[i] == "templates" and parts[i + 1] == "examples") or \
147
- (parts[i] == ".invar" and parts[i + 1] == "examples"):
147
+ if (parts[i] == "templates" and parts[i + 1] == "examples") or (
148
+ parts[i] == ".invar" and parts[i + 1] == "examples"
149
+ ):
148
150
  return True
149
151
  return False
150
152
 
151
153
  py_files = [
152
- f for f in files
153
- if f.suffix == ".py"
154
- and f.exists()
155
- and f.name != "conftest.py"
156
- and not is_excluded(f)
154
+ f
155
+ for f in files
156
+ if f.suffix == ".py" and f.exists() and f.name != "conftest.py" and not is_excluded(f)
157
157
  ]
158
158
  if not py_files:
159
159
  return Success({"status": "skipped", "reason": "no Python files", "files": []})
@@ -162,16 +162,26 @@ def run_doctests_on_files(
162
162
  if collect_coverage:
163
163
  # Use coverage run to wrap pytest
164
164
  cmd = [
165
- sys.executable, "-m", "coverage", "run",
165
+ sys.executable,
166
+ "-m",
167
+ "coverage",
168
+ "run",
166
169
  "--branch", # Enable branch coverage
167
170
  "--parallel-mode", # For merging with hypothesis later
168
- "-m", "pytest",
169
- "--doctest-modules", "-x", "--tb=short",
171
+ "-m",
172
+ "pytest",
173
+ "--doctest-modules",
174
+ "-x",
175
+ "--tb=short",
170
176
  ]
171
177
  else:
172
178
  cmd = [
173
- sys.executable, "-m", "pytest",
174
- "--doctest-modules", "-x", "--tb=short",
179
+ sys.executable,
180
+ "-m",
181
+ "pytest",
182
+ "--doctest-modules",
183
+ "-x",
184
+ "--tb=short",
175
185
  ]
176
186
  cmd.extend(str(f) for f in py_files)
177
187
  if verbose:
@@ -188,14 +198,16 @@ def run_doctests_on_files(
188
198
  )
189
199
  # Pytest exit codes: 0=passed, 5=no tests collected (also OK)
190
200
  is_passed = result.returncode in (0, 5)
191
- return Success({
192
- "status": "passed" if is_passed else "failed",
193
- "files": [str(f) for f in py_files],
194
- "exit_code": result.returncode,
195
- "stdout": result.stdout,
196
- "stderr": result.stderr,
197
- "coverage_collected": collect_coverage, # DX-37: Flag for caller
198
- })
201
+ return Success(
202
+ {
203
+ "status": "passed" if is_passed else "failed",
204
+ "files": [str(f) for f in py_files],
205
+ "exit_code": result.returncode,
206
+ "stdout": result.stdout,
207
+ "stderr": result.stderr,
208
+ "coverage_collected": collect_coverage, # DX-37: Flag for caller
209
+ }
210
+ )
199
211
  except subprocess.TimeoutExpired:
200
212
  return Failure(f"Doctest timeout ({timeout}s)")
201
213
  except Exception as e:
@@ -204,7 +216,11 @@ def run_doctests_on_files(
204
216
 
205
217
  # @shell_complexity: Property test orchestration with subprocess
206
218
  def run_test(
207
- target: str, json_output: bool = False, verbose: bool = False, timeout: int = 300
219
+ target: str,
220
+ json_output: bool = False,
221
+ verbose: bool = False,
222
+ timeout: int = 300,
223
+ cwd: Path | None = None,
208
224
  ) -> Result[dict, str]:
209
225
  """
210
226
  Run property-based tests using Hypothesis via deal.cases.
@@ -225,20 +241,25 @@ def run_test(
225
241
  return Failure(f"Target must be a Python file: {target}")
226
242
 
227
243
  cmd = [
228
- sys.executable, "-m", "pytest",
229
- str(target_path), "--doctest-modules", "-x", "--tb=short",
244
+ sys.executable,
245
+ "-m",
246
+ "pytest",
247
+ str(target_path),
248
+ "--doctest-modules",
249
+ "-x",
250
+ "--tb=short",
230
251
  ]
231
252
  if verbose:
232
253
  cmd.append("-v")
233
254
 
234
255
  try:
235
- # DX-52: Inject project venv site-packages for uvx compatibility
236
256
  result = subprocess.run(
237
257
  cmd,
238
258
  capture_output=True,
239
259
  text=True,
240
260
  timeout=timeout,
241
- env=build_subprocess_env(),
261
+ cwd=str(cwd) if cwd is not None else None,
262
+ env=build_subprocess_env(cwd=cwd),
242
263
  )
243
264
  test_result = {
244
265
  "status": "passed" if result.returncode == 0 else "failed",
@@ -274,6 +295,7 @@ def run_verify(
274
295
  json_output: bool = False,
275
296
  total_timeout: int = 300,
276
297
  per_condition_timeout: int = 30,
298
+ cwd: Path | None = None,
277
299
  ) -> Result[dict, str]:
278
300
  """
279
301
  Run symbolic verification using CrossHair.
@@ -302,23 +324,28 @@ def run_verify(
302
324
  return Failure(f"Target must be a Python file: {target}")
303
325
 
304
326
  cmd = [
305
- sys.executable, "-m", "crosshair", "check",
306
- str(target_path), f"--per_condition_timeout={per_condition_timeout}",
327
+ sys.executable,
328
+ "-m",
329
+ "crosshair",
330
+ "check",
331
+ str(target_path),
332
+ f"--per_condition_timeout={per_condition_timeout}",
307
333
  ]
308
334
 
309
335
  try:
310
- # DX-52: Inject project venv site-packages for uvx compatibility
311
336
  result = subprocess.run(
312
337
  cmd,
313
338
  capture_output=True,
314
339
  text=True,
315
340
  timeout=total_timeout,
316
- env=build_subprocess_env(),
341
+ cwd=str(cwd) if cwd is not None else None,
342
+ env=build_subprocess_env(cwd=cwd),
317
343
  )
318
344
 
319
345
  # CrossHair format: "file:line: error: Err when calling func(...)"
320
346
  counterexamples = [
321
- line.strip() for line in result.stdout.split("\n")
347
+ line.strip()
348
+ for line in result.stdout.split("\n")
322
349
  if ": error:" in line.lower() or "counterexample" in line.lower()
323
350
  ]
324
351
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: invar-tools
3
- Version: 1.17.18
3
+ Version: 1.17.20
4
4
  Summary: AI-native software engineering tools with design-by-contract verification
5
5
  Project-URL: Homepage, https://github.com/tefx/invar
6
6
  Project-URL: Documentation, https://github.com/tefx/invar#readme
@@ -23,13 +23,14 @@ Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.11
24
24
  Requires-Dist: crosshair-tool>=0.0.60
25
25
  Requires-Dist: hypothesis>=6.0
26
- Requires-Dist: invar-runtime>=1.0
26
+ Requires-Dist: invar-runtime>=1.3.0
27
27
  Requires-Dist: jedi>=0.19
28
28
  Requires-Dist: jinja2>=3.0
29
29
  Requires-Dist: markdown-it-py>=3.0
30
30
  Requires-Dist: mcp>=1.0
31
31
  Requires-Dist: pre-commit>=3.0
32
32
  Requires-Dist: pydantic>=2.0
33
+ Requires-Dist: pytest>=7.0
33
34
  Requires-Dist: questionary>=2.0
34
35
  Requires-Dist: returns>=0.20
35
36
  Requires-Dist: rich>=13.0
@@ -38,7 +39,6 @@ Provides-Extra: dev
38
39
  Requires-Dist: coverage[toml]>=7.0; extra == 'dev'
39
40
  Requires-Dist: mypy>=1.0; extra == 'dev'
40
41
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
41
- Requires-Dist: pytest>=7.0; extra == 'dev'
42
42
  Requires-Dist: ruff>=0.1; extra == 'dev'
43
43
  Description-Content-Type: text/markdown
44
44
 
@@ -56,7 +56,7 @@ invar/node_tools/.gitignore,sha256=M2kz8Iw7Kzmi44mKo1r7_HOZMh79a7dFDdRrqXyaEhI,5
56
56
  invar/node_tools/MANIFEST,sha256=2Z2at-27MK8K7DSjOjjtR4faTbt6eCiKQuEfvP_lwH8,145
57
57
  invar/node_tools/__init__.py,sha256=HzILh3jtP28Lm2jZwss1SY65ECxbtw2J2uFpXQA6Y94,1740
58
58
  invar/node_tools/ts-query.js,sha256=fEc_f0JT_Mb18dEoA4_vJoazvd7Lqv_rsed4eHSAbCg,13303
59
- invar/node_tools/eslint-plugin/cli.js,sha256=nnL-ZiuGON77c_iI5HzcOEOujQZqGHNjaoMWZzRFMhE,8204
59
+ invar/node_tools/eslint-plugin/cli.js,sha256=jgRHHHDRbyQiVbIWgmaxlsA9PlDvonWFEEYf9_XPaZ4,8481
60
60
  invar/node_tools/eslint-plugin/cli.js.map,sha256=AZt_DYAD6NnHGjR6oxXfGYaOHc6-vfDs0ENvYsbw5Ro,4489
61
61
  invar/node_tools/eslint-plugin/index.js,sha256=atrI37dn1yDznQrgKY-1xIhLnCdvJQTU53123ZI_9J4,5132
62
62
  invar/node_tools/eslint-plugin/index.js.map,sha256=MlFuNso9sZ4v9ggGJLyMq4Pt_JdEzP1OyouyCCki1wA,2344
@@ -2616,7 +2616,7 @@ invar/node_tools/eslint-plugin/rules/require-complete-validation.js,sha256=ZGeAE
2616
2616
  invar/node_tools/eslint-plugin/rules/require-complete-validation.js.map,sha256=UhEr4fql0TObHt3ya8DydqbFlig5CSDFKIIJZSf74rA,2497
2617
2617
  invar/node_tools/eslint-plugin/rules/require-jsdoc-example.js,sha256=Uql3FKdnTHFBMh_OkwGuOdMBzf4k94QHOaWo9frAcSM,4488
2618
2618
  invar/node_tools/eslint-plugin/rules/require-jsdoc-example.js.map,sha256=00cKhvu890f-8UXPZcEAaX44IjCxxw12E4l_gsi93JM,2960
2619
- invar/node_tools/eslint-plugin/rules/require-schema-validation.js,sha256=gdJaRr9-j8ZmUr8-u_MnafeQ4I92OoIq3RzUXeqrpBg,12372
2619
+ invar/node_tools/eslint-plugin/rules/require-schema-validation.js,sha256=6kRnMrTBuqeHmNOdBz78jZ0WBZgTmIUBoR_0ozWlK4k,12155
2620
2620
  invar/node_tools/eslint-plugin/rules/require-schema-validation.js.map,sha256=akTSZQq-OC6ZvwMklIBRaxsesJ9w6GHaPhRTJ_ihmaE,8211
2621
2621
  invar/node_tools/eslint-plugin/rules/shell-complexity.js,sha256=Bhc9i7ILYMNNAUvxf9l7PlwhC_Neb69Jl1nUNABu2jg,11260
2622
2622
  invar/node_tools/eslint-plugin/rules/shell-complexity.js.map,sha256=0oJoDxXpXg2VTrwGMa-UDZ6te15Sv9vq-LgwWjJB5Q4,7140
@@ -2657,31 +2657,31 @@ invar/node_tools/quick-check/cli.js,sha256=dwV3hdJleFQga2cKUn3PPfQDvvujhzKdjQcIv
2657
2657
  invar/node_tools/ts-analyzer/cli.js,sha256=SvZ6HyjmobpP8NAZqXFiy8BwH_t5Hb17Ytar_18udaQ,4092887
2658
2658
  invar/shell/__init__.py,sha256=FFw1mNbh_97PeKPcHIqQpQ7mw-JoIvyLM1yOdxLw5uk,204
2659
2659
  invar/shell/claude_hooks.py,sha256=hV4DfG3cVng32f0Rxoo070tliVlYFC5v9slIWEbAD7E,18899
2660
- invar/shell/config.py,sha256=6-kbo6--SxfROXoyU-v7InSLR8f_U1Mar_xEOdCXFkY,17633
2660
+ invar/shell/config.py,sha256=6N4AvhYPSUzd3YGXPIc8edF6Lp492W-cS8wwnHUJotI,18119
2661
2661
  invar/shell/contract_coverage.py,sha256=81OQkQqUVYUKytG5aiJyRK62gwh9UzbSG926vkvFTc8,12088
2662
2662
  invar/shell/coverage.py,sha256=m01o898IFIdBztEBQLwwL1Vt5PWrpUntO4lv4nWEkls,11344
2663
2663
  invar/shell/doc_tools.py,sha256=16gvo_ay9-_EK6lX16WkiRGg4OfTAKK_i0ucQkE7lbI,15149
2664
2664
  invar/shell/fs.py,sha256=ctqU-EX0NnKC4txudRCRpbWxWSgBZTInXMeOUnl3IM0,6196
2665
- invar/shell/git.py,sha256=s6RQxEDQuLrmK3mru88EoYP8__4hiFW8AozlcxmY47E,2784
2666
- invar/shell/guard_helpers.py,sha256=QeYgbW0lgUa9Z_RCjAMG7UJdiMzz5cW48Lb2u-qgQi8,15114
2665
+ invar/shell/git.py,sha256=R-ynlYa65xtCdnNjHeu42uPyrqoo9KZDzl7BZUW0oWU,2866
2666
+ invar/shell/guard_helpers.py,sha256=lpaFIe328ZISzim92TAxZHTT8jC4N0_TcQ7PV7u327w,16083
2667
2667
  invar/shell/guard_output.py,sha256=v3gG5P-_47nIFo8eAMKwdA_hLf2KZ0cQ-45Z6JjKp4w,12520
2668
2668
  invar/shell/mcp_config.py,sha256=-hC7Y5BGuVs285b6gBARk7ZyzVxHwPgXSyt_GoN0jfs,4580
2669
2669
  invar/shell/mutation.py,sha256=Lfyk2b8j8-hxAq-iwAgQeOhr7Ci6c5tRF1TXe3CxQCs,8914
2670
2670
  invar/shell/pattern_integration.py,sha256=pRcjfq3NvMW_tvQCnaXZnD1k5AVEWK8CYOE2jN6VTro,7842
2671
2671
  invar/shell/pi_hooks.py,sha256=ulZc1sP8mTRJTBsjwFHQzUgg-h8ajRIMp7iF1Y4UUtw,6885
2672
2672
  invar/shell/pi_tools.py,sha256=a3ACDDXykFV8fUB5UpBmgMvppwkmLvT1k_BWm0IY47k,4068
2673
- invar/shell/property_tests.py,sha256=N9JreyH5PqR89oF5yLcX7ZAV-Koyg5BKo-J05-GUPsA,9109
2673
+ invar/shell/property_tests.py,sha256=SB-3tS5TupYQP1jpEUNpQs6fWCwLWxck0akNsgVwGGM,10533
2674
2674
  invar/shell/py_refs.py,sha256=Vjz50lmt9prDBcBv4nkkODdiJ7_DKu5zO4UPZBjAfmM,4638
2675
2675
  invar/shell/skill_manager.py,sha256=Mr7Mh9rxPSKSAOTJCAM5ZHiG5nfUf6KQVCuD4LBNHSI,12440
2676
- invar/shell/subprocess_env.py,sha256=9oXl3eMEbzLsFEgMHqobEw6oW_wV0qMEP7pklwm58Pw,11453
2676
+ invar/shell/subprocess_env.py,sha256=ToPqGw5bnfJIEk5pdvJBM6dslKxn6gqspDnhr1VRCDk,11554
2677
2677
  invar/shell/template_engine.py,sha256=eNKMz7R8g9Xp3_1TGx-QH137jf52E0u3KaVcnotu1Tg,12056
2678
2678
  invar/shell/templates.py,sha256=ilhGysbUcdkUFqPgv6ySVmKI3imS_cwYNCWTCdyb5cY,15407
2679
- invar/shell/testing.py,sha256=rTNBH0Okh2qtG9ohSXOz487baQ2gXrWT3s_WECW3HJs,11143
2679
+ invar/shell/testing.py,sha256=ig-LVe5k2-qvdz2H85mrY3_7_v440DJJX72Q3TVP5Xk,11472
2680
2680
  invar/shell/ts_compiler.py,sha256=nA8brnOhThj9J_J3vAEGjDsM4NjbWQ_eX8Yf4pHPOgk,6672
2681
2681
  invar/shell/commands/__init__.py,sha256=MEkKwVyjI9DmkvBpJcuumXo2Pg_FFkfEr-Rr3nrAt7A,284
2682
2682
  invar/shell/commands/doc.py,sha256=SOLDoCXXGxx_JU0PKXlAIGEF36PzconHmmAtL-rM6D4,13819
2683
2683
  invar/shell/commands/feedback.py,sha256=lLxEeWW_71US_vlmorFrGXS8IARB9nbV6D0zruLs660,7640
2684
- invar/shell/commands/guard.py,sha256=I1BDqthsAVmz8FnUgwkbhvy2cozmOfjs3rxQwwqFemo,25391
2684
+ invar/shell/commands/guard.py,sha256=ZeiK5FvRlTNHMLS6StIDKl-mDAdyAFGOovkRy0tG4eQ,25703
2685
2685
  invar/shell/commands/hooks.py,sha256=W-SOnT4VQyUvXwipozkJwgEYfiOJGz7wksrbcdWegUg,2356
2686
2686
  invar/shell/commands/init.py,sha256=rtoPFsfq7xRZ6lfTipWT1OejNK5wfzqu1ncXi1kizU0,23634
2687
2687
  invar/shell/commands/merge.py,sha256=nuvKo8m32-OL-SCQlS4SLKmOZxQ3qj-1nGCx1Pgzifw,8183
@@ -2696,7 +2696,7 @@ invar/shell/commands/update.py,sha256=-NNQQScEb_0bV1B8YFxTjpUECk9c8dGAiNVRc64nWh
2696
2696
  invar/shell/prove/__init__.py,sha256=ZqlbmyMFJf6yAle8634jFuPRv8wNvHps8loMlOJyf8A,240
2697
2697
  invar/shell/prove/accept.py,sha256=cnY_6jzU1EBnpLF8-zWUWcXiSXtCwxPsXEYXsSVPG38,3717
2698
2698
  invar/shell/prove/cache.py,sha256=jbNdrvfLjvK7S0iqugErqeabb4YIbQuwIlcSRyCKbcg,4105
2699
- invar/shell/prove/crosshair.py,sha256=XhJDsQWIriX9SuqeflUYvJgp9gJTDH7I7Uka6zjNzZ0,16734
2699
+ invar/shell/prove/crosshair.py,sha256=XDOoLvOrU_6OGYm2rTjWacdvKgpJ3NjUDetMwyT9kIE,16997
2700
2700
  invar/shell/prove/guard_ts.py,sha256=8InEWFd6oqFxBUEaAdlqOvPi43p0VZKxJIrAaF0eReU,37936
2701
2701
  invar/shell/prove/hypothesis.py,sha256=QUclOOUg_VB6wbmHw8O2EPiL5qBOeBRqQeM04AVuLw0,9880
2702
2702
  invar/templates/CLAUDE.md.template,sha256=eaGU3SyRO_NEifw5b26k3srgQH4jyeujjCJ-HbM36_w,4913
@@ -2778,10 +2778,10 @@ invar/templates/skills/invar-reflect/template.md,sha256=Rr5hvbllvmd8jSLf_0ZjyKt6
2778
2778
  invar/templates/skills/investigate/SKILL.md.jinja,sha256=cp6TBEixBYh1rLeeHOR1yqEnFqv1NZYePORMnavLkQI,3231
2779
2779
  invar/templates/skills/propose/SKILL.md.jinja,sha256=6BuKiCqO1AEu3VtzMHy1QWGqr_xqG9eJlhbsKT4jev4,3463
2780
2780
  invar/templates/skills/review/SKILL.md.jinja,sha256=ET5mbdSe_eKgJbi2LbgFC-z1aviKcHOBw7J5Q28fr4U,14105
2781
- invar_tools-1.17.18.dist-info/METADATA,sha256=WbBqPPXAsiwB7rCjrpDjnYYBjGy-a1uEpjWoRdbkHO8,28596
2782
- invar_tools-1.17.18.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
- invar_tools-1.17.18.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
- invar_tools-1.17.18.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
- invar_tools-1.17.18.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
- invar_tools-1.17.18.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
- invar_tools-1.17.18.dist-info/RECORD,,
2781
+ invar_tools-1.17.20.dist-info/METADATA,sha256=_RDBs3-pAvqNBOLkNCMctnSLdDn-4Qs33RhgxIoEbok,28582
2782
+ invar_tools-1.17.20.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
2783
+ invar_tools-1.17.20.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
2784
+ invar_tools-1.17.20.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
2785
+ invar_tools-1.17.20.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
2786
+ invar_tools-1.17.20.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
2787
+ invar_tools-1.17.20.dist-info/RECORD,,