pretty-mod 0.2.0__tar.gz → 0.2.2__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.
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/CLAUDE.md +9 -6
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/Cargo.lock +125 -6
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/Cargo.toml +3 -1
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/PKG-INFO +8 -6
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/README.md +7 -5
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/RELEASE_NOTES.md +86 -0
- pretty_mod-0.2.2/python/pretty_mod/__main__.py +6 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/python/pretty_mod/_pretty_mod.pyi +7 -2
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/python/pretty_mod/cli.py +21 -2
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/explorer.rs +2 -1
- pretty_mod-0.2.2/src/import_resolver.rs +147 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/lib.rs +30 -10
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/module_info.rs +10 -1
- pretty_mod-0.2.2/src/output_format.rs +121 -0
- pretty_mod-0.2.2/src/semantic.rs +139 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/signature.rs +36 -10
- pretty_mod-0.2.2/tests/test_import_chain.py +66 -0
- pretty_mod-0.2.2/tests/test_json_output.py +103 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/.github/workflows/CI.yml +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/.github/workflows/tests.yml +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/.gitignore +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/.pre-commit-config.yaml +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/LICENSE +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/examples/0_hello.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/examples/1_tree.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/examples/2_sig.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/justfile +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/pyproject.toml +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/python/pretty_mod/__init__.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/python/pretty_mod/explorer.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/python/pretty_mod/py.typed +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/scripts/compare_local.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/scripts/compare_versions.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/scripts/perf_test.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/scripts/profile.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/config.rs +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/package_downloader.rs +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/stdlib.rs +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/tree_formatter.rs +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/src/utils.rs +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/tests/__init__.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/tests/conftest.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/tests/test_cli.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/tests/test_double_colon.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/tests/test_explorer.py +0 -0
- {pretty_mod-0.2.0 → pretty_mod-0.2.2}/uv.lock +0 -0
@@ -2,30 +2,33 @@
|
|
2
2
|
|
3
3
|
`pretty-mod` is a python package built on pyo3 to explore python packages for LLMs
|
4
4
|
|
5
|
-
|
5
|
+
## getting oriented
|
6
6
|
|
7
7
|
- read @RELEASE_NOTES.md, @README.md, @pyproject.toml, and @justfile
|
8
8
|
|
9
|
-
|
9
|
+
## run the tests
|
10
10
|
|
11
11
|
```
|
12
12
|
just test
|
13
13
|
```
|
14
14
|
|
15
|
-
if
|
15
|
+
if you only need to build (`just test` runs `just build` automatically)
|
16
16
|
|
17
17
|
```
|
18
18
|
just build
|
19
19
|
```
|
20
20
|
|
21
|
-
|
21
|
+
## run the local python package
|
22
22
|
|
23
23
|
```
|
24
24
|
uv run pretty-mod tree fastapi.routing
|
25
25
|
```
|
26
26
|
|
27
|
-
|
27
|
+
## run the remote python package
|
28
28
|
|
29
29
|
```
|
30
30
|
uvx pretty-mod tree fastapi.routing
|
31
|
-
```
|
31
|
+
```
|
32
|
+
|
33
|
+
# IMPORTANT
|
34
|
+
- avoid breaking changes to the public api defined by type stubs in @python/pretty_mod/_pretty_mod.pyi
|
@@ -478,6 +478,25 @@ version = "0.31.1"
|
|
478
478
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
479
479
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
480
480
|
|
481
|
+
[[package]]
|
482
|
+
name = "glob"
|
483
|
+
version = "0.3.2"
|
484
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
485
|
+
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
486
|
+
|
487
|
+
[[package]]
|
488
|
+
name = "globset"
|
489
|
+
version = "0.4.16"
|
490
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
491
|
+
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
492
|
+
dependencies = [
|
493
|
+
"aho-corasick",
|
494
|
+
"bstr",
|
495
|
+
"log",
|
496
|
+
"regex-automata",
|
497
|
+
"regex-syntax",
|
498
|
+
]
|
499
|
+
|
481
500
|
[[package]]
|
482
501
|
name = "hashbrown"
|
483
502
|
version = "0.15.4"
|
@@ -1017,13 +1036,15 @@ dependencies = [
|
|
1017
1036
|
|
1018
1037
|
[[package]]
|
1019
1038
|
name = "pretty-mod"
|
1020
|
-
version = "0.2.
|
1039
|
+
version = "0.2.2"
|
1021
1040
|
dependencies = [
|
1022
1041
|
"flate2",
|
1023
1042
|
"pyo3",
|
1024
1043
|
"reqwest",
|
1025
1044
|
"ruff_python_ast",
|
1026
1045
|
"ruff_python_parser",
|
1046
|
+
"ruff_python_resolver",
|
1047
|
+
"ruff_python_semantic",
|
1027
1048
|
"serde",
|
1028
1049
|
"serde_json",
|
1029
1050
|
"tar",
|
@@ -1240,11 +1261,34 @@ dependencies = [
|
|
1240
1261
|
"bitflags",
|
1241
1262
|
]
|
1242
1263
|
|
1264
|
+
[[package]]
|
1265
|
+
name = "regex"
|
1266
|
+
version = "1.11.1"
|
1267
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1268
|
+
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
1269
|
+
dependencies = [
|
1270
|
+
"aho-corasick",
|
1271
|
+
"memchr",
|
1272
|
+
"regex-automata",
|
1273
|
+
"regex-syntax",
|
1274
|
+
]
|
1275
|
+
|
1243
1276
|
[[package]]
|
1244
1277
|
name = "regex-automata"
|
1245
1278
|
version = "0.4.9"
|
1246
1279
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1247
1280
|
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
1281
|
+
dependencies = [
|
1282
|
+
"aho-corasick",
|
1283
|
+
"memchr",
|
1284
|
+
"regex-syntax",
|
1285
|
+
]
|
1286
|
+
|
1287
|
+
[[package]]
|
1288
|
+
name = "regex-syntax"
|
1289
|
+
version = "0.8.5"
|
1290
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1291
|
+
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
1248
1292
|
|
1249
1293
|
[[package]]
|
1250
1294
|
name = "reqwest"
|
@@ -1301,10 +1345,44 @@ dependencies = [
|
|
1301
1345
|
"windows-sys 0.52.0",
|
1302
1346
|
]
|
1303
1347
|
|
1348
|
+
[[package]]
|
1349
|
+
name = "ruff_cache"
|
1350
|
+
version = "0.0.0"
|
1351
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1352
|
+
dependencies = [
|
1353
|
+
"filetime",
|
1354
|
+
"glob",
|
1355
|
+
"globset",
|
1356
|
+
"itertools",
|
1357
|
+
"regex",
|
1358
|
+
"seahash",
|
1359
|
+
]
|
1360
|
+
|
1361
|
+
[[package]]
|
1362
|
+
name = "ruff_index"
|
1363
|
+
version = "0.0.0"
|
1364
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1365
|
+
dependencies = [
|
1366
|
+
"ruff_macros",
|
1367
|
+
]
|
1368
|
+
|
1369
|
+
[[package]]
|
1370
|
+
name = "ruff_macros"
|
1371
|
+
version = "0.0.0"
|
1372
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1373
|
+
dependencies = [
|
1374
|
+
"heck",
|
1375
|
+
"itertools",
|
1376
|
+
"proc-macro2",
|
1377
|
+
"quote",
|
1378
|
+
"ruff_python_trivia",
|
1379
|
+
"syn",
|
1380
|
+
]
|
1381
|
+
|
1304
1382
|
[[package]]
|
1305
1383
|
name = "ruff_python_ast"
|
1306
1384
|
version = "0.0.0"
|
1307
|
-
source = "git+https://github.com/astral-sh/ruff#
|
1385
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1308
1386
|
dependencies = [
|
1309
1387
|
"aho-corasick",
|
1310
1388
|
"bitflags",
|
@@ -1321,7 +1399,7 @@ dependencies = [
|
|
1321
1399
|
[[package]]
|
1322
1400
|
name = "ruff_python_parser"
|
1323
1401
|
version = "0.0.0"
|
1324
|
-
source = "git+https://github.com/astral-sh/ruff#
|
1402
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1325
1403
|
dependencies = [
|
1326
1404
|
"bitflags",
|
1327
1405
|
"bstr",
|
@@ -1337,10 +1415,45 @@ dependencies = [
|
|
1337
1415
|
"unicode_names2",
|
1338
1416
|
]
|
1339
1417
|
|
1418
|
+
[[package]]
|
1419
|
+
name = "ruff_python_resolver"
|
1420
|
+
version = "0.0.0"
|
1421
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1422
|
+
dependencies = [
|
1423
|
+
"log",
|
1424
|
+
]
|
1425
|
+
|
1426
|
+
[[package]]
|
1427
|
+
name = "ruff_python_semantic"
|
1428
|
+
version = "0.0.0"
|
1429
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1430
|
+
dependencies = [
|
1431
|
+
"bitflags",
|
1432
|
+
"is-macro",
|
1433
|
+
"ruff_cache",
|
1434
|
+
"ruff_index",
|
1435
|
+
"ruff_macros",
|
1436
|
+
"ruff_python_ast",
|
1437
|
+
"ruff_python_parser",
|
1438
|
+
"ruff_python_stdlib",
|
1439
|
+
"ruff_text_size",
|
1440
|
+
"rustc-hash",
|
1441
|
+
"smallvec",
|
1442
|
+
]
|
1443
|
+
|
1444
|
+
[[package]]
|
1445
|
+
name = "ruff_python_stdlib"
|
1446
|
+
version = "0.0.0"
|
1447
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1448
|
+
dependencies = [
|
1449
|
+
"bitflags",
|
1450
|
+
"unicode-ident",
|
1451
|
+
]
|
1452
|
+
|
1340
1453
|
[[package]]
|
1341
1454
|
name = "ruff_python_trivia"
|
1342
1455
|
version = "0.0.0"
|
1343
|
-
source = "git+https://github.com/astral-sh/ruff#
|
1456
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1344
1457
|
dependencies = [
|
1345
1458
|
"itertools",
|
1346
1459
|
"ruff_source_file",
|
@@ -1351,7 +1464,7 @@ dependencies = [
|
|
1351
1464
|
[[package]]
|
1352
1465
|
name = "ruff_source_file"
|
1353
1466
|
version = "0.0.0"
|
1354
|
-
source = "git+https://github.com/astral-sh/ruff#
|
1467
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1355
1468
|
dependencies = [
|
1356
1469
|
"memchr",
|
1357
1470
|
"ruff_text_size",
|
@@ -1360,7 +1473,7 @@ dependencies = [
|
|
1360
1473
|
[[package]]
|
1361
1474
|
name = "ruff_text_size"
|
1362
1475
|
version = "0.0.0"
|
1363
|
-
source = "git+https://github.com/astral-sh/ruff#
|
1476
|
+
source = "git+https://github.com/astral-sh/ruff#0724bee59c12da9cedc84f4238b08aabe9ecaaa1"
|
1364
1477
|
|
1365
1478
|
[[package]]
|
1366
1479
|
name = "rustc-demangle"
|
@@ -1455,6 +1568,12 @@ dependencies = [
|
|
1455
1568
|
"windows-sys 0.59.0",
|
1456
1569
|
]
|
1457
1570
|
|
1571
|
+
[[package]]
|
1572
|
+
name = "seahash"
|
1573
|
+
version = "4.1.0"
|
1574
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
1575
|
+
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
1576
|
+
|
1458
1577
|
[[package]]
|
1459
1578
|
name = "security-framework"
|
1460
1579
|
version = "3.2.0"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[package]
|
2
2
|
name = "pretty-mod"
|
3
|
-
version = "0.2.
|
3
|
+
version = "0.2.2"
|
4
4
|
edition = "2021"
|
5
5
|
|
6
6
|
[lib]
|
@@ -13,6 +13,8 @@ serde = { version = "1.0", features = ["derive"] }
|
|
13
13
|
serde_json = "1.0"
|
14
14
|
ruff_python_parser = { git = "https://github.com/astral-sh/ruff" }
|
15
15
|
ruff_python_ast = { git = "https://github.com/astral-sh/ruff" }
|
16
|
+
ruff_python_semantic = { git = "https://github.com/astral-sh/ruff" }
|
17
|
+
ruff_python_resolver = { git = "https://github.com/astral-sh/ruff" }
|
16
18
|
reqwest = { version = "0.12", default-features = false, features = [
|
17
19
|
"blocking",
|
18
20
|
"json",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pretty-mod
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
License-File: LICENSE
|
5
5
|
Summary: A python module tree explorer for LLMs (and humans)
|
6
6
|
Author-email: zzstoatzz <thrast36@gmail.com>
|
@@ -12,11 +12,9 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
12
12
|
|
13
13
|
a python module tree explorer for LLMs (and humans)
|
14
14
|
|
15
|
-
> [!IMPORTANT]
|
16
|
-
> for all versions `>=0.1.0`, wheels for different operating systems are built via `maturin` and published to pypi, install `<0.1.0` for a pure python version
|
17
|
-
|
18
15
|
> [!NOTE]
|
19
|
-
>
|
16
|
+
> - For all versions `>=0.1.0`, wheels for different operating systems are built via `maturin` and published to PyPI. Install `<0.1.0` for a pure Python version.
|
17
|
+
> - Starting from v0.2.0, output includes colors by default. Use `PRETTY_MOD_NO_COLOR=1` to disable.
|
20
18
|
|
21
19
|
```bash
|
22
20
|
# Explore module structure
|
@@ -36,7 +34,7 @@ a python module tree explorer for LLMs (and humans)
|
|
36
34
|
└── ⚡ functions: main
|
37
35
|
|
38
36
|
# Inspect function signatures (even if the package is not installed)
|
39
|
-
»
|
37
|
+
» uvx pretty-mod sig fastmcp:FastMCP --quiet
|
40
38
|
📎 FastMCP
|
41
39
|
├── Parameters:
|
42
40
|
├── self
|
@@ -89,6 +87,10 @@ pretty-mod tree requests --depth 3
|
|
89
87
|
|
90
88
|
# Display function signatures
|
91
89
|
pretty-mod sig json:loads
|
90
|
+
|
91
|
+
# Get JSON output for programmatic use
|
92
|
+
pretty-mod tree json -o json | jq '.tree.submodules | keys'
|
93
|
+
pretty-mod sig json:dumps -o json | jq '.parameters'
|
92
94
|
pretty-mod sig os.path:join
|
93
95
|
|
94
96
|
# Explore packages even without having them installed
|
@@ -2,11 +2,9 @@
|
|
2
2
|
|
3
3
|
a python module tree explorer for LLMs (and humans)
|
4
4
|
|
5
|
-
> [!IMPORTANT]
|
6
|
-
> for all versions `>=0.1.0`, wheels for different operating systems are built via `maturin` and published to pypi, install `<0.1.0` for a pure python version
|
7
|
-
|
8
5
|
> [!NOTE]
|
9
|
-
>
|
6
|
+
> - For all versions `>=0.1.0`, wheels for different operating systems are built via `maturin` and published to PyPI. Install `<0.1.0` for a pure Python version.
|
7
|
+
> - Starting from v0.2.0, output includes colors by default. Use `PRETTY_MOD_NO_COLOR=1` to disable.
|
10
8
|
|
11
9
|
```bash
|
12
10
|
# Explore module structure
|
@@ -26,7 +24,7 @@ a python module tree explorer for LLMs (and humans)
|
|
26
24
|
└── ⚡ functions: main
|
27
25
|
|
28
26
|
# Inspect function signatures (even if the package is not installed)
|
29
|
-
»
|
27
|
+
» uvx pretty-mod sig fastmcp:FastMCP --quiet
|
30
28
|
📎 FastMCP
|
31
29
|
├── Parameters:
|
32
30
|
├── self
|
@@ -79,6 +77,10 @@ pretty-mod tree requests --depth 3
|
|
79
77
|
|
80
78
|
# Display function signatures
|
81
79
|
pretty-mod sig json:loads
|
80
|
+
|
81
|
+
# Get JSON output for programmatic use
|
82
|
+
pretty-mod tree json -o json | jq '.tree.submodules | keys'
|
83
|
+
pretty-mod sig json:dumps -o json | jq '.parameters'
|
82
84
|
pretty-mod sig os.path:join
|
83
85
|
|
84
86
|
# Explore packages even without having them installed
|
@@ -1,3 +1,85 @@
|
|
1
|
+
# Release Notes - 0.2.2
|
2
|
+
|
3
|
+
## 🚀 comprehensive import chain resolution system
|
4
|
+
|
5
|
+
this release delivers the most requested feature: intuitive import chain resolution. no more guessing where symbols live in complex packages.
|
6
|
+
|
7
|
+
### ✨ major features
|
8
|
+
|
9
|
+
- **import chain resolution**: `prefect:flow`, `fastapi:FastAPI`, and `fastmcp:FastMCP` now just work
|
10
|
+
- automatically resolves re-exported symbols to their implementation
|
11
|
+
- pattern-based resolution for known library structures
|
12
|
+
- smart fallback for unknown patterns
|
13
|
+
|
14
|
+
- **enhanced method signatures**: complete support for extracting signatures from classes
|
15
|
+
- `__init__` methods for class constructors
|
16
|
+
- `__call__` methods for callable instances
|
17
|
+
- proper scope tracking distinguishes methods from functions
|
18
|
+
|
19
|
+
- **smart download management**: no more duplicate "downloading package" messages
|
20
|
+
- intelligent caching during resolution attempts
|
21
|
+
- efficient fallback strategies
|
22
|
+
|
23
|
+
### 🔧 technical implementation
|
24
|
+
|
25
|
+
- **ImportChainResolver**: pattern-based resolution engine
|
26
|
+
- handles namespace packages transparently
|
27
|
+
- integrated download logic for missing packages
|
28
|
+
- extensible pattern matching for new libraries
|
29
|
+
|
30
|
+
- **SemanticAnalyzer**: scope-aware ast visitor
|
31
|
+
- tracks module → class → function context
|
32
|
+
- distinguishes methods from functions accurately
|
33
|
+
- stores signatures under multiple keys for flexible lookup
|
34
|
+
|
35
|
+
### 📊 examples that now work
|
36
|
+
|
37
|
+
```bash
|
38
|
+
# these all resolve automatically
|
39
|
+
uv run pretty-mod sig prefect:flow # → FlowDecorator.__call__
|
40
|
+
uv run pretty-mod sig fastapi:FastAPI # → FastAPI.__init__
|
41
|
+
uv run pretty-mod sig pathlib:Path # → Path.__init__
|
42
|
+
|
43
|
+
# direct access still works
|
44
|
+
uv run pretty-mod sig prefect.flows:FlowDecorator
|
45
|
+
uv run pretty-mod sig pathlib.Path:exists
|
46
|
+
```
|
47
|
+
|
48
|
+
### 🐛 fixes
|
49
|
+
|
50
|
+
- duplicate download messages eliminated
|
51
|
+
- method signatures now correctly show self parameter
|
52
|
+
- namespace package exploration improved
|
53
|
+
|
54
|
+
this release closes issues #13, #14, and #15 in a single comprehensive implementation.
|
55
|
+
|
56
|
+
---
|
57
|
+
|
58
|
+
# Release Notes - v0.2.1
|
59
|
+
|
60
|
+
## 📊 JSON Output Support & Better Type Annotation Handling
|
61
|
+
|
62
|
+
This release adds machine-readable JSON output and fixes a critical bug with complex type annotations.
|
63
|
+
|
64
|
+
### ✨ New Features
|
65
|
+
|
66
|
+
- **📊 JSON Output Support**: Export tree and signature data as JSON for programmatic use
|
67
|
+
- `pretty-mod tree json -o json` - Get module structure as JSON
|
68
|
+
- `pretty-mod sig json:dumps -o json` - Get function signature as JSON
|
69
|
+
- Perfect for piping to `jq` or other JSON processors
|
70
|
+
- Follows the Kubernetes pattern of `-o <format>` for output selection
|
71
|
+
- Example: `pretty-mod tree json -o json | jq '.tree.submodules | keys'`
|
72
|
+
|
73
|
+
### 🏗️ Technical Improvements
|
74
|
+
|
75
|
+
- **Visitor Pattern**: Implemented output formatters using the Visitor pattern for extensibility
|
76
|
+
- Clean separation between data structure and formatting
|
77
|
+
- Easy to add new output formats in the future
|
78
|
+
- Type-safe implementation using Rust traits
|
79
|
+
|
80
|
+
|
81
|
+
---
|
82
|
+
|
1
83
|
# Release Notes - v0.2.0
|
2
84
|
|
3
85
|
## 🎨 Customizable Display & Colors + Enhanced Signature Support
|
@@ -64,6 +146,10 @@ This release introduces customizable display characters, color output, full type
|
|
64
146
|
|
65
147
|
### 🐛 Bug Fixes
|
66
148
|
|
149
|
+
- **Complex type annotations**: Fixed parameter splitting for nested generics
|
150
|
+
- Previously: `Callable[[Any], str]` would split incorrectly on the comma
|
151
|
+
- Now: Properly handles all nested brackets and quotes in type annotations
|
152
|
+
- Affects all complex types like `Dict[str, List[int]]`, `Literal['a', 'b']`, etc.
|
67
153
|
- **Stdlib module handling**: Built-in modules no longer trigger PyPI download attempts
|
68
154
|
- **Signature discovery**: Improved recursive search for symbols exported in `__all__`
|
69
155
|
- **Download messages**: Colored warning messages for better visibility
|
@@ -14,7 +14,12 @@ class ModuleTreeExplorer:
|
|
14
14
|
def get_tree_string(self) -> str: ...
|
15
15
|
|
16
16
|
def display_tree(
|
17
|
-
root_module_path: str,
|
17
|
+
root_module_path: str,
|
18
|
+
max_depth: int = 2,
|
19
|
+
quiet: bool = False,
|
20
|
+
format: str = "pretty",
|
18
21
|
) -> None: ...
|
19
|
-
def display_signature(
|
22
|
+
def display_signature(
|
23
|
+
import_path: str, quiet: bool = False, format: str = "pretty"
|
24
|
+
) -> str: ...
|
20
25
|
def import_object(import_path: str) -> Any: ...
|
@@ -26,6 +26,14 @@ def main():
|
|
26
26
|
action="store_true",
|
27
27
|
help="Suppress warnings and informational messages",
|
28
28
|
)
|
29
|
+
tree_parser.add_argument(
|
30
|
+
"-o",
|
31
|
+
"--output",
|
32
|
+
type=str,
|
33
|
+
choices=["pretty", "json"],
|
34
|
+
default="pretty",
|
35
|
+
help="Output format (default: pretty)",
|
36
|
+
)
|
29
37
|
|
30
38
|
sig_parser = subparsers.add_parser("sig", help="Display function signature")
|
31
39
|
sig_parser.add_argument(
|
@@ -36,14 +44,25 @@ def main():
|
|
36
44
|
action="store_true",
|
37
45
|
help="Suppress download messages",
|
38
46
|
)
|
47
|
+
sig_parser.add_argument(
|
48
|
+
"-o",
|
49
|
+
"--output",
|
50
|
+
type=str,
|
51
|
+
choices=["pretty", "json"],
|
52
|
+
default="pretty",
|
53
|
+
help="Output format (default: pretty)",
|
54
|
+
)
|
39
55
|
|
40
56
|
args = parser.parse_args()
|
41
57
|
|
42
58
|
try:
|
43
59
|
if args.command == "tree":
|
44
|
-
display_tree
|
60
|
+
# Call display_tree with format parameter
|
61
|
+
display_tree(args.module, args.depth, args.quiet, args.output)
|
45
62
|
elif args.command == "sig":
|
46
|
-
|
63
|
+
# Call display_signature with format parameter
|
64
|
+
result = display_signature(args.import_path, args.quiet, args.output)
|
65
|
+
print(result)
|
47
66
|
else:
|
48
67
|
parser.print_help()
|
49
68
|
sys.exit(1)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
use crate::module_info::ModuleInfo;
|
2
|
+
use crate::tree_formatter::format_tree_display;
|
2
3
|
use pyo3::prelude::*;
|
3
4
|
use std::fs;
|
4
5
|
use std::path::{Path, PathBuf};
|
@@ -116,7 +117,7 @@ impl ModuleTreeExplorer {
|
|
116
117
|
};
|
117
118
|
|
118
119
|
// Use the display_tree formatting logic, which expects the wrapped format
|
119
|
-
|
120
|
+
format_tree_display(py, &tree_obj, &self.root_module_path)
|
120
121
|
}
|
121
122
|
}
|
122
123
|
|
@@ -0,0 +1,147 @@
|
|
1
|
+
use crate::module_info::FunctionSignature;
|
2
|
+
use pyo3::prelude::*;
|
3
|
+
|
4
|
+
/// Resolves symbols through import chains using existing infrastructure
|
5
|
+
pub struct ImportChainResolver;
|
6
|
+
|
7
|
+
impl ImportChainResolver {
|
8
|
+
pub fn new() -> Self {
|
9
|
+
Self
|
10
|
+
}
|
11
|
+
|
12
|
+
/// Try to resolve a symbol by following import chains
|
13
|
+
/// Focus on specific patterns like prefect:flow -> prefect.flows:FlowDecorator
|
14
|
+
pub fn resolve_symbol_signature(
|
15
|
+
&self,
|
16
|
+
py: Python,
|
17
|
+
module_path: &str,
|
18
|
+
symbol_name: &str
|
19
|
+
) -> Option<FunctionSignature> {
|
20
|
+
// For known patterns, skip direct exploration and go straight to pattern matching
|
21
|
+
// This handles cases where the base module is a namespace package
|
22
|
+
if let Some(sig) = self.resolve_known_patterns(py, module_path, symbol_name) {
|
23
|
+
return Some(sig);
|
24
|
+
}
|
25
|
+
|
26
|
+
// Fallback: try to get module info using our existing system for direct symbols
|
27
|
+
let explorer = crate::explorer::ModuleTreeExplorer::new(module_path.to_string(), 2);
|
28
|
+
if let Ok(module_info) = explorer.explore_module_pure_filesystem(py, module_path) {
|
29
|
+
// Check if symbol is directly available
|
30
|
+
if let Some(sig) = module_info.signatures.get(symbol_name) {
|
31
|
+
return Some(sig.clone());
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
None
|
36
|
+
}
|
37
|
+
|
38
|
+
/// Handle known import patterns for specific libraries
|
39
|
+
fn resolve_known_patterns(
|
40
|
+
&self,
|
41
|
+
py: Python,
|
42
|
+
module_path: &str,
|
43
|
+
symbol_name: &str
|
44
|
+
) -> Option<FunctionSignature> {
|
45
|
+
match (module_path, symbol_name) {
|
46
|
+
// Prefect pattern: prefect:flow -> prefect.flows:FlowDecorator.__call__
|
47
|
+
("prefect", "flow") => {
|
48
|
+
self.try_resolve_from_submodule(py, "prefect.flows", "FlowDecorator")
|
49
|
+
}
|
50
|
+
|
51
|
+
// FastMCP pattern: fastmcp:FastMCP -> fastmcp.server:FastMCP.__init__
|
52
|
+
("fastmcp", "FastMCP") => {
|
53
|
+
// Try fastmcp.server first (allows download), then fastmcp (no download to avoid duplicates)
|
54
|
+
self.try_resolve_from_submodule(py, "fastmcp.server", "FastMCP")
|
55
|
+
.or_else(|| self.try_resolve_from_submodule_internal(py, "fastmcp", "FastMCP", false))
|
56
|
+
}
|
57
|
+
|
58
|
+
// FastAPI pattern: fastapi:FastAPI -> fastapi.applications:FastAPI.__init__
|
59
|
+
("fastapi", "FastAPI") => {
|
60
|
+
self.try_resolve_from_submodule(py, "fastapi.applications", "FastAPI")
|
61
|
+
}
|
62
|
+
|
63
|
+
// Add more patterns as needed
|
64
|
+
_ => None
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
/// Try to resolve a symbol from a specific submodule
|
69
|
+
fn try_resolve_from_submodule(
|
70
|
+
&self,
|
71
|
+
py: Python,
|
72
|
+
submodule_path: &str,
|
73
|
+
class_name: &str
|
74
|
+
) -> Option<FunctionSignature> {
|
75
|
+
self.try_resolve_from_submodule_internal(py, submodule_path, class_name, true)
|
76
|
+
}
|
77
|
+
|
78
|
+
/// Internal helper that can optionally skip download to avoid duplicates
|
79
|
+
fn try_resolve_from_submodule_internal(
|
80
|
+
&self,
|
81
|
+
py: Python,
|
82
|
+
submodule_path: &str,
|
83
|
+
class_name: &str,
|
84
|
+
allow_download: bool
|
85
|
+
) -> Option<FunctionSignature> {
|
86
|
+
// Helper function to try exploration
|
87
|
+
let try_get_signature = |py: Python| -> Option<FunctionSignature> {
|
88
|
+
let explorer = crate::explorer::ModuleTreeExplorer::new(submodule_path.to_string(), 2);
|
89
|
+
|
90
|
+
if let Ok(module_info) = explorer.explore_module_pure_filesystem(py, submodule_path) {
|
91
|
+
// Try to find the class signature (which should be __init__ or __call__)
|
92
|
+
if let Some(sig) = module_info.signatures.get(class_name) {
|
93
|
+
return Some(sig.clone());
|
94
|
+
}
|
95
|
+
|
96
|
+
// Also try looking for __call__ method signature
|
97
|
+
let call_method = format!("{}.{}", class_name, "__call__");
|
98
|
+
if let Some(sig) = module_info.signatures.get(&call_method) {
|
99
|
+
return Some(sig.clone());
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
None
|
104
|
+
};
|
105
|
+
|
106
|
+
// First try direct filesystem exploration
|
107
|
+
if let Some(sig) = try_get_signature(py) {
|
108
|
+
return Some(sig);
|
109
|
+
}
|
110
|
+
|
111
|
+
// If download is not allowed, return None
|
112
|
+
if !allow_download {
|
113
|
+
return None;
|
114
|
+
}
|
115
|
+
|
116
|
+
// If that fails, extract the base package and try downloading
|
117
|
+
let base_package = crate::utils::extract_base_package(submodule_path);
|
118
|
+
|
119
|
+
// Check if this is a stdlib module - if so, don't try to download
|
120
|
+
if crate::stdlib::is_stdlib_module(&base_package) {
|
121
|
+
return None;
|
122
|
+
}
|
123
|
+
|
124
|
+
// Try downloading the package and re-attempting exploration
|
125
|
+
let mut download_result = None;
|
126
|
+
if let Ok(()) = crate::utils::try_download_and_import(py, &base_package, false, || {
|
127
|
+
download_result = try_get_signature(py);
|
128
|
+
Ok(())
|
129
|
+
}) {
|
130
|
+
return download_result;
|
131
|
+
}
|
132
|
+
|
133
|
+
None
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
#[cfg(test)]
|
138
|
+
mod tests {
|
139
|
+
use super::*;
|
140
|
+
|
141
|
+
#[test]
|
142
|
+
fn test_import_resolver_creation() {
|
143
|
+
let resolver = ImportChainResolver::new();
|
144
|
+
// Just test that it can be created
|
145
|
+
assert!(std::mem::size_of_val(&resolver) >= 0);
|
146
|
+
}
|
147
|
+
}
|