skim-python 0.0.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.
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ dist/
9
+ build/
10
+ *.egg-info/
11
+ .venv/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Benedict Johannes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,523 @@
1
+ Metadata-Version: 2.5
2
+ Name: skim-python
3
+ Version: 0.0.1
4
+ Summary: Language-aware, AST-resilient outline and verbatim code extractor for Python tailored for AI agents
5
+ Project-URL: Homepage, https://github.com/benedictjohannes/programming-helper-utils
6
+ Project-URL: Repository, https://github.com/benedictjohannes/programming-helper-utils
7
+ Project-URL: Issues, https://github.com/benedictjohannes/programming-helper-utils/issues
8
+ Author: Benedict Johannes
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,ast,code-intelligence,developer-tools,extractor,outline
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+
26
+ # `skim-py`
27
+
28
+ [![PyPI version](https://img.shields.io/pypi/v/skim-python.svg)](https://pypi.org/project/skim-python/)
29
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../../LICENSE)
30
+
31
+ A fast, zero-dependency, AST-resilient Python outline and verbatim code block extractor tailored for AI coding agents and terminal workflows.
32
+
33
+ > Part of the [`skim-*` family - check out the complete suite](https://github.com/benedictjohannes/programming-helper-utils). Conforms to the [`skim-*` Family Design Specification](https://github.com/benedictjohannes/programming-helper-utils/blob/master/Design.md).
34
+
35
+ ---
36
+
37
+ ## Why `skim-py`?
38
+
39
+ When AI coding assistants (such as Claude, Gemini, ChatGPT, or Cursor) operate on real-world Python repositories, reading entire source files into prompt context leads to performance and cost degradation:
40
+
41
+ 1. **Context Window Exhaustion & Attention Drift**: Dumping 1,000–5,000 line source files burns through token budgets rapidly. Irrelevant implementation details dilute the model's attention, leading to hallucinations and missed context.
42
+ 2. **The Test Bloat Trap**: In modern Python codebases, test files (`test_*.py`, `*_test.py`, `tests/`) and in-file test suites (`class Test*`, `def test_*`) often make up 50–70% of a repository's volume. When exploring domain logic or architecture, test suites add immense noise.
43
+ 3. **Repeated File Reading Loops**: Agents often read file A, find a call to a method on class B in file B, read all of file B, and repeat—quickly exhausting tool calls and context windows.
44
+ 4. **Syntax Tolerance During Active Edits**: Codebases being actively refactored or edited often contain temporary syntax errors. Traditional strict parsers crash completely, leaving the agent unable to inspect surrounding structures.
45
+
46
+ `skim-py` solves this by giving agents (and developers) a pair of surgical tools powered by Python's built-in `ast` and `tokenize` modules (zero third-party dependencies):
47
+ - **`glance`**: Instant, high-density outlines of declarations, signatures, dataclass/Pydantic/enum fields, and line spans—with test files and in-file test suites filtered out by default.
48
+ - **`block`**: Verbatim code retrieval for an exact class, function, or qualified member (`Class.method`) with line gutters or raw code ready for editing.
49
+
50
+ ---
51
+
52
+ ## Recommended Agent Workflow
53
+
54
+ ```mermaid
55
+ flowchart TD
56
+ A["1. Survey Structure<br/><code>skim-py glance src/ -s</code>"] --> B{"Need specific<br/>identifier?"}
57
+ B -->|Yes| C["2. Locate Logic<br/><code>skim-py glance src/ -s -f keyword</code>"]
58
+ B -->|No| D["Inspect Overview"]
59
+ C --> E["3. Fetch Surgical Block<br/><code>skim-py block src/service.py UserService.get_user</code>"]
60
+ D --> E
61
+ E --> F["Form Accurate Hypotheses & Edit Safely"]
62
+ ```
63
+
64
+ 1. **Survey (`glance -s`)**: Get the structural topography of a module or package in under 100 tokens.
65
+ 2. **Locate (`glance -f <keyword>`)**: Narrow down matching declarations across files with case-insensitive substring filters.
66
+ 3. **Inspect (`block <file> <target>`)**: Fetch the exact verbatim implementation block with accurate file line numbers for precision editing.
67
+
68
+ ---
69
+
70
+ ## Features
71
+
72
+ - **Zero Third-Party Dependencies**: Pure Python standard library implementation using `ast` and `tokenize`.
73
+ - **Fast Startup & Minimal Overhead**: Launches instantaneously without waiting for heavy virtual environments or complex toolchains.
74
+ - **Test-Blind by Default**:
75
+ - Automatically excludes `test_*.py`, `*_test.py`, `conftest.py`, and test directories (`tests/`, `test/`, `testing/`, `__tests__/`) during traversals unless `-t` / `--include-tests` is specified.
76
+ - Automatically detects in-file test classes (`Test*`, `unittest.TestCase`) and test functions (`test_*`) and filters them out of outlines and target lookups, reporting skipped line ranges (`[tests skipped: 8..9, 16..17, 20..25]`).
77
+ - **Language-Idiomatic Qualified Targets**: Target methods via `Class.method`, constructors via `Class.__init__`, dunder methods via `Class.__str__`, or standalone functions, classes, and module-level variables.
78
+ - **Decorator & Signature Preservation**: Includes full decorator chains (`@classmethod`, `@property`, `@field_validator`) and return annotations in both outlines and block extractions.
79
+ - **Exact-Case Precedence with Case-Insensitive Fallback**: Matches `basemodel.model_dump` to `BaseModel.model_dump` while prioritizing exact case if both co-exist.
80
+ - **Clear Ambiguity Diagnostics**: If a target matches multiple candidates, `skim-py` exits with code `3` and presents numbered candidate locations with line spans and disambiguation tips.
81
+ - **Syntax Error Tolerance & AST Recovery**:
82
+ - Reports syntax notices cleanly under the file header without crashing, pinpointing the error line and message.
83
+ - Automatically recovers using a bounded iterative line-masking fallback (up to 100 iterations), isolating broken lines while preserving exact 1-to-1 line offsets so surrounding valid classes, methods, and functions remain fully outlined and extractable.
84
+
85
+ ---
86
+
87
+ ## Installation & Usage
88
+
89
+ ### On-Demand (`uvx` or `pipx`)
90
+ Run directly without installing:
91
+
92
+ ```bash
93
+ # Via uvx (Fastest & Recommended)
94
+ uvx --from skim-python skim-py glance src/ -s
95
+ uvx --from skim-python skim-py block src/models.py BaseModel
96
+
97
+ # Via pipx
98
+ pipx run --spec skim-python skim-py glance src/ -s
99
+ ```
100
+
101
+ ### Global Install (`uv` / `pipx` / `pip`)
102
+ Install globally to add `skim-py` directly to your `PATH`:
103
+
104
+ ```bash
105
+ # Via uv (Recommended)
106
+ uv tool install skim-python
107
+
108
+ # Via pipx
109
+ pipx install skim-python
110
+
111
+ # Via pip
112
+ pip install skim-python
113
+ ```
114
+
115
+ ### From Local Source
116
+
117
+ ```bash
118
+ git clone https://github.com/benedictjohannes/programming-helper-utils.git
119
+ cd programming-helper-utils/python/skim-py
120
+
121
+ # With uv:
122
+ uv tool install --editable .
123
+
124
+ # Or with pip:
125
+ pip install -e .
126
+
127
+ # Or link the standalone runner directly to your PATH:
128
+ mkdir -p ~/.local/bin
129
+ ln -sf "$(pwd)/skim-py" ~/.local/bin/skim-py
130
+ ```
131
+
132
+ ---
133
+
134
+ ## CLI Reference & Concrete Examples
135
+
136
+ > [!NOTE]
137
+ > Examples below are authentic outputs produced by running `skim-py` against the [`pydantic/pydantic`](https://github.com/pydantic/pydantic) repository and local fixtures.
138
+
139
+ ```text
140
+ Usage:
141
+ skim-py glance [options] <path>...
142
+ skim-py block [options] <file> <target>
143
+ ```
144
+
145
+ ---
146
+
147
+ ### 1. `glance` Subcommand
148
+
149
+ Outlines structure, signatures, fields, and imports across files or directories.
150
+
151
+ ```text
152
+ Usage:
153
+ skim-py glance [options] <path>...
154
+
155
+ Arguments:
156
+ <path>... One or more source files, directories, or globs
157
+
158
+ Options:
159
+ -s, --signatures Show declarations & types without function bodies
160
+ -d, --details Show class attributes, annotated fields, and enum members
161
+ -u, --uses, --imports Display top-level import statements
162
+ -f, --filter <text> Filter items containing <text> (case-insensitive, repeatable)
163
+ -t, --include-tests Include test files (test_*.py, etc.) and test blocks
164
+ ```
165
+
166
+ #### Example 1: Basic Structural Outline of a File
167
+
168
+ ```bash
169
+ skim-py glance testingRepositories/pydantic/pydantic/errors.py
170
+ ```
171
+ ```text
172
+ === testingRepositories/pydantic/pydantic/errors.py (185 lines) ===
173
+
174
+ Outline:
175
+ L15-L23 var __all__
176
+ L28-L28 var DEV_ERROR_DOCS_URL
177
+ L82-L98 class PydanticErrorMixin
178
+ L90-L92 method __init__
179
+ L94-L98 method __str__
180
+ L101-L102 class PydanticUserError
181
+ L105-L127 class PydanticUndefinedAnnotation
182
+ L113-L115 method __init__
183
+ L117-L127 method from_name_error
184
+ L130-L138 class PydanticImportError
185
+ L137-L138 method __init__
186
+ L141-L149 class PydanticSchemaGenerationError
187
+ L148-L149 method __init__
188
+ L152-L160 class PydanticInvalidForJsonSchema
189
+ L159-L160 method __init__
190
+ L163-L182 class PydanticForbiddenQualifier
191
+ L175-L182 method __init__
192
+ L185-L185 var __getattr__
193
+ ```
194
+
195
+ #### Example 2: Signatures and Imports (`-s -u`)
196
+
197
+ ```bash
198
+ skim-py glance testingRepositories/pydantic/pydantic/errors.py -s -u
199
+ ```
200
+ ```text
201
+ === testingRepositories/pydantic/pydantic/errors.py (185 lines) ===
202
+
203
+ Imports:
204
+ from __future__ import annotations as _annotations
205
+ from typing import Any, ClassVar, Literal
206
+ from typing_extensions import Self
207
+ from typing_inspection.introspection import Qualifier
208
+ from pydantic._internal import _repr
209
+ from ._migration import getattr_migration
210
+ from .version import version_short
211
+
212
+ Outline:
213
+ L15-L23 __all__ = ('PydanticUserError', 'PydanticUndefinedAnnotation', 'PydanticImportError', 'PydanticSchemaGenerationError', 'PydanticInvalidForJsonSchema', 'PydanticForbiddenQualifier', 'PydanticErrorCodes')
214
+ L28-L28 DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/'
215
+ L82-L98 class PydanticErrorMixin
216
+ L90-L92 def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None
217
+ L94-L98 def __str__(self) -> str
218
+ L101-L102 class PydanticUserError(PydanticErrorMixin, RuntimeError)
219
+ L105-L127 class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError)
220
+ L113-L115 def __init__(self, name: str | None, message: str) -> None
221
+ L117-L127 @classmethod def from_name_error(cls, name_error: NameError) -> Self
222
+ L130-L138 class PydanticImportError(PydanticErrorMixin, ImportError)
223
+ L137-L138 def __init__(self, message: str) -> None
224
+ L141-L149 class PydanticSchemaGenerationError(PydanticUserError)
225
+ L148-L149 def __init__(self, message: str) -> None
226
+ L152-L160 class PydanticInvalidForJsonSchema(PydanticUserError)
227
+ L159-L160 def __init__(self, message: str) -> None
228
+ L163-L182 class PydanticForbiddenQualifier(PydanticUserError)
229
+ L175-L182 def __init__(self, qualifier: Qualifier, annotation: Any) -> None
230
+ L185-L185 __getattr__ = getattr_migration(__name__)
231
+ ```
232
+
233
+ #### Example 3: Expand Details and Class Attributes (`-s -d`)
234
+
235
+ ```bash
236
+ skim-py glance testingRepositories/pydantic/pydantic/errors.py -s -d
237
+ ```
238
+ ```text
239
+ === testingRepositories/pydantic/pydantic/errors.py (185 lines) ===
240
+
241
+ Outline:
242
+ L15-L23 __all__ = ('PydanticUserError', 'PydanticUndefinedAnnotation', 'PydanticImportError', 'PydanticSchemaGenerationError', 'PydanticInvalidForJsonSchema', 'PydanticForbiddenQualifier', 'PydanticErrorCodes')
243
+ L28-L28 DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/'
244
+ L82-L98 class PydanticErrorMixin
245
+ L90-L92 def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None
246
+ L94-L98 def __str__(self) -> str
247
+ L101-L102 class PydanticUserError(PydanticErrorMixin, RuntimeError)
248
+ L105-L127 class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError)
249
+ L113-L115 def __init__(self, name: str | None, message: str) -> None
250
+ L117-L127 @classmethod def from_name_error(cls, name_error: NameError) -> Self
251
+ L130-L138 class PydanticImportError(PydanticErrorMixin, ImportError)
252
+ L137-L138 def __init__(self, message: str) -> None
253
+ L141-L149 class PydanticSchemaGenerationError(PydanticUserError)
254
+ L148-L149 def __init__(self, message: str) -> None
255
+ L152-L160 class PydanticInvalidForJsonSchema(PydanticUserError)
256
+ L159-L160 def __init__(self, message: str) -> None
257
+ L163-L182 class PydanticForbiddenQualifier(PydanticUserError)
258
+ | _qualifier_repr_map: ClassVar[dict[Qualifier, str]] = {'required': 'typing.Required', 'not_required': 'typing.NotRequired', 'read_only': 'typing.ReadOnly', 'class_var': 'typing.ClassVar', 'init_var': 'dataclasses.InitVar', 'final': 'typing.Final'}
259
+ L175-L182 def __init__(self, qualifier: Qualifier, annotation: Any) -> None
260
+ L185-L185 __getattr__ = getattr_migration(__name__)
261
+ ```
262
+
263
+ #### Example 4: Filter by Keyword across Declarations (`-f <keyword>`)
264
+
265
+ ```bash
266
+ skim-py glance testingRepositories/pydantic/pydantic/errors.py -s -f "from_name_error"
267
+ ```
268
+ ```text
269
+ === testingRepositories/pydantic/pydantic/errors.py (185 lines) ===
270
+
271
+ Outline:
272
+ L105-L127 class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError)
273
+ L117-L127 @classmethod def from_name_error(cls, name_error: NameError) -> Self
274
+ ```
275
+
276
+ #### Example 5: In-File Test Filtering & Inclusion (`-t`)
277
+
278
+ In-file test suites (`Test*`, `test_*`) are filtered out by default, with skipped ranges highlighted:
279
+
280
+ ```bash
281
+ # Default: in-file tests omitted
282
+ skim-py glance tests/fixtures/tests.py
283
+ ```
284
+ ```text
285
+ === tests/fixtures/tests.py (29 lines) [tests skipped: 8..9, 16..17, 20..25, 28..29] ===
286
+
287
+ Outline:
288
+ L4-L5 function normal_helper
289
+ L12-L17 class NormalClass
290
+ L13-L14 method regular_method
291
+ ```
292
+
293
+ When test inspection is desired, pass `-t`:
294
+
295
+ ```bash
296
+ skim-py glance tests/fixtures/tests.py -t
297
+ ```
298
+ ```text
299
+ === tests/fixtures/tests.py (29 lines) [test] ===
300
+
301
+ Outline:
302
+ L4-L5 function normal_helper
303
+ L8-L9 def test_helper_not_test() -> str
304
+ L12-L17 class NormalClass
305
+ L13-L14 method regular_method
306
+ L16-L17 def test_like_method(self) -> None
307
+ L20-L25 class TestSuite(unittest.TestCase)
308
+ L21-L22 def test_example(self) -> None
309
+ L24-L25 def test_another(self) -> None
310
+ L28-L29 def test_standalone_func() -> None
311
+ ```
312
+
313
+ Similarly, test files (`test_*.py`, `tests/`) are excluded by default during directory traversals and only included when `-t` is passed:
314
+
315
+ ```bash
316
+ skim-py glance testingRepositories/pydantic/tests/test_warnings.py -t
317
+ ```
318
+ ```text
319
+ === testingRepositories/pydantic/tests/test_warnings.py (41 lines) [test] ===
320
+
321
+ Outline:
322
+ L5-L10 def test_pydantic_deprecation_warning()
323
+ L13-L17 def test_pydantic_deprecation_warning_tailing_dot_in_message()
324
+ L20-L23 def test_pydantic_deprecation_warning_calculated_expected_removal()
325
+ L26-L32 def test_pydantic_deprecation_warning_2_0_migration_guide_link()
326
+ L35-L41 def test_pydantic_deprecated_since_2_0_warning()
327
+ ```
328
+
329
+ #### Example 6: Syntax Error Tolerance & Recovery
330
+
331
+ If a file has a syntax error during active editing or refactoring, `skim-py` does not crash; it pinpoints syntax errors directly under the file header and automatically recovers to outline valid surrounding declarations:
332
+
333
+ ```bash
334
+ skim-py glance tests/fixtures/syntax_error.py -s
335
+ ```
336
+ ```text
337
+ === tests/fixtures/syntax_error.py (12 lines) [! 1 syntax error] ===
338
+ Syntax Notice: L7: invalid syntax
339
+
340
+ Outline:
341
+ L1-L2 def valid_before()
342
+ L5-L8 def broken_function()
343
+ L11-L12 def valid_after()
344
+ ```
345
+
346
+ Surrounding valid functions remain fully extractable via `block`:
347
+
348
+ ```bash
349
+ skim-py block tests/fixtures/syntax_error.py valid_after
350
+ ```
351
+ ```text
352
+ === tests/fixtures/syntax_error.py (L11-L12) ===
353
+ 11 | def valid_after():
354
+ 12 | return 2
355
+ ```
356
+
357
+ And if an extracted block contains a syntax error within its line span, the warning is surfaced directly above the block:
358
+
359
+ ```bash
360
+ skim-py block tests/fixtures/syntax_error.py broken_function
361
+ ```
362
+ ```text
363
+ === tests/fixtures/syntax_error.py (L5-L8) [! 1 syntax error] ===
364
+ Syntax Notice: L7: invalid syntax
365
+ 5 | def broken_function():
366
+ 6 | x = 10
367
+ 7 | y =
368
+ 8 | return x
369
+ ```
370
+
371
+ ---
372
+
373
+ ### 2. `block` Subcommand
374
+
375
+ Extracts the exact verbatim source block for a class, function, variable, or `Class.method`.
376
+
377
+ ```text
378
+ Usage:
379
+ skim-py block [options] <file> <target>
380
+
381
+ Arguments:
382
+ <file> Path to the source file
383
+ <target> Name of class, method, function, or Class.method
384
+
385
+ Options:
386
+ -t, --include-tests Search inside test files and test blocks
387
+ --raw Omit line number gutter (clean code for edits/diffs)
388
+ ```
389
+
390
+ #### Example 7: Extract Class Definition
391
+
392
+ ```bash
393
+ skim-py block testingRepositories/pydantic/pydantic/warnings.py PydanticDeprecationWarning
394
+ ```
395
+ ```text
396
+ === testingRepositories/pydantic/pydantic/warnings.py (L23-L54) ===
397
+ 23 | class PydanticDeprecationWarning(DeprecationWarning):
398
+ 24 | """A Pydantic specific deprecation warning.
399
+ 25 |
400
+ 26 | This warning is raised when using deprecated functionality in Pydantic. It provides information on when the
401
+ 27 | deprecation was introduced and the expected version in which the corresponding functionality will be removed.
402
+ 28 |
403
+ 29 | Attributes:
404
+ 30 | message: Description of the warning.
405
+ 31 | since: Pydantic version in what the deprecation was introduced.
406
+ 32 | expected_removal: Pydantic version in what the corresponding functionality expected to be removed.
407
+ 33 | """
408
+ 34 |
409
+ 35 | message: str
410
+ 36 | since: tuple[int, int]
411
+ 37 | expected_removal: tuple[int, int]
412
+ 38 |
413
+ 39 | def __init__(
414
+ 40 | self, message: str, *args: object, since: tuple[int, int], expected_removal: tuple[int, int] | None = None
415
+ 41 | ) -> None:
416
+ 42 | super().__init__(message, *args)
417
+ 43 | self.message = message.rstrip('.')
418
+ 44 | self.since = since
419
+ 45 | self.expected_removal = expected_removal if expected_removal is not None else (since[0] + 1, 0)
420
+ 46 |
421
+ 47 | def __str__(self) -> str:
422
+ 48 | message = (
423
+ 49 | f'{self.message}. Deprecated in Pydantic V{self.since[0]}.{self.since[1]}'
424
+ 50 | f' to be removed in V{self.expected_removal[0]}.{self.expected_removal[1]}.'
425
+ 51 | )
426
+ 52 | if self.since == (2, 0):
427
+ 53 | message += f' See Pydantic V2 Migration Guide at https://errors.pydantic.dev/{version_short()}/migration/'
428
+ 54 | return message
429
+ ```
430
+
431
+ #### Example 8: Extract Qualified Method (`Class.method`)
432
+
433
+ ```bash
434
+ skim-py block testingRepositories/pydantic/pydantic/errors.py PydanticUndefinedAnnotation.from_name_error
435
+ ```
436
+ ```text
437
+ === testingRepositories/pydantic/pydantic/errors.py (L117-L127) ===
438
+ 117 | @classmethod
439
+ 118 | def from_name_error(cls, name_error: NameError) -> Self:
440
+ 119 | """Convert a `NameError` to a `PydanticUndefinedAnnotation` error.
441
+ 120 |
442
+ 121 | Args:
443
+ 122 | name_error: `NameError` to be converted.
444
+ 123 |
445
+ 124 | Returns:
446
+ 125 | Converted `PydanticUndefinedAnnotation` error.
447
+ 126 | """
448
+ 127 | return cls(name=name_error.name, message=str(name_error))
449
+ ```
450
+
451
+ Case-insensitive target matching is also supported:
452
+
453
+ ```bash
454
+ skim-py block testingRepositories/pydantic/pydantic/errors.py pydanticundefinedannotation.from_name_error
455
+ ```
456
+
457
+ #### Example 9: Raw Output for Seamless Patching (`--raw`)
458
+
459
+ ```bash
460
+ skim-py block testingRepositories/pydantic/pydantic/errors.py PydanticUndefinedAnnotation.from_name_error --raw
461
+ ```
462
+ ```python
463
+ @classmethod
464
+ def from_name_error(cls, name_error: NameError) -> Self:
465
+ """Convert a `NameError` to a `PydanticUndefinedAnnotation` error.
466
+
467
+ Args:
468
+ name_error: `NameError` to be converted.
469
+
470
+ Returns:
471
+ Converted `PydanticUndefinedAnnotation` error.
472
+ """
473
+ return cls(name=name_error.name, message=str(name_error))
474
+ ```
475
+
476
+ #### Example 10: Disambiguation Handling
477
+
478
+ When multiple candidates match an ambiguous target name, `skim-py` exits with status `3` and provides candidates and resolution tips:
479
+
480
+ ```bash
481
+ skim-py block testingRepositories/pydantic/pydantic/errors.py __init__
482
+ ```
483
+ ```text
484
+ Error: ambiguous target '__init__' matched 6 candidates in testingRepositories/pydantic/pydantic/errors.py:
485
+ 1) L90-L92 method PydanticErrorMixin.__init__
486
+ 2) L113-L115 method PydanticUndefinedAnnotation.__init__
487
+ 3) L137-L138 method PydanticImportError.__init__
488
+ 4) L148-L149 method PydanticSchemaGenerationError.__init__
489
+ 5) L159-L160 method PydanticInvalidForJsonSchema.__init__
490
+ 6) L175-L182 method PydanticForbiddenQualifier.__init__
491
+
492
+ Tip: Disambiguate with Class.method (e.g. BaseModel.model_dump) or read line range directly.
493
+ ```
494
+
495
+ ---
496
+
497
+ ## Exit Codes
498
+
499
+ `skim-py` adheres to the standardized POSIX / `skim-*` family exit codes:
500
+
501
+ | Code | Meaning | Description |
502
+ |:---:|:---|:---|
503
+ | `0` | **Success** | Operation completed normally. |
504
+ | `1` | **Error** | Path not found, I/O failure, or invalid CLI flag. |
505
+ | `2` | **Not Found** | The specified target symbol was not found in the file. |
506
+ | `3` | **Ambiguous** | Multiple candidates match the target symbol; disambiguation required. |
507
+
508
+ ---
509
+
510
+ ## Automated Tests
511
+
512
+ Unit tests and edge-case fixtures are located in [`tests/`](tests/).
513
+
514
+ Run test suite via `pytest`:
515
+ ```bash
516
+ pytest tests/
517
+ ```
518
+
519
+ ---
520
+
521
+ ## License
522
+
523
+ MIT