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.
Files changed (218) hide show
  1. scitex/__init__.py +47 -0
  2. scitex/_env_loader.py +156 -0
  3. scitex/_mcp_resources/__init__.py +37 -0
  4. scitex/_mcp_resources/_cheatsheet.py +135 -0
  5. scitex/_mcp_resources/_figrecipe.py +138 -0
  6. scitex/_mcp_resources/_formats.py +102 -0
  7. scitex/_mcp_resources/_modules.py +337 -0
  8. scitex/_mcp_resources/_session.py +149 -0
  9. scitex/_mcp_tools/__init__.py +4 -0
  10. scitex/_mcp_tools/audio.py +66 -0
  11. scitex/_mcp_tools/diagram.py +11 -95
  12. scitex/_mcp_tools/introspect.py +191 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +244 -0
  16. scitex/_mcp_tools/writer.py +21 -204
  17. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  18. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  19. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  20. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  21. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  22. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  23. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  24. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  25. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  26. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  27. scitex/audio/README.md +40 -36
  28. scitex/audio/__init__.py +127 -59
  29. scitex/audio/_branding.py +185 -0
  30. scitex/audio/_mcp/__init__.py +32 -0
  31. scitex/audio/_mcp/handlers.py +59 -6
  32. scitex/audio/_mcp/speak_handlers.py +238 -0
  33. scitex/audio/_relay.py +225 -0
  34. scitex/audio/engines/elevenlabs_engine.py +6 -1
  35. scitex/audio/mcp_server.py +228 -75
  36. scitex/canvas/README.md +1 -1
  37. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  38. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  39. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  40. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  41. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  42. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  43. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  44. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  45. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  46. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  47. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  48. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  49. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  50. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  51. scitex/cli/__init__.py +38 -43
  52. scitex/cli/audio.py +76 -27
  53. scitex/cli/capture.py +13 -20
  54. scitex/cli/introspect.py +443 -0
  55. scitex/cli/main.py +198 -109
  56. scitex/cli/mcp.py +60 -34
  57. scitex/cli/scholar/__init__.py +8 -0
  58. scitex/cli/scholar/_crossref_scitex.py +296 -0
  59. scitex/cli/scholar/_fetch.py +25 -3
  60. scitex/cli/social.py +314 -0
  61. scitex/cli/writer.py +117 -0
  62. scitex/config/README.md +1 -1
  63. scitex/config/__init__.py +16 -2
  64. scitex/config/_env_registry.py +191 -0
  65. scitex/diagram/__init__.py +42 -19
  66. scitex/diagram/mcp_server.py +13 -125
  67. scitex/introspect/__init__.py +75 -0
  68. scitex/introspect/_call_graph.py +303 -0
  69. scitex/introspect/_class_hierarchy.py +163 -0
  70. scitex/introspect/_core.py +42 -0
  71. scitex/introspect/_docstring.py +131 -0
  72. scitex/introspect/_examples.py +113 -0
  73. scitex/introspect/_imports.py +271 -0
  74. scitex/introspect/_mcp/__init__.py +37 -0
  75. scitex/introspect/_mcp/handlers.py +208 -0
  76. scitex/introspect/_members.py +151 -0
  77. scitex/introspect/_resolve.py +89 -0
  78. scitex/introspect/_signature.py +131 -0
  79. scitex/introspect/_source.py +80 -0
  80. scitex/introspect/_type_hints.py +172 -0
  81. scitex/io/bundle/README.md +1 -1
  82. scitex/mcp_server.py +98 -5
  83. scitex/plt/__init__.py +248 -550
  84. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  85. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  86. scitex/plt/gallery/README.md +1 -1
  87. scitex/plt/utils/_hitmap/__init__.py +82 -0
  88. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  89. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  90. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  91. scitex/plt/utils/_hitmap/_constants.py +40 -0
  92. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  93. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  94. scitex/plt/utils/_hitmap/_query.py +113 -0
  95. scitex/plt/utils/_hitmap.py +46 -1616
  96. scitex/plt/utils/_metadata/__init__.py +80 -0
  97. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  98. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  99. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  100. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  101. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  102. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  103. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  104. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  105. scitex/plt/utils/_metadata/_csv.py +416 -0
  106. scitex/plt/utils/_metadata/_detect.py +225 -0
  107. scitex/plt/utils/_metadata/_legend.py +127 -0
  108. scitex/plt/utils/_metadata/_rounding.py +117 -0
  109. scitex/plt/utils/_metadata/_verification.py +202 -0
  110. scitex/schema/README.md +1 -1
  111. scitex/scholar/__init__.py +8 -0
  112. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  113. scitex/scholar/core/Scholar.py +63 -1700
  114. scitex/scholar/core/_mixins/__init__.py +36 -0
  115. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  116. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  117. scitex/scholar/core/_mixins/_loaders.py +103 -0
  118. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  119. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  120. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  121. scitex/scholar/core/_mixins/_savers.py +69 -0
  122. scitex/scholar/core/_mixins/_search.py +103 -0
  123. scitex/scholar/core/_mixins/_services.py +88 -0
  124. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  125. scitex/scholar/crossref_scitex.py +367 -0
  126. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  127. scitex/scholar/examples/00_run_all.sh +120 -0
  128. scitex/scholar/jobs/_executors.py +27 -3
  129. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  130. scitex/scholar/pdf_download/_cli.py +154 -0
  131. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  132. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  133. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  134. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  135. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  136. scitex/scholar/pipelines/_single_steps.py +71 -36
  137. scitex/scholar/storage/_LibraryManager.py +97 -1695
  138. scitex/scholar/storage/_mixins/__init__.py +30 -0
  139. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  140. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  141. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  142. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  143. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  144. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  145. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  146. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  147. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  148. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  149. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  150. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  151. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  152. scitex/security/README.md +3 -3
  153. scitex/session/README.md +1 -1
  154. scitex/sh/README.md +1 -1
  155. scitex/social/__init__.py +153 -0
  156. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  157. scitex/template/README.md +1 -1
  158. scitex/template/clone_writer_directory.py +5 -5
  159. scitex/writer/README.md +1 -1
  160. scitex/writer/_mcp/handlers.py +11 -744
  161. scitex/writer/_mcp/tool_schemas.py +5 -335
  162. scitex-2.15.1.dist-info/METADATA +648 -0
  163. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/RECORD +166 -111
  164. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  165. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  166. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  167. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  168. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  169. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  170. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  171. scitex/diagram/_compile.py +0 -312
  172. scitex/diagram/_diagram.py +0 -355
  173. scitex/diagram/_mcp/__init__.py +0 -4
  174. scitex/diagram/_mcp/handlers.py +0 -400
  175. scitex/diagram/_mcp/tool_schemas.py +0 -157
  176. scitex/diagram/_presets.py +0 -173
  177. scitex/diagram/_schema.py +0 -182
  178. scitex/diagram/_split.py +0 -278
  179. scitex/plt/_mcp/__init__.py +0 -4
  180. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  181. scitex/plt/_mcp/_handlers_figure.py +0 -195
  182. scitex/plt/_mcp/_handlers_plot.py +0 -252
  183. scitex/plt/_mcp/_handlers_style.py +0 -219
  184. scitex/plt/_mcp/handlers.py +0 -74
  185. scitex/plt/_mcp/tool_schemas.py +0 -497
  186. scitex/plt/mcp_server.py +0 -231
  187. scitex/scholar/data/.gitkeep +0 -0
  188. scitex/scholar/data/README.md +0 -44
  189. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  190. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  191. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  192. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  193. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  194. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  195. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  196. scitex/scholar/data/bib_files/pac.bib +0 -698
  197. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  198. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  199. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  200. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  201. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  202. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  203. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  204. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  205. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  206. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  207. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  208. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  209. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  210. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  211. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  212. scitex/scholar/data/impact_factor.db +0 -0
  213. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  214. scitex/scholar/examples/dev.py +0 -38
  215. scitex-2.14.0.dist-info/METADATA +0 -1238
  216. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/WHEEL +0 -0
  217. {scitex-2.14.0.dist-info → scitex-2.15.1.dist-info}/entry_points.txt +0 -0
  218. {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)}