invar-tools 1.17.23__py3-none-any.whl → 1.17.25__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/config.py +31 -13
- invar/shell/guard_helpers.py +36 -2
- invar/shell/property_tests.py +45 -4
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/METADATA +1 -1
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/RECORD +10 -10
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/WHEEL +0 -0
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/entry_points.txt +0 -0
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/licenses/LICENSE +0 -0
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/licenses/LICENSE-GPL +0 -0
- {invar_tools-1.17.23.dist-info → invar_tools-1.17.25.dist-info}/licenses/NOTICE +0 -0
invar/shell/config.py
CHANGED
|
@@ -435,24 +435,42 @@ _DEFAULT_EXCLUDE_PATHS = [
|
|
|
435
435
|
]
|
|
436
436
|
|
|
437
437
|
|
|
438
|
+
# @shell_complexity: Config fallthrough requires checking multiple sources
|
|
438
439
|
def _get_classification_config(project_root: Path) -> Result[dict[str, Any], str]:
|
|
439
|
-
"""Get classification-related config (paths and patterns).
|
|
440
|
-
find_result = _find_config_source(project_root)
|
|
441
|
-
if isinstance(find_result, Failure):
|
|
442
|
-
return Success({}) # Return empty on error
|
|
443
|
-
config_path, source = find_result.unwrap()
|
|
440
|
+
"""Get classification-related config (paths and patterns).
|
|
444
441
|
|
|
445
|
-
if
|
|
446
|
-
|
|
442
|
+
Uses fallthrough logic: if pyproject.toml exists but has no [tool.invar.guard],
|
|
443
|
+
continues to check invar.toml and .invar/config.toml.
|
|
444
|
+
"""
|
|
445
|
+
# Build list of config sources to try (same order as load_config)
|
|
446
|
+
sources_to_try: list[tuple[Path, ConfigSource]] = []
|
|
447
|
+
|
|
448
|
+
pyproject = project_root / "pyproject.toml"
|
|
449
|
+
if pyproject.exists():
|
|
450
|
+
sources_to_try.append((pyproject, "pyproject"))
|
|
447
451
|
|
|
448
|
-
|
|
449
|
-
|
|
452
|
+
invar_toml = project_root / "invar.toml"
|
|
453
|
+
if invar_toml.exists():
|
|
454
|
+
sources_to_try.append((invar_toml, "invar"))
|
|
450
455
|
|
|
451
|
-
|
|
452
|
-
|
|
456
|
+
invar_config = project_root / ".invar" / "config.toml"
|
|
457
|
+
if invar_config.exists():
|
|
458
|
+
sources_to_try.append((invar_config, "invar_dir"))
|
|
459
|
+
|
|
460
|
+
# Try each source, fallback if no guard config found
|
|
461
|
+
for config_path, source in sources_to_try:
|
|
462
|
+
result = _read_toml(config_path)
|
|
463
|
+
if isinstance(result, Failure):
|
|
464
|
+
continue # Skip unreadable files
|
|
465
|
+
|
|
466
|
+
data = result.unwrap()
|
|
467
|
+
guard_config = extract_guard_section(data, source)
|
|
468
|
+
|
|
469
|
+
if guard_config: # Found valid guard config
|
|
470
|
+
return Success(guard_config)
|
|
453
471
|
|
|
454
|
-
|
|
455
|
-
return Success(
|
|
472
|
+
# No config found in any source, return empty dict
|
|
473
|
+
return Success({})
|
|
456
474
|
|
|
457
475
|
|
|
458
476
|
def get_path_classification(project_root: Path) -> Result[tuple[list[str], list[str]], str]:
|
invar/shell/guard_helpers.py
CHANGED
|
@@ -178,7 +178,24 @@ def run_crosshair_phase(
|
|
|
178
178
|
return True, {"status": "skipped", "reason": "no files to verify"}
|
|
179
179
|
|
|
180
180
|
# Only verify Core files (pure logic)
|
|
181
|
-
|
|
181
|
+
# BUG-57: Use config-based core detection instead of hardcoded "core" in path
|
|
182
|
+
from invar.core.utils import matches_path_prefix
|
|
183
|
+
from invar.shell.config import get_path_classification
|
|
184
|
+
|
|
185
|
+
path_result = get_path_classification(path)
|
|
186
|
+
if isinstance(path_result, Success):
|
|
187
|
+
core_paths, _ = path_result.unwrap()
|
|
188
|
+
else:
|
|
189
|
+
core_paths = ["src/core", "core"]
|
|
190
|
+
|
|
191
|
+
def is_core_file(f: Path) -> bool:
|
|
192
|
+
try:
|
|
193
|
+
rel = str(f.relative_to(path))
|
|
194
|
+
except ValueError:
|
|
195
|
+
rel = str(f)
|
|
196
|
+
return matches_path_prefix(rel, core_paths)
|
|
197
|
+
|
|
198
|
+
core_files = [f for f in checked_files if is_core_file(f)]
|
|
182
199
|
if not core_files:
|
|
183
200
|
return True, {"status": "skipped", "reason": "no core files found"}
|
|
184
201
|
|
|
@@ -306,7 +323,24 @@ def run_property_tests_phase(
|
|
|
306
323
|
return True, {"status": "skipped", "reason": "no files"}, None
|
|
307
324
|
|
|
308
325
|
# Only test Core files (with contracts)
|
|
309
|
-
|
|
326
|
+
# BUG-57: Use config-based core detection instead of hardcoded "core" in path
|
|
327
|
+
from invar.core.utils import matches_path_prefix
|
|
328
|
+
from invar.shell.config import get_path_classification
|
|
329
|
+
|
|
330
|
+
path_result = get_path_classification(project_root)
|
|
331
|
+
if isinstance(path_result, Success):
|
|
332
|
+
core_paths, _ = path_result.unwrap()
|
|
333
|
+
else:
|
|
334
|
+
core_paths = ["src/core", "core"]
|
|
335
|
+
|
|
336
|
+
def is_core_file(f: Path) -> bool:
|
|
337
|
+
try:
|
|
338
|
+
rel = str(f.relative_to(project_root))
|
|
339
|
+
except ValueError:
|
|
340
|
+
rel = str(f)
|
|
341
|
+
return matches_path_prefix(rel, core_paths)
|
|
342
|
+
|
|
343
|
+
core_files = [f for f in checked_files if is_core_file(f)]
|
|
310
344
|
if not core_files:
|
|
311
345
|
return True, {"status": "skipped", "reason": "no core files"}, None
|
|
312
346
|
|
invar/shell/property_tests.py
CHANGED
|
@@ -97,7 +97,7 @@ def run_property_tests_on_file(
|
|
|
97
97
|
|
|
98
98
|
root = project_root or file_path.parent
|
|
99
99
|
with _inject_project_site_packages(root):
|
|
100
|
-
module = _import_module_from_path(file_path)
|
|
100
|
+
module = _import_module_from_path(file_path, project_root=root)
|
|
101
101
|
|
|
102
102
|
if module is None:
|
|
103
103
|
return Failure(f"Could not import module: {file_path}")
|
|
@@ -218,15 +218,56 @@ def _accumulate_report(
|
|
|
218
218
|
combined_report.errors.extend(file_report.errors)
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
# @shell_complexity: BUG-57 fix requires package hierarchy setup for relative imports
|
|
222
|
+
def _import_module_from_path(file_path: Path, project_root: Path | None = None) -> object | None:
|
|
222
223
|
"""
|
|
223
224
|
Import a Python module from a file path.
|
|
224
225
|
|
|
226
|
+
BUG-57: Properly handles relative imports by setting up package context.
|
|
227
|
+
|
|
225
228
|
Returns None if import fails.
|
|
226
229
|
"""
|
|
227
230
|
try:
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
# Calculate the full module name from project root
|
|
232
|
+
if project_root and file_path.is_relative_to(project_root):
|
|
233
|
+
# Convert path to module name: my_package/main.py -> my_package.main
|
|
234
|
+
relative = file_path.relative_to(project_root)
|
|
235
|
+
parts = list(relative.with_suffix("").parts)
|
|
236
|
+
module_name = ".".join(parts)
|
|
237
|
+
else:
|
|
238
|
+
module_name = file_path.stem
|
|
239
|
+
|
|
240
|
+
# Ensure project root is in sys.path for relative imports
|
|
241
|
+
if project_root:
|
|
242
|
+
root_str = str(project_root)
|
|
243
|
+
if root_str not in sys.path:
|
|
244
|
+
sys.path.insert(0, root_str)
|
|
245
|
+
|
|
246
|
+
# For packages with relative imports, we need to set up parent packages first
|
|
247
|
+
if "." in module_name:
|
|
248
|
+
# Import parent packages first
|
|
249
|
+
parts = module_name.split(".")
|
|
250
|
+
for i in range(1, len(parts)):
|
|
251
|
+
parent_name = ".".join(parts[:i])
|
|
252
|
+
if parent_name not in sys.modules:
|
|
253
|
+
parent_path = project_root / "/".join(parts[:i]) if project_root else None
|
|
254
|
+
if parent_path and (parent_path / "__init__.py").exists():
|
|
255
|
+
parent_spec = importlib.util.spec_from_file_location(
|
|
256
|
+
parent_name,
|
|
257
|
+
parent_path / "__init__.py",
|
|
258
|
+
submodule_search_locations=[str(parent_path)],
|
|
259
|
+
)
|
|
260
|
+
if parent_spec and parent_spec.loader:
|
|
261
|
+
parent_module = importlib.util.module_from_spec(parent_spec)
|
|
262
|
+
sys.modules[parent_name] = parent_module
|
|
263
|
+
parent_spec.loader.exec_module(parent_module)
|
|
264
|
+
|
|
265
|
+
# Now import the target module
|
|
266
|
+
spec = importlib.util.spec_from_file_location(
|
|
267
|
+
module_name,
|
|
268
|
+
file_path,
|
|
269
|
+
submodule_search_locations=[str(file_path.parent)],
|
|
270
|
+
)
|
|
230
271
|
if spec is None or spec.loader is None:
|
|
231
272
|
return None
|
|
232
273
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: invar-tools
|
|
3
|
-
Version: 1.17.
|
|
3
|
+
Version: 1.17.25
|
|
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
|
|
@@ -2657,20 +2657,20 @@ 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=
|
|
2660
|
+
invar/shell/config.py,sha256=fPKY1AAqM28vcIIasvitYqqCv3g5VAjCdS8M7dNbQWA,19331
|
|
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
2665
|
invar/shell/git.py,sha256=R-ynlYa65xtCdnNjHeu42uPyrqoo9KZDzl7BZUW0oWU,2866
|
|
2666
|
-
invar/shell/guard_helpers.py,sha256=
|
|
2666
|
+
invar/shell/guard_helpers.py,sha256=IWiQEhDXnvN7QizGFrTNgfkSRN3ZVRF66gj-CeTjuJE,17261
|
|
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=
|
|
2673
|
+
invar/shell/property_tests.py,sha256=qt0CP5RH9Md2ZZV64ziNsjQ_-x0onCYtZwbQfqw9gbY,12586
|
|
2674
2674
|
invar/shell/py_refs.py,sha256=Vjz50lmt9prDBcBv4nkkODdiJ7_DKu5zO4UPZBjAfmM,4638
|
|
2675
2675
|
invar/shell/skill_manager.py,sha256=Mr7Mh9rxPSKSAOTJCAM5ZHiG5nfUf6KQVCuD4LBNHSI,12440
|
|
2676
2676
|
invar/shell/subprocess_env.py,sha256=hendEERSyAG4a8UFhYfPtOAlfspVRB03aVCYpj3uqk4,12745
|
|
@@ -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.
|
|
2782
|
-
invar_tools-1.17.
|
|
2783
|
-
invar_tools-1.17.
|
|
2784
|
-
invar_tools-1.17.
|
|
2785
|
-
invar_tools-1.17.
|
|
2786
|
-
invar_tools-1.17.
|
|
2787
|
-
invar_tools-1.17.
|
|
2781
|
+
invar_tools-1.17.25.dist-info/METADATA,sha256=t7Kc8ideD0woPDbz-PQiXfyQzlyBOPis0VZjDQHHCPU,28582
|
|
2782
|
+
invar_tools-1.17.25.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
2783
|
+
invar_tools-1.17.25.dist-info/entry_points.txt,sha256=RwH_EhqgtFPsnO6RcrwrAb70Zyfb8Mh6uUtztWnUxGk,102
|
|
2784
|
+
invar_tools-1.17.25.dist-info/licenses/LICENSE,sha256=qeFksp4H4kfTgQxPCIu3OdagXyiZcgBlVfsQ6M5oFyk,10767
|
|
2785
|
+
invar_tools-1.17.25.dist-info/licenses/LICENSE-GPL,sha256=IvZfC6ZbP7CLjytoHVzvpDZpD-Z3R_qa1GdMdWlWQ6Q,35157
|
|
2786
|
+
invar_tools-1.17.25.dist-info/licenses/NOTICE,sha256=joEyMyFhFY8Vd8tTJ-a3SirI0m2Sd0WjzqYt3sdcglc,2561
|
|
2787
|
+
invar_tools-1.17.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|