skim-python 0.0.1__tar.gz → 0.0.3__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.
- {skim_python-0.0.1 → skim_python-0.0.3}/PKG-INFO +33 -2
- {skim_python-0.0.1 → skim_python-0.0.3}/README.md +32 -1
- {skim_python-0.0.1 → skim_python-0.0.3}/pyproject.toml +5 -1
- {skim_python-0.0.1 → skim_python-0.0.3}/src/skim_py/block.py +4 -2
- {skim_python-0.0.1 → skim_python-0.0.3}/src/skim_py/cli.py +5 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/src/skim_py/glance.py +4 -2
- skim_python-0.0.3/src/skim_py/parser.py +581 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/src/skim_py/types.py +10 -0
- skim_python-0.0.3/tests/fixtures/syntax_error_cascade.py +11 -0
- skim_python-0.0.3/tests/test_skim_py.py +428 -0
- skim_python-0.0.1/src/skim_py/parser.py +0 -300
- skim_python-0.0.1/tests/test_skim_py.py +0 -120
- {skim_python-0.0.1 → skim_python-0.0.3}/.gitignore +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/LICENSE +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/skim-py +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/src/skim_py/__init__.py +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/tests/fixtures/casing.py +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/tests/fixtures/classes.py +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/tests/fixtures/dataclasses_enum.py +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/tests/fixtures/syntax_error.py +0 -0
- {skim_python-0.0.1 → skim_python-0.0.3}/tests/fixtures/tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: skim-python
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Language-aware, AST-resilient outline and verbatim code extractor for Python tailored for AI agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/benedictjohannes/programming-helper-utils
|
|
6
6
|
Project-URL: Repository, https://github.com/benedictjohannes/programming-helper-utils
|
|
@@ -335,6 +335,7 @@ skim-py glance tests/fixtures/syntax_error.py -s
|
|
|
335
335
|
```
|
|
336
336
|
```text
|
|
337
337
|
=== tests/fixtures/syntax_error.py (12 lines) [! 1 syntax error] ===
|
|
338
|
+
# parsed with 1 line masked (L7)
|
|
338
339
|
Syntax Notice: L7: invalid syntax
|
|
339
340
|
|
|
340
341
|
Outline:
|
|
@@ -343,12 +344,13 @@ Outline:
|
|
|
343
344
|
L11-L12 def valid_after()
|
|
344
345
|
```
|
|
345
346
|
|
|
346
|
-
Surrounding valid functions remain fully extractable via `block
|
|
347
|
+
Surrounding valid functions remain fully extractable via `block` (masking warnings are surfaced via stderr):
|
|
347
348
|
|
|
348
349
|
```bash
|
|
349
350
|
skim-py block tests/fixtures/syntax_error.py valid_after
|
|
350
351
|
```
|
|
351
352
|
```text
|
|
353
|
+
tests/fixtures/syntax_error.py: # parsed with 1 line masked (L7)
|
|
352
354
|
=== tests/fixtures/syntax_error.py (L11-L12) ===
|
|
353
355
|
11 | def valid_after():
|
|
354
356
|
12 | return 2
|
|
@@ -360,6 +362,7 @@ And if an extracted block contains a syntax error within its line span, the warn
|
|
|
360
362
|
skim-py block tests/fixtures/syntax_error.py broken_function
|
|
361
363
|
```
|
|
362
364
|
```text
|
|
365
|
+
tests/fixtures/syntax_error.py: # parsed with 1 line masked (L7)
|
|
363
366
|
=== tests/fixtures/syntax_error.py (L5-L8) [! 1 syntax error] ===
|
|
364
367
|
Syntax Notice: L7: invalid syntax
|
|
365
368
|
5 | def broken_function():
|
|
@@ -368,6 +371,34 @@ skim-py block tests/fixtures/syntax_error.py broken_function
|
|
|
368
371
|
8 | return x
|
|
369
372
|
```
|
|
370
373
|
|
|
374
|
+
For cascade-prone mid-edit states (such as an unclosed `def foo(x,` header or unclosed bracket structure), `skim-py` masks the entire broken block in one step, marks the masked line range in the notice, and salvages the broken declaration in the outline so it remains targetable by `block`:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
skim-py glance tests/fixtures/syntax_error_cascade.py -s
|
|
378
|
+
```
|
|
379
|
+
```text
|
|
380
|
+
=== tests/fixtures/syntax_error_cascade.py (11 lines) [! 1 syntax error] ===
|
|
381
|
+
# parsed with 3 lines masked (L5, L6, L7)
|
|
382
|
+
Syntax Notice: L5: '(' was never closed (L5-L7 masked)
|
|
383
|
+
|
|
384
|
+
Outline:
|
|
385
|
+
L1-L2 def valid_before()
|
|
386
|
+
L5-L7 def broken_header [syntax error]
|
|
387
|
+
L10-L11 def valid_after()
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
skim-py block tests/fixtures/syntax_error_cascade.py broken_header
|
|
392
|
+
```
|
|
393
|
+
```text
|
|
394
|
+
tests/fixtures/syntax_error_cascade.py: # parsed with 3 lines masked (L5, L6, L7)
|
|
395
|
+
=== tests/fixtures/syntax_error_cascade.py (L5-L7) [! 1 syntax error] ===
|
|
396
|
+
Syntax Notice: L5: '(' was never closed (L5-L7 masked)
|
|
397
|
+
5 | def broken_header(x,
|
|
398
|
+
6 | y = 1
|
|
399
|
+
7 | return y
|
|
400
|
+
```
|
|
401
|
+
|
|
371
402
|
---
|
|
372
403
|
|
|
373
404
|
### 2. `block` Subcommand
|
|
@@ -310,6 +310,7 @@ skim-py glance tests/fixtures/syntax_error.py -s
|
|
|
310
310
|
```
|
|
311
311
|
```text
|
|
312
312
|
=== tests/fixtures/syntax_error.py (12 lines) [! 1 syntax error] ===
|
|
313
|
+
# parsed with 1 line masked (L7)
|
|
313
314
|
Syntax Notice: L7: invalid syntax
|
|
314
315
|
|
|
315
316
|
Outline:
|
|
@@ -318,12 +319,13 @@ Outline:
|
|
|
318
319
|
L11-L12 def valid_after()
|
|
319
320
|
```
|
|
320
321
|
|
|
321
|
-
Surrounding valid functions remain fully extractable via `block
|
|
322
|
+
Surrounding valid functions remain fully extractable via `block` (masking warnings are surfaced via stderr):
|
|
322
323
|
|
|
323
324
|
```bash
|
|
324
325
|
skim-py block tests/fixtures/syntax_error.py valid_after
|
|
325
326
|
```
|
|
326
327
|
```text
|
|
328
|
+
tests/fixtures/syntax_error.py: # parsed with 1 line masked (L7)
|
|
327
329
|
=== tests/fixtures/syntax_error.py (L11-L12) ===
|
|
328
330
|
11 | def valid_after():
|
|
329
331
|
12 | return 2
|
|
@@ -335,6 +337,7 @@ And if an extracted block contains a syntax error within its line span, the warn
|
|
|
335
337
|
skim-py block tests/fixtures/syntax_error.py broken_function
|
|
336
338
|
```
|
|
337
339
|
```text
|
|
340
|
+
tests/fixtures/syntax_error.py: # parsed with 1 line masked (L7)
|
|
338
341
|
=== tests/fixtures/syntax_error.py (L5-L8) [! 1 syntax error] ===
|
|
339
342
|
Syntax Notice: L7: invalid syntax
|
|
340
343
|
5 | def broken_function():
|
|
@@ -343,6 +346,34 @@ skim-py block tests/fixtures/syntax_error.py broken_function
|
|
|
343
346
|
8 | return x
|
|
344
347
|
```
|
|
345
348
|
|
|
349
|
+
For cascade-prone mid-edit states (such as an unclosed `def foo(x,` header or unclosed bracket structure), `skim-py` masks the entire broken block in one step, marks the masked line range in the notice, and salvages the broken declaration in the outline so it remains targetable by `block`:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
skim-py glance tests/fixtures/syntax_error_cascade.py -s
|
|
353
|
+
```
|
|
354
|
+
```text
|
|
355
|
+
=== tests/fixtures/syntax_error_cascade.py (11 lines) [! 1 syntax error] ===
|
|
356
|
+
# parsed with 3 lines masked (L5, L6, L7)
|
|
357
|
+
Syntax Notice: L5: '(' was never closed (L5-L7 masked)
|
|
358
|
+
|
|
359
|
+
Outline:
|
|
360
|
+
L1-L2 def valid_before()
|
|
361
|
+
L5-L7 def broken_header [syntax error]
|
|
362
|
+
L10-L11 def valid_after()
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
skim-py block tests/fixtures/syntax_error_cascade.py broken_header
|
|
367
|
+
```
|
|
368
|
+
```text
|
|
369
|
+
tests/fixtures/syntax_error_cascade.py: # parsed with 3 lines masked (L5, L6, L7)
|
|
370
|
+
=== tests/fixtures/syntax_error_cascade.py (L5-L7) [! 1 syntax error] ===
|
|
371
|
+
Syntax Notice: L5: '(' was never closed (L5-L7 masked)
|
|
372
|
+
5 | def broken_header(x,
|
|
373
|
+
6 | y = 1
|
|
374
|
+
7 | return y
|
|
375
|
+
```
|
|
376
|
+
|
|
346
377
|
---
|
|
347
378
|
|
|
348
379
|
### 2. `block` Subcommand
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "skim-python"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.3"
|
|
8
8
|
description = "Language-aware, AST-resilient outline and verbatim code extractor for Python tailored for AI agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -38,3 +38,7 @@ skim-py = "skim_py.cli:main"
|
|
|
38
38
|
|
|
39
39
|
[tool.hatch.build.targets.wheel]
|
|
40
40
|
packages = ["src/skim_py"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
pythonpath = ["src"]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
9
|
from .types import OutlineItem, ParsedFile
|
|
10
|
-
from .parser import parse_source_file
|
|
10
|
+
from .parser import parse_source_file, split_source_lines
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def collect_all_items(items: List[OutlineItem]) -> List[OutlineItem]:
|
|
@@ -38,6 +38,8 @@ def run_block(
|
|
|
38
38
|
return 1
|
|
39
39
|
|
|
40
40
|
parsed = parse_source_file(file_path, content, include_tests=include_tests)
|
|
41
|
+
if parsed.masking_warning:
|
|
42
|
+
sys.stderr.write(f"{file_path}: {parsed.masking_warning}\n")
|
|
41
43
|
all_items = collect_all_items(parsed.items)
|
|
42
44
|
|
|
43
45
|
matches: List[OutlineItem] = []
|
|
@@ -123,7 +125,7 @@ def run_block(
|
|
|
123
125
|
return 3
|
|
124
126
|
|
|
125
127
|
chosen = matches[0]
|
|
126
|
-
src_lines = content
|
|
128
|
+
src_lines = split_source_lines(content, keepends=False)
|
|
127
129
|
block_lines = src_lines[chosen.start_line - 1 : chosen.end_line]
|
|
128
130
|
|
|
129
131
|
# Check for syntax errors within block range
|
|
@@ -68,6 +68,11 @@ Agent Operational Workflow:
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def main(args: Optional[List[str]] = None) -> int:
|
|
71
|
+
if sys.version_info < (3, 10):
|
|
72
|
+
current_ver = f"{sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]}"
|
|
73
|
+
sys.stderr.write(f"Error: skim-py requires Python 3.10+ (running on Python {current_ver}).\n")
|
|
74
|
+
return 1
|
|
75
|
+
|
|
71
76
|
if args is None:
|
|
72
77
|
args = sys.argv[1:]
|
|
73
78
|
|
|
@@ -8,7 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import List, Optional
|
|
9
9
|
|
|
10
10
|
from .types import OutlineItem, ParsedFile
|
|
11
|
-
from .parser import parse_source_file, is_test_file
|
|
11
|
+
from .parser import parse_source_file, is_test_file, split_source_lines
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
IGNORED_DIRS = {
|
|
@@ -121,7 +121,7 @@ def run_glance(
|
|
|
121
121
|
sys.stderr.write(f"Error reading {file_path}: {err}\n")
|
|
122
122
|
continue
|
|
123
123
|
|
|
124
|
-
src_lines = content
|
|
124
|
+
src_lines = split_source_lines(content, keepends=False)
|
|
125
125
|
parsed = parse_source_file(file_path, content, include_tests=include_tests)
|
|
126
126
|
|
|
127
127
|
if filters:
|
|
@@ -155,6 +155,8 @@ def run_glance(
|
|
|
155
155
|
rel_path = file_path
|
|
156
156
|
|
|
157
157
|
print(f"=== {rel_path} ({parsed.total_lines} lines){test_info}{error_tag} ===")
|
|
158
|
+
if parsed.masking_warning:
|
|
159
|
+
print(parsed.masking_warning)
|
|
158
160
|
|
|
159
161
|
for err in parsed.syntax_errors:
|
|
160
162
|
print(f" Syntax Notice: L{err.line}: {err.message}")
|