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,151 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-01-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_members.py
4
+
5
+ """Member listing utilities."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+ from typing import Literal
11
+
12
+ from ._resolve import get_type_info, resolve_object
13
+
14
+
15
+ def list_members(
16
+ dotted_path: str,
17
+ filter: Literal["all", "public", "private", "dunder"] = "public",
18
+ kind: Literal["all", "functions", "classes", "data", "modules"] | None = None,
19
+ include_inherited: bool = False,
20
+ ) -> dict:
21
+ """
22
+ List members of a module or class.
23
+
24
+ Like Python's `dir()` but with filtering and metadata.
25
+
26
+ Parameters
27
+ ----------
28
+ dotted_path : str
29
+ Dotted path to the module or class
30
+ filter : str
31
+ 'all' - All members
32
+ 'public' - Only public (no leading _)
33
+ 'private' - Only private (single _)
34
+ 'dunder' - Only dunder (__name__)
35
+ kind : str | None
36
+ Filter by type: 'functions', 'classes', 'data', 'modules'
37
+ include_inherited : bool
38
+ For classes, include inherited members
39
+
40
+ Returns
41
+ -------
42
+ dict
43
+ members: list[dict] - Each with name, kind, summary
44
+ count: int
45
+ type_info: dict
46
+ """
47
+ obj, error = resolve_object(dotted_path)
48
+ if error:
49
+ return {"success": False, "error": error}
50
+
51
+ type_info = get_type_info(obj)
52
+
53
+ if inspect.isclass(obj) and not include_inherited:
54
+ member_names = list(obj.__dict__.keys())
55
+ else:
56
+ member_names = dir(obj)
57
+
58
+ if filter == "public":
59
+ member_names = [n for n in member_names if not n.startswith("_")]
60
+ elif filter == "private":
61
+ member_names = [
62
+ n for n in member_names if n.startswith("_") and not n.startswith("__")
63
+ ]
64
+ elif filter == "dunder":
65
+ member_names = [
66
+ n for n in member_names if n.startswith("__") and n.endswith("__")
67
+ ]
68
+
69
+ members = []
70
+ for name in sorted(member_names):
71
+ try:
72
+ member = getattr(obj, name)
73
+ except AttributeError:
74
+ continue
75
+
76
+ member_type_info = get_type_info(member)
77
+ member_kind = member_type_info["kind"]
78
+
79
+ if kind:
80
+ kind_map = {
81
+ "functions": ("function", "method", "builtin_function_or_method"),
82
+ "classes": ("class",),
83
+ "data": ("data",),
84
+ "modules": ("module",),
85
+ }
86
+ if kind in kind_map and member_kind not in kind_map[kind]:
87
+ continue
88
+
89
+ doc = inspect.getdoc(member) or ""
90
+ summary = doc.split("\n")[0] if doc else ""
91
+
92
+ members.append(
93
+ {
94
+ "name": name,
95
+ "kind": member_kind,
96
+ "summary": summary[:100] + "..." if len(summary) > 100 else summary,
97
+ }
98
+ )
99
+
100
+ return {
101
+ "success": True,
102
+ "members": members,
103
+ "count": len(members),
104
+ "type_info": type_info,
105
+ }
106
+
107
+
108
+ def get_exports(dotted_path: str) -> dict:
109
+ """
110
+ Get the __all__ exports of a module.
111
+
112
+ Parameters
113
+ ----------
114
+ dotted_path : str
115
+ Dotted path to the module
116
+
117
+ Returns
118
+ -------
119
+ dict
120
+ exports: list[str] - Names in __all__
121
+ has_all: bool - Whether __all__ is defined
122
+ type_info: dict
123
+ """
124
+ obj, error = resolve_object(dotted_path)
125
+ if error:
126
+ return {"success": False, "error": error}
127
+
128
+ type_info = get_type_info(obj)
129
+
130
+ if not inspect.ismodule(obj):
131
+ return {
132
+ "success": False,
133
+ "error": f"'{dotted_path}' is not a module",
134
+ "type_info": type_info,
135
+ }
136
+
137
+ exports = getattr(obj, "__all__", None)
138
+
139
+ if exports is None:
140
+ exports = [n for n in dir(obj) if not n.startswith("_")]
141
+ has_all = False
142
+ else:
143
+ has_all = True
144
+
145
+ return {
146
+ "success": True,
147
+ "exports": list(exports),
148
+ "has_all": has_all,
149
+ "count": len(exports),
150
+ "type_info": type_info,
151
+ }
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-01-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_resolve.py
4
+
5
+ """Object resolution and type information utilities."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import importlib
10
+ import inspect
11
+ from typing import Any
12
+
13
+
14
+ def resolve_object(dotted_path: str) -> tuple[Any, str | None]:
15
+ """
16
+ Resolve a dotted path to a Python object.
17
+
18
+ Parameters
19
+ ----------
20
+ dotted_path : str
21
+ Dotted path like 'scitex.plt.plot' or 'scitex.audio'
22
+
23
+ Returns
24
+ -------
25
+ tuple[Any, str | None]
26
+ (resolved_object, error_message)
27
+ If successful, error_message is None
28
+
29
+ Examples
30
+ --------
31
+ >>> obj, err = resolve_object("scitex.plt")
32
+ >>> obj, err = resolve_object("scitex.audio.speak")
33
+ """
34
+ parts = dotted_path.split(".")
35
+ obj = None
36
+ last_error = None
37
+
38
+ for i in range(len(parts), 0, -1):
39
+ module_path = ".".join(parts[:i])
40
+ try:
41
+ obj = importlib.import_module(module_path)
42
+ for attr_name in parts[i:]:
43
+ obj = getattr(obj, attr_name)
44
+ return obj, None
45
+ except (ImportError, AttributeError) as e:
46
+ last_error = str(e)
47
+ continue
48
+
49
+ return None, f"Could not resolve '{dotted_path}': {last_error}"
50
+
51
+
52
+ def get_type_info(obj: Any) -> dict:
53
+ """
54
+ Get type information about an object.
55
+
56
+ Returns
57
+ -------
58
+ dict
59
+ type: str - The type name
60
+ kind: str - 'module', 'class', 'function', 'method', 'property', 'data'
61
+ module: str - Module where defined
62
+ qualname: str - Qualified name
63
+ """
64
+ type_name = type(obj).__name__
65
+
66
+ if inspect.ismodule(obj):
67
+ kind = "module"
68
+ elif inspect.isclass(obj):
69
+ kind = "class"
70
+ elif inspect.isfunction(obj) or inspect.isbuiltin(obj):
71
+ kind = "function"
72
+ elif inspect.ismethod(obj):
73
+ kind = "method"
74
+ elif isinstance(obj, property):
75
+ kind = "property"
76
+ elif callable(obj):
77
+ kind = "callable"
78
+ else:
79
+ kind = "data"
80
+
81
+ module_name = getattr(obj, "__module__", None)
82
+ qualname = getattr(obj, "__qualname__", getattr(obj, "__name__", str(obj)))
83
+
84
+ return {
85
+ "type": type_name,
86
+ "kind": kind,
87
+ "module": module_name,
88
+ "qualname": qualname,
89
+ }
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-01-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_signature.py
4
+
5
+ """Signature extraction utilities."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+ from typing import Any
11
+
12
+ from ._resolve import get_type_info, resolve_object
13
+
14
+
15
+ def _format_annotation(annotation: Any) -> str:
16
+ """Format a type annotation as a string."""
17
+ if annotation is None:
18
+ return "None"
19
+ if hasattr(annotation, "__name__"):
20
+ return annotation.__name__
21
+ return str(annotation).replace("typing.", "")
22
+
23
+
24
+ def _build_signature_string(
25
+ obj: Any,
26
+ parameters: list[dict],
27
+ return_annotation: str | None,
28
+ ) -> str:
29
+ """Build a human-readable signature string."""
30
+ name = getattr(obj, "__name__", "?")
31
+
32
+ param_strs = []
33
+ for p in parameters:
34
+ s = p["name"]
35
+ if "annotation" in p:
36
+ s += f": {p['annotation']}"
37
+ if "default" in p:
38
+ s += f" = {p['default']}"
39
+ param_strs.append(s)
40
+
41
+ sig = f"{name}({', '.join(param_strs)})"
42
+ if return_annotation:
43
+ sig += f" -> {return_annotation}"
44
+
45
+ return sig
46
+
47
+
48
+ def get_signature(
49
+ dotted_path: str,
50
+ include_defaults: bool = True,
51
+ include_annotations: bool = True,
52
+ ) -> dict:
53
+ """
54
+ Get the signature of a function, method, or class.
55
+
56
+ Like IPython's `func?` but returns structured data.
57
+
58
+ Parameters
59
+ ----------
60
+ dotted_path : str
61
+ Dotted path to the callable (e.g., 'scitex.plt.plot')
62
+ include_defaults : bool
63
+ Include default values in signature
64
+ include_annotations : bool
65
+ Include type annotations
66
+
67
+ Returns
68
+ -------
69
+ dict
70
+ name: str
71
+ signature: str - Human-readable signature
72
+ parameters: list[dict] - Detailed parameter info
73
+ return_annotation: str | None
74
+ type_info: dict
75
+
76
+ Examples
77
+ --------
78
+ >>> get_signature("scitex.plt.plot")
79
+ {'name': 'plot', 'signature': 'plot(spec: dict, ...) -> dict', ...}
80
+ """
81
+ obj, error = resolve_object(dotted_path)
82
+ if error:
83
+ return {"success": False, "error": error}
84
+
85
+ type_info = get_type_info(obj)
86
+
87
+ callable_obj = obj
88
+ if inspect.isclass(obj):
89
+ callable_obj = obj.__init__
90
+
91
+ try:
92
+ sig = inspect.signature(callable_obj)
93
+ except (ValueError, TypeError) as e:
94
+ return {
95
+ "success": False,
96
+ "error": f"Cannot get signature: {e}",
97
+ "type_info": type_info,
98
+ }
99
+
100
+ parameters = []
101
+ for name, param in sig.parameters.items():
102
+ if name == "self":
103
+ continue
104
+
105
+ param_info = {
106
+ "name": name,
107
+ "kind": str(param.kind).split(".")[-1],
108
+ }
109
+
110
+ if include_annotations and param.annotation != inspect.Parameter.empty:
111
+ param_info["annotation"] = _format_annotation(param.annotation)
112
+
113
+ if include_defaults and param.default != inspect.Parameter.empty:
114
+ param_info["default"] = repr(param.default)
115
+
116
+ parameters.append(param_info)
117
+
118
+ return_annotation = None
119
+ if include_annotations and sig.return_annotation != inspect.Signature.empty:
120
+ return_annotation = _format_annotation(sig.return_annotation)
121
+
122
+ sig_str = _build_signature_string(obj, parameters, return_annotation)
123
+
124
+ return {
125
+ "success": True,
126
+ "name": getattr(obj, "__name__", dotted_path.split(".")[-1]),
127
+ "signature": sig_str,
128
+ "parameters": parameters,
129
+ "return_annotation": return_annotation,
130
+ "type_info": type_info,
131
+ }
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-01-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_source.py
4
+
5
+ """Source code retrieval utilities."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+
11
+ from ._resolve import get_type_info, resolve_object
12
+
13
+
14
+ def get_source(
15
+ dotted_path: str,
16
+ max_lines: int | None = None,
17
+ include_decorators: bool = True,
18
+ ) -> dict:
19
+ """
20
+ Get the source code of a Python object.
21
+
22
+ Like IPython's `func??`.
23
+
24
+ Parameters
25
+ ----------
26
+ dotted_path : str
27
+ Dotted path to the object
28
+ max_lines : int | None
29
+ Limit output to first N lines (None = no limit)
30
+ include_decorators : bool
31
+ Include decorator lines
32
+
33
+ Returns
34
+ -------
35
+ dict
36
+ source: str
37
+ file: str - Source file path
38
+ line_start: int - Starting line number
39
+ line_count: int - Number of lines
40
+ type_info: dict
41
+ """
42
+ obj, error = resolve_object(dotted_path)
43
+ if error:
44
+ return {"success": False, "error": error}
45
+
46
+ type_info = get_type_info(obj)
47
+
48
+ try:
49
+ source = inspect.getsource(obj)
50
+ source_file = inspect.getfile(obj)
51
+ _, line_start = inspect.getsourcelines(obj)
52
+ except (TypeError, OSError) as e:
53
+ return {
54
+ "success": False,
55
+ "error": f"Cannot get source: {e}",
56
+ "type_info": type_info,
57
+ }
58
+
59
+ lines = source.split("\n")
60
+ line_count = len(lines)
61
+
62
+ if not include_decorators and lines:
63
+ i = 0
64
+ while i < len(lines) and lines[i].strip().startswith("@"):
65
+ i += 1
66
+ lines = lines[i:]
67
+ source = "\n".join(lines)
68
+
69
+ if max_lines and len(lines) > max_lines:
70
+ lines = lines[:max_lines]
71
+ source = "\n".join(lines) + f"\n... ({line_count - max_lines} more lines)"
72
+
73
+ return {
74
+ "success": True,
75
+ "source": source,
76
+ "file": source_file,
77
+ "line_start": line_start,
78
+ "line_count": line_count,
79
+ "type_info": type_info,
80
+ }
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: 2025-01-20
3
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_type_hints.py
4
+
5
+ """Type hint analysis utilities."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import inspect
10
+ import typing
11
+ from typing import Any, get_type_hints
12
+
13
+ from ._resolve import get_type_info, resolve_object
14
+
15
+
16
+ def get_type_hints_detailed(
17
+ dotted_path: str,
18
+ include_extras: bool = True,
19
+ ) -> dict:
20
+ """
21
+ Get detailed type hint information for a callable or class.
22
+
23
+ Parameters
24
+ ----------
25
+ dotted_path : str
26
+ Dotted path to the function, method, or class
27
+ include_extras : bool
28
+ Include typing extras (Annotated metadata, etc.)
29
+
30
+ Returns
31
+ -------
32
+ dict
33
+ hints: dict[str, dict] - Parameter name to type info
34
+ return_hint: dict | None - Return type info
35
+ type_info: dict
36
+
37
+ Examples
38
+ --------
39
+ >>> get_type_hints_detailed("json.dumps")
40
+ """
41
+ obj, error = resolve_object(dotted_path)
42
+ if error:
43
+ return {"success": False, "error": error}
44
+
45
+ type_info_obj = get_type_info(obj)
46
+
47
+ # Get the object to analyze
48
+ target = obj
49
+ if inspect.isclass(obj):
50
+ target = obj.__init__
51
+
52
+ try:
53
+ hints = get_type_hints(target, include_extras=include_extras)
54
+ except Exception as e:
55
+ # Some objects don't support get_type_hints
56
+ hints = getattr(target, "__annotations__", {})
57
+ if not hints:
58
+ return {
59
+ "success": False,
60
+ "error": f"Cannot get type hints: {e}",
61
+ "type_info": type_info_obj,
62
+ }
63
+
64
+ # Analyze each hint
65
+ analyzed_hints = {}
66
+ return_hint = None
67
+
68
+ for name, hint in hints.items():
69
+ hint_info = _analyze_type(hint)
70
+ if name == "return":
71
+ return_hint = hint_info
72
+ else:
73
+ analyzed_hints[name] = hint_info
74
+
75
+ return {
76
+ "success": True,
77
+ "hints": analyzed_hints,
78
+ "return_hint": return_hint,
79
+ "hint_count": len(analyzed_hints),
80
+ "type_info": type_info_obj,
81
+ }
82
+
83
+
84
+ def _analyze_type(hint: Any) -> dict:
85
+ """Analyze a type hint and return structured info."""
86
+ result = {
87
+ "raw": _format_type(hint),
88
+ "origin": None,
89
+ "args": [],
90
+ "is_optional": False,
91
+ "is_union": False,
92
+ "is_generic": False,
93
+ }
94
+
95
+ # Get origin (e.g., list from list[int])
96
+ origin = typing.get_origin(hint)
97
+ if origin is not None:
98
+ result["origin"] = _format_type(origin)
99
+ result["is_generic"] = True
100
+
101
+ # Get args (e.g., int from list[int])
102
+ args = typing.get_args(hint)
103
+ if args:
104
+ result["args"] = [_format_type(a) for a in args]
105
+
106
+ # Check for Union/Optional
107
+ if origin is typing.Union:
108
+ result["is_union"] = True
109
+ # Optional is Union[X, None]
110
+ if type(None) in args:
111
+ result["is_optional"] = True
112
+
113
+ return result
114
+
115
+
116
+ def _format_type(t: Any) -> str:
117
+ """Format a type as a readable string."""
118
+ if t is type(None):
119
+ return "None"
120
+ if hasattr(t, "__name__"):
121
+ return t.__name__
122
+ if hasattr(t, "_name"):
123
+ return t._name or str(t)
124
+ return str(t).replace("typing.", "")
125
+
126
+
127
+ def get_class_annotations(dotted_path: str) -> dict:
128
+ """
129
+ Get all annotations for a class (class vars and methods).
130
+
131
+ Parameters
132
+ ----------
133
+ dotted_path : str
134
+ Dotted path to the class
135
+
136
+ Returns
137
+ -------
138
+ dict
139
+ class_vars: dict - Class variable annotations
140
+ methods: dict - Method annotations (name -> hints)
141
+ """
142
+ obj, error = resolve_object(dotted_path)
143
+ if error:
144
+ return {"success": False, "error": error}
145
+
146
+ if not inspect.isclass(obj):
147
+ return {"success": False, "error": f"'{dotted_path}' is not a class"}
148
+
149
+ # Class-level annotations
150
+ class_vars = {}
151
+ for name, hint in getattr(obj, "__annotations__", {}).items():
152
+ class_vars[name] = _analyze_type(hint)
153
+
154
+ # Method annotations
155
+ methods = {}
156
+ for name, member in inspect.getmembers(obj):
157
+ if inspect.isfunction(member) or inspect.ismethod(member):
158
+ try:
159
+ hints = get_type_hints(member)
160
+ if hints:
161
+ methods[name] = {k: _analyze_type(v) for k, v in hints.items()}
162
+ except Exception:
163
+ pass
164
+
165
+ return {
166
+ "success": True,
167
+ "class": dotted_path,
168
+ "class_vars": class_vars,
169
+ "methods": methods,
170
+ "class_var_count": len(class_vars),
171
+ "method_count": len(methods),
172
+ }
@@ -1,7 +1,7 @@
1
1
  <!-- ---
2
2
  !-- Timestamp: 2026-01-07
3
3
  !-- Author: ywatanabe
4
- !-- File: /home/ywatanabe/proj/scitex-code/src/scitex/io/bundle/README.md
4
+ !-- File: /home/ywatanabe/proj/scitex-python/src/scitex/io/bundle/README.md
5
5
  !-- --- -->
6
6
 
7
7
  # SciTeX Bundle I/O