machineconfig 1.94__py3-none-any.whl → 1.96__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/cloud_manager.py +445 -343
- machineconfig/cluster/data_transfer.py +63 -56
- machineconfig/cluster/distribute.py +284 -280
- machineconfig/cluster/file_manager.py +234 -237
- machineconfig/cluster/job_params.py +133 -133
- machineconfig/cluster/loader_runner.py +183 -149
- machineconfig/cluster/remote_machine.py +269 -252
- machineconfig/cluster/script_execution.py +215 -209
- machineconfig/cluster/script_notify_upon_completion.py +50 -43
- machineconfig/cluster/self_ssh.py +52 -54
- machineconfig/cluster/sessions_managers/__init__.py +0 -0
- machineconfig/cluster/sessions_managers/archive/__init__.py +0 -0
- machineconfig/{jobs/python → cluster/sessions_managers/archive}/create_zellij_template.py +5 -3
- machineconfig/cluster/sessions_managers/archive/session_managers.py +184 -0
- machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +160 -0
- machineconfig/cluster/sessions_managers/wt_local.py +494 -0
- machineconfig/cluster/sessions_managers/wt_local_manager.py +577 -0
- machineconfig/cluster/sessions_managers/wt_remote.py +288 -0
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +483 -0
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +196 -0
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +418 -0
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +175 -0
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +300 -0
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +228 -0
- machineconfig/cluster/sessions_managers/zellij_local.py +418 -0
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +533 -0
- machineconfig/cluster/sessions_managers/zellij_remote.py +229 -0
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +188 -0
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +64 -0
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +126 -0
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +334 -0
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +68 -0
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +119 -0
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +85 -0
- machineconfig/cluster/templates/cli_click.py +0 -1
- machineconfig/cluster/templates/cli_gooey.py +102 -104
- machineconfig/cluster/templates/run_cloud.py +51 -51
- machineconfig/cluster/templates/run_cluster.py +103 -59
- machineconfig/cluster/templates/run_remote.py +57 -58
- machineconfig/cluster/templates/utils.py +69 -36
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/linux/msc/lid.sh +2 -4
- machineconfig/jobs/linux/msc/network.sh +3 -6
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/check_installations.py +258 -190
- machineconfig/jobs/python/create_bootable_media.py +7 -3
- machineconfig/jobs/python/python_cargo_build_share.py +50 -50
- machineconfig/jobs/python/python_ve_symlink.py +10 -10
- machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
- machineconfig/jobs/python/vscode/api.py +3 -3
- machineconfig/jobs/python/vscode/link_ve.py +6 -6
- machineconfig/jobs/python/vscode/select_interpreter.py +13 -9
- machineconfig/jobs/python/vscode/sync_code.py +14 -11
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/aider.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +7 -6
- machineconfig/jobs/python_custom_installers/dev/brave.py +11 -7
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +7 -8
- machineconfig/jobs/python_custom_installers/dev/code.py +7 -5
- machineconfig/jobs/python_custom_installers/dev/cursor.py +66 -5
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -5
- machineconfig/jobs/python_custom_installers/dev/espanso.py +21 -17
- machineconfig/jobs/python_custom_installers/dev/goes.py +6 -12
- machineconfig/jobs/python_custom_installers/dev/lvim.py +7 -6
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/redis.py +11 -7
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -8
- machineconfig/jobs/python_custom_installers/dev/winget.py +194 -0
- machineconfig/jobs/python_custom_installers/{dev/docker.py → docker.py} +12 -7
- machineconfig/jobs/python_custom_installers/gh.py +10 -9
- machineconfig/jobs/python_custom_installers/hx.py +36 -65
- machineconfig/jobs/python_custom_installers/scripts/linux/brave.sh +4 -8
- machineconfig/jobs/python_custom_installers/scripts/linux/docker.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/docker_start.sh +3 -6
- machineconfig/jobs/python_custom_installers/scripts/linux/edge.sh +3 -6
- machineconfig/jobs/python_custom_installers/scripts/linux/nerdfont.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/pgsql.sh +4 -8
- machineconfig/jobs/python_custom_installers/scripts/linux/redis.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/timescaledb.sh +6 -12
- machineconfig/jobs/python_custom_installers/scripts/linux/vscode.sh +101 -33
- machineconfig/jobs/python_custom_installers/scripts/linux/warp-cli.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/wezterm.sh +3 -6
- machineconfig/jobs/python_custom_installers/{dev/warp-cli.py → warp-cli.py} +5 -5
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/config.json +133 -9
- machineconfig/jobs/python_generic_installers/dev/config.json +208 -37
- machineconfig/jobs/python_generic_installers/update.py +3 -0
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_linux_installers/config.json +42 -6
- machineconfig/jobs/python_linux_installers/dev/config.json +79 -11
- machineconfig/jobs/python_windows_installers/config.json +6 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/profile/create.py +5 -5
- machineconfig/profile/create_hardlinks.py +5 -5
- machineconfig/profile/shell.py +69 -63
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/cloud/init.sh +9 -18
- machineconfig/scripts/linux/fire +5 -24
- machineconfig/scripts/linux/fire_agents +27 -0
- machineconfig/scripts/linux/share_cloud.sh +6 -12
- machineconfig/scripts/linux/wifi_conn +24 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/archive/im2text.py +28 -30
- machineconfig/scripts/python/archive/tmate_conn.py +10 -13
- machineconfig/scripts/python/archive/tmate_start.py +12 -16
- machineconfig/scripts/python/choose_wezterm_theme.py +12 -21
- machineconfig/scripts/python/cloud_copy.py +49 -104
- machineconfig/scripts/python/cloud_manager.py +77 -91
- machineconfig/scripts/python/cloud_mount.py +36 -46
- machineconfig/scripts/python/cloud_repo_sync.py +33 -79
- machineconfig/scripts/python/cloud_sync.py +35 -45
- machineconfig/scripts/python/croshell.py +70 -92
- machineconfig/scripts/python/devops.py +54 -113
- machineconfig/scripts/python/devops_add_identity.py +71 -109
- machineconfig/scripts/python/devops_add_ssh_key.py +42 -149
- machineconfig/scripts/python/devops_backup_retrieve.py +31 -119
- machineconfig/scripts/python/devops_devapps_install.py +6 -10
- machineconfig/scripts/python/devops_update_repos.py +5 -4
- machineconfig/scripts/python/dotfile.py +10 -7
- machineconfig/scripts/python/fire_agents.py +69 -0
- machineconfig/scripts/python/fire_jobs.py +117 -78
- machineconfig/scripts/python/ftpx.py +8 -8
- machineconfig/scripts/python/get_zellij_cmd.py +3 -3
- machineconfig/scripts/python/gh_models.py +6 -4
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +49 -46
- machineconfig/scripts/python/helpers/helpers2.py +26 -39
- machineconfig/scripts/python/helpers/helpers4.py +23 -35
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +33 -67
- machineconfig/scripts/python/mount_nfs.py +8 -11
- machineconfig/scripts/python/mount_nw_drive.py +4 -4
- machineconfig/scripts/python/mount_ssh.py +2 -2
- machineconfig/scripts/python/onetimeshare.py +56 -57
- machineconfig/scripts/python/pomodoro.py +55 -55
- machineconfig/scripts/python/repos.py +72 -49
- machineconfig/scripts/python/scheduler.py +70 -53
- machineconfig/scripts/python/snapshot.py +21 -24
- machineconfig/scripts/python/start_slidev.py +6 -5
- machineconfig/scripts/python/start_terminals.py +3 -1
- machineconfig/scripts/python/viewer.py +5 -4
- machineconfig/scripts/python/viewer_template.py +138 -140
- machineconfig/scripts/python/wifi_conn.py +416 -62
- machineconfig/scripts/python/wsl_windows_transfer.py +18 -3
- machineconfig/scripts/windows/fire.ps1 +27 -15
- machineconfig/settings/linters/.pylintrc +6 -7
- machineconfig/settings/lvim/windows/config.lua +0 -0
- machineconfig/settings/shells/bash/init.sh +6 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +7 -6
- machineconfig/settings/shells/pwsh/init.ps1 +6 -6
- machineconfig/settings/shells/wt/settings.json +51 -266
- machineconfig/setup_linux/nix/cli_installation.sh +9 -18
- machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +3 -6
- machineconfig/setup_linux/web_shortcuts/all.sh +5 -10
- machineconfig/setup_linux/web_shortcuts/ascii_art.sh +7 -14
- machineconfig/setup_linux/web_shortcuts/croshell.sh +6 -12
- machineconfig/setup_linux/web_shortcuts/interactive.sh +39 -70
- machineconfig/setup_linux/web_shortcuts/ssh.sh +8 -16
- machineconfig/setup_linux/web_shortcuts/update_system.sh +7 -14
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -6
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +27 -21
- machineconfig/utils/ai/browser_user_wrapper.py +60 -45
- machineconfig/utils/ai/generate_file_checklist.py +4 -7
- machineconfig/utils/ai/url2md.py +15 -7
- machineconfig/utils/cloud/onedrive/setup_oauth.py +59 -0
- machineconfig/utils/cloud/onedrive/transaction.py +796 -0
- machineconfig/utils/{utils_code.py → code.py} +25 -22
- machineconfig/utils/installer.py +81 -44
- machineconfig/utils/installer_utils/installer_abc.py +7 -6
- machineconfig/utils/installer_utils/installer_class.py +44 -25
- machineconfig/utils/io_save.py +107 -0
- machineconfig/utils/{utils_links.py → links.py} +28 -35
- machineconfig/utils/{utils_options.py → options.py} +14 -28
- machineconfig/utils/path.py +112 -0
- machineconfig/utils/path_reduced.py +608 -0
- machineconfig/utils/procs.py +134 -73
- machineconfig/utils/scheduling.py +312 -222
- machineconfig/utils/utils.py +19 -26
- machineconfig/utils/utils2.py +42 -0
- machineconfig/utils/utils5.py +84 -0
- machineconfig/utils/ve.py +49 -103
- {machineconfig-1.94.dist-info → machineconfig-1.96.dist-info}/METADATA +5 -3
- machineconfig-1.96.dist-info/RECORD +437 -0
- {machineconfig-1.94.dist-info → machineconfig-1.96.dist-info}/WHEEL +1 -1
- machineconfig/cluster/session_managers.py +0 -183
- machineconfig/cluster/templates/f.py +0 -4
- machineconfig/jobs/python/__pycache__/check_installations.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/checkout_version.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/checkout_version.py +0 -123
- machineconfig/jobs/python/vscode/__pycache__/api.cpython-311.pyc +0 -0
- machineconfig/jobs/python/vscode/__pycache__/link_ve.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/__pycache__/hx.cpython-311.pyc +0 -0
- machineconfig/jobs/python_windows_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/.mypy_cache/.gitignore +0 -2
- machineconfig/scripts/python/.mypy_cache/3.11/@plugins_snapshot.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/__future__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/__future__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_ast.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_ast.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_bz2.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_bz2.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_codecs.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_codecs.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_compression.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_compression.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_decimal.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_decimal.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_io.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_io.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_locale.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_locale.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_stat.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_stat.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_struct.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_struct.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_thread.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_thread.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_warnings.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_warnings.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakref.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakref.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/argparse.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/argparse.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/ast.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/ast.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/binascii.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/binascii.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/builtins.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/builtins.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/bz2.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/bz2.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/calendar.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/calendar.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/codecs.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/codecs.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/configparser.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/configparser.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/contextlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/contextlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/datetime.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/datetime.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/decimal.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/decimal.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dis.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dis.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/charset.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/charset.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/errors.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/errors.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/header.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/header.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/message.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/message.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/policy.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/policy.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/enum.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/enum.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/functools.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/functools.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/genericpath.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/genericpath.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/getpass.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/getpass.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/compat.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/compat.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/config.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/config.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/db.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/db.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/diff.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/diff.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/exc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/exc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/remote.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/remote.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/types.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/types.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/glob.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/glob.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gzip.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gzip.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/inspect.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/inspect.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/io.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/io.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/itertools.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/itertools.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/locale.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/locale.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mmap.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mmap.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/numbers.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/numbers.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/opcode.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/opcode.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/path.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/path.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/pathlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/pathlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/platform.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/platform.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/posixpath.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/posixpath.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/re.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/re.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/resource.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/resource.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shlex.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shlex.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shutil.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shutil.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/signal.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/signal.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/stat.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/stat.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/string.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/string.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/struct.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/struct.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/subprocess.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/subprocess.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tarfile.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tarfile.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tempfile.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tempfile.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/textwrap.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/textwrap.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/threading.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/threading.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/time.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/time.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/types.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/types.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/uuid.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/uuid.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/warnings.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/warnings.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/weakref.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/weakref.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/CACHEDIR.TAG +0 -3
- machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/gh_models.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/url2md.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/viewer.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/vscode_api.cpython-311.pyc +0 -0
- machineconfig/settings/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/settings/linters/.ruff_cache/.gitignore +0 -2
- machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +0 -1
- machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/playext.cpython-311.pyc +0 -0
- machineconfig/utils/utils_path.py +0 -151
- machineconfig/utils/ve_utils/ve1.py +0 -111
- machineconfig/utils/ve_utils/ve2.py +0 -142
- machineconfig-1.94.dist-info/RECORD +0 -710
- {machineconfig-1.94.dist-info → machineconfig-1.96.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from crocodile.core import List, timestamp, randstr, install_n_import, validate_name
|
|
6
|
+
from crocodile.file_management_helpers.file1 import encrypt, decrypt
|
|
7
|
+
from crocodile.file_management_helpers.file2 import Compression
|
|
8
|
+
from crocodile.file_management_helpers.file3 import Read
|
|
9
|
+
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import sys
|
|
13
|
+
import subprocess
|
|
14
|
+
from typing import Any, Optional, Union, Callable, TypeAlias, Literal
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
OPLike: TypeAlias = Union[str, 'P', Path, None]
|
|
18
|
+
PLike: TypeAlias = Union[str, 'P', Path]
|
|
19
|
+
FILE_MODE: TypeAlias = Literal['r', 'w', 'x', 'a']
|
|
20
|
+
SHUTIL_FORMATS: TypeAlias = Literal["zip", "tar", "gztar", "bztar", "xztar"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def modify_text(txt_raw: str, txt_search: str, txt_alt: Union[str, Callable[[str], str]], replace_line: bool = True, notfound_append: bool = False, prepend: bool = False, strict: bool = False):
|
|
24
|
+
lines, bingo = txt_raw.split("\n"), False
|
|
25
|
+
if not replace_line: # no need for line splitting
|
|
26
|
+
assert isinstance(txt_alt, str), f"txt_alt must be a string if notfound_append is True. It is not: {txt_alt}"
|
|
27
|
+
if txt_search in txt_raw: return txt_raw.replace(txt_search, txt_alt)
|
|
28
|
+
return txt_raw + "\n" + txt_alt if notfound_append else txt_raw
|
|
29
|
+
for idx, line in enumerate(lines):
|
|
30
|
+
if txt_search in line:
|
|
31
|
+
if isinstance(txt_alt, str): lines[idx] = txt_alt
|
|
32
|
+
elif callable(txt_alt): lines[idx] = txt_alt(line)
|
|
33
|
+
bingo = True
|
|
34
|
+
if strict and not bingo: raise ValueError(f"txt_search `{txt_search}` not found in txt_raw `{txt_raw}`")
|
|
35
|
+
if bingo is False and notfound_append is True:
|
|
36
|
+
assert isinstance(txt_alt, str), f"txt_alt must be a string if notfound_append is True. It is not: {txt_alt}"
|
|
37
|
+
if prepend: lines.insert(0, txt_alt)
|
|
38
|
+
else: lines.append(txt_alt) # txt not found, add it anyway.
|
|
39
|
+
return "\n".join(lines)
|
|
40
|
+
|
|
41
|
+
class P(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
42
|
+
# ============= Path management ==================
|
|
43
|
+
""" The default behaviour of methods acting on underlying disk object is to perform the action and return a new path referring to the mutated object in disk drive.
|
|
44
|
+
However, there is a flag `orig` that makes the function return orignal path object `self` as opposed to the new one pointing to new object.
|
|
45
|
+
Additionally, the fate of the original object can be decided by a flag `inplace` which means `replace` it defaults to False and in essence, it deletes the original underlying object.
|
|
46
|
+
This can be seen in `zip` and `encrypt` but not in `copy`, `move`, `retitle` because the fate of original file is dictated already.
|
|
47
|
+
Furthermore, those methods are accompanied with print statement explaining what happened to the object."""
|
|
48
|
+
def delete(self, sure: bool = False, verbose: bool = True) -> 'P': # slf = self.expanduser().resolve() don't resolve symlinks.
|
|
49
|
+
if not sure:
|
|
50
|
+
if verbose: print(f"❌ Did NOT DELETE because user is not sure. file: {repr(self)}.")
|
|
51
|
+
return self
|
|
52
|
+
if not self.exists():
|
|
53
|
+
self.unlink(missing_ok=True)
|
|
54
|
+
if verbose: print(f"❌ Could NOT DELETE nonexisting file {repr(self)}. ")
|
|
55
|
+
return self # broken symlinks exhibit funny existence behaviour, catch them here.
|
|
56
|
+
if self.is_file() or self.is_symlink(): self.unlink(missing_ok=True)
|
|
57
|
+
else:
|
|
58
|
+
import shutil
|
|
59
|
+
shutil.rmtree(self, ignore_errors=False)
|
|
60
|
+
if verbose: print(f"🗑️ ❌ DELETED {repr(self)}.")
|
|
61
|
+
return self
|
|
62
|
+
def move(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, rel2it: bool = False, overwrite: bool = False, verbose: bool = True, parents: bool = True, content: bool = False) -> 'P':
|
|
63
|
+
path = self._resolve_path(folder=folder, name=name, path=path, default_name=self.absolute().name, rel2it=rel2it)
|
|
64
|
+
if parents: path.parent.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
slf = self.expanduser().resolve()
|
|
66
|
+
if content:
|
|
67
|
+
assert self.is_dir(), NotADirectoryError(f"💥 When `content` flag is set to True, path must be a directory. It is not: `{repr(self)}`")
|
|
68
|
+
self.search("*").apply(lambda x: x.move(folder=path.parent, content=False, overwrite=overwrite))
|
|
69
|
+
return path # contents live within this directory.
|
|
70
|
+
if overwrite:
|
|
71
|
+
tmp_path = slf.rename(path.parent.absolute() / randstr())
|
|
72
|
+
path.delete(sure=True, verbose=verbose)
|
|
73
|
+
tmp_path.rename(path) # works if moving a path up and parent has same name
|
|
74
|
+
else:
|
|
75
|
+
try:
|
|
76
|
+
slf.rename(path) # self._return(res=path, inplace=True, operation='rename', orig=False, verbose=verbose, strict=True, msg='')
|
|
77
|
+
except OSError as oe: # OSError: [Errno 18] Invalid cross-device link:
|
|
78
|
+
# https://stackoverflow.com/questions/42392600/oserror-errno-18-invalid-cross-device-link
|
|
79
|
+
import shutil
|
|
80
|
+
shutil.move(str(slf), str(path))
|
|
81
|
+
_ = oe
|
|
82
|
+
if verbose: print(f"🚚 MOVED {repr(self)} ==> {repr(path)}`")
|
|
83
|
+
return path
|
|
84
|
+
def copy(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, content: bool = False, verbose: bool = True, append: Optional[str] = None, overwrite: bool = False, orig: bool = False) -> 'P': # tested %100 # TODO: replace `content` flag with ability to interpret "*" in resolve method.
|
|
85
|
+
dest = self._resolve_path(folder=folder, name=name, path=path, default_name=self.name, rel2it=False)
|
|
86
|
+
dest = dest.expanduser().resolve()
|
|
87
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
88
|
+
slf = self.expanduser().resolve()
|
|
89
|
+
if dest == slf:
|
|
90
|
+
dest = self.append(append if append is not None else f"_copy_{randstr()}")
|
|
91
|
+
if not content and overwrite and dest.exists(): dest.delete(sure=True)
|
|
92
|
+
if not content and not overwrite and dest.exists(): raise FileExistsError(f"💥 Destination already exists: {repr(dest)}")
|
|
93
|
+
if slf.is_file():
|
|
94
|
+
import shutil
|
|
95
|
+
shutil.copy(str(slf), str(dest))
|
|
96
|
+
if verbose: print(f"🖨️ COPIED {repr(slf)} ==> {repr(dest)}")
|
|
97
|
+
elif slf.is_dir():
|
|
98
|
+
dest = dest.parent if content else dest
|
|
99
|
+
# from distutils.dir_util import copy_tree
|
|
100
|
+
from shutil import copytree
|
|
101
|
+
copytree(str(slf), str(dest))
|
|
102
|
+
if verbose: print(f"🖨️ COPIED {'Content of ' if content else ''} {repr(slf)} ==> {repr(dest)}")
|
|
103
|
+
else: print(f"💥 Could NOT COPY. Not a file nor a path: {repr(slf)}.")
|
|
104
|
+
return dest if not orig else self
|
|
105
|
+
# ======================================= File Editing / Reading ===================================
|
|
106
|
+
def readit(self, reader: Optional[Callable[[PLike], Any]] = None, strict: bool = True, default: Optional[Any] = None, verbose: bool = False, **kwargs: Any) -> 'Any':
|
|
107
|
+
slf = self.expanduser().resolve()
|
|
108
|
+
if not slf.exists():
|
|
109
|
+
if strict: raise FileNotFoundError(f"`{slf}` is no where to be found!")
|
|
110
|
+
else:
|
|
111
|
+
if verbose: print(f"💥 P.readit warning: FileNotFoundError, skipping reading of file `{self}")
|
|
112
|
+
return default
|
|
113
|
+
if verbose: print(f"Reading {slf} ({slf.size()} MB) ...")
|
|
114
|
+
if '.tar.gz' in str(slf) or '.tgz' in str(slf) or '.gz' in str(slf) or '.tar.bz' in str(slf) or 'tbz' in str(slf) or 'tar.xz' in str(slf) or '.zip' in str(slf):
|
|
115
|
+
filename = slf.decompress(folder=slf.tmp(folder="tmp_unzipped"), verbose=True)
|
|
116
|
+
if filename.is_dir():
|
|
117
|
+
tmp_content = filename.search("*")
|
|
118
|
+
if len(tmp_content) == 1:
|
|
119
|
+
print(f"⚠️ Found only one file in the unzipped folder: {tmp_content[0]}")
|
|
120
|
+
filename = tmp_content.list[0]
|
|
121
|
+
else:
|
|
122
|
+
if strict: raise ValueError(f"❌ Expected only one file in the unzipped folder, but found {len(tmp_content)} files.")
|
|
123
|
+
else: print(f"⚠️ Found {len(tmp_content)} files in the unzipped folder. Using the first one: {tmp_content[0]}")
|
|
124
|
+
filename = tmp_content.list[0]
|
|
125
|
+
else: filename = slf
|
|
126
|
+
try:
|
|
127
|
+
return Read.read(filename, **kwargs) if reader is None else reader(str(filename), **kwargs)
|
|
128
|
+
except IOError as ioe: raise IOError from ioe
|
|
129
|
+
# DEPRECATED: append_text has been removed. Use the inline equivalent instead:
|
|
130
|
+
# p.write_text(p.read_text() + appendix)
|
|
131
|
+
# Returning the path (p) is preserved by write_text in this class.
|
|
132
|
+
# Example:
|
|
133
|
+
# p = p.write_text(p.read_text() + appendix)
|
|
134
|
+
# def append_text(self, appendix: str) -> 'P':
|
|
135
|
+
# self.write_text(self.read_text() + appendix)
|
|
136
|
+
# return self
|
|
137
|
+
# DEPRECATED: Instance method modify_text is deprecated and left commented-out to prevent new usage.
|
|
138
|
+
# Please inline using the module-level modify_text helper:
|
|
139
|
+
# current = p.read_text() if p.exists() else ""
|
|
140
|
+
# updated = modify_text(current, search, alt, replace_line=..., notfound_append=..., prepend=...)
|
|
141
|
+
# p.write_text(updated)
|
|
142
|
+
# def modify_text(self, txt_search: str, txt_alt: str, replace_line: bool = False, notfound_append: bool = False, prepend: bool = False, encoding: str = 'utf-8'):
|
|
143
|
+
# if not self.exists():
|
|
144
|
+
# self.parent.mkdir(parents=True, exist_ok=True)
|
|
145
|
+
# self.write_text(txt_search)
|
|
146
|
+
# return self.write_text(modify_text(txt_raw=self.read_text(encoding=encoding), txt_search=txt_search, txt_alt=txt_alt, replace_line=replace_line, notfound_append=notfound_append, prepend=prepend), encoding=encoding)
|
|
147
|
+
def download(self, folder: OPLike = None, name: Optional[str]= None, allow_redirects: bool = True, timeout: Optional[int] = None, params: Any = None) -> 'P':
|
|
148
|
+
import requests
|
|
149
|
+
response = requests.get(self.as_url_str(), allow_redirects=allow_redirects, timeout=timeout, params=params) # Alternative: from urllib import request; request.urlopen(url).read().decode('utf-8').
|
|
150
|
+
assert response.status_code == 200, f"Download failed with status code {response.status_code}\n{response.text}"
|
|
151
|
+
if name is not None: f_name = name
|
|
152
|
+
else:
|
|
153
|
+
try:
|
|
154
|
+
f_name = response.headers['Content-Disposition'].split('filename=')[1].replace('"', '')
|
|
155
|
+
except (KeyError, IndexError):
|
|
156
|
+
f_name = validate_name(str(P(response.history[-1].url).name if len(response.history) > 0 else P(response.url).name))
|
|
157
|
+
dest_path = (P.home().joinpath("Downloads") if folder is None else P(folder)).joinpath(f_name)
|
|
158
|
+
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
159
|
+
return dest_path.write_bytes(response.content)
|
|
160
|
+
def _return(self, res: Union['P', 'Path'], operation: Literal['rename', 'delete', 'Whack'], inplace: bool = False, overwrite: bool = False, orig: bool = False, verbose: bool = False, strict: bool = True, msg: str = "", __delayed_msg__: str = "") -> 'P':
|
|
161
|
+
res = P(res)
|
|
162
|
+
if inplace:
|
|
163
|
+
assert self.exists(), f"`inplace` flag is only relevant if the path exists. It doesn't {self}"
|
|
164
|
+
if operation == "rename":
|
|
165
|
+
if overwrite and res.exists(): res.delete(sure=True, verbose=verbose)
|
|
166
|
+
if not overwrite and res.exists():
|
|
167
|
+
if strict: raise FileExistsError(f"❌ RENAMING failed. File `{res}` already exists.")
|
|
168
|
+
else:
|
|
169
|
+
if verbose: print(f"⚠️ SKIPPED RENAMING {repr(self)} ➡️ {repr(res)} because FileExistsError and scrict=False policy.")
|
|
170
|
+
return self if orig else res
|
|
171
|
+
self.rename(res)
|
|
172
|
+
msg = msg or f"RENAMED {repr(self)} ➡️ {repr(res)}"
|
|
173
|
+
elif operation == "delete":
|
|
174
|
+
self.delete(sure=True, verbose=False)
|
|
175
|
+
__delayed_msg__ = f"DELETED 🗑️❌ {repr(self)}."
|
|
176
|
+
if verbose and msg != "":
|
|
177
|
+
try: print(msg) # emojie print error.
|
|
178
|
+
except UnicodeEncodeError: print("P._return warning: UnicodeEncodeError, could not print message.")
|
|
179
|
+
if verbose and __delayed_msg__ != "":
|
|
180
|
+
try: print(__delayed_msg__)
|
|
181
|
+
except UnicodeEncodeError: print("P._return warning: UnicodeEncodeError, could not print message.")
|
|
182
|
+
return self if orig else res
|
|
183
|
+
# # ================================ Path Object management ===========================================
|
|
184
|
+
# """ Distinction between Path object and the underlying file on disk that the path may refer to. Two distinct flags are used:
|
|
185
|
+
# `inplace`: the operation on the path object will affect the underlying file on disk if this flag is raised, otherwise the method will only alter the string.
|
|
186
|
+
# `inliue`: the method acts on the path object itself instead of creating a new one if this flag is raised.
|
|
187
|
+
# `orig`: whether the method returns the original path object or a new one."""
|
|
188
|
+
def append(self, name: str = '', index: bool = False, suffix: Optional[str] = None, verbose: bool = True, **kwargs: Any) -> 'P':
|
|
189
|
+
"""Returns a new path object with the name appended to the stem of the path. If `index` is True, the name will be the index of the path in the parent directory."""
|
|
190
|
+
if index:
|
|
191
|
+
appended_name = f'''{name}_{len(self.parent.search(f"*{self.name.split('.')[0]}*"))}'''
|
|
192
|
+
return self.append(name=appended_name, index=False, verbose=verbose, suffix=suffix, **kwargs)
|
|
193
|
+
full_name = (name or ("_" + str(timestamp())))
|
|
194
|
+
full_suffix = suffix or ''.join(('bruh' + self).suffixes)
|
|
195
|
+
subpath = self.name.split('.')[0] + full_name + full_suffix
|
|
196
|
+
return self._return(self.parent.joinpath(subpath), operation="rename", verbose=verbose, **kwargs)
|
|
197
|
+
def with_name(self, name: str, verbose: bool = True, inplace: bool = False, overwrite: bool = False, **kwargs: Any):
|
|
198
|
+
return self._return(self.parent / name, verbose=verbose, operation="rename", inplace=inplace, overwrite=overwrite, **kwargs)
|
|
199
|
+
# ============================= attributes of object ======================================
|
|
200
|
+
# @property
|
|
201
|
+
# def items(self) -> List[str]: return List(self.parts)
|
|
202
|
+
# def __len__(self) -> int: return len(self.parts)
|
|
203
|
+
# def __contains__(self, item: PLike): return P(item).as_posix() in self.as_posix()
|
|
204
|
+
# def __iter__(self): return self.parts.__iter__()
|
|
205
|
+
def __deepcopy__(self, *args: Any, **kwargs: Any) -> 'P':
|
|
206
|
+
_ = args, kwargs
|
|
207
|
+
return P(str(self))
|
|
208
|
+
def __getstate__(self) -> str: return str(self)
|
|
209
|
+
def __add__(self, other: PLike) -> 'P':
|
|
210
|
+
return self.parent.joinpath(self.name + str(other)) # used append and prepend if the addition wanted to be before suffix.
|
|
211
|
+
def __radd__(self, other: PLike) -> 'P':
|
|
212
|
+
return self.parent.joinpath(str(other) + self.name) # other + P and `other` doesn't know how to make this addition.
|
|
213
|
+
def __sub__(self, other: PLike) -> 'P':
|
|
214
|
+
res = P(str(self).replace(str(other), ""))
|
|
215
|
+
return (res[1:] if str(res[0]) in {"\\", "/"} else res) if len(res.parts) else res # paths starting with "/" are problematic. e.g ~ / "/path" doesn't work.
|
|
216
|
+
|
|
217
|
+
def rel2home(self, ) -> 'P': return self._return(P(self.expanduser().absolute().relative_to(Path.home())), operation='Whack') # very similat to collapseuser but without "~" being added so its consistent with rel2cwd.
|
|
218
|
+
def collapseuser(self, strict: bool = True, placeholder: str = "~") -> 'P': # opposite of `expanduser` resolve is crucial to fix Windows cases insensitivty problem.
|
|
219
|
+
if strict: assert P.home() in self.expanduser().absolute().resolve(), ValueError(f"`{P.home()}` is not in the subpath of `{self}`")
|
|
220
|
+
if (str(self).startswith(placeholder) or P.home().as_posix() not in self.resolve().as_posix()): return self
|
|
221
|
+
return self._return(res=P(placeholder) / (self.expanduser().absolute().resolve(strict=strict) - P.home()), operation='Whack') # resolve also solves the problem of Windows case insensitivty.
|
|
222
|
+
def __getitem__(self, slici: Union[int, list[int], slice]):
|
|
223
|
+
if isinstance(slici, list): return P(*[self[item] for item in slici])
|
|
224
|
+
elif isinstance(slici, int): return P(self.parts[slici])
|
|
225
|
+
return P(*self.parts[slici]) # must be a slice
|
|
226
|
+
def split(self, at: Optional[str] = None, index: Optional[int] = None, sep: Literal[-1, 0, 1] = 1, strict: bool = True):
|
|
227
|
+
if index is None and at is not None: # at is provided # ==================================== Splitting
|
|
228
|
+
if not strict: # behaves like split method of string
|
|
229
|
+
one, two = (items := str(self).split(sep=str(at)))[0], items[1]
|
|
230
|
+
one, two = P(one[:-1]) if one.endswith("/") else P(one), P(two[1:]) if two.startswith("/") else P(two)
|
|
231
|
+
else: # "strict": # raises an error if exact match is not found.
|
|
232
|
+
index = self.parts.index(str(at))
|
|
233
|
+
one, two = self[0:index], self[index + 1:] # both one and two do not include the split item.
|
|
234
|
+
elif index is not None and at is None: # index is provided
|
|
235
|
+
one, two = self[:index], P(*self.parts[index + 1:])
|
|
236
|
+
at = self.parts[index] # this is needed below.
|
|
237
|
+
else: raise ValueError("Either `index` or `at` can be provided. Both are not allowed simulatanesouly.")
|
|
238
|
+
if sep == 0: return one, two # neither of the portions get the sperator appended to it. # ================================ appending `at` to one of the portions
|
|
239
|
+
elif sep == 1: return one, P(at) / two # append it to right portion
|
|
240
|
+
elif sep == -1:
|
|
241
|
+
return one / at, two # append it to left portion.
|
|
242
|
+
else: raise ValueError(f"`sep` should take a value from the set [-1, 0, 1] but got {sep}")
|
|
243
|
+
def __repr__(self): # this is useful only for the console
|
|
244
|
+
if self.is_symlink():
|
|
245
|
+
try: target = self.resolve() # broken symolinks are funny, and almost always fail `resolve` method.
|
|
246
|
+
except Exception: target = "BROKEN LINK " + str(self) # avoid infinite recursions for broken links.
|
|
247
|
+
return "🔗 Symlink '" + str(self) + "' ==> " + (str(target) if target == self else str(target))
|
|
248
|
+
elif self.is_absolute(): return self._type() + " '" + str(self.clickable()) + "'" + (" | " + self.time(which="c").isoformat()[:-7].replace("T", " ") if self.exists() else "") + (f" | {self.size()} Mb" if self.is_file() else "")
|
|
249
|
+
elif "http" in str(self): return "🕸️ URL " + str(self.as_url_str())
|
|
250
|
+
else: return "📍 Relative " + "'" + str(self) + "'" # not much can be said about a relative path.
|
|
251
|
+
# def to_str(self) -> str: return str(self)
|
|
252
|
+
def size(self, units: Literal['b', 'kb', 'mb', 'gb'] = 'mb') -> float: # ===================================== File Specs ==========================================================================================
|
|
253
|
+
total_size = self.stat().st_size if self.is_file() else sum([item.stat().st_size for item in self.rglob("*") if item.is_file()])
|
|
254
|
+
tmp: int
|
|
255
|
+
match units:
|
|
256
|
+
case "b": tmp = 1024 ** 0
|
|
257
|
+
case "kb": tmp = 1024 ** 1
|
|
258
|
+
case "mb": tmp = 1024 ** 2
|
|
259
|
+
case "gb": tmp = 1024 ** 3
|
|
260
|
+
return round(number=total_size / tmp, ndigits=1)
|
|
261
|
+
def time(self, which: Literal["m", "c", "a"] = "m", **kwargs: Any):
|
|
262
|
+
"""* `m`: last mofidication of content, i.e. the time it was created.
|
|
263
|
+
* `c`: last status change (its inode is changed, permissions, path, but not content)
|
|
264
|
+
* `a`: last access (read)
|
|
265
|
+
"""
|
|
266
|
+
match which:
|
|
267
|
+
case "m": tmp = self.stat().st_mtime
|
|
268
|
+
case "a": tmp = self.stat().st_atime
|
|
269
|
+
case "c": tmp = self.stat().st_ctime
|
|
270
|
+
return datetime.fromtimestamp(tmp, **kwargs)
|
|
271
|
+
|
|
272
|
+
# ================================ String Nature management ====================================
|
|
273
|
+
def clickable(self, ) -> 'P': return self._return(res=P(self.expanduser().resolve().as_uri()), operation='Whack')
|
|
274
|
+
def as_url_str(self) -> 'str': return self.as_posix().replace("https:/", "https://").replace("http:/", "http://")
|
|
275
|
+
def as_zip_path(self):
|
|
276
|
+
import zipfile
|
|
277
|
+
res = self.expanduser().resolve()
|
|
278
|
+
return zipfile.Path(res) # .str.split(".zip") tmp=res[1]+(".zip" if len(res) > 2 else ""); root=res[0]+".zip", at=P(tmp).as_posix()) # TODO
|
|
279
|
+
# ========================== override =======================================
|
|
280
|
+
def __setitem__(self, key: Union['str', int, slice], value: PLike):
|
|
281
|
+
fullparts, new = list(self.parts), list(P(value).parts)
|
|
282
|
+
if type(key) is str:
|
|
283
|
+
idx = fullparts.index(key)
|
|
284
|
+
fullparts.remove(key)
|
|
285
|
+
fullparts = fullparts[:idx] + new + fullparts[idx + 1:]
|
|
286
|
+
elif type(key) is int: fullparts = fullparts[:key] + new + fullparts[key + 1:]
|
|
287
|
+
elif type(key) is slice: fullparts = fullparts[:(0 if key.start is None else key.start)] + new + fullparts[(len(fullparts) if key.stop is None else key.stop):]
|
|
288
|
+
self._str = str(P(*fullparts)) # pylint: disable=W0201 # similar attributes: # self._parts # self._pparts # self._cparts # self._cached_cparts
|
|
289
|
+
|
|
290
|
+
def _type(self):
|
|
291
|
+
if self.absolute():
|
|
292
|
+
if self.is_file(): return "📄"
|
|
293
|
+
elif self.is_dir(): return "📁"
|
|
294
|
+
return "👻NotExist"
|
|
295
|
+
return "📍Relative"
|
|
296
|
+
def write_text(self, data: str, encoding: str = 'utf-8', newline: Optional[str] = None) -> 'P':
|
|
297
|
+
self.parent.mkdir(parents=True, exist_ok=True)
|
|
298
|
+
super(P, self).write_text(data, encoding=encoding, newline=newline)
|
|
299
|
+
return self
|
|
300
|
+
def read_text(self, encoding: Optional[str] = 'utf-8') -> str: return super(P, self).read_text(encoding=encoding)
|
|
301
|
+
def write_bytes(self, data: bytes, overwrite: bool = False) -> 'P':
|
|
302
|
+
slf = self.expanduser().absolute()
|
|
303
|
+
if overwrite and slf.exists(): slf.delete(sure=True)
|
|
304
|
+
res = super(P, slf).write_bytes(data)
|
|
305
|
+
if res == 0: raise RuntimeError("Could not save file on disk.")
|
|
306
|
+
return self
|
|
307
|
+
# def touch(self, mode: int = 0o666, parents: bool = True, exist_ok: bool = True) -> 'P': # pylint: disable=W0237
|
|
308
|
+
# """Deprecated: rely on pathlib.Path.touch at call sites.
|
|
309
|
+
# Behavior was:
|
|
310
|
+
# - if parents: ensure parent directories exist
|
|
311
|
+
# - then call Path.touch(mode=mode, exist_ok=exist_ok)
|
|
312
|
+
# - return self
|
|
313
|
+
# Replace usages with:
|
|
314
|
+
# p.parent.mkdir(parents=True, exist_ok=True); p.touch(mode=..., exist_ok=...)
|
|
315
|
+
# """
|
|
316
|
+
# if parents: self.parent.mkdir(parents=parents, exist_ok=True)
|
|
317
|
+
# super(P, self).touch(mode=mode, exist_ok=exist_ok)
|
|
318
|
+
# return self
|
|
319
|
+
|
|
320
|
+
def symlink_to(self, target: PLike, verbose: bool = True, overwrite: bool = False, orig: bool = False, strict: bool = True): # pylint: disable=W0237
|
|
321
|
+
self.parent.mkdir(parents=True, exist_ok=True)
|
|
322
|
+
target_obj = P(target).expanduser().resolve()
|
|
323
|
+
if strict: assert target_obj.exists(), f"Target path `{target}` (aka `{target_obj}`) doesn't exist. This will create a broken link."
|
|
324
|
+
if overwrite and (self.is_symlink() or self.exists()): self.delete(sure=True, verbose=verbose)
|
|
325
|
+
from platform import system
|
|
326
|
+
from crocodile.meta import Terminal
|
|
327
|
+
if system() == "Windows" and not Terminal.is_user_admin(): # you cannot create symlink without priviliages.
|
|
328
|
+
Terminal.run_as_admin(file=sys.executable, params=f" -c \"from pathlib import Path; Path(r'{self.expanduser()}').symlink_to(r'{str(target_obj)}')\"", wait=True)
|
|
329
|
+
else: super(P, self.expanduser()).symlink_to(str(target_obj))
|
|
330
|
+
return self._return(target_obj, operation='Whack', inplace=False, orig=orig, verbose=verbose, msg=f"LINKED {repr(self)} ➡️ {repr(target_obj)}")
|
|
331
|
+
def resolve(self, strict: bool = False):
|
|
332
|
+
try: return super(P, self).resolve(strict=strict)
|
|
333
|
+
except OSError: return self
|
|
334
|
+
# ======================================== Folder management =======================================
|
|
335
|
+
def search(self, pattern: str = '*', r: bool = False, files: bool = True, folders: bool = True, compressed: bool = False, dotfiles: bool = False, filters_total: Optional[list[Callable[[Any], bool]]] = None, not_in: Optional[list[str]] = None,
|
|
336
|
+
exts: Optional[list[str]] = None, win_order: bool = False) -> List['P']:
|
|
337
|
+
if isinstance(not_in, list):
|
|
338
|
+
filters_notin = [lambda x: all([str(a_not_in) not in str(x) for a_not_in in not_in])] # type: ignore
|
|
339
|
+
else: filters_notin = []
|
|
340
|
+
if isinstance(exts, list):
|
|
341
|
+
filters_extension = [lambda x: any([ext in x.name for ext in exts])] # type: ignore
|
|
342
|
+
else: filters_extension = []
|
|
343
|
+
filters_total = (filters_total or []) + filters_notin + filters_extension
|
|
344
|
+
if not files: filters_total.append(lambda x: x.is_dir())
|
|
345
|
+
if not folders: filters_total.append(lambda x: x.is_file())
|
|
346
|
+
slf = self.expanduser().resolve()
|
|
347
|
+
if ".zip" in str(slf) and compressed: # the root (self) is itself a zip archive (as opposed to some search results are zip archives)
|
|
348
|
+
import zipfile
|
|
349
|
+
import fnmatch
|
|
350
|
+
root = slf.as_zip_path()
|
|
351
|
+
if not r:
|
|
352
|
+
raw = List(root.iterdir())
|
|
353
|
+
else:
|
|
354
|
+
raw = List(zipfile.ZipFile(str(slf)).namelist()).apply(root.joinpath)
|
|
355
|
+
res1 = raw.filter(lambda zip_path: fnmatch.fnmatch(zip_path.at, pattern)) # type: ignore
|
|
356
|
+
return res1.filter(lambda x: (folders or x.is_file()) and (files or x.is_dir())) # type: ignore
|
|
357
|
+
elif dotfiles: raw = slf.glob(pattern) if not r else self.rglob(pattern)
|
|
358
|
+
else:
|
|
359
|
+
from glob import glob
|
|
360
|
+
if r:
|
|
361
|
+
raw = glob(str(slf / "**" / pattern), recursive=r)
|
|
362
|
+
else:
|
|
363
|
+
raw = glob(str(slf.joinpath(pattern))) # glob ignroes dot and hidden files
|
|
364
|
+
if ".zip" not in str(slf) and compressed:
|
|
365
|
+
filters_notin = [P(comp_file).search(pattern=pattern, r=r, files=files, folders=folders, compressed=True, dotfiles=dotfiles, filters_total=filters_total, not_in=not_in, win_order=win_order) for comp_file in self.search("*.zip", r=r)]
|
|
366
|
+
haha = List(filters_notin).reduce(func=lambda x, y: x + y)
|
|
367
|
+
raw = raw + haha # type: ignore
|
|
368
|
+
processed = []
|
|
369
|
+
for item in raw:
|
|
370
|
+
item_ = P(item)
|
|
371
|
+
if all([afilter(item_) for afilter in filters_total]):
|
|
372
|
+
processed.append(item_)
|
|
373
|
+
if not win_order: return List(processed)
|
|
374
|
+
import re
|
|
375
|
+
processed.sort(key=lambda x: [int(k) if k.isdigit() else k for k in re.split('([0-9]+)', string=x.stem)])
|
|
376
|
+
return List(processed)
|
|
377
|
+
|
|
378
|
+
# def create(self, parents: bool = True, exist_ok: bool = True, parents_only: bool = False) -> 'P':
|
|
379
|
+
# """Deprecated. Use Path.mkdir directly at the call site:
|
|
380
|
+
# - When creating a directory: self.mkdir(parents=True, exist_ok=True)
|
|
381
|
+
# - When ensuring parent exists: self.parent.mkdir(parents=True, exist_ok=True)
|
|
382
|
+
# This method used to:
|
|
383
|
+
# target_path = self.parent if parents_only else self
|
|
384
|
+
# target_path.mkdir(parents=parents, exist_ok=exist_ok)
|
|
385
|
+
# return self
|
|
386
|
+
# """
|
|
387
|
+
# target_path = self.parent if parents_only else self
|
|
388
|
+
# target_path.mkdir(parents=parents, exist_ok=exist_ok)
|
|
389
|
+
# return self
|
|
390
|
+
|
|
391
|
+
@staticmethod
|
|
392
|
+
def tmpdir(prefix: str = "") -> 'P':
|
|
393
|
+
return P.tmp(folder=rf"tmp_dirs/{prefix + ('_' if prefix != '' else '') + randstr()}")
|
|
394
|
+
@staticmethod
|
|
395
|
+
def tmpfile(name: Optional[str]= None, suffix: str = "", folder: OPLike = None, tstamp: bool = False, noun: bool = False) -> 'P':
|
|
396
|
+
name_concrete = name or randstr(noun=noun)
|
|
397
|
+
return P.tmp(file=name_concrete + "_" + randstr() + (("_" + str(timestamp())) if tstamp else "") + suffix, folder=folder or "tmp_files")
|
|
398
|
+
@staticmethod
|
|
399
|
+
def tmp(folder: OPLike = None, file: Optional[str] = None, root: str = "~/tmp_results") -> 'P':
|
|
400
|
+
base = P(root).expanduser().joinpath(folder or "").joinpath(file or "")
|
|
401
|
+
target_path = base.parent if file else base
|
|
402
|
+
target_path.mkdir(parents=True, exist_ok=True)
|
|
403
|
+
return base
|
|
404
|
+
# ====================================== Compression & Encryption ===========================================
|
|
405
|
+
def zip(self, path: OPLike = None, folder: OPLike = None, name: Optional[str]= None, arcname: Optional[str] = None, inplace: bool = False, verbose: bool = True,
|
|
406
|
+
content: bool = False, orig: bool = False, use_7z: bool = False, pwd: Optional[str] = None, mode: FILE_MODE = 'w', **kwargs: Any) -> 'P':
|
|
407
|
+
path_resolved, slf = self._resolve_path(folder, name, path, self.name).expanduser().resolve(), self.expanduser().resolve()
|
|
408
|
+
if use_7z: # benefits over regular zip and encrypt: can handle very large files with low memory footprint
|
|
409
|
+
path_resolved = path_resolved + '.7z' if not path_resolved.suffix == '.7z' else path_resolved
|
|
410
|
+
with install_n_import("py7zr").SevenZipFile(file=path_resolved, mode=mode, password=pwd) as archive: archive.writeall(path=str(slf), arcname=None)
|
|
411
|
+
else:
|
|
412
|
+
arcname_obj = P(arcname or slf.name)
|
|
413
|
+
if arcname_obj.name != slf.name: arcname_obj /= slf.name # arcname has to start from somewhere and end with filename
|
|
414
|
+
if slf.is_file():
|
|
415
|
+
path_resolved = Compression.zip_file(ip_path=str(slf), op_path=str(path_resolved + ".zip" if path_resolved.suffix != ".zip" else path_resolved), arcname=str(arcname_obj), mode=mode, **kwargs)
|
|
416
|
+
else:
|
|
417
|
+
if content: root_dir, base_dir = slf, "."
|
|
418
|
+
else: root_dir, base_dir = slf.split(at=str(arcname_obj[0]), sep=1)[0], str(arcname_obj)
|
|
419
|
+
path_resolved = P(Compression.compress_folder(root_dir=str(root_dir), op_path=str(path_resolved), base_dir=base_dir, fmt='zip', **kwargs)) # TODO: see if this supports mode
|
|
420
|
+
return self._return(path_resolved, inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"ZIPPED {repr(slf)} ==> {repr(path)}")
|
|
421
|
+
def unzip(self, folder: OPLike = None, path: OPLike = None, name: Optional[str]= None, verbose: bool = True, content: bool = False, inplace: bool = False, overwrite: bool = False, orig: bool = False,
|
|
422
|
+
pwd: Optional[str] = None, tmp: bool = False, pattern: Optional[str] = None, merge: bool = False) -> 'P':
|
|
423
|
+
assert merge is False, "I have not implemented this yet"
|
|
424
|
+
assert path is None, "I have not implemented this yet"
|
|
425
|
+
if tmp: return self.unzip(folder=P.tmp().joinpath("tmp_unzips").joinpath(randstr()), content=True).joinpath(self.stem)
|
|
426
|
+
slf = zipfile__ = self.expanduser().resolve()
|
|
427
|
+
if any(ztype in str(slf.parent) for ztype in (".zip", ".7z")): # path include a zip archive in the middle.
|
|
428
|
+
tmp__ = [item for item in (".zip", ".7z", "") if item in str(slf)]
|
|
429
|
+
ztype = tmp__[0]
|
|
430
|
+
if ztype == "": return slf
|
|
431
|
+
zipfile__, name__ = slf.split(at=str(List(slf.parts).filter(lambda x: ztype in x)[0]), sep=-1)
|
|
432
|
+
name = str(name__)
|
|
433
|
+
folder = (zipfile__.parent / zipfile__.stem) if folder is None else P(folder).expanduser().absolute().resolve().joinpath(zipfile__.stem)
|
|
434
|
+
folder = folder if not content else folder.parent
|
|
435
|
+
if slf.suffix == ".7z":
|
|
436
|
+
if overwrite: P(folder).delete(sure=True)
|
|
437
|
+
result = folder
|
|
438
|
+
import py7zr
|
|
439
|
+
with py7zr.SevenZipFile(file=slf, mode='r', password=pwd) as archive:
|
|
440
|
+
if pattern is not None:
|
|
441
|
+
import re
|
|
442
|
+
pat = re.compile(pattern)
|
|
443
|
+
archive.extract(path=folder, targets=[f for f in archive.getnames() if pat.match(f)])
|
|
444
|
+
else: archive.extractall(path=folder)
|
|
445
|
+
else:
|
|
446
|
+
if overwrite:
|
|
447
|
+
if not content: P(folder).joinpath(name or "").delete(sure=True, verbose=True) # deletes a specific file / folder that has the same name as the zip file without extension.
|
|
448
|
+
else:
|
|
449
|
+
import zipfile
|
|
450
|
+
List([x for x in zipfile.ZipFile(str(self)).namelist() if "/" not in x or (len(x.split('/')) == 2 and x.endswith("/"))]).apply(lambda item: P(folder).joinpath(name or "", item.replace("/", "")).delete(sure=True, verbose=True))
|
|
451
|
+
result = Compression.unzip(str(zipfile__), str(folder), None if name is None else P(name).as_posix())
|
|
452
|
+
assert isinstance(result, Path)
|
|
453
|
+
return self._return(P(result), inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"UNZIPPED {repr(zipfile__)} ==> {repr(result)}")
|
|
454
|
+
def untar(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> 'P':
|
|
455
|
+
op_path = self._resolve_path(folder, name, path, self.name.replace(".tar", "")).expanduser().resolve()
|
|
456
|
+
Compression.untar(str(self.expanduser().resolve()), op_path=str(op_path))
|
|
457
|
+
return self._return(op_path, inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"UNTARRED {repr(self)} ==> {repr(op_path)}")
|
|
458
|
+
def ungz(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> 'P':
|
|
459
|
+
op_path = self._resolve_path(folder, name, path, self.name.replace(".gz", "")).expanduser().resolve()
|
|
460
|
+
Compression.ungz(str(self.expanduser().resolve()), op_path=str(op_path))
|
|
461
|
+
return self._return(op_path, inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"UNGZED {repr(self)} ==> {repr(op_path)}")
|
|
462
|
+
def unxz(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> 'P':
|
|
463
|
+
op_path = self._resolve_path(folder, name, path, self.name.replace(".xz", "")).expanduser().resolve()
|
|
464
|
+
Compression.unxz(str(self.expanduser().resolve()), op_path=str(op_path))
|
|
465
|
+
return self._return(op_path, inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"UNXZED {repr(self)} ==> {repr(op_path)}")
|
|
466
|
+
def unbz(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> 'P':
|
|
467
|
+
op_path = self._resolve_path(folder=folder, name=name, path=path, default_name=self.name.replace(".bz", "").replace(".tbz", ".tar")).expanduser().resolve()
|
|
468
|
+
Compression.unbz(str(self.expanduser().resolve()), op_path=str(op_path))
|
|
469
|
+
return self._return(op_path, inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"UNBZED {repr(self)} ==> {repr(op_path)}")
|
|
470
|
+
def decompress(self, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None, inplace: bool = False, orig: bool = False, verbose: bool = True) -> 'P':
|
|
471
|
+
if ".tar.gz" in str(self) or ".tgz" in str(self):
|
|
472
|
+
# res = self.ungz_untar(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
473
|
+
return self.ungz(name=f"tmp_{randstr()}.tar", inplace=inplace).untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose) # this works for .tgz suffix as well as .tar.gz
|
|
474
|
+
elif ".gz" in str(self): res = self.ungz(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
475
|
+
elif ".tar.bz" in str(self) or "tbz" in str(self):
|
|
476
|
+
res = self.unbz(name=f"tmp_{randstr()}.tar", inplace=inplace)
|
|
477
|
+
return res.untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose)
|
|
478
|
+
elif ".tar.xz" in str(self):
|
|
479
|
+
# res = self.unxz_untar(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
480
|
+
res = self.unxz(inplace=inplace).untar(folder=folder, name=name, path=path, inplace=True, orig=orig, verbose=verbose)
|
|
481
|
+
elif ".zip" in str(self): res = self.unzip(folder=folder, path=path, name=name, inplace=inplace, verbose=verbose, orig=orig)
|
|
482
|
+
else: res = self
|
|
483
|
+
return res
|
|
484
|
+
def encrypt(self, key: Optional[bytes] = None, pwd: Optional[str] = None, folder: OPLike = None, name: Optional[str]= None, path: OPLike = None,
|
|
485
|
+
verbose: bool = True, suffix: str = ".enc", inplace: bool = False, orig: bool = False) -> 'P':
|
|
486
|
+
# see: https://stackoverflow.com/questions/42568262/how-to-encrypt-text-with-a-password-in-python & https://stackoverflow.com/questions/2490334/simple-way-to-encode-a-string-according-to-a-password"""
|
|
487
|
+
slf = self.expanduser().resolve()
|
|
488
|
+
path = self._resolve_path(folder, name, path, slf.name + suffix)
|
|
489
|
+
assert slf.is_file(), f"Cannot encrypt a directory. You might want to try `zip_n_encrypt`. {self}"
|
|
490
|
+
path.write_bytes(encrypt(msg=slf.read_bytes(), key=key, pwd=pwd))
|
|
491
|
+
return self._return(path, inplace=inplace, operation="delete", orig=orig, verbose=verbose, msg=f"🔒🔑 ENCRYPTED: {repr(slf)} ==> {repr(path)}.")
|
|
492
|
+
def decrypt(self, key: Optional[bytes] = None, pwd: Optional[str] = None, path: OPLike = None, folder: OPLike = None, name: Optional[str]= None, verbose: bool = True, suffix: str = ".enc", inplace: bool = False) -> 'P':
|
|
493
|
+
slf = self.expanduser().resolve()
|
|
494
|
+
path = self._resolve_path(folder=folder, name=name, path=path, default_name=slf.name.replace(suffix, "") if suffix in slf.name else "decrypted_" + slf.name)
|
|
495
|
+
path.write_bytes(data=decrypt(token=slf.read_bytes(), key=key, pwd=pwd))
|
|
496
|
+
return self._return(path, operation="delete", verbose=verbose, msg=f"🔓🔑 DECRYPTED: {repr(slf)} ==> {repr(path)}.", inplace=inplace)
|
|
497
|
+
def zip_n_encrypt(self, key: Optional[bytes] = None, pwd: Optional[str] = None, inplace: bool = False, verbose: bool = True, orig: bool = False, content: bool = False) -> 'P':
|
|
498
|
+
return self.zip(inplace=inplace, verbose=verbose, content=content).encrypt(key=key, pwd=pwd, verbose=verbose, inplace=True) if not orig else self
|
|
499
|
+
def decrypt_n_unzip(self, key: Optional[bytes] = None, pwd: Optional[str] = None, inplace: bool = False, verbose: bool = True, orig: bool = False) -> 'P': return self.decrypt(key=key, pwd=pwd, verbose=verbose, inplace=inplace).unzip(folder=None, inplace=True, content=False) if not orig else self
|
|
500
|
+
def _resolve_path(self, folder: OPLike, name: Optional[str], path: OPLike, default_name: str, rel2it: bool = False) -> 'P':
|
|
501
|
+
""":param rel2it: `folder` or `path` are relative to `self` as opposed to cwd. This is used when resolving '../dir'"""
|
|
502
|
+
if path is not None:
|
|
503
|
+
path = P(self.joinpath(path).resolve() if rel2it else path).expanduser().resolve()
|
|
504
|
+
assert folder is None and name is None, "If `path` is passed, `folder` and `name` cannot be passed."
|
|
505
|
+
assert not path.is_dir(), f"`path` passed is a directory! it must not be that. If this is meant, pass it with `folder` kwarg. `{path}`"
|
|
506
|
+
return path
|
|
507
|
+
name, folder = (default_name if name is None else str(name)), (self.parent if folder is None else folder) # good for edge cases of path with single part. # means same directory, just different name
|
|
508
|
+
return P(self.joinpath(folder).resolve() if rel2it else folder).expanduser().resolve() / name
|
|
509
|
+
|
|
510
|
+
def get_remote_path(self, root: Optional[str], os_specific: bool = False, rel2home: bool = True, strict: bool = True, obfuscate: bool = False) -> 'P':
|
|
511
|
+
import platform
|
|
512
|
+
tmp1: str = (platform.system().lower() if os_specific else 'generic_os')
|
|
513
|
+
if not rel2home: path = self
|
|
514
|
+
else:
|
|
515
|
+
try: path = self.rel2home()
|
|
516
|
+
except ValueError as ve:
|
|
517
|
+
if strict: raise ve
|
|
518
|
+
path = self
|
|
519
|
+
if obfuscate:
|
|
520
|
+
from crocodile.msc.obfuscater import obfuscate as obfuscate_func
|
|
521
|
+
name = obfuscate_func(seed=P.home().joinpath('dotfiles/creds/data/obfuscation_seed').read_text().rstrip(), data=path.name)
|
|
522
|
+
path = path.with_name(name=name)
|
|
523
|
+
if isinstance(root, str): # the following is to avoid the confusing behaviour of A.joinpath(B) if B is absolute.
|
|
524
|
+
part1 = path.parts[0]
|
|
525
|
+
if part1 == "/": sanitized_path = path[1:].as_posix()
|
|
526
|
+
else: sanitized_path = path.as_posix()
|
|
527
|
+
return P(root + "/" + tmp1 + "/" + sanitized_path)
|
|
528
|
+
return tmp1 / path
|
|
529
|
+
def to_cloud(self, cloud: str, remotepath: OPLike = None, zip: bool = False,encrypt: bool = False, # pylint: disable=W0621, W0622
|
|
530
|
+
key: Optional[bytes] = None, pwd: Optional[str] = None, rel2home: bool = False, strict: bool = True,
|
|
531
|
+
obfuscate: bool = False,
|
|
532
|
+
share: bool = False, verbose: bool = True, os_specific: bool = False, transfers: int = 10, root: Optional[str] = "myhome") -> 'P':
|
|
533
|
+
to_del = []
|
|
534
|
+
localpath = self.expanduser().absolute() if not self.exists() else self
|
|
535
|
+
if zip:
|
|
536
|
+
localpath = localpath.zip(inplace=False)
|
|
537
|
+
to_del.append(localpath)
|
|
538
|
+
if encrypt:
|
|
539
|
+
localpath = localpath.encrypt(key=key, pwd=pwd, inplace=False)
|
|
540
|
+
to_del.append(localpath)
|
|
541
|
+
if remotepath is None:
|
|
542
|
+
rp = localpath.get_remote_path(root=root, os_specific=os_specific, rel2home=rel2home, strict=strict, obfuscate=obfuscate) # if rel2home else (P(root) / localpath if root is not None else localpath)
|
|
543
|
+
else: rp = P(remotepath)
|
|
544
|
+
rclone_cmd = f"""rclone copyto '{localpath.as_posix()}' '{cloud}:{rp.as_posix()}' {'--progress' if verbose else ''} --transfers={transfers}"""
|
|
545
|
+
from crocodile.meta import Terminal
|
|
546
|
+
if verbose: print(f"{'⬆️'*5} UPLOADING with `{rclone_cmd}`")
|
|
547
|
+
shell_to_use = "powershell" if sys.platform == "win32" else "bash"
|
|
548
|
+
res = Terminal(stdout=None if verbose else subprocess.PIPE).run(rclone_cmd, shell=shell_to_use).capture()
|
|
549
|
+
_ = [item.delete(sure=True) for item in to_del]
|
|
550
|
+
assert res.is_successful(strict_err=False, strict_returcode=True), res.print(capture=False, desc="Cloud Storage Operation")
|
|
551
|
+
if verbose: print(f"{'⬆️'*5} UPLOAD COMPLETED.")
|
|
552
|
+
if share:
|
|
553
|
+
if verbose: print("🔗 SHARING FILE")
|
|
554
|
+
shell_to_use = "powershell" if sys.platform == "win32" else "bash"
|
|
555
|
+
res = Terminal().run(f"""rclone link '{cloud}:{rp.as_posix()}'""", shell=shell_to_use).capture()
|
|
556
|
+
tmp = res.op2path(strict_err=False, strict_returncode=False)
|
|
557
|
+
if tmp is None:
|
|
558
|
+
res.print()
|
|
559
|
+
raise RuntimeError(f"💥 Could not get link for {self}.")
|
|
560
|
+
else:
|
|
561
|
+
res.print_if_unsuccessful(desc="Cloud Storage Operation", strict_err=True, strict_returncode=True)
|
|
562
|
+
return tmp
|
|
563
|
+
return self
|
|
564
|
+
def from_cloud(self, cloud: str, remotepath: OPLike = None, decrypt: bool = False, unzip: bool = False, # type: ignore # pylint: disable=W0621
|
|
565
|
+
key: Optional[bytes] = None, pwd: Optional[str] = None, rel2home: bool = False, os_specific: bool = False, strict: bool = True,
|
|
566
|
+
transfers: int = 10, root: Optional[str] = "myhome", verbose: bool = True, overwrite: bool = True, merge: bool = False,):
|
|
567
|
+
if remotepath is None:
|
|
568
|
+
remotepath = self.get_remote_path(root=root, os_specific=os_specific, rel2home=rel2home, strict=strict)
|
|
569
|
+
remotepath += ".zip" if unzip else ""
|
|
570
|
+
remotepath += ".enc" if decrypt else ""
|
|
571
|
+
else: remotepath = P(remotepath)
|
|
572
|
+
localpath = self.expanduser().absolute()
|
|
573
|
+
localpath += ".zip" if unzip else ""
|
|
574
|
+
localpath += ".enc" if decrypt else ""
|
|
575
|
+
rclone_cmd = f"""rclone copyto '{cloud}:{remotepath.as_posix()}' '{localpath.as_posix()}' {'--progress' if verbose else ''} --transfers={transfers}"""
|
|
576
|
+
from crocodile.meta import Terminal
|
|
577
|
+
if verbose: print(f"{'⬇️' * 5} DOWNLOADING with `{rclone_cmd}`")
|
|
578
|
+
shell_to_use = "powershell" if sys.platform == "win32" else "bash"
|
|
579
|
+
res = Terminal(stdout=None if verbose else subprocess.PIPE).run(rclone_cmd, shell=shell_to_use)
|
|
580
|
+
success = res.is_successful(strict_err=False, strict_returcode=True)
|
|
581
|
+
if not success:
|
|
582
|
+
res.print(capture=False, desc="Cloud Storage Operation")
|
|
583
|
+
return None
|
|
584
|
+
if decrypt: localpath = localpath.decrypt(key=key, pwd=pwd, inplace=True)
|
|
585
|
+
if unzip: localpath = localpath.unzip(inplace=True, verbose=True, overwrite=overwrite, content=True, merge=merge)
|
|
586
|
+
return localpath
|
|
587
|
+
def sync_to_cloud(self, cloud: str, sync_up: bool = False, sync_down: bool = False, os_specific: bool = False, rel2home: bool = True, transfers: int = 10, delete: bool = False, root: Optional[str] = "myhome", verbose: bool = True):
|
|
588
|
+
tmp_path_obj = self.expanduser().absolute()
|
|
589
|
+
tmp_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
590
|
+
tmp1, tmp2 = tmp_path_obj.as_posix(), self.get_remote_path(root=root, os_specific=os_specific).as_posix()
|
|
591
|
+
source, target = (tmp1, f"{cloud}:{tmp2 if rel2home else tmp1}") if sync_up else (f"{cloud}:{tmp2 if rel2home else tmp1}", tmp1) # in bisync direction is irrelavent.
|
|
592
|
+
if not sync_down and not sync_up:
|
|
593
|
+
_ = print(f"SYNCING 🔄️ {source} {'<>' * 7} {target}`") if verbose else None
|
|
594
|
+
rclone_cmd = f"""rclone bisync '{source}' '{target}' --resync --remove-empty-dirs """
|
|
595
|
+
else:
|
|
596
|
+
print(f"SYNCING 🔄️ {source} {'>' * 15} {target}`")
|
|
597
|
+
rclone_cmd = f"""rclone sync '{source}' '{target}' """
|
|
598
|
+
rclone_cmd += f" --progress --transfers={transfers} --verbose"
|
|
599
|
+
rclone_cmd += (" --delete-during" if delete else "")
|
|
600
|
+
from crocodile.meta import Terminal
|
|
601
|
+
if verbose : print(rclone_cmd)
|
|
602
|
+
shell_to_use = "powershell" if sys.platform == "win32" else "bash"
|
|
603
|
+
res = Terminal(stdout=None if verbose else subprocess.PIPE).run(rclone_cmd, shell=shell_to_use)
|
|
604
|
+
success = res.is_successful(strict_err=False, strict_returcode=True)
|
|
605
|
+
if not success:
|
|
606
|
+
res.print(capture=False, desc="Cloud Storage Operation")
|
|
607
|
+
return None
|
|
608
|
+
return self
|