scitex 2.14.0__py3-none-any.whl → 2.15.1__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.
- scitex/__init__.py +47 -0
- scitex/_env_loader.py +156 -0
- scitex/_mcp_resources/__init__.py +37 -0
- scitex/_mcp_resources/_cheatsheet.py +135 -0
- scitex/_mcp_resources/_figrecipe.py +138 -0
- scitex/_mcp_resources/_formats.py +102 -0
- scitex/_mcp_resources/_modules.py +337 -0
- scitex/_mcp_resources/_session.py +149 -0
- scitex/_mcp_tools/__init__.py +4 -0
- scitex/_mcp_tools/audio.py +66 -0
- scitex/_mcp_tools/diagram.py +11 -95
- scitex/_mcp_tools/introspect.py +191 -0
- scitex/_mcp_tools/plt.py +260 -305
- scitex/_mcp_tools/scholar.py +74 -0
- scitex/_mcp_tools/social.py +244 -0
- scitex/_mcp_tools/writer.py +21 -204
- scitex/ai/_gen_ai/_PARAMS.py +10 -7
- scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
- scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
- scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
- scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
- scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
- scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
- scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
- scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
- scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
- scitex/audio/README.md +40 -36
- scitex/audio/__init__.py +127 -59
- scitex/audio/_branding.py +185 -0
- scitex/audio/_mcp/__init__.py +32 -0
- scitex/audio/_mcp/handlers.py +59 -6
- scitex/audio/_mcp/speak_handlers.py +238 -0
- scitex/audio/_relay.py +225 -0
- scitex/audio/engines/elevenlabs_engine.py +6 -1
- scitex/audio/mcp_server.py +228 -75
- scitex/canvas/README.md +1 -1
- scitex/canvas/editor/_dearpygui/__init__.py +25 -0
- scitex/canvas/editor/_dearpygui/_editor.py +147 -0
- scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
- scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
- scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
- scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
- scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
- scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
- scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
- scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
- scitex/canvas/editor/_dearpygui/_selection.py +295 -0
- scitex/canvas/editor/_dearpygui/_state.py +93 -0
- scitex/canvas/editor/_dearpygui/_utils.py +61 -0
- scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
- scitex/cli/__init__.py +38 -43
- scitex/cli/audio.py +76 -27
- scitex/cli/capture.py +13 -20
- scitex/cli/introspect.py +443 -0
- scitex/cli/main.py +198 -109
- scitex/cli/mcp.py +60 -34
- scitex/cli/scholar/__init__.py +8 -0
- scitex/cli/scholar/_crossref_scitex.py +296 -0
- scitex/cli/scholar/_fetch.py +25 -3
- scitex/cli/social.py +314 -0
- scitex/cli/writer.py +117 -0
- scitex/config/README.md +1 -1
- scitex/config/__init__.py +16 -2
- scitex/config/_env_registry.py +191 -0
- scitex/diagram/__init__.py +42 -19
- scitex/diagram/mcp_server.py +13 -125
- scitex/introspect/__init__.py +75 -0
- scitex/introspect/_call_graph.py +303 -0
- scitex/introspect/_class_hierarchy.py +163 -0
- scitex/introspect/_core.py +42 -0
- scitex/introspect/_docstring.py +131 -0
- scitex/introspect/_examples.py +113 -0
- scitex/introspect/_imports.py +271 -0
- scitex/introspect/_mcp/__init__.py +37 -0
- scitex/introspect/_mcp/handlers.py +208 -0
- scitex/introspect/_members.py +151 -0
- scitex/introspect/_resolve.py +89 -0
- scitex/introspect/_signature.py +131 -0
- scitex/introspect/_source.py +80 -0
- scitex/introspect/_type_hints.py +172 -0
- scitex/io/bundle/README.md +1 -1
- scitex/mcp_server.py +98 -5
- scitex/plt/__init__.py +248 -550
- scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
- scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/plt/gallery/README.md +1 -1
- scitex/plt/utils/_hitmap/__init__.py +82 -0
- scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
- scitex/plt/utils/_hitmap/_color_application.py +346 -0
- scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
- scitex/plt/utils/_hitmap/_constants.py +40 -0
- scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
- scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
- scitex/plt/utils/_hitmap/_query.py +113 -0
- scitex/plt/utils/_hitmap.py +46 -1616
- scitex/plt/utils/_metadata/__init__.py +80 -0
- scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
- scitex/plt/utils/_metadata/_artists/_base.py +195 -0
- scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
- scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
- scitex/plt/utils/_metadata/_artists/_images.py +80 -0
- scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
- scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
- scitex/plt/utils/_metadata/_artists/_text.py +106 -0
- scitex/plt/utils/_metadata/_csv.py +416 -0
- scitex/plt/utils/_metadata/_detect.py +225 -0
- scitex/plt/utils/_metadata/_legend.py +127 -0
- scitex/plt/utils/_metadata/_rounding.py +117 -0
- scitex/plt/utils/_metadata/_verification.py +202 -0
- scitex/schema/README.md +1 -1
- scitex/scholar/__init__.py +8 -0
- scitex/scholar/_mcp/crossref_handlers.py +265 -0
- scitex/scholar/core/Scholar.py +63 -1700
- scitex/scholar/core/_mixins/__init__.py +36 -0
- scitex/scholar/core/_mixins/_enrichers.py +270 -0
- scitex/scholar/core/_mixins/_library_handlers.py +100 -0
- scitex/scholar/core/_mixins/_loaders.py +103 -0
- scitex/scholar/core/_mixins/_pdf_download.py +375 -0
- scitex/scholar/core/_mixins/_pipeline.py +312 -0
- scitex/scholar/core/_mixins/_project_handlers.py +125 -0
- scitex/scholar/core/_mixins/_savers.py +69 -0
- scitex/scholar/core/_mixins/_search.py +103 -0
- scitex/scholar/core/_mixins/_services.py +88 -0
- scitex/scholar/core/_mixins/_url_finding.py +105 -0
- scitex/scholar/crossref_scitex.py +367 -0
- scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/scholar/examples/00_run_all.sh +120 -0
- scitex/scholar/jobs/_executors.py +27 -3
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
- scitex/scholar/pdf_download/_cli.py +154 -0
- scitex/scholar/pdf_download/strategies/__init__.py +11 -8
- scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
- scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
- scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
- scitex/scholar/pipelines/_single_steps.py +71 -36
- scitex/scholar/storage/_LibraryManager.py +97 -1695
- scitex/scholar/storage/_mixins/__init__.py +30 -0
- scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
- scitex/scholar/storage/_mixins/_library_operations.py +218 -0
- scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
- scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
- scitex/scholar/storage/_mixins/_resolution.py +376 -0
- scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
- scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
- scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
- scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
- scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
- scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
- scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
- scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
- scitex/security/README.md +3 -3
- scitex/session/README.md +1 -1
- scitex/sh/README.md +1 -1
- scitex/social/__init__.py +153 -0
- scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/template/README.md +1 -1
- scitex/template/clone_writer_directory.py +5 -5
- scitex/writer/README.md +1 -1
- scitex/writer/_mcp/handlers.py +11 -744
- scitex/writer/_mcp/tool_schemas.py +5 -335
- scitex-2.15.1.dist-info/METADATA +648 -0
- {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/RECORD +166 -111
- scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
- scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
- scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
- scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
- scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
- scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
- scitex/diagram/_compile.py +0 -312
- scitex/diagram/_diagram.py +0 -355
- scitex/diagram/_mcp/__init__.py +0 -4
- scitex/diagram/_mcp/handlers.py +0 -400
- scitex/diagram/_mcp/tool_schemas.py +0 -157
- scitex/diagram/_presets.py +0 -173
- scitex/diagram/_schema.py +0 -182
- scitex/diagram/_split.py +0 -278
- scitex/plt/_mcp/__init__.py +0 -4
- scitex/plt/_mcp/_handlers_annotation.py +0 -102
- scitex/plt/_mcp/_handlers_figure.py +0 -195
- scitex/plt/_mcp/_handlers_plot.py +0 -252
- scitex/plt/_mcp/_handlers_style.py +0 -219
- scitex/plt/_mcp/handlers.py +0 -74
- scitex/plt/_mcp/tool_schemas.py +0 -497
- scitex/plt/mcp_server.py +0 -231
- scitex/scholar/data/.gitkeep +0 -0
- scitex/scholar/data/README.md +0 -44
- scitex/scholar/data/bib_files/bibliography.bib +0 -1952
- scitex/scholar/data/bib_files/neurovista.bib +0 -277
- scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
- scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
- scitex/scholar/data/bib_files/openaccess.bib +0 -89
- scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
- scitex/scholar/data/bib_files/pac.bib +0 -698
- scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
- scitex/scholar/data/bib_files/pac_processed.bib +0 -0
- scitex/scholar/data/bib_files/pac_titles.txt +0 -75
- scitex/scholar/data/bib_files/paywalled.bib +0 -98
- scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
- scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
- scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
- scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
- scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
- scitex/scholar/data/bib_files/test_seizure.bib +0 -46
- scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
- scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
- scitex/scholar/data/impact_factor.db +0 -0
- scitex/scholar/examples/SUGGESTIONS.md +0 -865
- scitex/scholar/examples/dev.py +0 -38
- scitex-2.14.0.dist-info/METADATA +0 -1238
- {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/WHEEL +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/entry_points.txt +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_examples.py
|
|
4
|
+
|
|
5
|
+
"""Usage example finding utilities."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import importlib
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from ._resolve import resolve_object
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def find_examples(
|
|
16
|
+
dotted_path: str,
|
|
17
|
+
search_paths: list[str] | None = None,
|
|
18
|
+
max_results: int = 10,
|
|
19
|
+
) -> dict:
|
|
20
|
+
"""
|
|
21
|
+
Find usage examples of a function/class in tests and examples.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
dotted_path : str
|
|
26
|
+
Dotted path to search for (e.g., 'scitex.plt.plot')
|
|
27
|
+
search_paths : list[str] | None
|
|
28
|
+
Paths to search. If None, auto-detect from module location
|
|
29
|
+
max_results : int
|
|
30
|
+
Maximum number of examples to return
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
dict
|
|
35
|
+
examples: list[dict] - Each with file, line, context
|
|
36
|
+
count: int
|
|
37
|
+
"""
|
|
38
|
+
obj, error = resolve_object(dotted_path)
|
|
39
|
+
if error:
|
|
40
|
+
return {"success": False, "error": error}
|
|
41
|
+
|
|
42
|
+
name = getattr(obj, "__name__", dotted_path.split(".")[-1])
|
|
43
|
+
|
|
44
|
+
if search_paths is None:
|
|
45
|
+
search_paths = []
|
|
46
|
+
|
|
47
|
+
module = dotted_path.split(".")[0]
|
|
48
|
+
try:
|
|
49
|
+
mod = importlib.import_module(module)
|
|
50
|
+
if hasattr(mod, "__file__") and mod.__file__:
|
|
51
|
+
mod_dir = Path(mod.__file__).parent
|
|
52
|
+
project_root = mod_dir.parent
|
|
53
|
+
for subdir in ["tests", "test", "examples", "example"]:
|
|
54
|
+
test_dir = project_root / subdir
|
|
55
|
+
if test_dir.exists():
|
|
56
|
+
search_paths.append(str(test_dir))
|
|
57
|
+
except ImportError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
if not search_paths:
|
|
61
|
+
return {
|
|
62
|
+
"success": True,
|
|
63
|
+
"examples": [],
|
|
64
|
+
"count": 0,
|
|
65
|
+
"message": "No test/example directories found",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
examples = []
|
|
69
|
+
|
|
70
|
+
for search_path in search_paths:
|
|
71
|
+
path = Path(search_path)
|
|
72
|
+
if not path.exists():
|
|
73
|
+
continue
|
|
74
|
+
|
|
75
|
+
for py_file in path.rglob("*.py"):
|
|
76
|
+
try:
|
|
77
|
+
content = py_file.read_text()
|
|
78
|
+
except Exception:
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
if name not in content:
|
|
82
|
+
continue
|
|
83
|
+
|
|
84
|
+
lines = content.split("\n")
|
|
85
|
+
for i, line in enumerate(lines):
|
|
86
|
+
if name in line:
|
|
87
|
+
start = max(0, i - 2)
|
|
88
|
+
end = min(len(lines), i + 3)
|
|
89
|
+
context = "\n".join(lines[start:end])
|
|
90
|
+
|
|
91
|
+
examples.append(
|
|
92
|
+
{
|
|
93
|
+
"file": str(py_file),
|
|
94
|
+
"line": i + 1,
|
|
95
|
+
"context": context,
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if len(examples) >= max_results:
|
|
100
|
+
break
|
|
101
|
+
|
|
102
|
+
if len(examples) >= max_results:
|
|
103
|
+
break
|
|
104
|
+
|
|
105
|
+
if len(examples) >= max_results:
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
"success": True,
|
|
110
|
+
"examples": examples,
|
|
111
|
+
"count": len(examples),
|
|
112
|
+
"search_paths": search_paths,
|
|
113
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_imports.py
|
|
4
|
+
|
|
5
|
+
"""Import analysis utilities using AST."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import ast
|
|
10
|
+
import inspect
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from ._resolve import get_type_info, resolve_object
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_imports(
|
|
17
|
+
dotted_path: str,
|
|
18
|
+
categorize: bool = True,
|
|
19
|
+
) -> dict:
|
|
20
|
+
"""
|
|
21
|
+
Get all imports from a module's source code using AST.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
dotted_path : str
|
|
26
|
+
Dotted path to the module
|
|
27
|
+
categorize : bool
|
|
28
|
+
Group imports by category (stdlib, third-party, local)
|
|
29
|
+
|
|
30
|
+
Returns
|
|
31
|
+
-------
|
|
32
|
+
dict
|
|
33
|
+
imports: list[dict] - All imports with details
|
|
34
|
+
categories: dict - Grouped by category (if categorize=True)
|
|
35
|
+
|
|
36
|
+
Examples
|
|
37
|
+
--------
|
|
38
|
+
>>> get_imports("scitex.audio")
|
|
39
|
+
"""
|
|
40
|
+
obj, error = resolve_object(dotted_path)
|
|
41
|
+
if error:
|
|
42
|
+
return {"success": False, "error": error}
|
|
43
|
+
|
|
44
|
+
type_info = get_type_info(obj)
|
|
45
|
+
|
|
46
|
+
if not inspect.ismodule(obj):
|
|
47
|
+
return {
|
|
48
|
+
"success": False,
|
|
49
|
+
"error": f"'{dotted_path}' is not a module",
|
|
50
|
+
"type_info": type_info,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Get source file
|
|
54
|
+
try:
|
|
55
|
+
source_file = inspect.getfile(obj)
|
|
56
|
+
except TypeError:
|
|
57
|
+
return {
|
|
58
|
+
"success": False,
|
|
59
|
+
"error": "Cannot get source file (builtin module?)",
|
|
60
|
+
"type_info": type_info,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# Read and parse source
|
|
64
|
+
try:
|
|
65
|
+
source = Path(source_file).read_text()
|
|
66
|
+
tree = ast.parse(source)
|
|
67
|
+
except Exception as e:
|
|
68
|
+
return {
|
|
69
|
+
"success": False,
|
|
70
|
+
"error": f"Cannot parse source: {e}",
|
|
71
|
+
"type_info": type_info,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
imports = _extract_imports(tree)
|
|
75
|
+
|
|
76
|
+
result = {
|
|
77
|
+
"success": True,
|
|
78
|
+
"module": dotted_path,
|
|
79
|
+
"source_file": source_file,
|
|
80
|
+
"imports": imports,
|
|
81
|
+
"import_count": len(imports),
|
|
82
|
+
"type_info": type_info,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if categorize:
|
|
86
|
+
result["categories"] = _categorize_imports(imports)
|
|
87
|
+
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _extract_imports(tree: ast.AST) -> list[dict]:
|
|
92
|
+
"""Extract all imports from an AST."""
|
|
93
|
+
imports = []
|
|
94
|
+
|
|
95
|
+
for node in ast.walk(tree):
|
|
96
|
+
if isinstance(node, ast.Import):
|
|
97
|
+
for alias in node.names:
|
|
98
|
+
imports.append(
|
|
99
|
+
{
|
|
100
|
+
"type": "import",
|
|
101
|
+
"module": alias.name,
|
|
102
|
+
"alias": alias.asname,
|
|
103
|
+
"line": node.lineno,
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
elif isinstance(node, ast.ImportFrom):
|
|
107
|
+
module = node.module or ""
|
|
108
|
+
level = node.level # Relative import level
|
|
109
|
+
|
|
110
|
+
for alias in node.names:
|
|
111
|
+
imports.append(
|
|
112
|
+
{
|
|
113
|
+
"type": "from",
|
|
114
|
+
"module": module,
|
|
115
|
+
"name": alias.name,
|
|
116
|
+
"alias": alias.asname,
|
|
117
|
+
"level": level,
|
|
118
|
+
"line": node.lineno,
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return imports
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _categorize_imports(imports: list[dict]) -> dict:
|
|
126
|
+
"""Categorize imports into stdlib, third-party, local."""
|
|
127
|
+
import sys
|
|
128
|
+
|
|
129
|
+
stdlib_modules = (
|
|
130
|
+
set(sys.stdlib_module_names)
|
|
131
|
+
if hasattr(sys, "stdlib_module_names")
|
|
132
|
+
else _get_stdlib_modules()
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
categories = {
|
|
136
|
+
"stdlib": [],
|
|
137
|
+
"third_party": [],
|
|
138
|
+
"local": [],
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for imp in imports:
|
|
142
|
+
module = imp["module"]
|
|
143
|
+
top_level = module.split(".")[0] if module else ""
|
|
144
|
+
|
|
145
|
+
# Relative imports are local
|
|
146
|
+
if imp.get("level", 0) > 0:
|
|
147
|
+
categories["local"].append(imp)
|
|
148
|
+
elif top_level in stdlib_modules:
|
|
149
|
+
categories["stdlib"].append(imp)
|
|
150
|
+
else:
|
|
151
|
+
categories["third_party"].append(imp)
|
|
152
|
+
|
|
153
|
+
return categories
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _get_stdlib_modules() -> set:
|
|
157
|
+
"""Get stdlib module names for Python < 3.10."""
|
|
158
|
+
import pkgutil
|
|
159
|
+
|
|
160
|
+
stdlib = set()
|
|
161
|
+
for module in pkgutil.iter_modules():
|
|
162
|
+
if module.name.startswith("_"):
|
|
163
|
+
continue
|
|
164
|
+
try:
|
|
165
|
+
spec = __import__(module.name).__spec__
|
|
166
|
+
if spec and spec.origin:
|
|
167
|
+
if "site-packages" not in spec.origin:
|
|
168
|
+
stdlib.add(module.name)
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
# Add common ones that might be missed
|
|
173
|
+
stdlib.update(
|
|
174
|
+
[
|
|
175
|
+
"abc",
|
|
176
|
+
"ast",
|
|
177
|
+
"asyncio",
|
|
178
|
+
"collections",
|
|
179
|
+
"contextlib",
|
|
180
|
+
"dataclasses",
|
|
181
|
+
"datetime",
|
|
182
|
+
"functools",
|
|
183
|
+
"inspect",
|
|
184
|
+
"io",
|
|
185
|
+
"itertools",
|
|
186
|
+
"json",
|
|
187
|
+
"logging",
|
|
188
|
+
"os",
|
|
189
|
+
"pathlib",
|
|
190
|
+
"re",
|
|
191
|
+
"sys",
|
|
192
|
+
"typing",
|
|
193
|
+
"unittest",
|
|
194
|
+
"warnings",
|
|
195
|
+
]
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return stdlib
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_dependencies(
|
|
202
|
+
dotted_path: str,
|
|
203
|
+
recursive: bool = False,
|
|
204
|
+
max_depth: int = 3,
|
|
205
|
+
) -> dict:
|
|
206
|
+
"""
|
|
207
|
+
Get module dependencies (what it imports).
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
dotted_path : str
|
|
212
|
+
Dotted path to the module
|
|
213
|
+
recursive : bool
|
|
214
|
+
Recursively analyze imported modules
|
|
215
|
+
max_depth : int
|
|
216
|
+
Maximum recursion depth
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
dict
|
|
221
|
+
dependencies: list[str] - Direct dependencies
|
|
222
|
+
tree: dict - Dependency tree (if recursive)
|
|
223
|
+
"""
|
|
224
|
+
result = get_imports(dotted_path, categorize=True)
|
|
225
|
+
if not result.get("success"):
|
|
226
|
+
return result
|
|
227
|
+
|
|
228
|
+
# Get unique module names
|
|
229
|
+
deps = set()
|
|
230
|
+
for imp in result["imports"]:
|
|
231
|
+
module = imp["module"]
|
|
232
|
+
if module:
|
|
233
|
+
deps.add(module.split(".")[0])
|
|
234
|
+
|
|
235
|
+
result["dependencies"] = sorted(deps)
|
|
236
|
+
result["dependency_count"] = len(deps)
|
|
237
|
+
|
|
238
|
+
if recursive:
|
|
239
|
+
result["tree"] = _build_dep_tree(dotted_path, max_depth, set())
|
|
240
|
+
|
|
241
|
+
return result
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _build_dep_tree(
|
|
245
|
+
module_path: str,
|
|
246
|
+
max_depth: int,
|
|
247
|
+
visited: set,
|
|
248
|
+
current_depth: int = 0,
|
|
249
|
+
) -> dict:
|
|
250
|
+
"""Build dependency tree recursively."""
|
|
251
|
+
if current_depth >= max_depth or module_path in visited:
|
|
252
|
+
return {"module": module_path, "truncated": True}
|
|
253
|
+
|
|
254
|
+
visited.add(module_path)
|
|
255
|
+
|
|
256
|
+
result = {"module": module_path, "imports": []}
|
|
257
|
+
|
|
258
|
+
imports_result = get_imports(module_path, categorize=False)
|
|
259
|
+
if not imports_result.get("success"):
|
|
260
|
+
return result
|
|
261
|
+
|
|
262
|
+
for imp in imports_result.get("imports", []):
|
|
263
|
+
module = imp["module"]
|
|
264
|
+
if module and module not in visited:
|
|
265
|
+
top_level = module.split(".")[0]
|
|
266
|
+
# Only recurse into non-stdlib
|
|
267
|
+
if top_level not in _get_stdlib_modules():
|
|
268
|
+
child = _build_dep_tree(module, max_depth, visited, current_depth + 1)
|
|
269
|
+
result["imports"].append(child)
|
|
270
|
+
|
|
271
|
+
return result
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_mcp/__init__.py
|
|
4
|
+
|
|
5
|
+
"""MCP tools for introspection."""
|
|
6
|
+
|
|
7
|
+
from .handlers import (
|
|
8
|
+
# Advanced
|
|
9
|
+
call_graph_handler,
|
|
10
|
+
class_hierarchy_handler,
|
|
11
|
+
dependencies_handler,
|
|
12
|
+
# Basic
|
|
13
|
+
docstring_handler,
|
|
14
|
+
examples_handler,
|
|
15
|
+
exports_handler,
|
|
16
|
+
imports_handler,
|
|
17
|
+
members_handler,
|
|
18
|
+
signature_handler,
|
|
19
|
+
source_handler,
|
|
20
|
+
type_hints_handler,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
# Basic
|
|
25
|
+
"signature_handler",
|
|
26
|
+
"docstring_handler",
|
|
27
|
+
"source_handler",
|
|
28
|
+
"members_handler",
|
|
29
|
+
"exports_handler",
|
|
30
|
+
"examples_handler",
|
|
31
|
+
# Advanced
|
|
32
|
+
"class_hierarchy_handler",
|
|
33
|
+
"type_hints_handler",
|
|
34
|
+
"imports_handler",
|
|
35
|
+
"dependencies_handler",
|
|
36
|
+
"call_graph_handler",
|
|
37
|
+
]
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_mcp/handlers.py
|
|
4
|
+
|
|
5
|
+
"""MCP handlers for introspection tools."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def signature_handler(
|
|
13
|
+
dotted_path: str,
|
|
14
|
+
include_defaults: bool = True,
|
|
15
|
+
include_annotations: bool = True,
|
|
16
|
+
) -> dict:
|
|
17
|
+
"""Get the signature of a function, method, or class."""
|
|
18
|
+
try:
|
|
19
|
+
from .. import get_signature
|
|
20
|
+
|
|
21
|
+
result = get_signature(
|
|
22
|
+
dotted_path,
|
|
23
|
+
include_defaults=include_defaults,
|
|
24
|
+
include_annotations=include_annotations,
|
|
25
|
+
)
|
|
26
|
+
return result
|
|
27
|
+
except Exception as e:
|
|
28
|
+
return {"success": False, "error": str(e)}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def docstring_handler(
|
|
32
|
+
dotted_path: str,
|
|
33
|
+
format: Literal["raw", "parsed", "summary"] = "raw",
|
|
34
|
+
) -> dict:
|
|
35
|
+
"""Get the docstring of a Python object."""
|
|
36
|
+
try:
|
|
37
|
+
from .. import get_docstring
|
|
38
|
+
|
|
39
|
+
result = get_docstring(dotted_path, format=format)
|
|
40
|
+
return result
|
|
41
|
+
except Exception as e:
|
|
42
|
+
return {"success": False, "error": str(e)}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def source_handler(
|
|
46
|
+
dotted_path: str,
|
|
47
|
+
max_lines: int | None = None,
|
|
48
|
+
include_decorators: bool = True,
|
|
49
|
+
) -> dict:
|
|
50
|
+
"""Get the source code of a Python object."""
|
|
51
|
+
try:
|
|
52
|
+
from .. import get_source
|
|
53
|
+
|
|
54
|
+
result = get_source(
|
|
55
|
+
dotted_path,
|
|
56
|
+
max_lines=max_lines,
|
|
57
|
+
include_decorators=include_decorators,
|
|
58
|
+
)
|
|
59
|
+
return result
|
|
60
|
+
except Exception as e:
|
|
61
|
+
return {"success": False, "error": str(e)}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def members_handler(
|
|
65
|
+
dotted_path: str,
|
|
66
|
+
filter: Literal["all", "public", "private", "dunder"] = "public",
|
|
67
|
+
kind: Literal["all", "functions", "classes", "data", "modules"] | None = None,
|
|
68
|
+
include_inherited: bool = False,
|
|
69
|
+
) -> dict:
|
|
70
|
+
"""List members of a module or class."""
|
|
71
|
+
try:
|
|
72
|
+
from .. import list_members
|
|
73
|
+
|
|
74
|
+
result = list_members(
|
|
75
|
+
dotted_path,
|
|
76
|
+
filter=filter,
|
|
77
|
+
kind=kind,
|
|
78
|
+
include_inherited=include_inherited,
|
|
79
|
+
)
|
|
80
|
+
return result
|
|
81
|
+
except Exception as e:
|
|
82
|
+
return {"success": False, "error": str(e)}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
async def exports_handler(dotted_path: str) -> dict:
|
|
86
|
+
"""Get the __all__ exports of a module."""
|
|
87
|
+
try:
|
|
88
|
+
from .. import get_exports
|
|
89
|
+
|
|
90
|
+
result = get_exports(dotted_path)
|
|
91
|
+
return result
|
|
92
|
+
except Exception as e:
|
|
93
|
+
return {"success": False, "error": str(e)}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def examples_handler(
|
|
97
|
+
dotted_path: str,
|
|
98
|
+
search_paths: list[str] | None = None,
|
|
99
|
+
max_results: int = 10,
|
|
100
|
+
) -> dict:
|
|
101
|
+
"""Find usage examples of a function/class in tests and examples."""
|
|
102
|
+
try:
|
|
103
|
+
from .. import find_examples
|
|
104
|
+
|
|
105
|
+
result = find_examples(
|
|
106
|
+
dotted_path,
|
|
107
|
+
search_paths=search_paths,
|
|
108
|
+
max_results=max_results,
|
|
109
|
+
)
|
|
110
|
+
return result
|
|
111
|
+
except Exception as e:
|
|
112
|
+
return {"success": False, "error": str(e)}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Advanced handlers
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def class_hierarchy_handler(
|
|
119
|
+
dotted_path: str,
|
|
120
|
+
include_builtins: bool = False,
|
|
121
|
+
max_depth: int = 10,
|
|
122
|
+
) -> dict:
|
|
123
|
+
"""Get the inheritance hierarchy of a class."""
|
|
124
|
+
try:
|
|
125
|
+
from .. import get_class_hierarchy
|
|
126
|
+
|
|
127
|
+
result = get_class_hierarchy(
|
|
128
|
+
dotted_path,
|
|
129
|
+
include_builtins=include_builtins,
|
|
130
|
+
max_depth=max_depth,
|
|
131
|
+
)
|
|
132
|
+
return result
|
|
133
|
+
except Exception as e:
|
|
134
|
+
return {"success": False, "error": str(e)}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def type_hints_handler(
|
|
138
|
+
dotted_path: str,
|
|
139
|
+
include_extras: bool = True,
|
|
140
|
+
) -> dict:
|
|
141
|
+
"""Get detailed type hint information."""
|
|
142
|
+
try:
|
|
143
|
+
from .. import get_type_hints_detailed
|
|
144
|
+
|
|
145
|
+
result = get_type_hints_detailed(
|
|
146
|
+
dotted_path,
|
|
147
|
+
include_extras=include_extras,
|
|
148
|
+
)
|
|
149
|
+
return result
|
|
150
|
+
except Exception as e:
|
|
151
|
+
return {"success": False, "error": str(e)}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def imports_handler(
|
|
155
|
+
dotted_path: str,
|
|
156
|
+
categorize: bool = True,
|
|
157
|
+
) -> dict:
|
|
158
|
+
"""Get all imports from a module."""
|
|
159
|
+
try:
|
|
160
|
+
from .. import get_imports
|
|
161
|
+
|
|
162
|
+
result = get_imports(
|
|
163
|
+
dotted_path,
|
|
164
|
+
categorize=categorize,
|
|
165
|
+
)
|
|
166
|
+
return result
|
|
167
|
+
except Exception as e:
|
|
168
|
+
return {"success": False, "error": str(e)}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
async def dependencies_handler(
|
|
172
|
+
dotted_path: str,
|
|
173
|
+
recursive: bool = False,
|
|
174
|
+
max_depth: int = 3,
|
|
175
|
+
) -> dict:
|
|
176
|
+
"""Get module dependencies."""
|
|
177
|
+
try:
|
|
178
|
+
from .. import get_dependencies
|
|
179
|
+
|
|
180
|
+
result = get_dependencies(
|
|
181
|
+
dotted_path,
|
|
182
|
+
recursive=recursive,
|
|
183
|
+
max_depth=max_depth,
|
|
184
|
+
)
|
|
185
|
+
return result
|
|
186
|
+
except Exception as e:
|
|
187
|
+
return {"success": False, "error": str(e)}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
async def call_graph_handler(
|
|
191
|
+
dotted_path: str,
|
|
192
|
+
max_depth: int = 2,
|
|
193
|
+
timeout_seconds: int = 10,
|
|
194
|
+
internal_only: bool = True,
|
|
195
|
+
) -> dict:
|
|
196
|
+
"""Get the call graph of a function or module."""
|
|
197
|
+
try:
|
|
198
|
+
from .. import get_call_graph
|
|
199
|
+
|
|
200
|
+
result = get_call_graph(
|
|
201
|
+
dotted_path,
|
|
202
|
+
max_depth=max_depth,
|
|
203
|
+
timeout_seconds=timeout_seconds,
|
|
204
|
+
internal_only=internal_only,
|
|
205
|
+
)
|
|
206
|
+
return result
|
|
207
|
+
except Exception as e:
|
|
208
|
+
return {"success": False, "error": str(e)}
|