scitex 2.14.0__py3-none-any.whl → 2.15.2__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 +71 -17
- 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 +210 -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/template.py +24 -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/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
- scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
- scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
- scitex/audio/README.md +40 -36
- scitex/audio/__init__.py +129 -61
- 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/_tts.py +18 -10
- scitex/audio/engines/base.py +17 -10
- scitex/audio/engines/elevenlabs_engine.py +7 -2
- 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/_core/__init__.py +27 -0
- scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
- scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
- scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
- scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
- scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
- scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
- scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
- scitex/canvas/editor/flask_editor/_core.py +25 -1684
- 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 +481 -0
- scitex/cli/main.py +200 -109
- scitex/cli/mcp.py +60 -34
- scitex/cli/plt.py +357 -0
- scitex/cli/repro.py +15 -8
- scitex/cli/resource.py +15 -8
- scitex/cli/scholar/__init__.py +23 -8
- scitex/cli/scholar/_crossref_scitex.py +296 -0
- scitex/cli/scholar/_fetch.py +25 -3
- scitex/cli/social.py +314 -0
- scitex/cli/stats.py +15 -8
- scitex/cli/template.py +129 -12
- scitex/cli/tex.py +15 -8
- scitex/cli/writer.py +132 -8
- scitex/cloud/__init__.py +41 -2
- scitex/config/README.md +1 -1
- scitex/config/__init__.py +16 -2
- scitex/config/_env_registry.py +256 -0
- scitex/context/__init__.py +22 -0
- scitex/dev/__init__.py +20 -1
- scitex/diagram/__init__.py +42 -19
- scitex/diagram/mcp_server.py +13 -125
- scitex/gen/__init__.py +50 -14
- scitex/gen/_list_packages.py +4 -4
- scitex/introspect/__init__.py +82 -0
- scitex/introspect/_call_graph.py +303 -0
- scitex/introspect/_class_hierarchy.py +163 -0
- scitex/introspect/_core.py +41 -0
- scitex/introspect/_docstring.py +131 -0
- scitex/introspect/_examples.py +113 -0
- scitex/introspect/_imports.py +271 -0
- scitex/{gen/_inspect_module.py → introspect/_list_api.py} +43 -54
- scitex/introspect/_mcp/__init__.py +41 -0
- scitex/introspect/_mcp/handlers.py +233 -0
- scitex/introspect/_members.py +155 -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/_save.py +1 -2
- scitex/io/bundle/README.md +1 -1
- scitex/logging/_formatters.py +19 -9
- scitex/mcp_server.py +98 -5
- scitex/os/__init__.py +4 -0
- scitex/{gen → os}/_check_host.py +4 -5
- scitex/plt/__init__.py +245 -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/session/__init__.py +26 -7
- scitex/session/_decorator.py +1 -1
- scitex/sh/README.md +1 -1
- scitex/sh/__init__.py +7 -4
- scitex/social/__init__.py +155 -0
- scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
- scitex/stats/_mcp/_handlers/__init__.py +31 -0
- scitex/stats/_mcp/_handlers/_corrections.py +113 -0
- scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
- scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
- scitex/stats/_mcp/_handlers/_format.py +94 -0
- scitex/stats/_mcp/_handlers/_normality.py +110 -0
- scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
- scitex/stats/_mcp/_handlers/_power.py +247 -0
- scitex/stats/_mcp/_handlers/_recommend.py +102 -0
- scitex/stats/_mcp/_handlers/_run_test.py +279 -0
- scitex/stats/_mcp/_handlers/_stars.py +48 -0
- scitex/stats/_mcp/handlers.py +19 -1171
- scitex/stats/auto/_stat_style.py +175 -0
- scitex/stats/auto/_style_definitions.py +411 -0
- scitex/stats/auto/_styles.py +22 -620
- scitex/stats/descriptive/__init__.py +11 -8
- scitex/stats/descriptive/_ci.py +39 -0
- scitex/stats/power/_power.py +15 -4
- scitex/str/__init__.py +2 -1
- scitex/str/_title_case.py +63 -0
- scitex/template/README.md +1 -1
- scitex/template/__init__.py +25 -10
- scitex/template/_code_templates.py +147 -0
- scitex/template/_mcp/handlers.py +81 -0
- scitex/template/_mcp/tool_schemas.py +55 -0
- scitex/template/_templates/__init__.py +51 -0
- scitex/template/_templates/audio.py +233 -0
- scitex/template/_templates/canvas.py +312 -0
- scitex/template/_templates/capture.py +268 -0
- scitex/template/_templates/config.py +43 -0
- scitex/template/_templates/diagram.py +294 -0
- scitex/template/_templates/io.py +107 -0
- scitex/template/_templates/module.py +53 -0
- scitex/template/_templates/plt.py +202 -0
- scitex/template/_templates/scholar.py +267 -0
- scitex/template/_templates/session.py +130 -0
- scitex/template/_templates/session_minimal.py +43 -0
- scitex/template/_templates/session_plot.py +67 -0
- scitex/template/_templates/session_stats.py +77 -0
- scitex/template/_templates/stats.py +323 -0
- scitex/template/_templates/writer.py +296 -0
- scitex/template/clone_writer_directory.py +5 -5
- scitex/ui/_backends/_email.py +10 -2
- scitex/ui/_backends/_webhook.py +5 -1
- scitex/web/_search_pubmed.py +10 -6
- scitex/writer/README.md +1 -1
- scitex/writer/_mcp/handlers.py +11 -744
- scitex/writer/_mcp/tool_schemas.py +5 -335
- scitex-2.15.2.dist-info/METADATA +648 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/RECORD +246 -150
- 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/gen/_ci.py +0 -12
- scitex/gen/_title_case.py +0 -89
- 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/{gen → context}/_detect_environment.py +0 -0
- /scitex/{gen → context}/_get_notebook_path.py +0 -0
- /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/WHEEL +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
- {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
scitex/gen/__init__.py
CHANGED
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Scitex gen module.
|
|
2
|
+
"""Scitex gen module.
|
|
3
|
+
|
|
4
|
+
NOTE: This module is being refactored. Many functions are being moved to
|
|
5
|
+
more appropriate locations. For backward compatibility, they are re-exported
|
|
6
|
+
here with deprecation warnings.
|
|
7
|
+
|
|
8
|
+
Recommended imports:
|
|
9
|
+
- ci -> scitex.stats.descriptive.ci
|
|
10
|
+
- check_host, is_host, verify_host -> scitex.os
|
|
11
|
+
- detect_environment, is_notebook, is_script -> scitex.context
|
|
12
|
+
- list_api -> scitex.introspect
|
|
13
|
+
- run_shellcommand, run_shellscript -> scitex.sh
|
|
14
|
+
- xml2dict, XmlDictConfig, XmlListConfig -> scitex.io
|
|
15
|
+
- title_case -> scitex.str
|
|
16
|
+
- symlink -> scitex.path
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import warnings
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _deprecation_warning(old_path, new_path):
|
|
23
|
+
warnings.warn(
|
|
24
|
+
f"{old_path} is deprecated, use {new_path} instead",
|
|
25
|
+
DeprecationWarning,
|
|
26
|
+
stacklevel=3,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ci -> scitex.stats.descriptive.ci (with re-export for backward compat)
|
|
31
|
+
from scitex.stats.descriptive import ci
|
|
3
32
|
|
|
4
33
|
# Optional: DimHandler requires torch
|
|
5
34
|
try:
|
|
6
35
|
from ._DimHandler import DimHandler
|
|
7
36
|
except ImportError:
|
|
8
37
|
DimHandler = None
|
|
38
|
+
# check_host moved to scitex.os (re-export for backward compatibility)
|
|
39
|
+
from scitex.os import check_host, is_host, verify_host
|
|
40
|
+
|
|
9
41
|
from ._alternate_kwarg import alternate_kwarg
|
|
10
42
|
from ._cache import cache
|
|
11
|
-
from ._check_host import check_host, is_host, verify_host
|
|
12
|
-
from ._ci import ci
|
|
13
43
|
from ._deprecated_close import (
|
|
14
44
|
close as _deprecated_close,
|
|
15
45
|
)
|
|
@@ -27,7 +57,9 @@ try:
|
|
|
27
57
|
from ._embed import embed
|
|
28
58
|
except ImportError:
|
|
29
59
|
embed = None
|
|
30
|
-
|
|
60
|
+
# list_api moved to scitex.introspect (re-export for backward compatibility)
|
|
61
|
+
from scitex.introspect import list_api
|
|
62
|
+
|
|
31
63
|
from ._is_ipython import is_ipython, is_script
|
|
32
64
|
from ._less import less
|
|
33
65
|
from ._list_packages import list_packages, main
|
|
@@ -51,9 +83,11 @@ except ImportError:
|
|
|
51
83
|
to_nanz = None
|
|
52
84
|
to_z = None
|
|
53
85
|
unbias = None
|
|
86
|
+
# shell functions moved to scitex.sh (re-export for backward compatibility)
|
|
87
|
+
from scitex.sh import run_shellcommand, run_shellscript
|
|
88
|
+
|
|
54
89
|
from ._paste import paste
|
|
55
90
|
from ._print_config import print_config, print_config_main
|
|
56
|
-
from ._shell import run_shellcommand, run_shellscript
|
|
57
91
|
from ._src import src
|
|
58
92
|
from ._TimeStamper import TimeStamper
|
|
59
93
|
|
|
@@ -62,20 +96,23 @@ start = _deprecated_start
|
|
|
62
96
|
close = _deprecated_close
|
|
63
97
|
running2finished = _deprecated_running2finished
|
|
64
98
|
|
|
65
|
-
|
|
99
|
+
# environment detection moved to scitex.context (re-export for backward compatibility)
|
|
100
|
+
from scitex.context import (
|
|
66
101
|
detect_environment,
|
|
67
|
-
get_output_directory,
|
|
68
|
-
is_notebook,
|
|
69
|
-
)
|
|
70
|
-
from ._get_notebook_path import (
|
|
71
102
|
get_notebook_directory,
|
|
103
|
+
get_notebook_info_simple,
|
|
72
104
|
get_notebook_name,
|
|
73
105
|
get_notebook_path,
|
|
106
|
+
get_output_directory,
|
|
107
|
+
is_notebook,
|
|
74
108
|
)
|
|
109
|
+
|
|
110
|
+
# title_case moved to scitex.str (re-export for backward compatibility)
|
|
111
|
+
from scitex.str import title_case
|
|
112
|
+
|
|
75
113
|
from ._symlink import symlink
|
|
76
114
|
from ._symlog import symlog
|
|
77
115
|
from ._title2path import title2path
|
|
78
|
-
from ._title_case import main, title_case
|
|
79
116
|
from ._to_even import to_even
|
|
80
117
|
from ._to_odd import to_odd
|
|
81
118
|
|
|
@@ -99,10 +136,9 @@ except ImportError:
|
|
|
99
136
|
pass # Already set to None above
|
|
100
137
|
from ._wrap import wrap
|
|
101
138
|
from ._xml2dict import XmlDictConfig, XmlListConfig, xml2dict
|
|
102
|
-
from .misc import float_linspace
|
|
103
139
|
|
|
104
140
|
# Import from misc module
|
|
105
|
-
from .misc import connect_nums
|
|
141
|
+
from .misc import connect_nums, float_linspace
|
|
106
142
|
|
|
107
143
|
__all__ = [
|
|
108
144
|
"ArrayLike",
|
|
@@ -122,7 +158,7 @@ __all__ = [
|
|
|
122
158
|
"dir2npy",
|
|
123
159
|
"embed",
|
|
124
160
|
"float_linspace",
|
|
125
|
-
"
|
|
161
|
+
"list_api",
|
|
126
162
|
"is_host",
|
|
127
163
|
"is_ipython",
|
|
128
164
|
"is_script",
|
scitex/gen/_list_packages.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
2
|
# Time-stamp: "2024-11-03 02:11:54 (ywatanabe)"
|
|
4
3
|
# File: ./scitex_repo/src/scitex/gen/_list_packages.py
|
|
5
4
|
"""
|
|
@@ -25,8 +24,6 @@ except ImportError:
|
|
|
25
24
|
# Fallback for older Python versions
|
|
26
25
|
from importlib_metadata import distributions
|
|
27
26
|
|
|
28
|
-
from ._inspect_module import inspect_module
|
|
29
|
-
|
|
30
27
|
|
|
31
28
|
def list_packages(
|
|
32
29
|
max_depth: int = 1,
|
|
@@ -80,10 +77,12 @@ def list_packages(
|
|
|
80
77
|
pkg for pkg in installed_packages if pkg not in safelist
|
|
81
78
|
]
|
|
82
79
|
|
|
80
|
+
from scitex.introspect import list_api
|
|
81
|
+
|
|
83
82
|
all_dfs = []
|
|
84
83
|
for package_name in installed_packages:
|
|
85
84
|
try:
|
|
86
|
-
df =
|
|
85
|
+
df = list_api(
|
|
87
86
|
package_name,
|
|
88
87
|
docstring=False, # Speed up by skipping docstrings
|
|
89
88
|
print_output=False,
|
|
@@ -116,6 +115,7 @@ def main() -> Optional[int]:
|
|
|
116
115
|
|
|
117
116
|
if __name__ == "__main__":
|
|
118
117
|
import matplotlib.pyplot as plt
|
|
118
|
+
|
|
119
119
|
import scitex
|
|
120
120
|
|
|
121
121
|
CONFIG, sys.stdout, sys.stderr, plt, CC = scitex.session.start(
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/__init__.py
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Introspection utilities for Python packages.
|
|
7
|
+
|
|
8
|
+
Provides IPython-like introspection capabilities for any Python package.
|
|
9
|
+
|
|
10
|
+
IPython-style shortcuts:
|
|
11
|
+
- q: Function/class signature with type hints (like `func?`)
|
|
12
|
+
- qq: Full source code (like `func??`)
|
|
13
|
+
- dir: List attributes/methods (like `dir()`)
|
|
14
|
+
- list_api: Recursive module API tree
|
|
15
|
+
|
|
16
|
+
Basic Introspection:
|
|
17
|
+
- get_docstring: Docstring extraction with parsing
|
|
18
|
+
- get_exports: Module's __all__ contents
|
|
19
|
+
- find_examples: Find usage examples in tests/examples
|
|
20
|
+
|
|
21
|
+
Advanced Introspection:
|
|
22
|
+
- get_class_hierarchy: Inheritance tree (MRO + subclasses)
|
|
23
|
+
- get_mro: Method Resolution Order only
|
|
24
|
+
- get_type_hints_detailed: Detailed type annotation analysis
|
|
25
|
+
- get_class_annotations: Class variable and method annotations
|
|
26
|
+
- get_imports: Static import analysis using AST
|
|
27
|
+
- get_dependencies: Module dependency analysis
|
|
28
|
+
- get_call_graph: Function call graph (with timeout protection)
|
|
29
|
+
- get_function_calls: Simple outgoing calls list
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from ._core import (
|
|
33
|
+
# IPython-style names
|
|
34
|
+
dir,
|
|
35
|
+
# Basic
|
|
36
|
+
find_examples,
|
|
37
|
+
# Advanced - Call graph
|
|
38
|
+
get_call_graph,
|
|
39
|
+
# Advanced - Type hints
|
|
40
|
+
get_class_annotations,
|
|
41
|
+
# Advanced - Class hierarchy
|
|
42
|
+
get_class_hierarchy,
|
|
43
|
+
# Advanced - Imports
|
|
44
|
+
get_dependencies,
|
|
45
|
+
get_docstring,
|
|
46
|
+
get_exports,
|
|
47
|
+
get_function_calls,
|
|
48
|
+
get_imports,
|
|
49
|
+
get_mro,
|
|
50
|
+
get_type_hints_detailed,
|
|
51
|
+
get_type_info,
|
|
52
|
+
q,
|
|
53
|
+
qq,
|
|
54
|
+
resolve_object,
|
|
55
|
+
)
|
|
56
|
+
from ._list_api import list_api
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
# IPython-style names
|
|
60
|
+
"q",
|
|
61
|
+
"qq",
|
|
62
|
+
"dir",
|
|
63
|
+
"list_api",
|
|
64
|
+
# Basic
|
|
65
|
+
"get_docstring",
|
|
66
|
+
"get_exports",
|
|
67
|
+
"find_examples",
|
|
68
|
+
"resolve_object",
|
|
69
|
+
"get_type_info",
|
|
70
|
+
# Advanced - Class hierarchy
|
|
71
|
+
"get_class_hierarchy",
|
|
72
|
+
"get_mro",
|
|
73
|
+
# Advanced - Type hints
|
|
74
|
+
"get_type_hints_detailed",
|
|
75
|
+
"get_class_annotations",
|
|
76
|
+
# Advanced - Imports
|
|
77
|
+
"get_imports",
|
|
78
|
+
"get_dependencies",
|
|
79
|
+
# Advanced - Call graph
|
|
80
|
+
"get_call_graph",
|
|
81
|
+
"get_function_calls",
|
|
82
|
+
]
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_call_graph.py
|
|
4
|
+
|
|
5
|
+
"""Call graph analysis using AST with timeout protection."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import ast
|
|
10
|
+
import inspect
|
|
11
|
+
import signal
|
|
12
|
+
from contextlib import contextmanager
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from ._resolve import get_type_info, resolve_object
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TimeoutError(Exception):
|
|
19
|
+
"""Raised when operation times out."""
|
|
20
|
+
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@contextmanager
|
|
25
|
+
def timeout(seconds: int):
|
|
26
|
+
"""Context manager for timeout (Unix only)."""
|
|
27
|
+
|
|
28
|
+
def handler(signum, frame):
|
|
29
|
+
raise TimeoutError(f"Operation timed out after {seconds}s")
|
|
30
|
+
|
|
31
|
+
# Only works on Unix
|
|
32
|
+
try:
|
|
33
|
+
old_handler = signal.signal(signal.SIGALRM, handler)
|
|
34
|
+
signal.alarm(seconds)
|
|
35
|
+
try:
|
|
36
|
+
yield
|
|
37
|
+
finally:
|
|
38
|
+
signal.alarm(0)
|
|
39
|
+
signal.signal(signal.SIGALRM, old_handler)
|
|
40
|
+
except (ValueError, AttributeError):
|
|
41
|
+
# Windows or signal not available - no timeout
|
|
42
|
+
yield
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_call_graph(
|
|
46
|
+
dotted_path: str,
|
|
47
|
+
max_depth: int = 2,
|
|
48
|
+
timeout_seconds: int = 10,
|
|
49
|
+
internal_only: bool = True,
|
|
50
|
+
) -> dict:
|
|
51
|
+
"""
|
|
52
|
+
Get the call graph of a function or module using static AST analysis.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
dotted_path : str
|
|
57
|
+
Dotted path to the function or module
|
|
58
|
+
max_depth : int
|
|
59
|
+
Maximum depth to traverse calls
|
|
60
|
+
timeout_seconds : int
|
|
61
|
+
Timeout in seconds (0 = no timeout)
|
|
62
|
+
internal_only : bool
|
|
63
|
+
Only show calls to functions in the same module
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
dict
|
|
68
|
+
calls: list[dict] - Functions this function calls
|
|
69
|
+
called_by: list[dict] - Functions that call this (if module)
|
|
70
|
+
graph: dict - Full call graph tree
|
|
71
|
+
|
|
72
|
+
Examples
|
|
73
|
+
--------
|
|
74
|
+
>>> get_call_graph("scitex.audio.speak")
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
if timeout_seconds > 0:
|
|
78
|
+
with timeout(timeout_seconds):
|
|
79
|
+
return _analyze_call_graph(dotted_path, max_depth, internal_only)
|
|
80
|
+
else:
|
|
81
|
+
return _analyze_call_graph(dotted_path, max_depth, internal_only)
|
|
82
|
+
except TimeoutError as e:
|
|
83
|
+
return {
|
|
84
|
+
"success": False,
|
|
85
|
+
"error": str(e),
|
|
86
|
+
"partial": True,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _analyze_call_graph(
|
|
91
|
+
dotted_path: str,
|
|
92
|
+
max_depth: int,
|
|
93
|
+
internal_only: bool,
|
|
94
|
+
) -> dict:
|
|
95
|
+
"""Perform the actual call graph analysis."""
|
|
96
|
+
obj, error = resolve_object(dotted_path)
|
|
97
|
+
if error:
|
|
98
|
+
return {"success": False, "error": error}
|
|
99
|
+
|
|
100
|
+
type_info = get_type_info(obj)
|
|
101
|
+
|
|
102
|
+
# Get source file
|
|
103
|
+
try:
|
|
104
|
+
source_file = inspect.getfile(obj)
|
|
105
|
+
source = Path(source_file).read_text()
|
|
106
|
+
tree = ast.parse(source)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
return {
|
|
109
|
+
"success": False,
|
|
110
|
+
"error": f"Cannot parse source: {e}",
|
|
111
|
+
"type_info": type_info,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Build function index for the module
|
|
115
|
+
func_index = _build_function_index(tree)
|
|
116
|
+
|
|
117
|
+
if inspect.isfunction(obj):
|
|
118
|
+
# Analyze single function
|
|
119
|
+
func_name = obj.__name__
|
|
120
|
+
if func_name not in func_index:
|
|
121
|
+
return {
|
|
122
|
+
"success": False,
|
|
123
|
+
"error": f"Function '{func_name}' not found in source",
|
|
124
|
+
"type_info": type_info,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
calls = _get_function_calls(func_index[func_name], internal_only, func_index)
|
|
128
|
+
called_by = _find_callers(func_name, func_index)
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
"success": True,
|
|
132
|
+
"function": func_name,
|
|
133
|
+
"calls": calls,
|
|
134
|
+
"call_count": len(calls),
|
|
135
|
+
"called_by": called_by,
|
|
136
|
+
"caller_count": len(called_by),
|
|
137
|
+
"type_info": type_info,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
elif inspect.ismodule(obj):
|
|
141
|
+
# Analyze entire module
|
|
142
|
+
graph = {}
|
|
143
|
+
for func_name, func_node in func_index.items():
|
|
144
|
+
calls = _get_function_calls(func_node, internal_only, func_index)
|
|
145
|
+
graph[func_name] = {
|
|
146
|
+
"calls": calls,
|
|
147
|
+
"line": func_node.lineno,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"success": True,
|
|
152
|
+
"module": dotted_path,
|
|
153
|
+
"graph": graph,
|
|
154
|
+
"function_count": len(graph),
|
|
155
|
+
"type_info": type_info,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
else:
|
|
159
|
+
return {
|
|
160
|
+
"success": False,
|
|
161
|
+
"error": "Can only analyze functions or modules",
|
|
162
|
+
"type_info": type_info,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _build_function_index(tree: ast.AST) -> dict[str, ast.FunctionDef]:
|
|
167
|
+
"""Build index of all functions in the AST."""
|
|
168
|
+
index = {}
|
|
169
|
+
for node in ast.walk(tree):
|
|
170
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
171
|
+
index[node.name] = node
|
|
172
|
+
return index
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _get_function_calls(
|
|
176
|
+
func_node: ast.FunctionDef,
|
|
177
|
+
internal_only: bool,
|
|
178
|
+
func_index: dict,
|
|
179
|
+
) -> list[dict]:
|
|
180
|
+
"""Extract all function calls from a function."""
|
|
181
|
+
calls = []
|
|
182
|
+
seen = set()
|
|
183
|
+
|
|
184
|
+
for node in ast.walk(func_node):
|
|
185
|
+
if isinstance(node, ast.Call):
|
|
186
|
+
call_info = _extract_call_info(node)
|
|
187
|
+
if call_info and call_info["name"] not in seen:
|
|
188
|
+
# Filter to internal only if requested
|
|
189
|
+
if internal_only and call_info["name"] not in func_index:
|
|
190
|
+
continue
|
|
191
|
+
seen.add(call_info["name"])
|
|
192
|
+
calls.append(call_info)
|
|
193
|
+
|
|
194
|
+
return calls
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _extract_call_info(node: ast.Call) -> dict | None:
|
|
198
|
+
"""Extract information about a function call."""
|
|
199
|
+
func = node.func
|
|
200
|
+
|
|
201
|
+
if isinstance(func, ast.Name):
|
|
202
|
+
# Simple call: func()
|
|
203
|
+
return {
|
|
204
|
+
"name": func.id,
|
|
205
|
+
"type": "function",
|
|
206
|
+
"line": node.lineno,
|
|
207
|
+
}
|
|
208
|
+
elif isinstance(func, ast.Attribute):
|
|
209
|
+
# Method call: obj.method()
|
|
210
|
+
if isinstance(func.value, ast.Name):
|
|
211
|
+
return {
|
|
212
|
+
"name": f"{func.value.id}.{func.attr}",
|
|
213
|
+
"type": "method",
|
|
214
|
+
"object": func.value.id,
|
|
215
|
+
"method": func.attr,
|
|
216
|
+
"line": node.lineno,
|
|
217
|
+
}
|
|
218
|
+
else:
|
|
219
|
+
return {
|
|
220
|
+
"name": func.attr,
|
|
221
|
+
"type": "method",
|
|
222
|
+
"method": func.attr,
|
|
223
|
+
"line": node.lineno,
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _find_callers(
|
|
230
|
+
func_name: str,
|
|
231
|
+
func_index: dict[str, ast.FunctionDef],
|
|
232
|
+
) -> list[dict]:
|
|
233
|
+
"""Find all functions that call the given function."""
|
|
234
|
+
callers = []
|
|
235
|
+
|
|
236
|
+
for caller_name, caller_node in func_index.items():
|
|
237
|
+
if caller_name == func_name:
|
|
238
|
+
continue
|
|
239
|
+
|
|
240
|
+
for node in ast.walk(caller_node):
|
|
241
|
+
if isinstance(node, ast.Call):
|
|
242
|
+
call_info = _extract_call_info(node)
|
|
243
|
+
if call_info and call_info["name"] == func_name:
|
|
244
|
+
callers.append(
|
|
245
|
+
{
|
|
246
|
+
"name": caller_name,
|
|
247
|
+
"line": caller_node.lineno,
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
break
|
|
251
|
+
|
|
252
|
+
return callers
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def get_function_calls(
|
|
256
|
+
dotted_path: str,
|
|
257
|
+
include_methods: bool = True,
|
|
258
|
+
include_builtins: bool = False,
|
|
259
|
+
) -> dict:
|
|
260
|
+
"""
|
|
261
|
+
Get just the outgoing calls from a function.
|
|
262
|
+
|
|
263
|
+
Simpler version of get_call_graph for quick lookup.
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
dotted_path : str
|
|
268
|
+
Dotted path to the function
|
|
269
|
+
include_methods : bool
|
|
270
|
+
Include method calls (obj.method())
|
|
271
|
+
include_builtins : bool
|
|
272
|
+
Include builtin function calls
|
|
273
|
+
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
dict
|
|
277
|
+
calls: list[str] - Names of called functions
|
|
278
|
+
"""
|
|
279
|
+
result = get_call_graph(dotted_path, max_depth=1, internal_only=False)
|
|
280
|
+
|
|
281
|
+
if not result.get("success"):
|
|
282
|
+
return result
|
|
283
|
+
|
|
284
|
+
calls = result.get("calls", [])
|
|
285
|
+
|
|
286
|
+
# Filter
|
|
287
|
+
filtered = []
|
|
288
|
+
builtins = {"print", "len", "range", "str", "int", "float", "list", "dict", "set"}
|
|
289
|
+
|
|
290
|
+
for call in calls:
|
|
291
|
+
name = call["name"]
|
|
292
|
+
if not include_methods and call.get("type") == "method":
|
|
293
|
+
continue
|
|
294
|
+
if not include_builtins and name in builtins:
|
|
295
|
+
continue
|
|
296
|
+
filtered.append(name)
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
"success": True,
|
|
300
|
+
"function": dotted_path,
|
|
301
|
+
"calls": filtered,
|
|
302
|
+
"call_count": len(filtered),
|
|
303
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Timestamp: 2025-01-20
|
|
3
|
+
# File: /home/ywatanabe/proj/scitex-code/src/scitex/introspect/_class_hierarchy.py
|
|
4
|
+
|
|
5
|
+
"""Class hierarchy analysis 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_class_hierarchy(
|
|
15
|
+
dotted_path: str,
|
|
16
|
+
include_builtins: bool = False,
|
|
17
|
+
max_depth: int = 10,
|
|
18
|
+
) -> dict:
|
|
19
|
+
"""
|
|
20
|
+
Get the inheritance hierarchy of a class.
|
|
21
|
+
|
|
22
|
+
Shows both parent classes (MRO) and known subclasses.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
dotted_path : str
|
|
27
|
+
Dotted path to the class (e.g., 'pandas.DataFrame')
|
|
28
|
+
include_builtins : bool
|
|
29
|
+
Include builtin classes like object, type in hierarchy
|
|
30
|
+
max_depth : int
|
|
31
|
+
Maximum depth for subclass traversal
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
dict
|
|
36
|
+
mro: list[str] - Method Resolution Order (parent classes)
|
|
37
|
+
subclasses: list[dict] - Known subclasses (recursive)
|
|
38
|
+
type_info: dict
|
|
39
|
+
|
|
40
|
+
Examples
|
|
41
|
+
--------
|
|
42
|
+
>>> get_class_hierarchy("collections.abc.Mapping")
|
|
43
|
+
"""
|
|
44
|
+
obj, error = resolve_object(dotted_path)
|
|
45
|
+
if error:
|
|
46
|
+
return {"success": False, "error": error}
|
|
47
|
+
|
|
48
|
+
type_info = get_type_info(obj)
|
|
49
|
+
|
|
50
|
+
if not inspect.isclass(obj):
|
|
51
|
+
return {
|
|
52
|
+
"success": False,
|
|
53
|
+
"error": f"'{dotted_path}' is not a class",
|
|
54
|
+
"type_info": type_info,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Get MRO (parent classes)
|
|
58
|
+
mro = []
|
|
59
|
+
for cls in inspect.getmro(obj):
|
|
60
|
+
if not include_builtins and cls.__module__ == "builtins":
|
|
61
|
+
continue
|
|
62
|
+
mro.append(
|
|
63
|
+
{
|
|
64
|
+
"name": cls.__name__,
|
|
65
|
+
"module": cls.__module__,
|
|
66
|
+
"qualname": f"{cls.__module__}.{cls.__name__}",
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Get subclasses recursively
|
|
71
|
+
subclasses = _get_subclasses_recursive(obj, max_depth, include_builtins)
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
"success": True,
|
|
75
|
+
"class": dotted_path,
|
|
76
|
+
"mro": mro,
|
|
77
|
+
"mro_count": len(mro),
|
|
78
|
+
"subclasses": subclasses,
|
|
79
|
+
"subclass_count": _count_subclasses(subclasses),
|
|
80
|
+
"type_info": type_info,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _get_subclasses_recursive(
|
|
85
|
+
cls: type,
|
|
86
|
+
max_depth: int,
|
|
87
|
+
include_builtins: bool,
|
|
88
|
+
current_depth: int = 0,
|
|
89
|
+
) -> list[dict]:
|
|
90
|
+
"""Recursively get all subclasses."""
|
|
91
|
+
if current_depth >= max_depth:
|
|
92
|
+
return []
|
|
93
|
+
|
|
94
|
+
result = []
|
|
95
|
+
try:
|
|
96
|
+
for sub in cls.__subclasses__():
|
|
97
|
+
if not include_builtins and sub.__module__ == "builtins":
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
sub_info = {
|
|
101
|
+
"name": sub.__name__,
|
|
102
|
+
"module": sub.__module__,
|
|
103
|
+
"qualname": f"{sub.__module__}.{sub.__name__}",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
children = _get_subclasses_recursive(
|
|
107
|
+
sub, max_depth, include_builtins, current_depth + 1
|
|
108
|
+
)
|
|
109
|
+
if children:
|
|
110
|
+
sub_info["subclasses"] = children
|
|
111
|
+
|
|
112
|
+
result.append(sub_info)
|
|
113
|
+
except Exception:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _count_subclasses(subclasses: list[dict]) -> int:
|
|
120
|
+
"""Count total subclasses including nested."""
|
|
121
|
+
count = len(subclasses)
|
|
122
|
+
for sub in subclasses:
|
|
123
|
+
if "subclasses" in sub:
|
|
124
|
+
count += _count_subclasses(sub["subclasses"])
|
|
125
|
+
return count
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def get_mro(dotted_path: str, include_builtins: bool = False) -> dict:
|
|
129
|
+
"""
|
|
130
|
+
Get just the Method Resolution Order (parent classes).
|
|
131
|
+
|
|
132
|
+
Simpler version of get_class_hierarchy for just parents.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
dotted_path : str
|
|
137
|
+
Dotted path to the class
|
|
138
|
+
include_builtins : bool
|
|
139
|
+
Include builtin classes
|
|
140
|
+
|
|
141
|
+
Returns
|
|
142
|
+
-------
|
|
143
|
+
dict
|
|
144
|
+
mro: list[str] - Qualified names in MRO order
|
|
145
|
+
"""
|
|
146
|
+
obj, error = resolve_object(dotted_path)
|
|
147
|
+
if error:
|
|
148
|
+
return {"success": False, "error": error}
|
|
149
|
+
|
|
150
|
+
if not inspect.isclass(obj):
|
|
151
|
+
return {"success": False, "error": f"'{dotted_path}' is not a class"}
|
|
152
|
+
|
|
153
|
+
mro = []
|
|
154
|
+
for cls in inspect.getmro(obj):
|
|
155
|
+
if not include_builtins and cls.__module__ == "builtins":
|
|
156
|
+
continue
|
|
157
|
+
mro.append(f"{cls.__module__}.{cls.__name__}")
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
"success": True,
|
|
161
|
+
"class": dotted_path,
|
|
162
|
+
"mro": mro,
|
|
163
|
+
}
|