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,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
|
+
}
|
scitex/io/bundle/README.md
CHANGED