machineconfig 8.14__py3-none-any.whl → 8.50__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.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/cluster/remote/run_cluster.py +1 -1
- machineconfig/cluster/remote/run_remote.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +10 -8
- machineconfig/cluster/sessions_managers/wt_local.py +1 -1
- machineconfig/cluster/sessions_managers/wt_local_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
- machineconfig/jobs/installer/checks/check_installations.py +133 -0
- machineconfig/jobs/installer/checks/install_utils.py +132 -0
- machineconfig/jobs/installer/checks/report_utils.py +39 -0
- machineconfig/jobs/installer/checks/vt_utils.py +89 -0
- machineconfig/jobs/installer/installer_data.json +225 -140
- machineconfig/jobs/installer/linux_scripts/docker.sh +6 -9
- machineconfig/jobs/installer/package_groups.py +10 -9
- machineconfig/jobs/installer/python_scripts/boxes.py +1 -2
- machineconfig/jobs/installer/python_scripts/code.py +10 -8
- machineconfig/jobs/installer/python_scripts/hx.py +30 -13
- machineconfig/jobs/installer/python_scripts/nerfont_windows_helper.py +6 -5
- machineconfig/jobs/installer/python_scripts/sysabc.py +25 -19
- machineconfig/jobs/installer/python_scripts/yazi.py +33 -17
- machineconfig/jobs/scripts/powershell_scripts/cmatrix.ps1 +52 -0
- machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +1 -1
- machineconfig/jobs/scripts_dynamic/a.py +413 -10
- machineconfig/profile/create_links.py +77 -20
- machineconfig/profile/create_links_export.py +63 -58
- machineconfig/profile/mapper_data.toml +30 -0
- machineconfig/profile/mapper_dotfiles.toml +253 -0
- machineconfig/scripts/python/agents.py +70 -172
- machineconfig/scripts/python/ai/initai.py +3 -1
- machineconfig/scripts/python/ai/scripts/__init__.py +1 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +2 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +7 -5
- machineconfig/scripts/python/ai/solutions/claude/claude.py +1 -1
- machineconfig/scripts/python/ai/solutions/cline/cline.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +29 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.py +1 -1
- machineconfig/scripts/python/ai/solutions/cursor/cursors.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/gemini.py +1 -1
- machineconfig/scripts/python/ai/solutions/gemini/settings.json +3 -0
- machineconfig/scripts/python/ai/{solutions → utils}/generic.py +2 -15
- machineconfig/scripts/python/ai/utils/vscode_tasks.py +6 -3
- machineconfig/scripts/python/cloud.py +58 -11
- machineconfig/scripts/python/croshell.py +4 -156
- machineconfig/scripts/python/devops.py +57 -40
- machineconfig/scripts/python/devops_navigator.py +17 -3
- machineconfig/scripts/python/fire_jobs.py +8 -207
- machineconfig/scripts/python/ftpx.py +5 -225
- machineconfig/scripts/python/graph/cli_graph.json +8743 -0
- machineconfig/scripts/python/{env_manager → helper_env}/path_manager_tui.py +2 -2
- machineconfig/scripts/python/{env_manager → helpers/helper_env}/env_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers/helper_env/path_manager_tui.py +228 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_cursor_agents.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_gemini.py +1 -1
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_qwen.py +1 -1
- machineconfig/scripts/python/helpers/helpers_agents/agents_impl.py +168 -0
- machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_launch.py +5 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_copy.py +6 -6
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_mount.py +10 -5
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_sync.py +3 -3
- machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers/helpers_croshell/croshell_impl.py +225 -0
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/scheduler.py +4 -4
- machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/start_slidev.py +7 -6
- machineconfig/scripts/python/helpers/helpers_devops/backup_config.py +149 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_backup_retrieve.py +267 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config.py +98 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_config_dotfile.py +274 -0
- machineconfig/scripts/python/helpers/helpers_devops/cli_data.py +76 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_nw.py +52 -72
- machineconfig/scripts/python/helpers/helpers_devops/cli_repos.py +274 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_self.py +40 -23
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_file.py +44 -30
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_server.py +26 -43
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/cli_share_terminal.py +12 -6
- machineconfig/scripts/python/helpers/helpers_devops/cli_ssh.py +167 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_status.py +12 -6
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/devops_update_repos.py +1 -1
- machineconfig/scripts/python/{interactive.py → helpers/helpers_devops/interactive.py} +68 -52
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/run_script.py +75 -58
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.ps1 +41 -0
- machineconfig/scripts/python/helpers/helpers_devops/themes/choose_starship_theme.sh +48 -0
- machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_wezterm_theme.py +3 -3
- machineconfig/scripts/python/helpers/helpers_fire_command/fire_jobs_impl.py +233 -0
- machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_route_helper.py +3 -3
- machineconfig/scripts/python/helpers/helpers_msearch/msearch_impl.py +248 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_linux/fzfg +4 -3
- machineconfig/scripts/python/helpers/helpers_msearch/scripts_linux/search_with_context.sh +48 -0
- machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/scripts_windows/fzfg.ps1 +1 -1
- machineconfig/scripts/python/helpers/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers/helpers_navigator/cli_graph_loader.py +234 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/command_builder.py +61 -13
- machineconfig/scripts/python/helpers/helpers_navigator/command_detail.py +153 -0
- machineconfig/scripts/python/helpers/helpers_navigator/command_tree.py +45 -0
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/data_models.py +18 -11
- machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/main_app.py +5 -5
- machineconfig/scripts/python/helpers/helpers_network/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address.py +15 -17
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/address_switch.py +1 -1
- machineconfig/scripts/python/helpers/helpers_network/ftpx_impl.py +276 -0
- machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_ssh.py +2 -2
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_identity.py +73 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_add_ssh_key.py +175 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_linux.py +319 -0
- machineconfig/scripts/python/helpers/helpers_network/ssh_debug_windows.py +275 -0
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/action_helper.py +3 -3
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/cloud_repo_sync.py +117 -33
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/grource.py +3 -2
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/record.py +33 -13
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_2.py +63 -19
- machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/update.py +0 -6
- machineconfig/scripts/python/helpers/helpers_search/script_help.py +81 -0
- machineconfig/scripts/python/helpers/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_sessions/sessions_impl.py +186 -0
- machineconfig/scripts/python/{helpers_sessions → helpers/helpers_sessions}/sessions_multiprocess.py +1 -1
- machineconfig/scripts/python/helpers/helpers_terminal/__init__.py +0 -0
- machineconfig/scripts/python/helpers/helpers_terminal/terminal_impl.py +96 -0
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/download.py +1 -1
- machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/python.py +47 -26
- machineconfig/scripts/python/helpers/helpers_utils/specs.py +246 -0
- machineconfig/scripts/python/mcfg_entry.py +133 -48
- machineconfig/scripts/python/msearch.py +15 -61
- machineconfig/scripts/python/sessions.py +59 -194
- machineconfig/scripts/python/terminal.py +18 -96
- machineconfig/scripts/python/utils.py +101 -20
- machineconfig/settings/atuin/config.toml +294 -0
- machineconfig/settings/atuin/themes/catppuccin-mocha-mauve.toml +12 -0
- machineconfig/settings/linters/.ruff.toml +1 -0
- machineconfig/settings/mprocs/windows/mprocs.yaml +2 -2
- machineconfig/settings/shells/bash/init.sh +6 -3
- machineconfig/settings/shells/pwsh/init.ps1 +69 -1
- machineconfig/settings/shells/pwsh/search_pwsh_history.ps1 +99 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +4 -1
- machineconfig/settings/shells/wt/settings.json +20 -7
- machineconfig/settings/shells/zsh/init.sh +25 -4
- machineconfig/settings/television/cable_unix/bash-history.toml +1 -1
- machineconfig/settings/television/cable_windows/pwsh-history.toml +1 -1
- machineconfig/settings/tv/config.toml +234 -0
- machineconfig/settings/tv/themes/catppuccin-mocha-sky.toml +22 -0
- machineconfig/settings/wsl/.wslconfig +5 -30
- machineconfig/settings/yazi/yazi_linux.toml +18 -8
- machineconfig/settings/zellij/layouts/st.kdl +2 -2
- machineconfig/settings/zellij/layouts/st2.kdl +1 -1
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +3 -0
- machineconfig/setup_mac/__init__.py +0 -2
- machineconfig/setup_windows/__init__.py +0 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +14 -13
- machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +4 -3
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -3
- machineconfig/type_hinting/sql/__init__.py +1 -0
- machineconfig/type_hinting/sql/base.py +216 -0
- machineconfig/type_hinting/sql/core_schema.py +64 -0
- machineconfig/type_hinting/sql/core_schema_typeddict.py +41 -0
- machineconfig/type_hinting/sql/typeddict_codegen.py +222 -0
- machineconfig/type_hinting/typedict/__init__.py +1 -0
- machineconfig/type_hinting/typedict/ast_utils.py +130 -0
- machineconfig/type_hinting/typedict/generator_helpers.py +319 -0
- machineconfig/type_hinting/typedict/generators.py +231 -0
- machineconfig/type_hinting/typedict/polars_schema.py +24 -0
- machineconfig/type_hinting/typedict/polars_schema_typeddict.py +63 -0
- machineconfig/utils/accessories.py +24 -0
- machineconfig/utils/code.py +41 -13
- machineconfig/utils/files/ascii_art.py +10 -14
- machineconfig/utils/files/headers.py +3 -5
- machineconfig/utils/files/read.py +8 -1
- machineconfig/utils/installer_utils/github_release_bulk.py +11 -91
- machineconfig/utils/installer_utils/github_release_scraper.py +99 -0
- machineconfig/utils/installer_utils/install_from_url.py +1 -1
- machineconfig/utils/installer_utils/installer_class.py +12 -4
- machineconfig/utils/installer_utils/installer_cli.py +1 -15
- machineconfig/utils/installer_utils/installer_helper.py +2 -2
- machineconfig/utils/installer_utils/installer_locator_utils.py +13 -13
- machineconfig/utils/installer_utils/installer_runner.py +4 -4
- machineconfig/utils/io.py +25 -8
- machineconfig/utils/meta.py +6 -4
- machineconfig/utils/options.py +49 -19
- machineconfig/utils/options_utils/__init__.py +0 -0
- machineconfig/utils/options_utils/options_tv_linux.py +211 -0
- machineconfig/utils/options_utils/options_tv_windows.py +88 -0
- machineconfig/utils/options_utils/tv_options.py +37 -0
- machineconfig/utils/path_extended.py +6 -6
- machineconfig/utils/scheduler.py +8 -2
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/source_of_truth.py +6 -1
- machineconfig/utils/ssh.py +69 -18
- machineconfig/utils/ssh_utils/abc.py +1 -1
- machineconfig/utils/ssh_utils/copy_from_here.py +17 -12
- machineconfig/utils/ssh_utils/utils.py +21 -5
- machineconfig/utils/ssh_utils/wsl.py +107 -170
- machineconfig/utils/ssh_utils/wsl_helper.py +217 -0
- machineconfig/utils/upgrade_packages.py +4 -8
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/METADATA +29 -22
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/RECORD +251 -211
- machineconfig/jobs/installer/check_installations.py +0 -248
- machineconfig/profile/backup.toml +0 -49
- machineconfig/profile/mapper.toml +0 -263
- machineconfig/scripts/python/helpers_devops/cli_config.py +0 -105
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +0 -89
- machineconfig/scripts/python/helpers_devops/cli_data.py +0 -25
- machineconfig/scripts/python/helpers_devops/cli_repos.py +0 -208
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +0 -80
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +0 -3
- machineconfig/scripts/python/helpers_navigator/__init__.py +0 -20
- machineconfig/scripts/python/helpers_navigator/command_detail.py +0 -44
- machineconfig/scripts/python/helpers_navigator/command_tree.py +0 -620
- machineconfig/scripts/python/helpers_network/ssh_add_identity.py +0 -116
- machineconfig/scripts/python/helpers_network/ssh_add_ssh_key.py +0 -153
- machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +0 -391
- machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +0 -338
- machineconfig/scripts/python/helpers_repos/entrypoint.py +0 -77
- machineconfig/setup_mac/ssh/openssh_setup.sh +0 -114
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +0 -29
- machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -37
- machineconfig/utils/options_tv.py +0 -119
- machineconfig/utils/tst.py +0 -20
- /machineconfig/{scripts/python/helpers_agents → jobs/installer/checks}/__init__.py +0 -0
- /machineconfig/scripts/python/ai/{solutions/_shared.py → utils/shared.py} +0 -0
- /machineconfig/scripts/python/{helpers_agents/agentic_frameworks → graph}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/__init__.py +0 -0
- /machineconfig/scripts/python/{env_manager → helpers/helper_env}/path_manager_backend.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_agents}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_agents/agentic_frameworks}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/agentic_frameworks/fire_crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_help_search.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_helper_types.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/fire_agents_load_balancer.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aichat/config.yaml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/aider/.aider.conf.yml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/copilot/config.yml +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/crush/crush.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/configs/gemini/settings.json +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/privacy/privacy.py +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/prompt.txt +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_agents → helpers/helpers_agents}/templates/template.sh +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes → helpers/helpers_cloud}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/cloud_helpers.py +0 -0
- /machineconfig/scripts/python/{helpers_cloud → helpers/helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_croshell}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/crosh.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/pomodoro.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer.py +0 -0
- /machineconfig/scripts/python/{helpers_croshell → helpers/helpers_croshell}/viewer_template.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_devops}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_sessions → helpers/helpers_devops/themes}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_devops → helpers/helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
- /machineconfig/scripts/python/{helpers_devops/themes/choose_starship_theme.ps1 → helpers/helpers_fire_command/__init__.py} +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/cloud_manager.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/f.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/file_wrangler.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_args_helper.py +0 -0
- /machineconfig/scripts/python/{helpers_fire_command → helpers/helpers_fire_command}/fire_jobs_streamlit_helper.py +0 -0
- /machineconfig/scripts/python/{helpers_msearch → helpers/helpers_msearch}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_navigator → helpers/helpers_navigator}/search_bar.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nfs.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/mount_nw_drive.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/onetimeshare.py +0 -0
- /machineconfig/scripts/python/{helpers_network → helpers/helpers_network}/wifi_conn.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/clone.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/repo_analyzer_1.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → helpers/helpers_repos}/sync.py +0 -0
- /machineconfig/scripts/python/helpers/{ast_search.py → helpers_search/ast_search.py} +0 -0
- /machineconfig/scripts/python/helpers/{qr_code.py → helpers_search/qr_code.py} +0 -0
- /machineconfig/scripts/python/helpers/{repo_rag.py → helpers_search/repo_rag.py} +0 -0
- /machineconfig/scripts/python/helpers/{symantic_search.py → helpers_search/symantic_search.py} +0 -0
- /machineconfig/scripts/python/{helpers_utils → helpers/helpers_utils}/pdf.py +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/WHEEL +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/entry_points.txt +0 -0
- {machineconfig-8.14.dist-info → machineconfig-8.50.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import inspect
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
WRAPPER_TYPES = {"ReadOnly", "NotRequired", "Optional", "Required", "Final"}
|
|
8
|
+
BUILTIN_TYPES = {"str", "int", "float", "bool", "bytes", "None", "list", "dict", "set", "tuple", "Any", "Literal"}
|
|
9
|
+
PYTHON_TYPE_TO_SERIES_TYPE: dict[str, str] = {"str": "str", "int": "int", "float": "float", "bool": "bool", "bytes": "bytes"}
|
|
10
|
+
PYTHON_TYPE_TO_POLARS_DTYPE: dict[str, str] = {"str": '"pl.String"', "int": '"pl.Int64"', "float": '"pl.Float64"', "bool": '"pl.Boolean"', "bytes": '"pl.Binary"'}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_types_class_name(class_name: str) -> str:
|
|
14
|
+
return f"{class_name}_Types"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def collect_type_names_from_annotation(annotation: ast.expr | None) -> set[str]:
|
|
18
|
+
if annotation is None:
|
|
19
|
+
return set()
|
|
20
|
+
names: set[str] = set()
|
|
21
|
+
if isinstance(annotation, ast.Name):
|
|
22
|
+
if annotation.id not in BUILTIN_TYPES and annotation.id not in WRAPPER_TYPES:
|
|
23
|
+
names.add(annotation.id)
|
|
24
|
+
elif isinstance(annotation, ast.Subscript):
|
|
25
|
+
names.update(collect_type_names_from_annotation(annotation.value))
|
|
26
|
+
names.update(collect_type_names_from_annotation(annotation.slice))
|
|
27
|
+
elif isinstance(annotation, ast.BinOp):
|
|
28
|
+
names.update(collect_type_names_from_annotation(annotation.left))
|
|
29
|
+
names.update(collect_type_names_from_annotation(annotation.right))
|
|
30
|
+
elif isinstance(annotation, ast.Tuple):
|
|
31
|
+
for elt in annotation.elts:
|
|
32
|
+
names.update(collect_type_names_from_annotation(elt))
|
|
33
|
+
return names
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def extract_imports_from_source(source_file_path: Path) -> tuple[dict[str, str], dict[str, str], set[str]]:
|
|
37
|
+
source_content = source_file_path.read_text(encoding="utf-8")
|
|
38
|
+
tree = ast.parse(source_content)
|
|
39
|
+
imports: dict[str, str] = {}
|
|
40
|
+
local_type_aliases: dict[str, str] = {}
|
|
41
|
+
local_classes: set[str] = set()
|
|
42
|
+
for node in ast.walk(tree):
|
|
43
|
+
if isinstance(node, ast.ImportFrom) and node.module:
|
|
44
|
+
for alias in node.names:
|
|
45
|
+
name = alias.asname or alias.name
|
|
46
|
+
imports[name] = node.module
|
|
47
|
+
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
|
|
48
|
+
if isinstance(node.annotation, ast.Name) and node.annotation.id == "TypeAlias" and node.value is not None:
|
|
49
|
+
local_type_aliases[node.target.id] = ast.unparse(node.value)
|
|
50
|
+
elif isinstance(node, ast.ClassDef):
|
|
51
|
+
local_classes.add(node.name)
|
|
52
|
+
return imports, local_type_aliases, local_classes
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def unwrap_type_annotation(annotation: ast.expr | None) -> ast.expr | None:
|
|
56
|
+
if annotation is None:
|
|
57
|
+
return None
|
|
58
|
+
if isinstance(annotation, ast.Subscript) and isinstance(annotation.value, ast.Name) and annotation.value.id in WRAPPER_TYPES:
|
|
59
|
+
return unwrap_type_annotation(annotation.slice)
|
|
60
|
+
if isinstance(annotation, ast.Subscript) and isinstance(annotation.value, ast.Attribute) and annotation.value.attr in WRAPPER_TYPES:
|
|
61
|
+
return unwrap_type_annotation(annotation.slice)
|
|
62
|
+
return annotation
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def is_builtin_type(annotation: ast.expr) -> bool:
|
|
66
|
+
if isinstance(annotation, ast.Name):
|
|
67
|
+
return annotation.id in BUILTIN_TYPES
|
|
68
|
+
if isinstance(annotation, ast.Constant):
|
|
69
|
+
return True
|
|
70
|
+
if isinstance(annotation, ast.Subscript):
|
|
71
|
+
return is_builtin_type(annotation.value) and is_builtin_type(annotation.slice)
|
|
72
|
+
if isinstance(annotation, ast.Attribute):
|
|
73
|
+
return False
|
|
74
|
+
if isinstance(annotation, ast.BinOp):
|
|
75
|
+
return is_builtin_type(annotation.left) and is_builtin_type(annotation.right)
|
|
76
|
+
if isinstance(annotation, ast.Tuple):
|
|
77
|
+
return all(is_builtin_type(elt) for elt in annotation.elts)
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def python_type_to_iterable_str(annotation: ast.expr | None) -> str:
|
|
82
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
83
|
+
if unwrapped is None:
|
|
84
|
+
return 'Iterable["Any"]'
|
|
85
|
+
type_str = ast.unparse(unwrapped)
|
|
86
|
+
if not is_builtin_type(unwrapped):
|
|
87
|
+
return f'Iterable["{type_str}"]'
|
|
88
|
+
return f"Iterable[{type_str}]"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_iterable_type_for_col(annotation: ast.expr | None) -> str:
|
|
92
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
93
|
+
if unwrapped is None:
|
|
94
|
+
return 'Annotated["pl.Series", Any]'
|
|
95
|
+
if isinstance(unwrapped, ast.Name):
|
|
96
|
+
dtype = PYTHON_TYPE_TO_POLARS_DTYPE.get(unwrapped.id)
|
|
97
|
+
if dtype:
|
|
98
|
+
return f'Annotated["pl.Series", {dtype}]'
|
|
99
|
+
return 'Annotated["pl.Series", Any]'
|
|
100
|
+
if isinstance(unwrapped, ast.Subscript) and isinstance(unwrapped.value, ast.Name):
|
|
101
|
+
if unwrapped.value.id == "list":
|
|
102
|
+
return 'Annotated["pl.Series", "pl.List"]'
|
|
103
|
+
return 'Annotated["pl.Series", Any]'
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def get_random_value_expr(annotation: ast.expr | None) -> str:
|
|
107
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
108
|
+
if unwrapped is None:
|
|
109
|
+
return "None"
|
|
110
|
+
if isinstance(unwrapped, ast.Name):
|
|
111
|
+
type_name = unwrapped.id
|
|
112
|
+
if type_name == "str":
|
|
113
|
+
return "pl.Series([secrets.token_hex(8) for _ in range(n_rows)])"
|
|
114
|
+
if type_name == "int":
|
|
115
|
+
return "pl.Series(random.choices(range(-1000, 1000), k=n_rows))"
|
|
116
|
+
if type_name == "float":
|
|
117
|
+
return "pl.Series([random.uniform(-1000.0, 1000.0) for _ in range(n_rows)])"
|
|
118
|
+
if type_name == "bool":
|
|
119
|
+
return "pl.Series(random.choices([True, False], k=n_rows))"
|
|
120
|
+
if type_name == "bytes":
|
|
121
|
+
return "pl.Series([secrets.token_bytes(16) for _ in range(n_rows)])"
|
|
122
|
+
return "pl.Series([None] * n_rows)"
|
|
123
|
+
if isinstance(unwrapped, ast.Subscript) and isinstance(unwrapped.value, ast.Name):
|
|
124
|
+
if unwrapped.value.id == "list":
|
|
125
|
+
return "pl.Series([[random.randint(0, 100) for _ in range(3)] for _ in range(n_rows)])"
|
|
126
|
+
return "pl.Series([None] * n_rows)"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# def _check_random_imports_needed(field_infos: list[tuple[str, ast.expr | None]]) -> tuple[bool, bool]:
|
|
130
|
+
# needs_random = False
|
|
131
|
+
# needs_secrets = False
|
|
132
|
+
# for _field_name, annotation in field_infos:
|
|
133
|
+
# unwrapped = unwrap_type_annotation(annotation)
|
|
134
|
+
# if unwrapped is None:
|
|
135
|
+
# continue
|
|
136
|
+
# if isinstance(unwrapped, ast.Name):
|
|
137
|
+
# type_name = unwrapped.id
|
|
138
|
+
# if type_name in {"int", "float", "bool"}:
|
|
139
|
+
# needs_random = True
|
|
140
|
+
# elif type_name in {"str", "bytes"}:
|
|
141
|
+
# needs_secrets = True
|
|
142
|
+
# elif isinstance(unwrapped, ast.Subscript) and isinstance(unwrapped.value, ast.Name):
|
|
143
|
+
# if unwrapped.value.id == "list":
|
|
144
|
+
# needs_random = True
|
|
145
|
+
# return needs_random, needs_secrets
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def quote_pl_in_annotation(annotation_str: str) -> str:
|
|
149
|
+
"""Quote pl.X types in annotation strings since pl is only available under TYPE_CHECKING."""
|
|
150
|
+
import re
|
|
151
|
+
# Match pl.X only when NOT already inside quotes (preceded by ' or ")
|
|
152
|
+
# Use negative lookbehind for quotes
|
|
153
|
+
return re.sub(r"(?<!['\"])(\bpl\.\w+)(?!['\"])", r"'\1'", annotation_str)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _get_module_name_from_output_path(file_path: Path) -> str:
|
|
157
|
+
"""Get the module name from the output file path."""
|
|
158
|
+
resolved = file_path.resolve()
|
|
159
|
+
parts: list[str] = []
|
|
160
|
+
current = resolved.parent
|
|
161
|
+
while current != current.parent:
|
|
162
|
+
if not (current / "__init__.py").exists():
|
|
163
|
+
break
|
|
164
|
+
parts.append(current.name)
|
|
165
|
+
current = current.parent
|
|
166
|
+
parts.reverse()
|
|
167
|
+
return ".".join(parts) if parts else ""
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_module_level_helper_functions() -> list[str]:
|
|
171
|
+
"""Get helper functions to be defined once at module level for self-contained mode.
|
|
172
|
+
Returns only the function definitions, not imports (imports are handled separately at file top).
|
|
173
|
+
"""
|
|
174
|
+
from machineconfig.type_hinting.typedict import polars_schema_typeddict
|
|
175
|
+
|
|
176
|
+
lines = []
|
|
177
|
+
lines.append("# Helper functions for self-contained mode (defined once at module level)")
|
|
178
|
+
lines.append("")
|
|
179
|
+
|
|
180
|
+
# Add all helper functions
|
|
181
|
+
for func_name in ["_unwrap_type", "_get_polars_type", "get_polars_schema_from_typeddict", "get_polars_df_random_data_from_typeddict"]:
|
|
182
|
+
func = getattr(polars_schema_typeddict, func_name)
|
|
183
|
+
source = inspect.getsource(func)
|
|
184
|
+
for line in source.splitlines():
|
|
185
|
+
lines.append(line)
|
|
186
|
+
lines.append("")
|
|
187
|
+
|
|
188
|
+
return lines
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def generate_for_class(class_name: str, field_infos: list[tuple[str, ast.expr | None]], source_module: str, dependency: Literal["import", "self-contained"] = "self-contained", output_file_path: Path | None = None) -> list[str]:
|
|
192
|
+
lines: list[str] = []
|
|
193
|
+
field_names = [fn for fn, _ in field_infos]
|
|
194
|
+
|
|
195
|
+
types_class_name = get_types_class_name(class_name)
|
|
196
|
+
|
|
197
|
+
names_class_name = f"{class_name}Names"
|
|
198
|
+
lines.append(f"class {names_class_name}:")
|
|
199
|
+
if field_infos:
|
|
200
|
+
for field_name in field_names:
|
|
201
|
+
lines.append(f' {field_name}: Literal["{field_name}"] = "{field_name}"')
|
|
202
|
+
else:
|
|
203
|
+
lines.append(" pass")
|
|
204
|
+
lines.append("")
|
|
205
|
+
lines.append("")
|
|
206
|
+
|
|
207
|
+
names_literal = f"{class_name}_NAMES"
|
|
208
|
+
if field_names:
|
|
209
|
+
literal_values = ", ".join(f'"{fn}"' for fn in field_names)
|
|
210
|
+
lines.append(f'{names_literal}: TypeAlias = Literal[{literal_values}]')
|
|
211
|
+
else:
|
|
212
|
+
lines.append(f'{names_literal}: TypeAlias = Literal[""]')
|
|
213
|
+
lines.append("")
|
|
214
|
+
lines.append("")
|
|
215
|
+
|
|
216
|
+
lines.append(f"class {types_class_name}:")
|
|
217
|
+
if field_infos:
|
|
218
|
+
for field_name, annotation in field_infos:
|
|
219
|
+
if annotation is None:
|
|
220
|
+
raise ValueError(f"Field '{field_name}' in class '{class_name}' lacks an annotation")
|
|
221
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
222
|
+
if unwrapped is None:
|
|
223
|
+
raise ValueError(f"Field '{field_name}' in class '{class_name}' lacks an annotation")
|
|
224
|
+
annotation_source = ast.unparse(unwrapped)
|
|
225
|
+
annotation_source = quote_pl_in_annotation(annotation_source)
|
|
226
|
+
lines.append(f" {field_name}: TypeAlias = {annotation_source}")
|
|
227
|
+
else:
|
|
228
|
+
lines.append(" pass")
|
|
229
|
+
lines.append("")
|
|
230
|
+
lines.append("")
|
|
231
|
+
|
|
232
|
+
wrapper_class_name = f"{class_name}_Wrapper"
|
|
233
|
+
lines.append(f"class {wrapper_class_name}:")
|
|
234
|
+
lines.append(f" c = {names_class_name}")
|
|
235
|
+
lines.append(f" ct: TypeAlias = {names_literal}")
|
|
236
|
+
lines.append(f' e: TypeAlias = {class_name}')
|
|
237
|
+
lines.append(f" t: TypeAlias = {types_class_name}")
|
|
238
|
+
lines.append("")
|
|
239
|
+
lines.append(' def __init__(self, df: "pl.DataFrame") -> None:')
|
|
240
|
+
lines.append(" self.df = df")
|
|
241
|
+
lines.append("")
|
|
242
|
+
|
|
243
|
+
if field_infos:
|
|
244
|
+
# Group fields by their return type to reduce number of overloads
|
|
245
|
+
grouped_fields: dict[str, list[str]] = {}
|
|
246
|
+
for field_name, annotation in field_infos:
|
|
247
|
+
col_type = get_iterable_type_for_col(annotation)
|
|
248
|
+
grouped_fields.setdefault(col_type, []).append(field_name)
|
|
249
|
+
|
|
250
|
+
if len(grouped_fields) == 1:
|
|
251
|
+
col_type = list(grouped_fields.keys())[0]
|
|
252
|
+
lines.append(f' def get_col(self, name: {names_literal}) -> {col_type}:')
|
|
253
|
+
lines.append(" return self.df.select(name).to_series()")
|
|
254
|
+
lines.append("")
|
|
255
|
+
else:
|
|
256
|
+
for col_type, fields in grouped_fields.items():
|
|
257
|
+
lines.append(" @overload")
|
|
258
|
+
if len(fields) == 1:
|
|
259
|
+
lines.append(f' def get_col(self, name: Literal["{fields[0]}"]) -> {col_type}: ...')
|
|
260
|
+
else:
|
|
261
|
+
literals = ", ".join(f'"{f}"' for f in fields)
|
|
262
|
+
lines.append(f' def get_col(self, name: Literal[{literals}]) -> {col_type}: ...')
|
|
263
|
+
|
|
264
|
+
lines.append(f' def get_col(self, name: {names_literal}) -> Annotated["pl.Series", Any]:')
|
|
265
|
+
lines.append(" return self.df.select(name).to_series()")
|
|
266
|
+
lines.append("")
|
|
267
|
+
|
|
268
|
+
params: list[str] = []
|
|
269
|
+
dict_entries: list[str] = []
|
|
270
|
+
for field_name, annotation in field_infos:
|
|
271
|
+
iterable_type = python_type_to_iterable_str(annotation)
|
|
272
|
+
params.append(f"{field_name}: {iterable_type}")
|
|
273
|
+
dict_entries.append(f'"{field_name}": {field_name}')
|
|
274
|
+
params_str = ", ".join(params)
|
|
275
|
+
dict_str = "{" + ", ".join(dict_entries) + "}"
|
|
276
|
+
lines.append(" @staticmethod")
|
|
277
|
+
lines.append(f' def make({params_str}) -> "{wrapper_class_name}":')
|
|
278
|
+
|
|
279
|
+
if dependency == "import":
|
|
280
|
+
# Use fully qualified import from dtypes_utils
|
|
281
|
+
if output_file_path:
|
|
282
|
+
output_module = _get_module_name_from_output_path(output_file_path)
|
|
283
|
+
lines.append(f" from {output_module}.dtypes_utils import get_polars_schema_from_typeddict as get_polars_schema")
|
|
284
|
+
else:
|
|
285
|
+
lines.append(" from machineconfig.type_hinting.polars_schema_typeddict import get_polars_schema_from_typeddict as get_polars_schema")
|
|
286
|
+
else: # self-contained - use module-level function
|
|
287
|
+
lines.append(" get_polars_schema = get_polars_schema_from_typeddict")
|
|
288
|
+
|
|
289
|
+
lines.append(f" return {wrapper_class_name}(pl.DataFrame({dict_str}, schema=get_polars_schema({class_name})))")
|
|
290
|
+
lines.append("")
|
|
291
|
+
|
|
292
|
+
lines.append(" @staticmethod")
|
|
293
|
+
lines.append(f' def make_fake(n_rows: int) -> "{wrapper_class_name}":')
|
|
294
|
+
|
|
295
|
+
if dependency == "import":
|
|
296
|
+
# Use fully qualified import from dtypes_utils
|
|
297
|
+
if output_file_path:
|
|
298
|
+
output_module = _get_module_name_from_output_path(output_file_path)
|
|
299
|
+
lines.append(f" from {output_module}.dtypes_utils import get_polars_df_random_data_from_typeddict")
|
|
300
|
+
else:
|
|
301
|
+
lines.append(" from machineconfig.type_hinting.polars_schema_typeddict import get_polars_df_random_data_from_typeddict")
|
|
302
|
+
else: # self-contained - use module-level function
|
|
303
|
+
pass # No imports needed, function is at module level
|
|
304
|
+
|
|
305
|
+
lines.append(f" return {wrapper_class_name}(get_polars_df_random_data_from_typeddict({class_name}, n_rows))")
|
|
306
|
+
else:
|
|
307
|
+
lines.append(" @staticmethod")
|
|
308
|
+
lines.append(f' def make() -> "{wrapper_class_name}":')
|
|
309
|
+
lines.append(" import polars as pl")
|
|
310
|
+
lines.append(f" return {wrapper_class_name}(pl.DataFrame())")
|
|
311
|
+
lines.append("")
|
|
312
|
+
lines.append(" @staticmethod")
|
|
313
|
+
lines.append(f' def make_fake(n_rows: int) -> "{wrapper_class_name}":')
|
|
314
|
+
lines.append(" import polars as pl")
|
|
315
|
+
lines.append(" _ = n_rows")
|
|
316
|
+
lines.append(f" return {wrapper_class_name}(pl.DataFrame())")
|
|
317
|
+
lines.append("")
|
|
318
|
+
lines.append("")
|
|
319
|
+
return lines
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from machineconfig.type_hinting.typedict.ast_utils import load_target_class_fields
|
|
7
|
+
from machineconfig.type_hinting.typedict.generator_helpers import (
|
|
8
|
+
collect_type_names_from_annotation,
|
|
9
|
+
extract_imports_from_source,
|
|
10
|
+
generate_for_class,
|
|
11
|
+
get_types_class_name,
|
|
12
|
+
unwrap_type_annotation,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _get_module_name_from_path(file_path: Path) -> str:
|
|
17
|
+
resolved = file_path.resolve()
|
|
18
|
+
parts: list[str] = [resolved.stem]
|
|
19
|
+
current = resolved.parent
|
|
20
|
+
while current != current.parent:
|
|
21
|
+
if not (current / "__init__.py").exists():
|
|
22
|
+
break
|
|
23
|
+
parts.append(current.name)
|
|
24
|
+
current = current.parent
|
|
25
|
+
parts.reverse()
|
|
26
|
+
return ".".join(parts)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def generate_names_file(source_file_path: Path, output_file_path: Path, search_paths: list[Path] | None = None, dependency: Literal["import", "self-contained"] = "self-contained") -> Path:
|
|
30
|
+
target_classes = load_target_class_fields(source_file_path, search_paths)
|
|
31
|
+
target_path = Path(output_file_path).resolve()
|
|
32
|
+
|
|
33
|
+
# Handle dependency mode: copy dtypes_utils.py for 'import' mode
|
|
34
|
+
if dependency == "import":
|
|
35
|
+
source_polars_schema = Path(__file__).parent / "polars_schema_typeddict.py"
|
|
36
|
+
target_dtypes_utils = target_path.parent / "dtypes_utils.py"
|
|
37
|
+
shutil.copy2(source_polars_schema, target_dtypes_utils)
|
|
38
|
+
|
|
39
|
+
source_imports, local_type_aliases, local_classes = extract_imports_from_source(Path(source_file_path))
|
|
40
|
+
|
|
41
|
+
all_custom_types: dict[str, str | None] = {}
|
|
42
|
+
for _class_name, field_infos in target_classes:
|
|
43
|
+
for _field_name, annotation, field_imports in field_infos:
|
|
44
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
45
|
+
type_names = collect_type_names_from_annotation(unwrapped)
|
|
46
|
+
for type_name in type_names:
|
|
47
|
+
if type_name not in all_custom_types:
|
|
48
|
+
if type_name in field_imports:
|
|
49
|
+
all_custom_types[type_name] = field_imports[type_name][0]
|
|
50
|
+
else:
|
|
51
|
+
all_custom_types[type_name] = None
|
|
52
|
+
|
|
53
|
+
grouped_imports: dict[str, list[str]] = {}
|
|
54
|
+
needed_local_aliases: list[str] = []
|
|
55
|
+
needed_local_classes: list[str] = []
|
|
56
|
+
for type_name in sorted(all_custom_types.keys()):
|
|
57
|
+
module = all_custom_types[type_name]
|
|
58
|
+
if module:
|
|
59
|
+
grouped_imports.setdefault(module, []).append(type_name)
|
|
60
|
+
elif type_name in source_imports:
|
|
61
|
+
module = source_imports[type_name]
|
|
62
|
+
grouped_imports.setdefault(module, []).append(type_name)
|
|
63
|
+
elif type_name in local_type_aliases:
|
|
64
|
+
needed_local_aliases.append(type_name)
|
|
65
|
+
elif type_name in local_classes:
|
|
66
|
+
needed_local_classes.append(type_name)
|
|
67
|
+
|
|
68
|
+
# Check if Any is actually used in get_col return type annotations
|
|
69
|
+
uses_any = False
|
|
70
|
+
for _class_name, field_infos in target_classes:
|
|
71
|
+
if not field_infos: # Empty class won't have get_col with Any
|
|
72
|
+
continue
|
|
73
|
+
# Check if there are multiple different column types (which means we need Any fallback)
|
|
74
|
+
from machineconfig.type_hinting.typedict.generator_helpers import get_iterable_type_for_col
|
|
75
|
+
col_types = set()
|
|
76
|
+
for _field_name, annotation in [(fn, ann) for fn, ann, _ in field_infos]:
|
|
77
|
+
col_types.add(get_iterable_type_for_col(annotation))
|
|
78
|
+
if len(col_types) > 1:
|
|
79
|
+
uses_any = True
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
# Build typing imports based on what's needed
|
|
83
|
+
typing_imports = ["Annotated", "Literal", "TypeAlias", "TYPE_CHECKING", "overload"]
|
|
84
|
+
if uses_any:
|
|
85
|
+
typing_imports.insert(1, "Any") # Insert after Annotated
|
|
86
|
+
|
|
87
|
+
lines: list[str] = ["from collections.abc import Iterable", f"from typing import {', '.join(typing_imports)}"]
|
|
88
|
+
|
|
89
|
+
# Add self-contained mode imports right at the top
|
|
90
|
+
if dependency == "self-contained":
|
|
91
|
+
lines.append("from typing import ReadOnly, get_origin, get_args")
|
|
92
|
+
lines.append("import polars as pl")
|
|
93
|
+
lines.append("import random")
|
|
94
|
+
lines.append("import secrets")
|
|
95
|
+
|
|
96
|
+
lines.append("")
|
|
97
|
+
|
|
98
|
+
target_class_names = [class_name for class_name, _ in target_classes]
|
|
99
|
+
source_module = _get_module_name_from_path(source_file_path)
|
|
100
|
+
if target_class_names:
|
|
101
|
+
lines.append(f"from {source_module} import {', '.join(sorted(target_class_names))}")
|
|
102
|
+
|
|
103
|
+
for alias_name in sorted(needed_local_aliases):
|
|
104
|
+
lines.append(f"{alias_name}: TypeAlias = {local_type_aliases[alias_name]}")
|
|
105
|
+
if needed_local_aliases:
|
|
106
|
+
lines.append("")
|
|
107
|
+
|
|
108
|
+
# Runtime imports: types used in _Types classes (TypeAlias assignments) need to be available at runtime
|
|
109
|
+
for module, type_names in sorted(grouped_imports.items()):
|
|
110
|
+
lines.append(f"from {module} import {', '.join(sorted(type_names))}")
|
|
111
|
+
|
|
112
|
+
# Check if numpy (np) is used in type aliases - look through all annotations
|
|
113
|
+
needs_numpy = False
|
|
114
|
+
for _class_name, field_infos in target_classes:
|
|
115
|
+
for _field_name, annotation, _field_imports in field_infos:
|
|
116
|
+
if annotation:
|
|
117
|
+
import ast
|
|
118
|
+
annotation_str = ast.unparse(annotation)
|
|
119
|
+
if 'np.' in annotation_str:
|
|
120
|
+
needs_numpy = True
|
|
121
|
+
break
|
|
122
|
+
if needs_numpy:
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
if needs_numpy:
|
|
126
|
+
lines.append("import numpy as np")
|
|
127
|
+
|
|
128
|
+
lines.append("")
|
|
129
|
+
# Only add TYPE_CHECKING block for non-self-contained mode (self-contained imports polars at top)
|
|
130
|
+
if dependency != "self-contained":
|
|
131
|
+
lines.append("if TYPE_CHECKING:")
|
|
132
|
+
lines.append(" import polars as pl")
|
|
133
|
+
else:
|
|
134
|
+
# For self-contained, polars is already imported at the top
|
|
135
|
+
lines.append("if TYPE_CHECKING:")
|
|
136
|
+
lines.append(" pass")
|
|
137
|
+
all_type_checking_imports = sorted(set(needed_local_classes) - set(target_class_names))
|
|
138
|
+
if all_type_checking_imports:
|
|
139
|
+
lines.append(f" from {source_module} import {', '.join(all_type_checking_imports)}")
|
|
140
|
+
lines.append("")
|
|
141
|
+
|
|
142
|
+
# For self-contained mode, add helper functions at module level
|
|
143
|
+
if dependency == "self-contained":
|
|
144
|
+
from machineconfig.type_hinting.typedict.generator_helpers import get_module_level_helper_functions
|
|
145
|
+
helper_lines = get_module_level_helper_functions()
|
|
146
|
+
lines.extend(helper_lines)
|
|
147
|
+
lines.append("")
|
|
148
|
+
|
|
149
|
+
for class_name, field_infos in target_classes:
|
|
150
|
+
source_module = _get_module_name_from_path(source_file_path)
|
|
151
|
+
stripped_field_infos = [(fn, ann) for fn, ann, _ in field_infos]
|
|
152
|
+
lines.extend(generate_for_class(class_name, stripped_field_infos, source_module, dependency=dependency, output_file_path=target_path))
|
|
153
|
+
|
|
154
|
+
lines.append("")
|
|
155
|
+
lines.append('if __name__ == "__main__":')
|
|
156
|
+
lines.append(" import polars as pl")
|
|
157
|
+
lines.append("")
|
|
158
|
+
|
|
159
|
+
output_content = "\n".join(lines)
|
|
160
|
+
with target_path.open(mode="w", encoding="utf-8") as f:
|
|
161
|
+
f.write(output_content)
|
|
162
|
+
return target_path
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def generate_types_file(source_file_path: Path, output_file_path: Path, search_paths: list[Path] | None = None) -> Path:
|
|
166
|
+
target_classes = load_target_class_fields(source_file_path, search_paths)
|
|
167
|
+
target_path = Path(output_file_path).resolve()
|
|
168
|
+
|
|
169
|
+
source_imports, local_type_aliases, local_classes = extract_imports_from_source(Path(source_file_path))
|
|
170
|
+
|
|
171
|
+
all_custom_types: dict[str, str | None] = {}
|
|
172
|
+
for _class_name, field_infos in target_classes:
|
|
173
|
+
for _field_name, annotation, field_imports in field_infos:
|
|
174
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
175
|
+
type_names = collect_type_names_from_annotation(unwrapped)
|
|
176
|
+
for type_name in type_names:
|
|
177
|
+
if type_name not in all_custom_types:
|
|
178
|
+
if type_name in field_imports:
|
|
179
|
+
all_custom_types[type_name] = field_imports[type_name][0]
|
|
180
|
+
else:
|
|
181
|
+
all_custom_types[type_name] = None
|
|
182
|
+
|
|
183
|
+
grouped_imports: dict[str, list[str]] = {}
|
|
184
|
+
needed_local_aliases: list[str] = []
|
|
185
|
+
needed_local_classes: list[str] = []
|
|
186
|
+
for type_name in sorted(all_custom_types.keys()):
|
|
187
|
+
module = all_custom_types[type_name]
|
|
188
|
+
if module:
|
|
189
|
+
grouped_imports.setdefault(module, []).append(type_name)
|
|
190
|
+
elif type_name in source_imports:
|
|
191
|
+
module = source_imports[type_name]
|
|
192
|
+
grouped_imports.setdefault(module, []).append(type_name)
|
|
193
|
+
elif type_name in local_type_aliases:
|
|
194
|
+
needed_local_aliases.append(type_name)
|
|
195
|
+
elif type_name in local_classes:
|
|
196
|
+
needed_local_classes.append(type_name)
|
|
197
|
+
|
|
198
|
+
lines: list[str] = ["from typing import TypeAlias"]
|
|
199
|
+
for module, type_names in sorted(grouped_imports.items()):
|
|
200
|
+
lines.append(f"from {module} import {', '.join(sorted(type_names))}")
|
|
201
|
+
if needed_local_classes:
|
|
202
|
+
source_module = _get_module_name_from_path(source_file_path)
|
|
203
|
+
lines.append(f"from {source_module} import {', '.join(sorted(needed_local_classes))}")
|
|
204
|
+
lines.append("")
|
|
205
|
+
for alias_name in sorted(needed_local_aliases):
|
|
206
|
+
lines.append(f"{alias_name}: TypeAlias = {local_type_aliases[alias_name]}")
|
|
207
|
+
if needed_local_aliases:
|
|
208
|
+
lines.append("")
|
|
209
|
+
lines.append("")
|
|
210
|
+
|
|
211
|
+
for class_name, field_infos in target_classes:
|
|
212
|
+
types_class_name = get_types_class_name(class_name)
|
|
213
|
+
lines.append(f"class {types_class_name}:")
|
|
214
|
+
if field_infos:
|
|
215
|
+
for field_name, annotation, _ in field_infos:
|
|
216
|
+
if annotation is None:
|
|
217
|
+
raise ValueError(f"Field '{field_name}' in class '{class_name}' lacks an annotation")
|
|
218
|
+
unwrapped = unwrap_type_annotation(annotation)
|
|
219
|
+
if unwrapped is None:
|
|
220
|
+
raise ValueError(f"Field '{field_name}' in class '{class_name}' lacks an annotation")
|
|
221
|
+
annotation_source = ast.unparse(unwrapped)
|
|
222
|
+
lines.append(f" {field_name}: TypeAlias = {annotation_source}")
|
|
223
|
+
else:
|
|
224
|
+
lines.append(" pass")
|
|
225
|
+
lines.append("")
|
|
226
|
+
lines.append("")
|
|
227
|
+
|
|
228
|
+
output_content = "\n".join(lines)
|
|
229
|
+
with target_path.open(mode="w", encoding="utf-8") as f:
|
|
230
|
+
f.write(output_content)
|
|
231
|
+
return target_path
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Any, TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
import polars as pl
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_polars_schema(typed_dict: type) -> "dict[str, pl.DataType]":
|
|
8
|
+
import polars as pl
|
|
9
|
+
|
|
10
|
+
def _get_polars_type(python_type: Any) -> pl.DataType:
|
|
11
|
+
if python_type == str:
|
|
12
|
+
return pl.String()
|
|
13
|
+
if python_type == float:
|
|
14
|
+
return pl.Float64()
|
|
15
|
+
if python_type == int:
|
|
16
|
+
return pl.Int64()
|
|
17
|
+
if python_type == bool:
|
|
18
|
+
return pl.Boolean()
|
|
19
|
+
return pl.String()
|
|
20
|
+
|
|
21
|
+
schema: dict[str, pl.DataType] = {}
|
|
22
|
+
for k, v in typed_dict.__annotations__.items():
|
|
23
|
+
schema[k] = _get_polars_type(v)
|
|
24
|
+
return schema
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Any, ReadOnly, get_origin, get_args, TYPE_CHECKING
|
|
2
|
+
import random
|
|
3
|
+
import secrets
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
import polars as pl
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _unwrap_type(python_type: Any) -> Any:
|
|
10
|
+
"""Unwrap ReadOnly and other type wrappers to get the inner type."""
|
|
11
|
+
origin = get_origin(python_type)
|
|
12
|
+
if origin is ReadOnly:
|
|
13
|
+
args = get_args(python_type)
|
|
14
|
+
return args[0] if args else python_type
|
|
15
|
+
return python_type
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _get_polars_type(python_type: Any) -> "pl.DataType":
|
|
19
|
+
"""Convert Python types to appropriate Polars types, handling ReadOnly wrappers."""
|
|
20
|
+
unwrapped = _unwrap_type(python_type)
|
|
21
|
+
import polars as pl
|
|
22
|
+
if unwrapped is str:
|
|
23
|
+
return pl.String()
|
|
24
|
+
if unwrapped is float:
|
|
25
|
+
return pl.Float64()
|
|
26
|
+
if unwrapped is int:
|
|
27
|
+
return pl.Int64()
|
|
28
|
+
if unwrapped is bool:
|
|
29
|
+
return pl.Boolean()
|
|
30
|
+
return pl.String()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_polars_schema_from_typeddict(typed_dict: type) -> dict[str, "pl.DataType"]:
|
|
34
|
+
"""Convert a TypedDict to a Polars schema, properly handling ReadOnly wrappers."""
|
|
35
|
+
# import polars as pl
|
|
36
|
+
schema: dict[str, "pl.DataType"] = {}
|
|
37
|
+
for k, v in typed_dict.__annotations__.items():
|
|
38
|
+
schema[k] = _get_polars_type(v)
|
|
39
|
+
return schema
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_polars_df_random_data_from_typeddict(typed_dict: type, n_rows: int) -> "pl.DataFrame":
|
|
43
|
+
"""Generate a Polars DataFrame with random data based on a TypedDict definition."""
|
|
44
|
+
data: dict[str, "pl.Series"] = {}
|
|
45
|
+
import polars as pl
|
|
46
|
+
for k, v in typed_dict.__annotations__.items():
|
|
47
|
+
unwrapped = _unwrap_type(v)
|
|
48
|
+
if unwrapped is str:
|
|
49
|
+
data[k] = pl.Series([secrets.token_hex(8) for _ in range(n_rows)])
|
|
50
|
+
elif unwrapped is int:
|
|
51
|
+
data[k] = pl.Series(random.choices(range(-1000, 1000), k=n_rows))
|
|
52
|
+
elif unwrapped is float:
|
|
53
|
+
data[k] = pl.Series([random.uniform(-1000.0, 1000.0) for _ in range(n_rows)])
|
|
54
|
+
elif unwrapped is bool:
|
|
55
|
+
data[k] = pl.Series(random.choices([True, False], k=n_rows))
|
|
56
|
+
elif unwrapped is bytes:
|
|
57
|
+
data[k] = pl.Series([secrets.token_bytes(16) for _ in range(n_rows)])
|
|
58
|
+
elif get_origin(unwrapped) is list:
|
|
59
|
+
data[k] = pl.Series([[random.randint(0, 100) for _ in range(3)] for _ in range(n_rows)])
|
|
60
|
+
else:
|
|
61
|
+
data[k] = pl.Series([None] * n_rows)
|
|
62
|
+
|
|
63
|
+
return pl.DataFrame(data, schema=get_polars_schema_from_typeddict(typed_dict))
|
|
@@ -110,3 +110,27 @@ def get_repo_root(path: "Path") -> Optional["Path"]:
|
|
|
110
110
|
|
|
111
111
|
if __name__ == "__main__":
|
|
112
112
|
from pathlib import Path
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def display_with_flashy_style(msg: str, title: str) -> None:
|
|
116
|
+
"""Display a flashy, unmissable share URL announcement."""
|
|
117
|
+
from rich.console import Console
|
|
118
|
+
from rich.panel import Panel
|
|
119
|
+
from rich.text import Text
|
|
120
|
+
from rich.align import Align
|
|
121
|
+
console = Console()
|
|
122
|
+
url_text = Text(msg, style="bold bright_cyan underline")
|
|
123
|
+
message = Text.assemble(
|
|
124
|
+
("🚀 ", "bright_red"),
|
|
125
|
+
url_text,
|
|
126
|
+
(" 🚀", "bright_red")
|
|
127
|
+
)
|
|
128
|
+
panel = Panel(
|
|
129
|
+
Align.center(message),
|
|
130
|
+
title=f"[bold bright_green]🌐 {title} 🌐[/bold bright_green]",
|
|
131
|
+
subtitle="[italic bright_yellow]⚡ Click the link above to access your shared files! ⚡[/italic bright_yellow]",
|
|
132
|
+
border_style="bright_magenta",
|
|
133
|
+
padding=(1, 2),
|
|
134
|
+
expand=False
|
|
135
|
+
)
|
|
136
|
+
console.print(panel)
|