meerschaum 2.2.1__py3-none-any.whl → 2.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerschaum/_internal/shell/Shell.py +40 -16
- meerschaum/_internal/term/__init__.py +3 -2
- meerschaum/_internal/term/tools.py +1 -1
- meerschaum/actions/api.py +65 -31
- meerschaum/actions/python.py +56 -24
- meerschaum/actions/start.py +2 -4
- meerschaum/actions/uninstall.py +5 -9
- meerschaum/actions/upgrade.py +11 -3
- meerschaum/api/__init__.py +1 -0
- meerschaum/api/dash/callbacks/__init__.py +4 -0
- meerschaum/api/dash/callbacks/custom.py +39 -0
- meerschaum/api/dash/callbacks/dashboard.py +39 -6
- meerschaum/api/dash/callbacks/login.py +3 -1
- meerschaum/api/dash/components.py +5 -2
- meerschaum/api/dash/pipes.py +145 -97
- meerschaum/config/_default.py +1 -0
- meerschaum/config/_paths.py +12 -12
- meerschaum/config/_version.py +1 -1
- meerschaum/config/paths.py +10 -0
- meerschaum/config/static/__init__.py +1 -1
- meerschaum/connectors/__init__.py +1 -1
- meerschaum/core/Pipe/__init__.py +5 -0
- meerschaum/core/Pipe/_sync.py +2 -3
- meerschaum/plugins/__init__.py +67 -9
- meerschaum/utils/daemon/Daemon.py +7 -2
- meerschaum/utils/misc.py +6 -0
- meerschaum/utils/packages/__init__.py +212 -53
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/process.py +12 -2
- meerschaum/utils/schedule.py +1 -1
- meerschaum/utils/venv/__init__.py +46 -11
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/METADATA +5 -1
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/RECORD +39 -37
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/WHEEL +1 -1
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/LICENSE +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/NOTICE +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.2.1.dist-info → meerschaum-2.2.2.dist-info}/zip-safe +0 -0
@@ -46,6 +46,7 @@ def get_module_path(
|
|
46
46
|
"""
|
47
47
|
Get a module's path without importing.
|
48
48
|
"""
|
49
|
+
import site
|
49
50
|
if debug:
|
50
51
|
from meerschaum.utils.debug import dprint
|
51
52
|
if not _try_install_name_on_fail:
|
@@ -54,33 +55,58 @@ def get_module_path(
|
|
54
55
|
import_name_lower = install_name_lower
|
55
56
|
else:
|
56
57
|
import_name_lower = import_name.lower().replace('-', '_')
|
58
|
+
|
57
59
|
vtp = venv_target_path(venv, allow_nonexistent=True, debug=debug)
|
58
60
|
if not vtp.exists():
|
59
61
|
if debug:
|
60
|
-
dprint(
|
62
|
+
dprint(
|
63
|
+
(
|
64
|
+
"Venv '{venv}' does not exist, cannot import "
|
65
|
+
+ f"'{import_name}'."
|
66
|
+
),
|
67
|
+
color = False,
|
68
|
+
)
|
61
69
|
return None
|
70
|
+
|
71
|
+
venv_target_candidate_paths = [vtp]
|
72
|
+
if venv is None:
|
73
|
+
site_user_packages_dirs = [pathlib.Path(site.getusersitepackages())]
|
74
|
+
site_packages_dirs = [pathlib.Path(path) for path in site.getsitepackages()]
|
75
|
+
|
76
|
+
paths_to_add = [
|
77
|
+
path
|
78
|
+
for path in site_user_packages_dirs + site_packages_dirs
|
79
|
+
if path not in venv_target_candidate_paths
|
80
|
+
]
|
81
|
+
venv_target_candidate_paths += paths_to_add
|
82
|
+
|
62
83
|
candidates = []
|
63
|
-
for
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
if file_name.endswith('dist_info'):
|
84
|
+
for venv_target_candidate in venv_target_candidate_paths:
|
85
|
+
try:
|
86
|
+
file_names = os.listdir(venv_target_candidate)
|
87
|
+
except FileNotFoundError:
|
68
88
|
continue
|
69
|
-
|
89
|
+
for file_name in file_names:
|
90
|
+
file_name_lower = file_name.lower().replace('-', '_')
|
91
|
+
if not file_name_lower.startswith(import_name_lower):
|
92
|
+
continue
|
93
|
+
if file_name.endswith('dist_info'):
|
94
|
+
continue
|
95
|
+
file_path = venv_target_candidate / file_name
|
70
96
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
97
|
+
### Most likely: Is a directory with __init__.py
|
98
|
+
if file_name_lower == import_name_lower and file_path.is_dir():
|
99
|
+
init_path = file_path / '__init__.py'
|
100
|
+
if init_path.exists():
|
101
|
+
candidates.append(init_path)
|
76
102
|
|
77
|
-
|
78
|
-
|
79
|
-
|
103
|
+
### May be a standalone .py file.
|
104
|
+
elif file_name_lower == import_name_lower + '.py':
|
105
|
+
candidates.append(file_path)
|
80
106
|
|
81
|
-
|
82
|
-
|
83
|
-
|
107
|
+
### Compiled wheels (e.g. pyodbc)
|
108
|
+
elif file_name_lower.startswith(import_name_lower + '.'):
|
109
|
+
candidates.append(file_path)
|
84
110
|
|
85
111
|
if len(candidates) == 1:
|
86
112
|
return candidates[0]
|
@@ -466,12 +492,13 @@ def _get_package_metadata(import_name: str, venv: Optional[str]) -> Dict[str, st
|
|
466
492
|
import re
|
467
493
|
from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
|
468
494
|
install_name = _import_to_install_name(import_name)
|
469
|
-
_args = ['show', install_name]
|
495
|
+
_args = ['pip', 'show', install_name]
|
470
496
|
if venv is not None:
|
471
497
|
cache_dir_path = VIRTENV_RESOURCES_PATH / venv / 'cache'
|
472
|
-
_args += ['--cache-dir',
|
498
|
+
_args += ['--cache-dir', cache_dir_path.as_posix()]
|
499
|
+
|
473
500
|
proc = run_python_package(
|
474
|
-
'
|
501
|
+
'uv', _args,
|
475
502
|
capture_output=True, as_proc=True, venv=venv, universal_newlines=True,
|
476
503
|
)
|
477
504
|
outs, errs = proc.communicate()
|
@@ -680,12 +707,22 @@ def need_update(
|
|
680
707
|
return False
|
681
708
|
|
682
709
|
|
683
|
-
def get_pip(
|
710
|
+
def get_pip(
|
711
|
+
venv: Optional[str] = 'mrsm',
|
712
|
+
color: bool = True,
|
713
|
+
debug: bool = False,
|
714
|
+
) -> bool:
|
684
715
|
"""
|
685
716
|
Download and run the get-pip.py script.
|
686
717
|
|
687
718
|
Parameters
|
688
719
|
----------
|
720
|
+
venv: Optional[str], default 'mrsm'
|
721
|
+
The virtual environment into which to install `pip`.
|
722
|
+
|
723
|
+
color: bool, default True
|
724
|
+
If `True`, force color output.
|
725
|
+
|
689
726
|
debug: bool, default False
|
690
727
|
Verbosity toggle.
|
691
728
|
|
@@ -708,7 +745,7 @@ def get_pip(venv: Optional[str] = 'mrsm', debug: bool=False) -> bool:
|
|
708
745
|
if venv is not None:
|
709
746
|
init_venv(venv=venv, debug=debug)
|
710
747
|
cmd_list = [venv_executable(venv=venv), dest.as_posix()]
|
711
|
-
return subprocess.call(cmd_list, env=_get_pip_os_env()) == 0
|
748
|
+
return subprocess.call(cmd_list, env=_get_pip_os_env(color=color)) == 0
|
712
749
|
|
713
750
|
|
714
751
|
def pip_install(
|
@@ -721,6 +758,8 @@ def pip_install(
|
|
721
758
|
check_pypi: bool = True,
|
722
759
|
check_wheel: bool = True,
|
723
760
|
_uninstall: bool = False,
|
761
|
+
_from_completely_uninstall: bool = False,
|
762
|
+
_install_uv_pip: bool = True,
|
724
763
|
color: bool = True,
|
725
764
|
silent: bool = False,
|
726
765
|
debug: bool = False,
|
@@ -776,7 +815,9 @@ def pip_install(
|
|
776
815
|
|
777
816
|
"""
|
778
817
|
from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
|
818
|
+
from meerschaum.config import get_config
|
779
819
|
from meerschaum.utils.warnings import warn
|
820
|
+
from meerschaum.utils.misc import is_android
|
780
821
|
if args is None:
|
781
822
|
args = ['--upgrade'] if not _uninstall else []
|
782
823
|
if color:
|
@@ -787,10 +828,43 @@ def pip_install(
|
|
787
828
|
have_wheel = venv_contains_package('wheel', venv=venv, debug=debug)
|
788
829
|
|
789
830
|
_args = list(args)
|
790
|
-
have_pip = venv_contains_package('pip', venv=
|
831
|
+
have_pip = venv_contains_package('pip', venv=None, debug=debug)
|
832
|
+
try:
|
833
|
+
import pip
|
834
|
+
have_pip = True
|
835
|
+
except ImportError:
|
836
|
+
have_pip = False
|
837
|
+
try:
|
838
|
+
import uv
|
839
|
+
uv_bin = uv.find_uv_bin()
|
840
|
+
have_uv_pip = True
|
841
|
+
except (ImportError, FileNotFoundError):
|
842
|
+
uv_bin = None
|
843
|
+
have_uv_pip = False
|
844
|
+
if have_pip and not have_uv_pip and _install_uv_pip and not is_android():
|
845
|
+
if not pip_install(
|
846
|
+
'uv',
|
847
|
+
venv = None,
|
848
|
+
debug = debug,
|
849
|
+
_install_uv_pip = False,
|
850
|
+
check_update = False,
|
851
|
+
check_pypi = False,
|
852
|
+
check_wheel = False,
|
853
|
+
):
|
854
|
+
warn(
|
855
|
+
f"Failed to install `uv` for virtual environment '{venv}'.",
|
856
|
+
color = False,
|
857
|
+
)
|
858
|
+
|
859
|
+
use_uv_pip = (
|
860
|
+
venv_contains_package('uv', venv=None, debug=debug)
|
861
|
+
and uv_bin is not None
|
862
|
+
and venv is not None
|
863
|
+
)
|
864
|
+
|
791
865
|
import sys
|
792
|
-
if not have_pip:
|
793
|
-
if not get_pip(venv=venv, debug=debug):
|
866
|
+
if not have_pip and not use_uv_pip:
|
867
|
+
if not get_pip(venv=venv, color=color, debug=debug):
|
794
868
|
import sys
|
795
869
|
minor = sys.version_info.minor
|
796
870
|
print(
|
@@ -806,13 +880,18 @@ def pip_install(
|
|
806
880
|
|
807
881
|
with Venv(venv, debug=debug):
|
808
882
|
if venv is not None:
|
809
|
-
if
|
883
|
+
if (
|
884
|
+
'--ignore-installed' not in args
|
885
|
+
and '-I' not in _args
|
886
|
+
and not _uninstall
|
887
|
+
and not use_uv_pip
|
888
|
+
):
|
810
889
|
_args += ['--ignore-installed']
|
811
890
|
if '--cache-dir' not in args and not _uninstall:
|
812
891
|
cache_dir_path = VIRTENV_RESOURCES_PATH / venv / 'cache'
|
813
892
|
_args += ['--cache-dir', str(cache_dir_path)]
|
814
893
|
|
815
|
-
if 'pip' not in ' '.join(_args):
|
894
|
+
if 'pip' not in ' '.join(_args) and not use_uv_pip:
|
816
895
|
if check_update and not _uninstall:
|
817
896
|
pip = attempt_import('pip', venv=venv, install=False, debug=debug, lazy=False)
|
818
897
|
if need_update(pip, check_pypi=check_pypi, debug=debug):
|
@@ -820,17 +899,20 @@ def pip_install(
|
|
820
899
|
|
821
900
|
_args = (['install'] if not _uninstall else ['uninstall']) + _args
|
822
901
|
|
823
|
-
if check_wheel and not _uninstall:
|
902
|
+
if check_wheel and not _uninstall and not use_uv_pip:
|
824
903
|
if not have_wheel:
|
825
904
|
if not pip_install(
|
826
|
-
'setuptools', 'wheel',
|
905
|
+
'setuptools', 'wheel', 'uv',
|
827
906
|
venv = venv,
|
828
|
-
check_update = False,
|
829
|
-
|
907
|
+
check_update = False,
|
908
|
+
check_pypi = False,
|
909
|
+
check_wheel = False,
|
910
|
+
debug = debug,
|
911
|
+
_install_uv_pip = False,
|
830
912
|
):
|
831
913
|
warn(
|
832
914
|
(
|
833
|
-
"Failed to install `setuptools` and `
|
915
|
+
"Failed to install `setuptools`, `wheel`, and `uv` for virtual "
|
834
916
|
+ f"environment '{venv}'."
|
835
917
|
),
|
836
918
|
color = False,
|
@@ -838,24 +920,24 @@ def pip_install(
|
|
838
920
|
|
839
921
|
if requirements_file_path is not None:
|
840
922
|
_args.append('-r')
|
841
|
-
_args.append(
|
923
|
+
_args.append(pathlib.Path(requirements_file_path).resolve().as_posix())
|
842
924
|
|
843
925
|
if not ANSI and '--no-color' not in _args:
|
844
926
|
_args.append('--no-color')
|
845
927
|
|
846
|
-
if '--no-input' not in _args:
|
928
|
+
if '--no-input' not in _args and not use_uv_pip:
|
847
929
|
_args.append('--no-input')
|
848
930
|
|
849
|
-
if _uninstall and '-y' not in _args:
|
931
|
+
if _uninstall and '-y' not in _args and not use_uv_pip:
|
850
932
|
_args.append('-y')
|
851
933
|
|
852
|
-
if '--no-warn-conflicts' not in _args and not _uninstall:
|
934
|
+
if '--no-warn-conflicts' not in _args and not _uninstall and not use_uv_pip:
|
853
935
|
_args.append('--no-warn-conflicts')
|
854
936
|
|
855
|
-
if '--disable-pip-version-check' not in _args:
|
937
|
+
if '--disable-pip-version-check' not in _args and not use_uv_pip:
|
856
938
|
_args.append('--disable-pip-version-check')
|
857
939
|
|
858
|
-
if '--target' not in _args and '-t' not in _args and not _uninstall:
|
940
|
+
if '--target' not in _args and '-t' not in _args and not (not use_uv_pip and _uninstall):
|
859
941
|
if venv is not None:
|
860
942
|
_args += ['--target', venv_target_path(venv, debug=debug)]
|
861
943
|
elif (
|
@@ -863,12 +945,14 @@ def pip_install(
|
|
863
945
|
and '-t' not in _args
|
864
946
|
and not inside_venv()
|
865
947
|
and not _uninstall
|
948
|
+
and not use_uv_pip
|
866
949
|
):
|
867
950
|
_args += ['--user']
|
868
951
|
|
869
952
|
if debug:
|
870
953
|
if '-v' not in _args or '-vv' not in _args or '-vvv' not in _args:
|
871
|
-
|
954
|
+
if use_uv_pip:
|
955
|
+
_args.append('--verbose')
|
872
956
|
else:
|
873
957
|
if '-q' not in _args or '-qq' not in _args or '-qqq' not in _args:
|
874
958
|
pass
|
@@ -883,10 +967,10 @@ def pip_install(
|
|
883
967
|
if not silent:
|
884
968
|
print(msg)
|
885
969
|
|
886
|
-
if not
|
970
|
+
if _uninstall and not _from_completely_uninstall and not use_uv_pip:
|
887
971
|
for install_name in _packages:
|
888
972
|
_install_no_version = get_install_no_version(install_name)
|
889
|
-
if _install_no_version in ('pip', 'wheel'):
|
973
|
+
if _install_no_version in ('pip', 'wheel', 'uv'):
|
890
974
|
continue
|
891
975
|
if not completely_uninstall_package(
|
892
976
|
_install_no_version,
|
@@ -896,11 +980,17 @@ def pip_install(
|
|
896
980
|
f"Failed to clean up package '{_install_no_version}'.",
|
897
981
|
)
|
898
982
|
|
983
|
+
### NOTE: Only append the `--prerelease=allow` flag if we explicitly depend on a prerelease.
|
984
|
+
if use_uv_pip:
|
985
|
+
_args.insert(0, 'pip')
|
986
|
+
if not _uninstall and get_prerelease_dependencies(_packages):
|
987
|
+
_args.append('--prerelease=allow')
|
988
|
+
|
899
989
|
rc = run_python_package(
|
900
|
-
'pip',
|
990
|
+
('pip' if not use_uv_pip else 'uv'),
|
901
991
|
_args + _packages,
|
902
|
-
venv =
|
903
|
-
env = _get_pip_os_env(),
|
992
|
+
venv = None,
|
993
|
+
env = _get_pip_os_env(color=color),
|
904
994
|
debug = debug,
|
905
995
|
)
|
906
996
|
if debug:
|
@@ -918,6 +1008,33 @@ def pip_install(
|
|
918
1008
|
return success
|
919
1009
|
|
920
1010
|
|
1011
|
+
def get_prerelease_dependencies(_packages: Optional[List[str]] = None):
|
1012
|
+
"""
|
1013
|
+
Return a list of explicitly prerelease dependencies from a list of packages.
|
1014
|
+
"""
|
1015
|
+
if _packages is None:
|
1016
|
+
_packages = list(all_packages.keys())
|
1017
|
+
prelrease_strings = ['dev', 'rc', 'a']
|
1018
|
+
prerelease_packages = []
|
1019
|
+
for install_name in _packages:
|
1020
|
+
_install_no_version = get_install_no_version(install_name)
|
1021
|
+
import_name = _install_to_import_name(install_name)
|
1022
|
+
install_with_version = _import_to_install_name(import_name)
|
1023
|
+
version_only = (
|
1024
|
+
install_with_version.lower().replace(_install_no_version.lower(), '')
|
1025
|
+
.split(']')[-1]
|
1026
|
+
)
|
1027
|
+
|
1028
|
+
is_prerelease = False
|
1029
|
+
for prelrease_string in prelrease_strings:
|
1030
|
+
if prelrease_string in version_only:
|
1031
|
+
is_prerelease = True
|
1032
|
+
|
1033
|
+
if is_prerelease:
|
1034
|
+
prerelease_packages.append(install_name)
|
1035
|
+
return prerelease_packages
|
1036
|
+
|
1037
|
+
|
921
1038
|
def completely_uninstall_package(
|
922
1039
|
install_name: str,
|
923
1040
|
venv: str = 'mrsm',
|
@@ -944,7 +1061,7 @@ def completely_uninstall_package(
|
|
944
1061
|
continue
|
945
1062
|
installed_versions.append(file_name)
|
946
1063
|
|
947
|
-
max_attempts = len(installed_versions)
|
1064
|
+
max_attempts = len(installed_versions)
|
948
1065
|
while attempts < max_attempts:
|
949
1066
|
if not venv_contains_package(
|
950
1067
|
_install_to_import_name(_install_no_version),
|
@@ -953,8 +1070,10 @@ def completely_uninstall_package(
|
|
953
1070
|
return True
|
954
1071
|
if not pip_uninstall(
|
955
1072
|
_install_no_version,
|
956
|
-
venv=venv,
|
957
|
-
silent=(not debug),
|
1073
|
+
venv = venv,
|
1074
|
+
silent = (not debug),
|
1075
|
+
_from_completely_uninstall = True,
|
1076
|
+
debug = debug,
|
958
1077
|
):
|
959
1078
|
return False
|
960
1079
|
attempts += 1
|
@@ -1031,6 +1150,10 @@ def run_python_package(
|
|
1031
1150
|
if cwd is not None:
|
1032
1151
|
os.chdir(cwd)
|
1033
1152
|
executable = venv_executable(venv=venv)
|
1153
|
+
venv_path = (VIRTENV_RESOURCES_PATH / venv) if venv is not None else None
|
1154
|
+
env_dict = kw.get('env', os.environ).copy()
|
1155
|
+
if venv_path is not None:
|
1156
|
+
env_dict.update({'VIRTUAL_ENV': venv_path.as_posix()})
|
1034
1157
|
command = [executable, '-m', str(package_name)] + [str(a) for a in args]
|
1035
1158
|
import traceback
|
1036
1159
|
if debug:
|
@@ -1055,7 +1178,7 @@ def run_python_package(
|
|
1055
1178
|
command,
|
1056
1179
|
stdout = stdout,
|
1057
1180
|
stderr = stderr,
|
1058
|
-
env =
|
1181
|
+
env = env_dict,
|
1059
1182
|
)
|
1060
1183
|
to_return = proc if as_proc else proc.wait()
|
1061
1184
|
except KeyboardInterrupt:
|
@@ -1075,9 +1198,10 @@ def attempt_import(
|
|
1075
1198
|
check_update: bool = False,
|
1076
1199
|
check_pypi: bool = False,
|
1077
1200
|
check_is_installed: bool = True,
|
1201
|
+
allow_outside_venv: bool = True,
|
1078
1202
|
color: bool = True,
|
1079
1203
|
debug: bool = False
|
1080
|
-
) ->
|
1204
|
+
) -> Any:
|
1081
1205
|
"""
|
1082
1206
|
Raise a warning if packages are not installed; otherwise import and return modules.
|
1083
1207
|
If `lazy` is `True`, return lazy-imported modules.
|
@@ -1120,6 +1244,15 @@ def attempt_import(
|
|
1120
1244
|
check_is_installed: bool, default True
|
1121
1245
|
If `True`, check if the package is contained in the virtual environment.
|
1122
1246
|
|
1247
|
+
allow_outside_venv: bool, default True
|
1248
|
+
If `True`, search outside of the specified virtual environment
|
1249
|
+
if the package cannot be found.
|
1250
|
+
Setting to `False` will reinstall the package into a virtual environment, even if it
|
1251
|
+
is installed outside.
|
1252
|
+
|
1253
|
+
color: bool, default True
|
1254
|
+
If `False`, do not print ANSI colors.
|
1255
|
+
|
1123
1256
|
Returns
|
1124
1257
|
-------
|
1125
1258
|
The specified modules. If they're not available and `install` is `True`, it will first
|
@@ -1201,6 +1334,7 @@ def attempt_import(
|
|
1201
1334
|
name,
|
1202
1335
|
venv = venv,
|
1203
1336
|
split = split,
|
1337
|
+
allow_outside_venv = allow_outside_venv,
|
1204
1338
|
debug = debug,
|
1205
1339
|
)
|
1206
1340
|
_is_installed_first_check[name] = package_is_installed
|
@@ -1346,7 +1480,9 @@ def import_rich(
|
|
1346
1480
|
'pygments', lazy=False,
|
1347
1481
|
)
|
1348
1482
|
rich = attempt_import(
|
1349
|
-
'rich', lazy=lazy,
|
1483
|
+
'rich', lazy=lazy,
|
1484
|
+
**kw
|
1485
|
+
)
|
1350
1486
|
return rich
|
1351
1487
|
|
1352
1488
|
|
@@ -1580,10 +1716,26 @@ def is_installed(
|
|
1580
1716
|
import_name: str,
|
1581
1717
|
venv: Optional[str] = 'mrsm',
|
1582
1718
|
split: bool = True,
|
1719
|
+
allow_outside_venv: bool = True,
|
1583
1720
|
debug: bool = False,
|
1584
1721
|
) -> bool:
|
1585
1722
|
"""
|
1586
1723
|
Check whether a package is installed.
|
1724
|
+
|
1725
|
+
Parameters
|
1726
|
+
----------
|
1727
|
+
import_name: str
|
1728
|
+
The import name of the module.
|
1729
|
+
|
1730
|
+
venv: Optional[str], default 'mrsm'
|
1731
|
+
The venv in which to search for the module.
|
1732
|
+
|
1733
|
+
split: bool, default True
|
1734
|
+
If `True`, split on periods to determine the root module name.
|
1735
|
+
|
1736
|
+
allow_outside_venv: bool, default True
|
1737
|
+
If `True`, search outside of the specified virtual environment
|
1738
|
+
if the package cannot be found.
|
1587
1739
|
"""
|
1588
1740
|
if debug:
|
1589
1741
|
from meerschaum.utils.debug import dprint
|
@@ -1594,7 +1746,11 @@ def is_installed(
|
|
1594
1746
|
spec_path = pathlib.Path(
|
1595
1747
|
get_module_path(root_name, venv=venv, debug=debug)
|
1596
1748
|
or
|
1597
|
-
|
1749
|
+
(
|
1750
|
+
importlib.util.find_spec(root_name).origin
|
1751
|
+
if venv is not None and allow_outside_venv
|
1752
|
+
else None
|
1753
|
+
)
|
1598
1754
|
)
|
1599
1755
|
except (ModuleNotFoundError, ValueError, AttributeError, TypeError) as e:
|
1600
1756
|
spec_path = None
|
@@ -1623,6 +1779,8 @@ def venv_contains_package(
|
|
1623
1779
|
"""
|
1624
1780
|
Search the contents of a virtual environment for a package.
|
1625
1781
|
"""
|
1782
|
+
import site
|
1783
|
+
import pathlib
|
1626
1784
|
root_name = import_name.split('.')[0] if split else import_name
|
1627
1785
|
return get_module_path(root_name, venv=venv, debug=debug) is not None
|
1628
1786
|
|
@@ -1686,7 +1844,7 @@ def _monkey_patch_get_distribution(_dist: str, _version: str) -> None:
|
|
1686
1844
|
pkg_resources.get_distribution = _get_distribution
|
1687
1845
|
|
1688
1846
|
|
1689
|
-
def _get_pip_os_env():
|
1847
|
+
def _get_pip_os_env(color: bool = True):
|
1690
1848
|
"""
|
1691
1849
|
Return the environment variables context in which `pip` should be run.
|
1692
1850
|
See PEP 668 for why we are overriding the environment.
|
@@ -1695,5 +1853,6 @@ def _get_pip_os_env():
|
|
1695
1853
|
pip_os_env = os.environ.copy()
|
1696
1854
|
pip_os_env.update({
|
1697
1855
|
'PIP_BREAK_SYSTEM_PACKAGES': 'true',
|
1856
|
+
('FORCE_COLOR' if color else 'NO_COLOR'): '1',
|
1698
1857
|
})
|
1699
1858
|
return pip_os_env
|
meerschaum/utils/process.py
CHANGED
@@ -9,10 +9,16 @@ See `meerschaum.utils.pool` for multiprocessing and
|
|
9
9
|
"""
|
10
10
|
|
11
11
|
from __future__ import annotations
|
12
|
-
import os, signal, subprocess, sys, platform
|
12
|
+
import os, signal, subprocess, sys, platform, traceback
|
13
13
|
from meerschaum.utils.typing import Union, Optional, Any, Callable, Dict, Tuple
|
14
14
|
from meerschaum.config.static import STATIC_CONFIG
|
15
15
|
|
16
|
+
_child_processes = []
|
17
|
+
def signal_handler(sig, frame):
|
18
|
+
for child in _child_processes:
|
19
|
+
child.send_signal(sig)
|
20
|
+
child.wait()
|
21
|
+
|
16
22
|
def run_process(
|
17
23
|
*args,
|
18
24
|
foreground: bool = False,
|
@@ -73,6 +79,7 @@ def run_process(
|
|
73
79
|
sys.stdout.write(line.decode('utf-8'))
|
74
80
|
sys.stdout.flush()
|
75
81
|
|
82
|
+
|
76
83
|
if capture_output or line_callback is not None:
|
77
84
|
kw['stdout'] = subprocess.PIPE
|
78
85
|
kw['stderr'] = subprocess.STDOUT
|
@@ -123,6 +130,7 @@ def run_process(
|
|
123
130
|
|
124
131
|
try:
|
125
132
|
child = subprocess.Popen(*args, **kw)
|
133
|
+
_child_processes.append(child)
|
126
134
|
|
127
135
|
# we can't set the process group id from the parent since the child
|
128
136
|
# will already have exec'd. and we can't SIGSTOP it before exec,
|
@@ -147,7 +155,9 @@ def run_process(
|
|
147
155
|
store_proc_dict[store_proc_key] = child
|
148
156
|
_ret = poll_process(child, line_callback) if line_callback is not None else child.wait()
|
149
157
|
ret = _ret if not as_proc else child
|
150
|
-
|
158
|
+
except KeyboardInterrupt:
|
159
|
+
child.send_signal(signal.SIGINT)
|
160
|
+
ret = child.wait() if not as_proc else child
|
151
161
|
finally:
|
152
162
|
if foreground:
|
153
163
|
# we have to mask SIGTTOU because tcsetpgrp
|
meerschaum/utils/schedule.py
CHANGED
@@ -8,7 +8,7 @@ Schedule processes and threads.
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
import sys
|
11
|
-
from datetime import datetime, timezone, timedelta
|
11
|
+
from datetime import datetime, timezone, timedelta
|
12
12
|
import meerschaum as mrsm
|
13
13
|
from meerschaum.utils.typing import Callable, Any, Optional, List, Dict
|
14
14
|
|
@@ -15,7 +15,7 @@ __all__ = sorted([
|
|
15
15
|
'activate_venv', 'deactivate_venv', 'init_venv',
|
16
16
|
'inside_venv', 'is_venv_active', 'venv_exec',
|
17
17
|
'venv_executable', 'venv_exists', 'venv_target_path',
|
18
|
-
'Venv', 'get_venvs', 'verify_venv',
|
18
|
+
'Venv', 'get_venvs', 'verify_venv', 'get_module_venv',
|
19
19
|
])
|
20
20
|
__pdoc__ = {'Venv': True}
|
21
21
|
|
@@ -79,7 +79,7 @@ def activate_venv(
|
|
79
79
|
else:
|
80
80
|
threads_active_venvs[thread_id][venv] += 1
|
81
81
|
|
82
|
-
target =
|
82
|
+
target = venv_target_path(venv, debug=debug).as_posix()
|
83
83
|
if venv in active_venvs_order:
|
84
84
|
sys.path.remove(target)
|
85
85
|
try:
|
@@ -171,7 +171,7 @@ def deactivate_venv(
|
|
171
171
|
if sys.path is None:
|
172
172
|
return False
|
173
173
|
|
174
|
-
target =
|
174
|
+
target = venv_target_path(venv, allow_nonexistent=force, debug=debug).as_posix()
|
175
175
|
with LOCKS['sys.path']:
|
176
176
|
if target in sys.path:
|
177
177
|
sys.path.remove(target)
|
@@ -361,6 +361,8 @@ def init_venv(
|
|
361
361
|
verified_venvs.add(venv)
|
362
362
|
return True
|
363
363
|
|
364
|
+
import io
|
365
|
+
from contextlib import redirect_stdout, redirect_stderr
|
364
366
|
import sys, platform, os, pathlib, shutil
|
365
367
|
from meerschaum.config.static import STATIC_CONFIG
|
366
368
|
from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
|
@@ -381,25 +383,34 @@ def init_venv(
|
|
381
383
|
verified_venvs.add(venv)
|
382
384
|
return True
|
383
385
|
|
384
|
-
from meerschaum.utils.packages import run_python_package, attempt_import
|
386
|
+
from meerschaum.utils.packages import run_python_package, attempt_import, _get_pip_os_env
|
385
387
|
global tried_virtualenv
|
386
388
|
try:
|
387
389
|
import venv as _venv
|
390
|
+
uv = attempt_import('uv', venv=None, debug=debug)
|
388
391
|
virtualenv = None
|
389
392
|
except ImportError:
|
390
393
|
_venv = None
|
394
|
+
uv = None
|
391
395
|
virtualenv = None
|
392
|
-
|
393
396
|
|
394
397
|
_venv_success = False
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
+
|
399
|
+
if uv is not None:
|
400
|
+
_venv_success = run_python_package(
|
401
|
+
'uv',
|
402
|
+
['venv', venv_path.as_posix(), '-q'],
|
403
|
+
venv = None,
|
404
|
+
env = _get_pip_os_env(),
|
405
|
+
debug = debug,
|
406
|
+
) == 0
|
407
|
+
|
408
|
+
if _venv is not None and not _venv_success:
|
398
409
|
f = io.StringIO()
|
399
410
|
with redirect_stdout(f):
|
400
411
|
_venv_success = run_python_package(
|
401
412
|
'venv',
|
402
|
-
[
|
413
|
+
[venv_path.as_posix()] + (
|
403
414
|
['--symlinks'] if platform.system() != 'Windows' else []
|
404
415
|
),
|
405
416
|
venv=None, debug=debug
|
@@ -438,7 +449,7 @@ def init_venv(
|
|
438
449
|
except Exception as e:
|
439
450
|
import traceback
|
440
451
|
traceback.print_exc()
|
441
|
-
virtualenv.cli_run([
|
452
|
+
virtualenv.cli_run([venv_path.as_posix()])
|
442
453
|
if dist_packages_path.exists():
|
443
454
|
vtp.mkdir(exist_ok=True, parents=True)
|
444
455
|
for file_path in dist_packages_path.glob('*'):
|
@@ -614,7 +625,7 @@ def venv_target_path(
|
|
614
625
|
return site_path
|
615
626
|
|
616
627
|
### Allow for dist-level paths (running as root).
|
617
|
-
for possible_dist in
|
628
|
+
for possible_dist in site.getsitepackages():
|
618
629
|
dist_path = pathlib.Path(possible_dist)
|
619
630
|
if not dist_path.exists():
|
620
631
|
continue
|
@@ -698,4 +709,28 @@ def get_venvs() -> List[str]:
|
|
698
709
|
return venvs
|
699
710
|
|
700
711
|
|
712
|
+
def get_module_venv(module) -> Union[str, None]:
|
713
|
+
"""
|
714
|
+
Return the virtual environment where an imported module is installed.
|
715
|
+
|
716
|
+
Parameters
|
717
|
+
----------
|
718
|
+
module: ModuleType
|
719
|
+
The imported module to inspect.
|
720
|
+
|
721
|
+
Returns
|
722
|
+
-------
|
723
|
+
The name of a venv or `None`.
|
724
|
+
"""
|
725
|
+
import pathlib
|
726
|
+
from meerschaum.config.paths import VIRTENV_RESOURCES_PATH
|
727
|
+
module_path = pathlib.Path(module.__file__).resolve()
|
728
|
+
try:
|
729
|
+
rel_path = module_path.relative_to(VIRTENV_RESOURCES_PATH)
|
730
|
+
except ValueError:
|
731
|
+
return None
|
732
|
+
|
733
|
+
return rel_path.as_posix().split('/', maxsplit=1)[0]
|
734
|
+
|
735
|
+
|
701
736
|
from meerschaum.utils.venv._Venv import Venv
|