agent-readable 0.1.0__tar.gz → 0.1.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.
- {agent_readable-0.1.0 → agent_readable-0.1.1}/AGENT-PROMPT.md +6 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/PKG-INFO +28 -8
- {agent_readable-0.1.0 → agent_readable-0.1.1}/README.md +26 -6
- agent_readable-0.1.1/docs/agent_help_vs_help.gif +0 -0
- agent_readable-0.1.0/examples/module_support.py → agent_readable-0.1.1/examples/modules_and_functions.py +22 -4
- {agent_readable-0.1.0 → agent_readable-0.1.1}/pyproject.toml +2 -2
- {agent_readable-0.1.0 → agent_readable-0.1.1}/src/agent_readable/__main__.py +15 -11
- {agent_readable-0.1.0 → agent_readable-0.1.1}/src/agent_readable/_protocol.py +55 -1
- {agent_readable-0.1.0 → agent_readable-0.1.1}/tests/test_cli.py +20 -3
- {agent_readable-0.1.0 → agent_readable-0.1.1}/tests/test_protocol.py +105 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/.github/workflows/publish.yml +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/.github/workflows/test.yml +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/.gitignore +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/LICENSE +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/examples/any_class.py +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/examples/duck_type.py +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/examples/sqlite_connection.py +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/examples/temperature.py +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/src/agent_readable/__init__.py +0 -0
- {agent_readable-0.1.0 → agent_readable-0.1.1}/tests/__init__.py +0 -0
|
@@ -15,6 +15,12 @@ print(agent_help(ClassName))
|
|
|
15
15
|
# or
|
|
16
16
|
print(agent_help(package.module))
|
|
17
17
|
# or: python -m agent_readable package.module
|
|
18
|
+
|
|
19
|
+
# functions and methods also work
|
|
20
|
+
print(agent_help(package.module.func))
|
|
21
|
+
print(agent_help(ClassName.method))
|
|
22
|
+
# or: python -m agent_readable package.module:func
|
|
23
|
+
# or: python -m agent_readable package.module:ClassName.method
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
Treat the output as the canonical usage guide. Follow do/don't rules, anti-patterns, and lifecycle constraints exactly. Do not invent behavior that contradicts it.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-readable
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: A lightweight protocol for
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A lightweight Python protocol for agent-oriented documentation
|
|
5
5
|
Project-URL: Repository, https://github.com/zydo/agent-readable
|
|
6
6
|
Author: zydo and agent-readable contributors
|
|
7
7
|
License-Expression: MIT
|
|
@@ -28,6 +28,13 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
|
|
29
29
|
A lightweight Python protocol for exposing agent-oriented documentation from classes and modules.
|
|
30
30
|
|
|
31
|
+
<!-- markdownlint-disable MD033 -->
|
|
32
|
+
<p align="center">
|
|
33
|
+
<strong><code>logging.Logger</code> compared with <code>agent_help()</code> and <code>help()</code></strong><br>
|
|
34
|
+
<img src="docs/agent_help_vs_help.gif" alt="agent_help vs help">
|
|
35
|
+
</p>
|
|
36
|
+
<!-- markdownlint-enable MD033 -->
|
|
37
|
+
|
|
31
38
|
## Problem
|
|
32
39
|
|
|
33
40
|
AI coding agents recognize established libraries from their training data, but they hallucinate when APIs change, when libraries are new, or when correct usage depends on rules that aren't visible from the API surface — pre-conditions, lifecycle order, anti-patterns, *"use `call()` for non-streaming, `stream()` for streaming."*
|
|
@@ -404,9 +411,9 @@ Instances of the Logger class represent a single logging channel. A
|
|
|
404
411
|
|
|
405
412
|
No mixin, no duck-typing — just pass any class to `agent_help()`.
|
|
406
413
|
|
|
407
|
-
## Example 5: Modules
|
|
414
|
+
## Example 5: Modules, functions, and methods
|
|
408
415
|
|
|
409
|
-
`agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/
|
|
416
|
+
`agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/modules_and_functions.py`](examples/modules_and_functions.py).
|
|
410
417
|
|
|
411
418
|
```python
|
|
412
419
|
import sys
|
|
@@ -429,7 +436,7 @@ Demonstrates both shapes of module support:
|
|
|
429
436
|
2. A stdlib module (pathlib).
|
|
430
437
|
|
|
431
438
|
Run this file to see both outputs:
|
|
432
|
-
python examples/
|
|
439
|
+
python examples/modules_and_functions.py
|
|
433
440
|
|
|
434
441
|
## Public API
|
|
435
442
|
|
|
@@ -445,7 +452,7 @@ Run this file to see both outputs:
|
|
|
445
452
|
- If usage is ambiguous, prefer the simplest documented usage pattern.
|
|
446
453
|
````
|
|
447
454
|
|
|
448
|
-
You can also pass any stdlib or third-party module — same `
|
|
455
|
+
You can also pass any stdlib or third-party module — same `modules_and_functions.py` shows it.
|
|
449
456
|
|
|
450
457
|
```python
|
|
451
458
|
import pathlib
|
|
@@ -494,6 +501,15 @@ operating systems.
|
|
|
494
501
|
|
|
495
502
|
Modules support less customization than classes — there is no mixin inheritance or `__agent_notes__()`. You can override the auto-generated output entirely by setting a module-level `__agent_help__` attribute (callable or string), but this is discouraged since it replaces the auto-generated summary — signatures, purpose, and public API listing are all lost. Prefer clear docstrings on the module and its functions/classes instead.
|
|
496
503
|
|
|
504
|
+
You can also pass a function or method directly — `agent_help()` renders the signature, full docstring, and usage rules. The output is close to `help()` for a single callable; the bigger wins are still on classes and modules.
|
|
505
|
+
|
|
506
|
+
```python
|
|
507
|
+
import pathlib
|
|
508
|
+
from agent_readable import agent_help
|
|
509
|
+
|
|
510
|
+
print(agent_help(pathlib.Path.read_text))
|
|
511
|
+
```
|
|
512
|
+
|
|
497
513
|
```python
|
|
498
514
|
import sys
|
|
499
515
|
|
|
@@ -515,9 +531,13 @@ python -m agent_readable agent_readable:AgentReadableMixin
|
|
|
515
531
|
|
|
516
532
|
# Any module
|
|
517
533
|
python -m agent_readable pathlib
|
|
534
|
+
|
|
535
|
+
# A function or method
|
|
536
|
+
python -m agent_readable json:dumps
|
|
537
|
+
python -m agent_readable pathlib:Path.read_text
|
|
518
538
|
```
|
|
519
539
|
|
|
520
|
-
Outputs agent-oriented documentation for the given class or
|
|
540
|
+
Outputs agent-oriented documentation for the given class, module, function, or method to stdout.
|
|
521
541
|
|
|
522
542
|
## FAQ
|
|
523
543
|
|
|
@@ -575,7 +595,7 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
|
|
|
575
595
|
The two dunders intentionally encode different composition rules:
|
|
576
596
|
|
|
577
597
|
| Aspect | `__agent_help__()` | `__agent_notes__()` |
|
|
578
|
-
|
|
598
|
+
| --------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
|
|
579
599
|
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
|
|
580
600
|
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
|
|
581
601
|
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight Python protocol for exposing agent-oriented documentation from classes and modules.
|
|
4
4
|
|
|
5
|
+
<!-- markdownlint-disable MD033 -->
|
|
6
|
+
<p align="center">
|
|
7
|
+
<strong><code>logging.Logger</code> compared with <code>agent_help()</code> and <code>help()</code></strong><br>
|
|
8
|
+
<img src="docs/agent_help_vs_help.gif" alt="agent_help vs help">
|
|
9
|
+
</p>
|
|
10
|
+
<!-- markdownlint-enable MD033 -->
|
|
11
|
+
|
|
5
12
|
## Problem
|
|
6
13
|
|
|
7
14
|
AI coding agents recognize established libraries from their training data, but they hallucinate when APIs change, when libraries are new, or when correct usage depends on rules that aren't visible from the API surface — pre-conditions, lifecycle order, anti-patterns, *"use `call()` for non-streaming, `stream()` for streaming."*
|
|
@@ -378,9 +385,9 @@ Instances of the Logger class represent a single logging channel. A
|
|
|
378
385
|
|
|
379
386
|
No mixin, no duck-typing — just pass any class to `agent_help()`.
|
|
380
387
|
|
|
381
|
-
## Example 5: Modules
|
|
388
|
+
## Example 5: Modules, functions, and methods
|
|
382
389
|
|
|
383
|
-
`agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/
|
|
390
|
+
`agent_help()` also works on modules — it generates a summary with the module docstring, public functions, and classes. Full example: [`examples/modules_and_functions.py`](examples/modules_and_functions.py).
|
|
384
391
|
|
|
385
392
|
```python
|
|
386
393
|
import sys
|
|
@@ -403,7 +410,7 @@ Demonstrates both shapes of module support:
|
|
|
403
410
|
2. A stdlib module (pathlib).
|
|
404
411
|
|
|
405
412
|
Run this file to see both outputs:
|
|
406
|
-
python examples/
|
|
413
|
+
python examples/modules_and_functions.py
|
|
407
414
|
|
|
408
415
|
## Public API
|
|
409
416
|
|
|
@@ -419,7 +426,7 @@ Run this file to see both outputs:
|
|
|
419
426
|
- If usage is ambiguous, prefer the simplest documented usage pattern.
|
|
420
427
|
````
|
|
421
428
|
|
|
422
|
-
You can also pass any stdlib or third-party module — same `
|
|
429
|
+
You can also pass any stdlib or third-party module — same `modules_and_functions.py` shows it.
|
|
423
430
|
|
|
424
431
|
```python
|
|
425
432
|
import pathlib
|
|
@@ -468,6 +475,15 @@ operating systems.
|
|
|
468
475
|
|
|
469
476
|
Modules support less customization than classes — there is no mixin inheritance or `__agent_notes__()`. You can override the auto-generated output entirely by setting a module-level `__agent_help__` attribute (callable or string), but this is discouraged since it replaces the auto-generated summary — signatures, purpose, and public API listing are all lost. Prefer clear docstrings on the module and its functions/classes instead.
|
|
470
477
|
|
|
478
|
+
You can also pass a function or method directly — `agent_help()` renders the signature, full docstring, and usage rules. The output is close to `help()` for a single callable; the bigger wins are still on classes and modules.
|
|
479
|
+
|
|
480
|
+
```python
|
|
481
|
+
import pathlib
|
|
482
|
+
from agent_readable import agent_help
|
|
483
|
+
|
|
484
|
+
print(agent_help(pathlib.Path.read_text))
|
|
485
|
+
```
|
|
486
|
+
|
|
471
487
|
```python
|
|
472
488
|
import sys
|
|
473
489
|
|
|
@@ -489,9 +505,13 @@ python -m agent_readable agent_readable:AgentReadableMixin
|
|
|
489
505
|
|
|
490
506
|
# Any module
|
|
491
507
|
python -m agent_readable pathlib
|
|
508
|
+
|
|
509
|
+
# A function or method
|
|
510
|
+
python -m agent_readable json:dumps
|
|
511
|
+
python -m agent_readable pathlib:Path.read_text
|
|
492
512
|
```
|
|
493
513
|
|
|
494
|
-
Outputs agent-oriented documentation for the given class or
|
|
514
|
+
Outputs agent-oriented documentation for the given class, module, function, or method to stdout.
|
|
495
515
|
|
|
496
516
|
## FAQ
|
|
497
517
|
|
|
@@ -549,7 +569,7 @@ Classes that define a `@classmethod` named `__agent_help__` returning a `str` ar
|
|
|
549
569
|
The two dunders intentionally encode different composition rules:
|
|
550
570
|
|
|
551
571
|
| Aspect | `__agent_help__()` | `__agent_notes__()` |
|
|
552
|
-
|
|
572
|
+
| --------------- | ----------------------------------------------- | --------------------------------------------------------------------------- |
|
|
553
573
|
| Semantics | **Replacement** — returned string IS the output | **Additive** — appended to auto-generated docs |
|
|
554
574
|
| Composition | Single class wins (the one closest in MRO) | Accumulated across the MRO; leaf class wins on conflict (header marks this) |
|
|
555
575
|
| When to use | Total control over the rendered text | "Auto-doc + my extra do/don't rules" |
|
|
Binary file
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Example: Using agent_help() on modules.
|
|
2
|
+
Example: Using agent_help() on modules, functions, and methods.
|
|
3
3
|
|
|
4
|
-
Demonstrates
|
|
4
|
+
Demonstrates non-class targets:
|
|
5
5
|
1. A custom module (this file itself).
|
|
6
6
|
2. A stdlib module (pathlib).
|
|
7
|
+
3. A function (connect, defined below).
|
|
8
|
+
4. A method (Query.execute, defined below).
|
|
7
9
|
|
|
8
|
-
Run this file to see
|
|
9
|
-
python examples/
|
|
10
|
+
Run this file to see all outputs:
|
|
11
|
+
python examples/modules_and_functions.py
|
|
10
12
|
"""
|
|
11
13
|
|
|
12
14
|
import os
|
|
@@ -47,3 +49,19 @@ if __name__ == "__main__":
|
|
|
47
49
|
print("=== agent_help(pathlib) — stdlib module ===")
|
|
48
50
|
print()
|
|
49
51
|
print(agent_help(pathlib))
|
|
52
|
+
|
|
53
|
+
print()
|
|
54
|
+
print("=" * 72)
|
|
55
|
+
print()
|
|
56
|
+
|
|
57
|
+
print("=== agent_help(connect) — function ===")
|
|
58
|
+
print()
|
|
59
|
+
print(agent_help(connect))
|
|
60
|
+
|
|
61
|
+
print()
|
|
62
|
+
print("=" * 72)
|
|
63
|
+
print()
|
|
64
|
+
|
|
65
|
+
print("=== agent_help(Query.execute) — method ===")
|
|
66
|
+
print()
|
|
67
|
+
print(agent_help(Query.execute))
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-readable"
|
|
7
|
-
version = "0.1.
|
|
8
|
-
description = "A lightweight protocol for
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "A lightweight Python protocol for agent-oriented documentation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
"""CLI: ``python -m agent_readable package.module:ClassName``,
|
|
2
2
|
``python -m agent_readable package.module.ClassName``,
|
|
3
|
+
``python -m agent_readable package.module:Class.method``,
|
|
3
4
|
or ``python -m agent_readable package.module``."""
|
|
4
5
|
|
|
5
6
|
from __future__ import annotations
|
|
6
7
|
|
|
7
8
|
import importlib
|
|
9
|
+
import inspect
|
|
8
10
|
import sys
|
|
9
11
|
import types
|
|
12
|
+
from typing import Any
|
|
10
13
|
|
|
11
14
|
from . import agent_help
|
|
12
15
|
|
|
13
16
|
|
|
14
|
-
def _resolve(dotted_path: str) ->
|
|
15
|
-
"""Import and resolve a target path to a class or
|
|
17
|
+
def _resolve(dotted_path: str) -> Any:
|
|
18
|
+
"""Import and resolve a target path to a class, module, function, or method.
|
|
16
19
|
|
|
17
|
-
Accepts ``package.module:ClassName``, ``package.module.ClassName``,
|
|
18
|
-
``package.module``.
|
|
20
|
+
Accepts ``package.module:ClassName``, ``package.module.ClassName``,
|
|
21
|
+
``package.module:Class.method``, or ``package.module``.
|
|
19
22
|
"""
|
|
20
23
|
if ":" in dotted_path:
|
|
21
24
|
module_path, _, attr = dotted_path.partition(":")
|
|
@@ -24,25 +27,26 @@ def _resolve(dotted_path: str) -> type | types.ModuleType:
|
|
|
24
27
|
else:
|
|
25
28
|
return importlib.import_module(dotted_path)
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
target: Any = importlib.import_module(module_path)
|
|
28
31
|
|
|
29
32
|
for part in attr.split(".") if attr else []:
|
|
30
|
-
|
|
33
|
+
target = getattr(target, part)
|
|
31
34
|
|
|
32
|
-
if not isinstance(
|
|
35
|
+
if not (isinstance(target, (type, types.ModuleType)) or inspect.isroutine(target)):
|
|
33
36
|
raise TypeError(
|
|
34
|
-
f"{dotted_path!r} resolved to {type(
|
|
35
|
-
"expected a class or
|
|
37
|
+
f"{dotted_path!r} resolved to {type(target).__name__}, "
|
|
38
|
+
"expected a class, module, function, or method"
|
|
36
39
|
)
|
|
37
40
|
|
|
38
|
-
return
|
|
41
|
+
return target
|
|
39
42
|
|
|
40
43
|
|
|
41
44
|
def main() -> None:
|
|
42
45
|
if len(sys.argv) < 2:
|
|
43
46
|
print(
|
|
44
47
|
"Usage: python -m agent_readable "
|
|
45
|
-
"(package.module:
|
|
48
|
+
"(package.module:Target | package.module.Target | package.module) "
|
|
49
|
+
"where Target is a class, function, or Class.method",
|
|
46
50
|
file=sys.stderr,
|
|
47
51
|
)
|
|
48
52
|
sys.exit(1)
|
|
@@ -66,7 +66,7 @@ class AgentReadableMixin:
|
|
|
66
66
|
|
|
67
67
|
def agent_help(obj: Any) -> str:
|
|
68
68
|
"""
|
|
69
|
-
Return agent-oriented help for a class, instance, or
|
|
69
|
+
Return agent-oriented help for a class, instance, module, function, or method.
|
|
70
70
|
|
|
71
71
|
Dispatch for classes/instances:
|
|
72
72
|
|
|
@@ -89,6 +89,11 @@ def agent_help(obj: Any) -> str:
|
|
|
89
89
|
or string), it is used directly. Otherwise auto-generated docs are produced
|
|
90
90
|
via ``_module_doc()``. Module ``__agent_notes__`` is not part of the
|
|
91
91
|
protocol — modules don't have an MRO to accumulate over.
|
|
92
|
+
|
|
93
|
+
For functions and methods (anything ``inspect.isroutine`` accepts): an
|
|
94
|
+
``__agent_help__`` attribute on the routine — callable or string — is used
|
|
95
|
+
directly if present; otherwise ``_function_doc()`` renders signature, full
|
|
96
|
+
docstring, and agent usage rules.
|
|
92
97
|
"""
|
|
93
98
|
if inspect.ismodule(obj):
|
|
94
99
|
fn = getattr(obj, "__agent_help__", None)
|
|
@@ -104,6 +109,20 @@ def agent_help(obj: Any) -> str:
|
|
|
104
109
|
return fn
|
|
105
110
|
return _module_doc(obj)
|
|
106
111
|
|
|
112
|
+
if inspect.isroutine(obj):
|
|
113
|
+
override = getattr(obj, "__agent_help__", None)
|
|
114
|
+
if callable(override):
|
|
115
|
+
try:
|
|
116
|
+
result = override()
|
|
117
|
+
if isinstance(result, str):
|
|
118
|
+
return result
|
|
119
|
+
return str(result)
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
if isinstance(override, str):
|
|
123
|
+
return override
|
|
124
|
+
return _function_doc(obj)
|
|
125
|
+
|
|
107
126
|
target = obj if inspect.isclass(obj) else obj.__class__
|
|
108
127
|
|
|
109
128
|
fn = getattr(target, "__agent_help__", None)
|
|
@@ -183,6 +202,41 @@ def _collect_module_api(module: types.ModuleType) -> list[str]:
|
|
|
183
202
|
return lines
|
|
184
203
|
|
|
185
204
|
|
|
205
|
+
def _function_doc(fn: Any) -> str:
|
|
206
|
+
"""Generate compact Markdown documentation for a function or method."""
|
|
207
|
+
parts: list[str] = []
|
|
208
|
+
|
|
209
|
+
short_name = getattr(fn, "__name__", None) or "function"
|
|
210
|
+
display_name = getattr(fn, "__qualname__", None) or short_name
|
|
211
|
+
parts.append(f"# {display_name}")
|
|
212
|
+
parts.append("")
|
|
213
|
+
|
|
214
|
+
sig = _safe_signature(fn)
|
|
215
|
+
parts.append("## Signature")
|
|
216
|
+
parts.append("")
|
|
217
|
+
parts.append("```python")
|
|
218
|
+
parts.append(f"{short_name}{sig}")
|
|
219
|
+
parts.append("```")
|
|
220
|
+
parts.append("")
|
|
221
|
+
|
|
222
|
+
doc = inspect.getdoc(fn)
|
|
223
|
+
if doc:
|
|
224
|
+
parts.append("## Purpose")
|
|
225
|
+
parts.append("")
|
|
226
|
+
parts.append(doc)
|
|
227
|
+
parts.append("")
|
|
228
|
+
|
|
229
|
+
parts.append("## Agent usage rules")
|
|
230
|
+
parts.append("")
|
|
231
|
+
parts.append("- Call with the documented signature.")
|
|
232
|
+
parts.append("- Do not invent unsupported behavior.")
|
|
233
|
+
parts.append(
|
|
234
|
+
"- If usage is ambiguous, prefer the simplest documented usage pattern."
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return "\n".join(parts)
|
|
238
|
+
|
|
239
|
+
|
|
186
240
|
def _base_agent_doc(cls: type) -> str:
|
|
187
241
|
"""
|
|
188
242
|
Generate compact Markdown documentation for AI coding agents.
|
|
@@ -71,9 +71,26 @@ def test_cli_nonexistent_attribute():
|
|
|
71
71
|
_run_main("tests.test_cli:NonExistent")
|
|
72
72
|
|
|
73
73
|
|
|
74
|
-
def
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
def test_cli_function_target(capsys):
|
|
75
|
+
code = _run_main("agent_readable:agent_help")
|
|
76
|
+
assert code == 0
|
|
77
|
+
out = capsys.readouterr().out
|
|
78
|
+
assert "agent_help" in out
|
|
79
|
+
assert "## Signature" in out
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_cli_method_target(capsys):
|
|
83
|
+
code = _run_main("tests.test_cli:Cache.get")
|
|
84
|
+
assert code == 0
|
|
85
|
+
out = capsys.readouterr().out
|
|
86
|
+
assert "Cache.get" in out
|
|
87
|
+
assert "## Signature" in out
|
|
88
|
+
assert "Retrieve a value by key." in out
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_cli_invalid_target_type():
|
|
92
|
+
with pytest.raises(TypeError, match="expected a class, module, function"):
|
|
93
|
+
_run_main("agent_readable:__version__")
|
|
77
94
|
|
|
78
95
|
|
|
79
96
|
def test_cli_module(capsys):
|
|
@@ -584,3 +584,108 @@ def test_agent_help_module_invalid_attr_falls_back():
|
|
|
584
584
|
result = agent_help(mod)
|
|
585
585
|
assert "# mymod" in result
|
|
586
586
|
assert "Fallback doc." in result
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
# -- Function / method tests -------------------------------------------------
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def test_agent_help_function_basic():
|
|
593
|
+
def greet(name: str) -> str:
|
|
594
|
+
"""Say hello to someone."""
|
|
595
|
+
return f"Hello, {name}"
|
|
596
|
+
|
|
597
|
+
result = agent_help(greet)
|
|
598
|
+
assert result.startswith("#") and "greet" in result.splitlines()[0]
|
|
599
|
+
assert "## Signature" in result
|
|
600
|
+
assert "greet(name: str) -> str" in result
|
|
601
|
+
assert "## Purpose" in result
|
|
602
|
+
assert "Say hello to someone." in result
|
|
603
|
+
assert "## Agent usage rules" in result
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def test_agent_help_unbound_method_keeps_self():
|
|
607
|
+
class Pool:
|
|
608
|
+
def rotated(self, n: int) -> "Pool":
|
|
609
|
+
"""Rotate the pool by n positions."""
|
|
610
|
+
return self
|
|
611
|
+
|
|
612
|
+
result = agent_help(Pool.rotated)
|
|
613
|
+
assert "Pool.rotated" in result.splitlines()[0]
|
|
614
|
+
assert "rotated(self, n: int)" in result
|
|
615
|
+
assert "Rotate the pool by n positions." in result
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
def test_agent_help_bound_method_strips_self():
|
|
619
|
+
class Pool:
|
|
620
|
+
def rotated(self, n: int) -> "Pool":
|
|
621
|
+
"""Rotate the pool by n positions."""
|
|
622
|
+
return self
|
|
623
|
+
|
|
624
|
+
result = agent_help(Pool().rotated)
|
|
625
|
+
assert "rotated(n: int)" in result
|
|
626
|
+
assert "self" not in result.split("## Signature", 1)[1].split("##", 1)[0]
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def test_agent_help_classmethod_strips_cls():
|
|
630
|
+
class Pool:
|
|
631
|
+
@classmethod
|
|
632
|
+
def of(cls, n: int) -> "Pool":
|
|
633
|
+
"""Construct a pool of size n."""
|
|
634
|
+
return cls()
|
|
635
|
+
|
|
636
|
+
result = agent_help(Pool.of)
|
|
637
|
+
assert "of(n: int)" in result
|
|
638
|
+
assert "Construct a pool of size n." in result
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def test_agent_help_function_without_docstring():
|
|
642
|
+
def f(x: int) -> int:
|
|
643
|
+
return x
|
|
644
|
+
|
|
645
|
+
result = agent_help(f)
|
|
646
|
+
assert result.startswith("#") and "f" in result.splitlines()[0]
|
|
647
|
+
assert "## Purpose" not in result
|
|
648
|
+
assert "## Agent usage rules" in result
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def test_agent_help_function_custom_callable_override():
|
|
652
|
+
def f():
|
|
653
|
+
"""Auto doc."""
|
|
654
|
+
|
|
655
|
+
f.__agent_help__ = lambda: "Custom function help."
|
|
656
|
+
assert agent_help(f) == "Custom function help."
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
def test_agent_help_function_custom_string_override():
|
|
660
|
+
def f():
|
|
661
|
+
"""Auto doc."""
|
|
662
|
+
|
|
663
|
+
f.__agent_help__ = "String function help."
|
|
664
|
+
assert agent_help(f) == "String function help."
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def test_agent_help_function_non_string_return():
|
|
668
|
+
def f():
|
|
669
|
+
"""Auto doc."""
|
|
670
|
+
|
|
671
|
+
f.__agent_help__ = lambda: 99
|
|
672
|
+
assert agent_help(f) == "99"
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def test_agent_help_function_override_exception_falls_back():
|
|
676
|
+
def f():
|
|
677
|
+
"""Fallback doc."""
|
|
678
|
+
|
|
679
|
+
def boom():
|
|
680
|
+
raise RuntimeError("boom")
|
|
681
|
+
|
|
682
|
+
f.__agent_help__ = boom
|
|
683
|
+
result = agent_help(f)
|
|
684
|
+
assert "Fallback doc." in result
|
|
685
|
+
assert "## Signature" in result
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def test_agent_help_builtin_function():
|
|
689
|
+
result = agent_help(len)
|
|
690
|
+
assert "# len" in result
|
|
691
|
+
assert "## Signature" in result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|