diff --git a/.venv/lib/python3.11/site-packages/dill/tests/__init__.py b/.venv/lib/python3.11/site-packages/dill/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3fbec382c8b8ad0967d0056ae85ed281bb6a4541 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/__init__.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2018-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +to run this test suite, first build and install `dill`. + + $ python -m pip install ../.. + + +then run the tests with: + + $ python -m dill.tests + + +or, if `nose` is installed: + + $ nosetests + +""" diff --git a/.venv/lib/python3.11/site-packages/dill/tests/__pycache__/test_moduledict.cpython-311.pyc b/.venv/lib/python3.11/site-packages/dill/tests/__pycache__/test_moduledict.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1073dbfd5783d246a125d829256dc73a00a8d3f9 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/dill/tests/__pycache__/test_moduledict.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_abc.py b/.venv/lib/python3.11/site-packages/dill/tests/test_abc.py new file mode 100644 index 0000000000000000000000000000000000000000..b16d30920a079894d26d72b3418c118377d11d73 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_abc.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2023-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +test dill's ability to pickle abstract base class objects +""" +import dill +import abc +from abc import ABC +import warnings + +from types import FunctionType + +dill.settings['recurse'] = True + +class OneTwoThree(ABC): + @abc.abstractmethod + def foo(self): + """A method""" + pass + + @property + @abc.abstractmethod + def bar(self): + """Property getter""" + pass + + @bar.setter + @abc.abstractmethod + def bar(self, value): + """Property setter""" + pass + + @classmethod + @abc.abstractmethod + def cfoo(cls): + """Class method""" + pass + + @staticmethod + @abc.abstractmethod + def sfoo(): + """Static method""" + pass + +class EasyAsAbc(OneTwoThree): + def __init__(self): + self._bar = None + + def foo(self): + return "Instance Method FOO" + + @property + def bar(self): + return self._bar + + @bar.setter + def bar(self, value): + self._bar = value + + @classmethod + def cfoo(cls): + return "Class Method CFOO" + + @staticmethod + def sfoo(): + return "Static Method SFOO" + +def test_abc_non_local(): + assert dill.copy(OneTwoThree) is not OneTwoThree + assert dill.copy(EasyAsAbc) is not EasyAsAbc + + with warnings.catch_warnings(): + warnings.simplefilter("ignore", dill.PicklingWarning) + assert dill.copy(OneTwoThree, byref=True) is OneTwoThree + assert dill.copy(EasyAsAbc, byref=True) is EasyAsAbc + + instance = EasyAsAbc() + # Set a property that StockPickle can't preserve + instance.bar = lambda x: x**2 + depickled = dill.copy(instance) + assert type(depickled) is type(instance) #NOTE: issue #612, test_abc_local + #NOTE: dill.copy of local (or non-local) classes should (not) be the same? + assert type(depickled.bar) is FunctionType + assert depickled.bar(3) == 9 + assert depickled.sfoo() == "Static Method SFOO" + assert depickled.cfoo() == "Class Method CFOO" + assert depickled.foo() == "Instance Method FOO" + +def test_abc_local(): + """ + Test using locally scoped ABC class + """ + class LocalABC(ABC): + @abc.abstractmethod + def foo(self): + pass + + def baz(self): + return repr(self) + + labc = dill.copy(LocalABC) + assert labc is not LocalABC + assert type(labc) is type(LocalABC) + #NOTE: dill.copy of local (or non-local) classes should (not) be the same? + # + # .LocalABC'> + + class Real(labc): + def foo(self): + return "True!" + + def baz(self): + return "My " + super(Real, self).baz() + + real = Real() + assert real.foo() == "True!" + + try: + labc() + except TypeError as e: + # Expected error + pass + else: + print('Failed to raise type error') + assert False + + labc2, pik = dill.copy((labc, Real())) + assert 'Real' == type(pik).__name__ + assert '.Real' in type(pik).__qualname__ + assert type(pik) is not Real + assert labc2 is not LocalABC + assert labc2 is not labc + assert isinstance(pik, labc2) + assert not isinstance(pik, labc) + assert not isinstance(pik, LocalABC) + assert pik.baz() == "My " + repr(pik) + +def test_meta_local_no_cache(): + """ + Test calling metaclass and cache registration + """ + LocalMetaABC = abc.ABCMeta('LocalMetaABC', (), {}) + + class ClassyClass: + pass + + class KlassyClass: + pass + + LocalMetaABC.register(ClassyClass) + + assert not issubclass(KlassyClass, LocalMetaABC) + assert issubclass(ClassyClass, LocalMetaABC) + + res = dill.dumps((LocalMetaABC, ClassyClass, KlassyClass)) + + lmabc, cc, kc = dill.loads(res) + assert type(lmabc) == type(LocalMetaABC) + assert not issubclass(kc, lmabc) + assert issubclass(cc, lmabc) + +if __name__ == '__main__': + test_abc_non_local() + test_abc_local() + test_meta_local_no_cache() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_dataclasses.py b/.venv/lib/python3.11/site-packages/dill/tests/test_dataclasses.py new file mode 100644 index 0000000000000000000000000000000000000000..10dc51c50a129b7a28b4959d4a01c68e2c76a346 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_dataclasses.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) +# Copyright (c) 2022-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +test pickling a dataclass +""" + +import dill +import dataclasses + +def test_dataclasses(): + # Issue #500 + @dataclasses.dataclass + class A: + x: int + y: str + + @dataclasses.dataclass + class B: + a: A + + a = A(1, "test") + before = B(a) + save = dill.dumps(before) + after = dill.loads(save) + assert before != after # classes don't match + assert before == B(A(**dataclasses.asdict(after.a))) + assert dataclasses.asdict(before) == dataclasses.asdict(after) + +if __name__ == '__main__': + test_dataclasses() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_dictviews.py b/.venv/lib/python3.11/site-packages/dill/tests/test_dictviews.py new file mode 100644 index 0000000000000000000000000000000000000000..4e94ce32c5e50442ff991dd85e061d24f7e50223 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_dictviews.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Author: Anirudh Vegesana (avegesan@cs.stanford.edu) +# Copyright (c) 2021-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import dill +from dill._dill import OLD310, MAPPING_PROXY_TRICK, DictProxyType + +def test_dictproxy(): + assert dill.copy(DictProxyType({'a': 2})) + +def test_dictviews(): + x = {'a': 1} + assert dill.copy(x.keys()) + assert dill.copy(x.values()) + assert dill.copy(x.items()) + +def test_dictproxy_trick(): + if not OLD310 and MAPPING_PROXY_TRICK: + x = {'a': 1} + all_views = (x.values(), x.items(), x.keys(), x) + seperate_views = dill.copy(all_views) + new_x = seperate_views[-1] + new_x['b'] = 2 + new_x['c'] = 1 + assert len(new_x) == 3 and len(x) == 1 + assert len(seperate_views[0]) == 3 and len(all_views[0]) == 1 + assert len(seperate_views[1]) == 3 and len(all_views[1]) == 1 + assert len(seperate_views[2]) == 3 and len(all_views[2]) == 1 + assert dict(all_views[1]) == x + assert dict(seperate_views[1]) == new_x + +if __name__ == '__main__': + test_dictproxy() + test_dictviews() + test_dictproxy_trick() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_functions.py b/.venv/lib/python3.11/site-packages/dill/tests/test_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..305acc6571e969ca755f79220a3d2fc2a68cdef4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_functions.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2019-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import functools +import dill +import sys +dill.settings['recurse'] = True + + +def function_a(a): + return a + + +def function_b(b, b1): + return b + b1 + + +def function_c(c, c1=1): + return c + c1 + + +def function_d(d, d1, d2=1): + """doc string""" + return d + d1 + d2 + +function_d.__module__ = 'a module' + + +exec(''' +def function_e(e, *e1, e2=1, e3=2): + return e + sum(e1) + e2 + e3''') + +globalvar = 0 + +@functools.lru_cache(None) +def function_with_cache(x): + global globalvar + globalvar += x + return globalvar + + +def function_with_unassigned_variable(): + if False: + value = None + return (lambda: value) + + +def test_issue_510(): + # A very bizzare use of functions and methods that pickle doesn't get + # correctly for odd reasons. + class Foo: + def __init__(self): + def f2(self): + return self + self.f2 = f2.__get__(self) + + import dill, pickletools + f = Foo() + f1 = dill.copy(f) + assert f1.f2() is f1 + + +def test_functions(): + dumped_func_a = dill.dumps(function_a) + assert dill.loads(dumped_func_a)(0) == 0 + + dumped_func_b = dill.dumps(function_b) + assert dill.loads(dumped_func_b)(1,2) == 3 + + dumped_func_c = dill.dumps(function_c) + assert dill.loads(dumped_func_c)(1) == 2 + assert dill.loads(dumped_func_c)(1, 2) == 3 + + dumped_func_d = dill.dumps(function_d) + assert dill.loads(dumped_func_d).__doc__ == function_d.__doc__ + assert dill.loads(dumped_func_d).__module__ == function_d.__module__ + assert dill.loads(dumped_func_d)(1, 2) == 4 + assert dill.loads(dumped_func_d)(1, 2, 3) == 6 + assert dill.loads(dumped_func_d)(1, 2, d2=3) == 6 + + function_with_cache(1) + globalvar = 0 + dumped_func_cache = dill.dumps(function_with_cache) + assert function_with_cache(2) == 3 + assert function_with_cache(1) == 1 + assert function_with_cache(3) == 6 + assert function_with_cache(2) == 3 + + empty_cell = function_with_unassigned_variable() + cell_copy = dill.loads(dill.dumps(empty_cell)) + assert 'empty' in str(cell_copy.__closure__[0]) + try: + cell_copy() + except Exception: + # this is good + pass + else: + raise AssertionError('cell_copy() did not read an empty cell') + + exec(''' +dumped_func_e = dill.dumps(function_e) +assert dill.loads(dumped_func_e)(1, 2) == 6 +assert dill.loads(dumped_func_e)(1, 2, 3) == 9 +assert dill.loads(dumped_func_e)(1, 2, e2=3) == 8 +assert dill.loads(dumped_func_e)(1, 2, e2=3, e3=4) == 10 +assert dill.loads(dumped_func_e)(1, 2, 3, e2=4) == 12 +assert dill.loads(dumped_func_e)(1, 2, 3, e2=4, e3=5) == 15''') + +def test_code_object(): + import warnings + from dill._dill import ALL_CODE_PARAMS, CODE_PARAMS, CODE_VERSION, _create_code + code = function_c.__code__ + warnings.filterwarnings('ignore', category=DeprecationWarning) # issue 597 + LNOTAB = getattr(code, 'co_lnotab', b'') + if warnings.filters: del warnings.filters[0] + fields = {f: getattr(code, 'co_'+f) for f in CODE_PARAMS} + fields.setdefault('posonlyargcount', 0) # python >= 3.8 + fields.setdefault('lnotab', LNOTAB) # python <= 3.9 + fields.setdefault('linetable', b'') # python >= 3.10 + fields.setdefault('qualname', fields['name']) # python >= 3.11 + fields.setdefault('exceptiontable', b'') # python >= 3.11 + fields.setdefault('endlinetable', None) # python == 3.11a + fields.setdefault('columntable', None) # python == 3.11a + + for version, _, params in ALL_CODE_PARAMS: + args = tuple(fields[p] for p in params.split()) + try: + _create_code(*args) + if version >= (3,10): + _create_code(fields['lnotab'], *args) + except Exception as error: + raise Exception("failed to construct code object with format version {}".format(version)) from error + +if __name__ == '__main__': + test_functions() + test_issue_510() + test_code_object() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_recursive.py b/.venv/lib/python3.11/site-packages/dill/tests/test_recursive.py new file mode 100644 index 0000000000000000000000000000000000000000..b84f19e4bd5fecadaf4ac1fe63dd08a583e88358 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_recursive.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2019-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import dill +from functools import partial +import warnings + + +def copy(obj, byref=False, recurse=False): + if byref: + try: + return dill.copy(obj, byref=byref, recurse=recurse) + except Exception: + pass + else: + raise AssertionError('Copy of %s with byref=True should have given a warning!' % (obj,)) + + warnings.simplefilter('ignore') + val = dill.copy(obj, byref=byref, recurse=recurse) + warnings.simplefilter('error') + return val + else: + return dill.copy(obj, byref=byref, recurse=recurse) + + +class obj1(object): + def __init__(self): + super(obj1, self).__init__() + +class obj2(object): + def __init__(self): + super(obj2, self).__init__() + +class obj3(object): + super_ = super + def __init__(self): + obj3.super_(obj3, self).__init__() + + +def test_super(): + assert copy(obj1(), byref=True) + assert copy(obj1(), byref=True, recurse=True) + assert copy(obj1(), recurse=True) + assert copy(obj1()) + + assert copy(obj2(), byref=True) + assert copy(obj2(), byref=True, recurse=True) + assert copy(obj2(), recurse=True) + assert copy(obj2()) + + assert copy(obj3(), byref=True) + assert copy(obj3(), byref=True, recurse=True) + assert copy(obj3(), recurse=True) + assert copy(obj3()) + + +def get_trigger(model): + pass + +class Machine(object): + def __init__(self): + self.child = Model() + self.trigger = partial(get_trigger, self) + self.child.trigger = partial(get_trigger, self.child) + +class Model(object): + pass + + + +def test_partial(): + assert copy(Machine(), byref=True) + assert copy(Machine(), byref=True, recurse=True) + assert copy(Machine(), recurse=True) + assert copy(Machine()) + + +class Machine2(object): + def __init__(self): + self.go = partial(self.member, self) + def member(self, model): + pass + + +class SubMachine(Machine2): + def __init__(self): + super(SubMachine, self).__init__() + + +def test_partials(): + assert copy(SubMachine(), byref=True) + assert copy(SubMachine(), byref=True, recurse=True) + assert copy(SubMachine(), recurse=True) + assert copy(SubMachine()) + + +class obj4(object): + def __init__(self): + super(obj4, self).__init__() + a = self + class obj5(object): + def __init__(self): + super(obj5, self).__init__() + self.a = a + self.b = obj5() + + +def test_circular_reference(): + assert copy(obj4()) + obj4_copy = dill.loads(dill.dumps(obj4())) + assert type(obj4_copy) is type(obj4_copy).__init__.__closure__[0].cell_contents + assert type(obj4_copy.b) is type(obj4_copy.b).__init__.__closure__[0].cell_contents + + +def f(): + def g(): + return g + return g + + +def test_function_cells(): + assert copy(f()) + + +def fib(n): + assert n >= 0 + if n <= 1: + return n + else: + return fib(n-1) + fib(n-2) + + +def test_recursive_function(): + global fib + fib2 = copy(fib, recurse=True) + fib3 = copy(fib) + fib4 = fib + del fib + assert fib2(5) == 5 + for _fib in (fib3, fib4): + try: + _fib(5) + except Exception: + # This is expected to fail because fib no longer exists + pass + else: + raise AssertionError("Function fib shouldn't have been found") + fib = fib4 + + +def collection_function_recursion(): + d = {} + def g(): + return d + d['g'] = g + return g + + +def test_collection_function_recursion(): + g = copy(collection_function_recursion()) + assert g()['g'] is g + + +if __name__ == '__main__': + with warnings.catch_warnings(): + warnings.simplefilter('error') + test_super() + test_partial() + test_partials() + test_circular_reference() + test_function_cells() + test_recursive_function() + test_collection_function_recursion() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_restricted.py b/.venv/lib/python3.11/site-packages/dill/tests/test_restricted.py new file mode 100644 index 0000000000000000000000000000000000000000..cdb773e36df6304e44c5a883f9f4e5beaee0d674 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_restricted.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# +# Author: Kirill Makhonin (@kirillmakhonin) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import dill + +class RestrictedType: + def __bool__(*args, **kwargs): + raise Exception('Restricted function') + + __eq__ = __lt__ = __le__ = __ne__ = __gt__ = __ge__ = __hash__ = __bool__ + +glob_obj = RestrictedType() + +def restricted_func(): + a = glob_obj + +def test_function_with_restricted_object(): + deserialized = dill.loads(dill.dumps(restricted_func, recurse=True)) + + +if __name__ == '__main__': + test_function_with_restricted_object() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_selected.py b/.venv/lib/python3.11/site-packages/dill/tests/test_selected.py new file mode 100644 index 0000000000000000000000000000000000000000..bcc640afca8157642f0573112be5db7b0958ce94 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_selected.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2008-2016 California Institute of Technology. +# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE +""" +testing some selected object types +""" + +import dill +dill.settings['recurse'] = True + +verbose = False + +def test_dict_contents(): + c = type.__dict__ + for i,j in c.items(): + #try: + ok = dill.pickles(j) + #except Exception: + # print ("FAIL: %s with %s" % (i, dill.detect.errors(j))) + if verbose: print ("%s: %s, %s" % (ok, type(j), j)) + assert ok + if verbose: print ("") + +def _g(x): yield x; + +def _f(): + try: raise + except Exception: + from sys import exc_info + e, er, tb = exc_info() + return er, tb + +class _d(object): + def _method(self): + pass + +from dill import objects +from dill import load_types +load_types(pickleable=True,unpickleable=False) +_newclass = objects['ClassObjectType'] +# some clean-up #FIXME: should happen internal to dill +objects['TemporaryFileType'].close() +objects['TextWrapperType'].close() +if 'BufferedRandomType' in objects: + objects['BufferedRandomType'].close() +objects['BufferedReaderType'].close() +objects['BufferedWriterType'].close() +objects['FileType'].close() +del objects + +# getset_descriptor for new-style classes (fails on '_method', if not __main__) +def test_class_descriptors(): + d = _d.__dict__ + for i in d.values(): + ok = dill.pickles(i) + if verbose: print ("%s: %s, %s" % (ok, type(i), i)) + assert ok + if verbose: print ("") + od = _newclass.__dict__ + for i in od.values(): + ok = dill.pickles(i) + if verbose: print ("%s: %s, %s" % (ok, type(i), i)) + assert ok + if verbose: print ("") + +# (__main__) class instance for new-style classes +def test_class(): + o = _d() + oo = _newclass() + ok = dill.pickles(o) + if verbose: print ("%s: %s, %s" % (ok, type(o), o)) + assert ok + ok = dill.pickles(oo) + if verbose: print ("%s: %s, %s" % (ok, type(oo), oo)) + assert ok + if verbose: print ("") + +# frames, generators, and tracebacks (all depend on frame) +def test_frame_related(): + g = _g(1) + f = g.gi_frame + e,t = _f() + _is = lambda ok: ok + ok = dill.pickles(f) + if verbose: print ("%s: %s, %s" % (ok, type(f), f)) + assert not ok + ok = dill.pickles(g) + if verbose: print ("%s: %s, %s" % (ok, type(g), g)) + assert _is(not ok) #XXX: dill fails + ok = dill.pickles(t) + if verbose: print ("%s: %s, %s" % (ok, type(t), t)) + assert not ok #XXX: dill fails + ok = dill.pickles(e) + if verbose: print ("%s: %s, %s" % (ok, type(e), e)) + assert ok + if verbose: print ("") + +def test_typing(): + import typing + x = typing.Any + assert x == dill.copy(x) + x = typing.Dict[int, str] + assert x == dill.copy(x) + x = typing.List[int] + assert x == dill.copy(x) + x = typing.Tuple[int, str] + assert x == dill.copy(x) + x = typing.Tuple[int] + assert x == dill.copy(x) + x = typing.Tuple[()] + assert x == dill.copy(x) + x = typing.Tuple[()].copy_with(()) + assert x == dill.copy(x) + return + + +if __name__ == '__main__': + test_frame_related() + test_dict_contents() + test_class() + test_class_descriptors() + test_typing() diff --git a/.venv/lib/python3.11/site-packages/dill/tests/test_threads.py b/.venv/lib/python3.11/site-packages/dill/tests/test_threads.py new file mode 100644 index 0000000000000000000000000000000000000000..45f1f58c5ca05d7dc90d10141f9eef0ca3b8ee0b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/dill/tests/test_threads.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# +# Author: Mike McKerns (mmckerns @caltech and @uqfoundation) +# Copyright (c) 2024 The Uncertainty Quantification Foundation. +# License: 3-clause BSD. The full license text is available at: +# - https://github.com/uqfoundation/dill/blob/master/LICENSE + +import dill +dill.settings['recurse'] = True + + +def test_new_thread(): + import threading + t = threading.Thread() + t_ = dill.copy(t) + assert t.is_alive() == t_.is_alive() + for i in ['daemon','name','ident','native_id']: + if hasattr(t, i): + assert getattr(t, i) == getattr(t_, i) + +def test_run_thread(): + import threading + t = threading.Thread() + t.start() + t_ = dill.copy(t) + assert t.is_alive() == t_.is_alive() + for i in ['daemon','name','ident','native_id']: + if hasattr(t, i): + assert getattr(t, i) == getattr(t_, i) + +def test_join_thread(): + import threading + t = threading.Thread() + t.start() + t.join() + t_ = dill.copy(t) + assert t.is_alive() == t_.is_alive() + for i in ['daemon','name','ident','native_id']: + if hasattr(t, i): + assert getattr(t, i) == getattr(t_, i) + + +if __name__ == '__main__': + test_new_thread() + test_run_thread() + test_join_thread() diff --git a/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36b045a34fcbaeeddba40b8a249707b52fe7a38c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/__main__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4aed11b9020477a9eb467ae230a1ad7daf60139 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/__main__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/info.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/info.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..498dfaff88a63cfbf45d2231cd62760c92d27ed3 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/info.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/report.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/report.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06a78568674899cdf2000c1db147b4c5db3e330e Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/report.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/version.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/version.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50004e26e576034ba0342dcc66f9b371f326aeb9 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/__pycache__/version.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5aa4a9d7691dc44168553afa31aab43fde2c9adc --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/__init__.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from .bash import BashActivator +from .batch import BatchActivator +from .cshell import CShellActivator +from .fish import FishActivator +from .nushell import NushellActivator +from .powershell import PowerShellActivator +from .python import PythonActivator + +__all__ = [ + "BashActivator", + "BatchActivator", + "CShellActivator", + "FishActivator", + "NushellActivator", + "PowerShellActivator", + "PythonActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cc6df223f5ea043b87d0ac6eed906f676ab595c Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/activator.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/activator.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5489609acc96f661ac8a194c98a24813c95f1ac0 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/activator.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/via_template.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/via_template.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1a149cdd382e3693bc3483ef7ef25be7c75caa1 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/__pycache__/via_template.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/activator.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/activator.py new file mode 100644 index 0000000000000000000000000000000000000000..dd404b47ce9dacfa7b053f3b830a028ad4f4ffd2 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/activator.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import os +from abc import ABC, abstractmethod + + +class Activator(ABC): + """Generates activate script for the virtual environment.""" + + def __init__(self, options) -> None: + """ + Create a new activator generator. + + :param options: the parsed options as defined within :meth:`add_parser_arguments` + """ + self.flag_prompt = os.path.basename(os.getcwd()) if options.prompt == "." else options.prompt + + @classmethod + def supports(cls, interpreter): # noqa: ARG003 + """ + Check if the activation script is supported in the given interpreter. + + :param interpreter: the interpreter we need to support + :return: ``True`` if supported, ``False`` otherwise + """ + return True + + @classmethod # noqa: B027 + def add_parser_arguments(cls, parser, interpreter): + """ + Add CLI arguments for this activation script. + + :param parser: the CLI parser + :param interpreter: the interpreter this virtual environment is based of + """ + + @abstractmethod + def generate(self, creator): + """ + Generate activate script for the given creator. + + :param creator: the creator (based of :class:`virtualenv.create.creator.Creator`) we used to create this \ + virtual environment + """ + raise NotImplementedError + + +__all__ = [ + "Activator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5e095ddf04f3ccc1170370720401dfe48fe0df28 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/__init__.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from pathlib import Path + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class BashActivator(ViaTemplateActivator): + def templates(self): + yield "activate.sh" + + def as_name(self, template): + return Path(template).stem + + +__all__ = [ + "BashActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44fe77b335b51184531870f65f466ce6ee8426d4 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/activate.sh b/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/activate.sh new file mode 100644 index 0000000000000000000000000000000000000000..d3cf34784dfc181b7a327f6578f2ed02773a98de --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/bash/activate.sh @@ -0,0 +1,87 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + + +if [ "${BASH_SOURCE-}" = "$0" ]; then + echo "You must source this script: \$ source $0" >&2 + exit 33 +fi + +deactivate () { + unset -f pydoc >/dev/null 2>&1 || true + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # The hash command must be called to get it to forget past + # commands. Without forgetting past commands the $PATH changes + # we made may not be respected + hash -r 2>/dev/null + + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV=__VIRTUAL_ENV__ +if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then + VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/"__BIN_NAME__":$PATH" +export PATH + +if [ "x"__VIRTUAL_PROMPT__ != x ] ; then + VIRTUAL_ENV_PROMPT=__VIRTUAL_PROMPT__ +else + VIRTUAL_ENV_PROMPT=$(basename "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV_PROMPT + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + PS1="(${VIRTUAL_ENV_PROMPT}) ${PS1-}" + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# The hash command must be called to get it to forget past +# commands. Without forgetting past commands the $PATH changes +# we made may not be respected +hash -r 2>/dev/null || true diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3d74ba8350571c28aba60a6e1acea1ee661e6dcd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/__init__.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import os + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class BatchActivator(ViaTemplateActivator): + @classmethod + def supports(cls, interpreter): + return interpreter.os == "nt" + + def templates(self): + yield "activate.bat" + yield "deactivate.bat" + yield "pydoc.bat" + + @staticmethod + def quote(string): + return string + + def instantiate_template(self, replacements, template, creator): + # ensure the text has all newlines as \r\n - required by batch + base = super().instantiate_template(replacements, template, creator) + return base.replace(os.linesep, "\n").replace("\n", os.linesep) + + +__all__ = [ + "BatchActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f361159da922fb1c3e0e2dafacc64d858744f14 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/activate.bat b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/activate.bat new file mode 100644 index 0000000000000000000000000000000000000000..165f56a505575959525035bd43fb9873ac7c7ee6 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/activate.bat @@ -0,0 +1,50 @@ +@REM This file is UTF-8 encoded, so we need to update the current code page while executing it +@for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do @set _OLD_CODEPAGE=%%a + +@if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" 65001 > nul +) + +@set "VIRTUAL_ENV=__VIRTUAL_ENV__" + +@set "VIRTUAL_ENV_PROMPT=__VIRTUAL_PROMPT__" +@if NOT DEFINED VIRTUAL_ENV_PROMPT ( + @for %%d in ("%VIRTUAL_ENV%") do @set "VIRTUAL_ENV_PROMPT=%%~nxd" +) + +@if defined _OLD_VIRTUAL_PROMPT ( + @set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) else ( + @if not defined PROMPT ( + @set "PROMPT=$P$G" + ) + @if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + @set "_OLD_VIRTUAL_PROMPT=%PROMPT%" + ) +) +@if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + @set "PROMPT=(%VIRTUAL_ENV_PROMPT%) %PROMPT%" +) + +@REM Don't use () to avoid problems with them in %PATH% +@if defined _OLD_VIRTUAL_PYTHONHOME @goto ENDIFVHOME + @set "_OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%" +:ENDIFVHOME + +@set PYTHONHOME= + +@REM if defined _OLD_VIRTUAL_PATH ( +@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH1 + @set "PATH=%_OLD_VIRTUAL_PATH%" +:ENDIFVPATH1 +@REM ) else ( +@if defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH2 + @set "_OLD_VIRTUAL_PATH=%PATH%" +:ENDIFVPATH2 + +@set "PATH=%VIRTUAL_ENV%\__BIN_NAME__;%PATH%" + +@if defined _OLD_CODEPAGE ( + "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + @set _OLD_CODEPAGE= +) diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/deactivate.bat b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/deactivate.bat new file mode 100644 index 0000000000000000000000000000000000000000..24a03a3f2fc5d15050f9897b9fe53e2c1319af69 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/deactivate.bat @@ -0,0 +1,18 @@ +@set VIRTUAL_ENV= +@set VIRTUAL_ENV_PROMPT= + +@REM Don't use () to avoid problems with them in %PATH% +@if not defined _OLD_VIRTUAL_PROMPT @goto ENDIFVPROMPT + @set "PROMPT=%_OLD_VIRTUAL_PROMPT%" + @set _OLD_VIRTUAL_PROMPT= +:ENDIFVPROMPT + +@if not defined _OLD_VIRTUAL_PYTHONHOME @goto ENDIFVHOME + @set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + @set _OLD_VIRTUAL_PYTHONHOME= +:ENDIFVHOME + +@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH + @set "PATH=%_OLD_VIRTUAL_PATH%" + @set _OLD_VIRTUAL_PATH= +:ENDIFVPATH diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/pydoc.bat b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/pydoc.bat new file mode 100644 index 0000000000000000000000000000000000000000..45ddc132751ba8f752c0c3b4a6a70ad2f486d681 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/batch/pydoc.bat @@ -0,0 +1 @@ +python.exe -m pydoc %* diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7001f999af2211e3cf51340fc270a325659c7afd --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/__init__.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class CShellActivator(ViaTemplateActivator): + @classmethod + def supports(cls, interpreter): + return interpreter.os != "nt" + + def templates(self): + yield "activate.csh" + + +__all__ = [ + "CShellActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33341cbc9152abe1d1761fd4323ee1bee7ddd118 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/activate.csh b/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/activate.csh new file mode 100644 index 0000000000000000000000000000000000000000..24de5508b9771284693dc5141909435f9b97304e --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/cshell/activate.csh @@ -0,0 +1,55 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +set newline='\ +' + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV __VIRTUAL_ENV__ + +set _OLD_VIRTUAL_PATH="$PATH:q" +setenv PATH "$VIRTUAL_ENV:q/"__BIN_NAME__":$PATH:q" + + + +if (__VIRTUAL_PROMPT__ != "") then + setenv VIRTUAL_ENV_PROMPT __VIRTUAL_PROMPT__ +else + setenv VIRTUAL_ENV_PROMPT "$VIRTUAL_ENV:t:q" +endif + +if ( $?VIRTUAL_ENV_DISABLE_PROMPT ) then + if ( $VIRTUAL_ENV_DISABLE_PROMPT == "" ) then + set do_prompt = "1" + else + set do_prompt = "0" + endif +else + set do_prompt = "1" +endif + +if ( $do_prompt == "1" ) then + # Could be in a non-interactive environment, + # in which case, $prompt is undefined and we wouldn't + # care about the prompt anyway. + if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt:q" + if ( "$prompt:q" =~ *"$newline:q"* ) then + : + else + set prompt = '('"$VIRTUAL_ENV_PROMPT:q"') '"$prompt:q" + endif + endif +endif + +unset env_name +unset do_prompt + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..57f790f47382be6c1ed5d10a9e627e71be562e42 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class FishActivator(ViaTemplateActivator): + def templates(self): + yield "activate.fish" + + +__all__ = [ + "FishActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b326268e930fc99fb77420f671ab6b49d865369 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/activate.fish b/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/activate.fish new file mode 100644 index 0000000000000000000000000000000000000000..f3cd1f2ab1451087e2a0f4f26cd46be52136548f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/fish/activate.fish @@ -0,0 +1,103 @@ +# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. +# Do not run it directly. + +function _bashify_path -d "Converts a fish path to something bash can recognize" + set fishy_path $argv + set bashy_path $fishy_path[1] + for path_part in $fishy_path[2..-1] + set bashy_path "$bashy_path:$path_part" + end + echo $bashy_path +end + +function _fishify_path -d "Converts a bash path to something fish can recognize" + echo $argv | tr ':' '\n' +end + +function deactivate -d 'Exit virtualenv mode and return to the normal environment.' + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling + if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx PATH (_fishify_path "$_OLD_VIRTUAL_PATH") + else + set -gx PATH $_OLD_VIRTUAL_PATH + end + set -e _OLD_VIRTUAL_PATH + end + + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME "$_OLD_VIRTUAL_PYTHONHOME" + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + and functions -q _old_fish_prompt + # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. + set -l fish_function_path + + # Erase virtualenv's `fish_prompt` and restore the original. + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + + if test "$argv[1]" != 'nondestructive' + # Self-destruct! + functions -e pydoc + functions -e deactivate + functions -e _bashify_path + functions -e _fishify_path + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV __VIRTUAL_ENV__ + +# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling +if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH) +else + set -gx _OLD_VIRTUAL_PATH $PATH +end +set -gx PATH "$VIRTUAL_ENV"'/'__BIN_NAME__ $PATH + +# Prompt override provided? +# If not, just use the environment name. +if test -n __VIRTUAL_PROMPT__ + set -gx VIRTUAL_ENV_PROMPT __VIRTUAL_PROMPT__ +else + set -gx VIRTUAL_ENV_PROMPT (basename "$VIRTUAL_ENV") +end + +# Unset `$PYTHONHOME` if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +function pydoc + python -m pydoc $argv +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # Copy the current `fish_prompt` function as `_old_fish_prompt`. + functions -c fish_prompt _old_fish_prompt + + function fish_prompt + # Run the user's prompt first; it might depend on (pipe)status. + set -l prompt (_old_fish_prompt) + + printf '(%s) ' $VIRTUAL_ENV_PROMPT + + string join -- \n $prompt # handle multi-line prompts + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ef7a79a9c4237f07d754d3476707ffce1ff28df4 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/__init__.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class NushellActivator(ViaTemplateActivator): + def templates(self): + yield "activate.nu" + + @staticmethod + def quote(string): + """ + Nushell supports raw strings like: r###'this is a string'###. + + This method finds the maximum continuous sharps in the string and then + quote it with an extra sharp. + """ + max_sharps = 0 + current_sharps = 0 + for char in string: + if char == "#": + current_sharps += 1 + max_sharps = max(current_sharps, max_sharps) + else: + current_sharps = 0 + wrapping = "#" * (max_sharps + 1) + return f"r{wrapping}'{string}'{wrapping}" + + def replacements(self, creator, dest_folder): # noqa: ARG002 + return { + "__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt, + "__VIRTUAL_ENV__": str(creator.dest), + "__VIRTUAL_NAME__": creator.env_name, + "__BIN_NAME__": str(creator.bin_dir.relative_to(creator.dest)), + } + + +__all__ = [ + "NushellActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61cd293e76285bc079c07e7b72d4deb00210bdfa Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/activate.nu b/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/activate.nu new file mode 100644 index 0000000000000000000000000000000000000000..4da1c8c07916c22513ef2569f76b7556345afb90 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/nushell/activate.nu @@ -0,0 +1,96 @@ +# virtualenv activation module +# Activate with `overlay use activate.nu` +# Deactivate with `deactivate`, as usual +# +# To customize the overlay name, you can call `overlay use activate.nu as foo`, +# but then simply `deactivate` won't work because it is just an alias to hide +# the "activate" overlay. You'd need to call `overlay hide foo` manually. + +export-env { + def is-string [x] { + ($x | describe) == 'string' + } + + def has-env [...names] { + $names | each {|n| + $n in $env + } | all {|i| $i == true} + } + + # Emulates a `test -z`, but better as it handles e.g 'false' + def is-env-true [name: string] { + if (has-env $name) { + # Try to parse 'true', '0', '1', and fail if not convertible + let parsed = (do -i { $env | get $name | into bool }) + if ($parsed | describe) == 'bool' { + $parsed + } else { + not ($env | get -i $name | is-empty) + } + } else { + false + } + } + + let virtual_env = __VIRTUAL_ENV__ + let bin = __BIN_NAME__ + + let is_windows = ($nu.os-info.family) == 'windows' + let path_name = (if (has-env 'Path') { + 'Path' + } else { + 'PATH' + } + ) + + let venv_path = ([$virtual_env $bin] | path join) + let new_path = ($env | get $path_name | prepend $venv_path) + + # If there is no default prompt, then use the env name instead + let virtual_env_prompt = (if (__VIRTUAL_PROMPT__ | is-empty) { + ($virtual_env | path basename) + } else { + __VIRTUAL_PROMPT__ + }) + + let new_env = { + $path_name : $new_path + VIRTUAL_ENV : $virtual_env + VIRTUAL_ENV_PROMPT : $virtual_env_prompt + } + + let new_env = (if (is-env-true 'VIRTUAL_ENV_DISABLE_PROMPT') { + $new_env + } else { + # Creating the new prompt for the session + let virtual_prefix = $'(char lparen)($virtual_env_prompt)(char rparen) ' + + # Back up the old prompt builder + let old_prompt_command = (if (has-env 'PROMPT_COMMAND') { + $env.PROMPT_COMMAND + } else { + '' + }) + + let new_prompt = (if (has-env 'PROMPT_COMMAND') { + if 'closure' in ($old_prompt_command | describe) { + {|| $'($virtual_prefix)(do $old_prompt_command)' } + } else { + {|| $'($virtual_prefix)($old_prompt_command)' } + } + } else { + {|| $'($virtual_prefix)' } + }) + + $new_env | merge { + PROMPT_COMMAND : $new_prompt + VIRTUAL_PREFIX : $virtual_prefix + } + }) + + # Environment variables that will be loaded as the virtual env + load-env $new_env +} + +export alias pydoc = python -m pydoc +export alias deactivate = overlay hide activate diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8489656cc7e9ffa6a6a03fa2694052a0f94aa76f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/__init__.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class PowerShellActivator(ViaTemplateActivator): + def templates(self): + yield "activate.ps1" + + @staticmethod + def quote(string): + """ + This should satisfy PowerShell quoting rules [1], unless the quoted + string is passed directly to Windows native commands [2]. + + [1]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules + [2]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parsing#passing-arguments-that-contain-quote-characters + """ # noqa: D205 + string = string.replace("'", "''") + return f"'{string}'" + + +__all__ = [ + "PowerShellActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44c7b977eae1c94d14d92564ba634003b506af5b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/activate.ps1 b/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/activate.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..bd30e2eed53cb4167a62ea7769be6a4b7a3f63a8 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/powershell/activate.ps1 @@ -0,0 +1,61 @@ +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent + +function global:deactivate([switch] $NonDestructive) { + if (Test-Path variable:_OLD_VIRTUAL_PATH) { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global + } + + if (Test-Path function:_old_virtual_prompt) { + $function:prompt = $function:_old_virtual_prompt + Remove-Item function:\_old_virtual_prompt + } + + if ($env:VIRTUAL_ENV) { + Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue + } + + if ($env:VIRTUAL_ENV_PROMPT) { + Remove-Item env:VIRTUAL_ENV_PROMPT -ErrorAction SilentlyContinue + } + + if (!$NonDestructive) { + # Self destruct! + Remove-Item function:deactivate + Remove-Item function:pydoc + } +} + +function global:pydoc { + python -m pydoc $args +} + +# unset irrelevant variables +deactivate -nondestructive + +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV + +if (__VIRTUAL_PROMPT__ -ne "") { + $env:VIRTUAL_ENV_PROMPT = __VIRTUAL_PROMPT__ +} +else { + $env:VIRTUAL_ENV_PROMPT = $( Split-Path $env:VIRTUAL_ENV -Leaf ) +} + +New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH + +$env:PATH = "$env:VIRTUAL_ENV/" + __BIN_NAME__ + __PATH_SEP__ + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) { + function global:_old_virtual_prompt { + "" + } + $function:_old_virtual_prompt = $function:prompt + + function global:prompt { + # Add the custom prefix to the existing prompt + $previous_prompt_value = & $function:_old_virtual_prompt + ("(" + $env:VIRTUAL_ENV_PROMPT + ") " + $previous_prompt_value) + } +} diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e900f7ec9a871d79c5ba896d864bc457d10904fa --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__init__.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import os +from collections import OrderedDict + +from virtualenv.activation.via_template import ViaTemplateActivator + + +class PythonActivator(ViaTemplateActivator): + def templates(self): + yield "activate_this.py" + + @staticmethod + def quote(string): + return repr(string) + + def replacements(self, creator, dest_folder): + replacements = super().replacements(creator, dest_folder) + lib_folders = OrderedDict((os.path.relpath(str(i), str(dest_folder)), None) for i in creator.libs) + lib_folders = os.pathsep.join(lib_folders.keys()) + replacements.update( + { + "__LIB_FOLDERS__": lib_folders, + "__DECODE_PATH__": "", + }, + ) + return replacements + + +__all__ = [ + "PythonActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..293fe69b540f8808fd6fe449b23caff05136ecb3 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__pycache__/activate_this.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__pycache__/activate_this.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af522f82996618f7e7d84eaf082945b420613f6b Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/__pycache__/activate_this.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/python/activate_this.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/activate_this.py new file mode 100644 index 0000000000000000000000000000000000000000..9cc816fab287a42ce82fa3f5c3cd787c7c395f45 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/python/activate_this.py @@ -0,0 +1,38 @@ +""" +Activate virtualenv for current interpreter: + +import runpy +runpy.run_path(this_file) + +This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. +""" # noqa: D415 + +from __future__ import annotations + +import os +import site +import sys + +try: + abs_file = os.path.abspath(__file__) +except NameError as exc: + msg = "You must use import runpy; runpy.run_path(this_file)" + raise AssertionError(msg) from exc + +bin_dir = os.path.dirname(abs_file) +base = bin_dir[: -len(__BIN_NAME__) - 1] # strip away the bin part from the __file__, plus the path separator + +# prepend bin to PATH (this file is inside the bin directory) +os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)]) +os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory +os.environ["VIRTUAL_ENV_PROMPT"] = __VIRTUAL_PROMPT__ or os.path.basename(base) + +# add the virtual environments libraries to the host python import mechanism +prev_length = len(sys.path) +for lib in __LIB_FOLDERS__.split(os.pathsep): + path = os.path.realpath(os.path.join(bin_dir, lib)) + site.addsitedir(path.decode("utf-8") if __DECODE_PATH__ else path) +sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length] + +sys.real_prefix = sys.prefix +sys.prefix = base diff --git a/.venv/lib/python3.11/site-packages/virtualenv/activation/via_template.py b/.venv/lib/python3.11/site-packages/virtualenv/activation/via_template.py new file mode 100644 index 0000000000000000000000000000000000000000..6fa4474d24aa342a44a8ca58260a8ef8df080b6d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/activation/via_template.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +import os +import shlex +import sys +from abc import ABC, abstractmethod + +from .activator import Activator + +if sys.version_info >= (3, 10): + from importlib.resources import files + + def read_binary(module_name: str, filename: str) -> bytes: + return (files(module_name) / filename).read_bytes() + +else: + from importlib.resources import read_binary + + +class ViaTemplateActivator(Activator, ABC): + @abstractmethod + def templates(self): + raise NotImplementedError + + @staticmethod + def quote(string): + """ + Quote strings in the activation script. + + :param string: the string to quote + :return: quoted string that works in the activation script + """ + return shlex.quote(string) + + def generate(self, creator): + dest_folder = creator.bin_dir + replacements = self.replacements(creator, dest_folder) + generated = self._generate(replacements, self.templates(), dest_folder, creator) + if self.flag_prompt is not None: + creator.pyenv_cfg["prompt"] = self.flag_prompt + return generated + + def replacements(self, creator, dest_folder): # noqa: ARG002 + return { + "__VIRTUAL_PROMPT__": "" if self.flag_prompt is None else self.flag_prompt, + "__VIRTUAL_ENV__": str(creator.dest), + "__VIRTUAL_NAME__": creator.env_name, + "__BIN_NAME__": str(creator.bin_dir.relative_to(creator.dest)), + "__PATH_SEP__": os.pathsep, + } + + def _generate(self, replacements, templates, to_folder, creator): + generated = [] + for template in templates: + text = self.instantiate_template(replacements, template, creator) + dest = to_folder / self.as_name(template) + # remove the file if it already exists - this prevents permission + # errors when the dest is not writable + if dest.exists(): + dest.unlink() + # Powershell assumes Windows 1252 encoding when reading files without BOM + encoding = "utf-8-sig" if str(template).endswith(".ps1") else "utf-8" + # use write_bytes to avoid platform specific line normalization (\n -> \r\n) + dest.write_bytes(text.encode(encoding)) + generated.append(dest) + return generated + + def as_name(self, template): + return template + + def instantiate_template(self, replacements, template, creator): + # read content as binary to avoid platform specific line normalization (\n -> \r\n) + binary = read_binary(self.__module__, template) + text = binary.decode("utf-8", errors="strict") + for key, value in replacements.items(): + value_uni = self._repr_unicode(creator, value) + text = text.replace(key, self.quote(value_uni)) + return text + + @staticmethod + def _repr_unicode(creator, value): # noqa: ARG004 + return value # by default, we just let it be unicode + + +__all__ = [ + "ViaTemplateActivator", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d7f1480237fce79a4323d98eef961e1d69b3ecee --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__init__.py @@ -0,0 +1,58 @@ +"""Application data stored by virtualenv.""" + +from __future__ import annotations + +import logging +import os + +from platformdirs import user_data_dir + +from .na import AppDataDisabled +from .read_only import ReadOnlyAppData +from .via_disk_folder import AppDataDiskFolder +from .via_tempdir import TempAppData + +LOGGER = logging.getLogger(__name__) + + +def _default_app_data_dir(env): + key = "VIRTUALENV_OVERRIDE_APP_DATA" + if key in env: + return env[key] + return user_data_dir(appname="virtualenv", appauthor="pypa") + + +def make_app_data(folder, **kwargs): + is_read_only = kwargs.pop("read_only") + env = kwargs.pop("env") + if kwargs: # py3+ kwonly + msg = "unexpected keywords: {}" + raise TypeError(msg) + + if folder is None: + folder = _default_app_data_dir(env) + folder = os.path.abspath(folder) + + if is_read_only: + return ReadOnlyAppData(folder) + + if not os.path.isdir(folder): + try: + os.makedirs(folder) + LOGGER.debug("created app data folder %s", folder) + except OSError as exception: + LOGGER.info("could not create app data folder %s due to %r", folder, exception) + + if os.access(folder, os.W_OK): + return AppDataDiskFolder(folder) + LOGGER.debug("app data folder %s has no write access", folder) + return TempAppData() + + +__all__ = ( + "AppDataDisabled", + "AppDataDiskFolder", + "ReadOnlyAppData", + "TempAppData", + "make_app_data", +) diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08cf0d6946dede05303b2d6af2e54ff041a127d9 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/base.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/base.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..035b0beffb7b5e899b007b647c6222156324fddc Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/base.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/na.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/na.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0a5f6481947f08886c61daf60f5a29b0f46b048 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/na.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/read_only.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/read_only.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89176a839b28da0614f6d22936e80422075ae099 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/read_only.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/via_disk_folder.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/via_disk_folder.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae50321519f11c7294996a85d6472299363cadcb Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/via_disk_folder.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/via_tempdir.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/via_tempdir.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74c24bb40f8b35cbbb746a0e293bb88c59c1e8aa Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/app_data/__pycache__/via_tempdir.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/base.py b/.venv/lib/python3.11/site-packages/virtualenv/app_data/base.py new file mode 100644 index 0000000000000000000000000000000000000000..2077deebdfdcb806966a5f98aa4032f49da69d51 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/app_data/base.py @@ -0,0 +1,96 @@ +"""Application data stored by virtualenv.""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from contextlib import contextmanager + +from virtualenv.info import IS_ZIPAPP + + +class AppData(ABC): + """Abstract storage interface for the virtualenv application.""" + + @abstractmethod + def close(self): + """Called before virtualenv exits.""" + + @abstractmethod + def reset(self): + """Called when the user passes in the reset app data.""" + + @abstractmethod + def py_info(self, path): + raise NotImplementedError + + @abstractmethod + def py_info_clear(self): + raise NotImplementedError + + @property + def can_update(self): + raise NotImplementedError + + @abstractmethod + def embed_update_log(self, distribution, for_py_version): + raise NotImplementedError + + @property + def house(self): + raise NotImplementedError + + @property + def transient(self): + raise NotImplementedError + + @abstractmethod + def wheel_image(self, for_py_version, name): + raise NotImplementedError + + @contextmanager + def ensure_extracted(self, path, to_folder=None): + """Some paths might be within the zipapp, unzip these to a path on the disk.""" + if IS_ZIPAPP: + with self.extract(path, to_folder) as result: + yield result + else: + yield path + + @abstractmethod + @contextmanager + def extract(self, path, to_folder): + raise NotImplementedError + + @abstractmethod + @contextmanager + def locked(self, path): + raise NotImplementedError + + +class ContentStore(ABC): + @abstractmethod + def exists(self): + raise NotImplementedError + + @abstractmethod + def read(self): + raise NotImplementedError + + @abstractmethod + def write(self, content): + raise NotImplementedError + + @abstractmethod + def remove(self): + raise NotImplementedError + + @abstractmethod + @contextmanager + def locked(self): + pass + + +__all__ = [ + "AppData", + "ContentStore", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/na.py b/.venv/lib/python3.11/site-packages/virtualenv/app_data/na.py new file mode 100644 index 0000000000000000000000000000000000000000..921e83a812e622256bd67cfa5bfdc21bcd5ee9c3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/app_data/na.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from contextlib import contextmanager + +from .base import AppData, ContentStore + + +class AppDataDisabled(AppData): + """No application cache available (most likely as we don't have write permissions).""" + + transient = True + can_update = False + + def __init__(self) -> None: + pass + + error = RuntimeError("no app data folder available, probably no write access to the folder") + + def close(self): + """Do nothing.""" + + def reset(self): + """Do nothing.""" + + def py_info(self, path): # noqa: ARG002 + return ContentStoreNA() + + def embed_update_log(self, distribution, for_py_version): # noqa: ARG002 + return ContentStoreNA() + + def extract(self, path, to_folder): # noqa: ARG002 + raise self.error + + @contextmanager + def locked(self, path): # noqa: ARG002 + """Do nothing.""" + yield + + @property + def house(self): + raise self.error + + def wheel_image(self, for_py_version, name): # noqa: ARG002 + raise self.error + + def py_info_clear(self): + """Nothing to clear.""" + + +class ContentStoreNA(ContentStore): + def exists(self): + return False + + def read(self): + """Nothing to read.""" + return + + def write(self, content): + """Nothing to write.""" + + def remove(self): + """Nothing to remove.""" + + @contextmanager + def locked(self): + yield + + +__all__ = [ + "AppDataDisabled", + "ContentStoreNA", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/read_only.py b/.venv/lib/python3.11/site-packages/virtualenv/app_data/read_only.py new file mode 100644 index 0000000000000000000000000000000000000000..952dbad5a259e9e89bd7bea7934bdd55c971686b --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/app_data/read_only.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import os.path + +from virtualenv.util.lock import NoOpFileLock + +from .via_disk_folder import AppDataDiskFolder, PyInfoStoreDisk + + +class ReadOnlyAppData(AppDataDiskFolder): + can_update = False + + def __init__(self, folder: str) -> None: + if not os.path.isdir(folder): + msg = f"read-only app data directory {folder} does not exist" + raise RuntimeError(msg) + super().__init__(folder) + self.lock = NoOpFileLock(folder) + + def reset(self) -> None: + msg = "read-only app data does not support reset" + raise RuntimeError(msg) + + def py_info_clear(self) -> None: + raise NotImplementedError + + def py_info(self, path): + return _PyInfoStoreDiskReadOnly(self.py_info_at, path) + + def embed_update_log(self, distribution, for_py_version): + raise NotImplementedError + + +class _PyInfoStoreDiskReadOnly(PyInfoStoreDisk): + def write(self, content): # noqa: ARG002 + msg = "read-only app data python info cannot be updated" + raise RuntimeError(msg) + + +__all__ = [ + "ReadOnlyAppData", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/via_disk_folder.py b/.venv/lib/python3.11/site-packages/virtualenv/app_data/via_disk_folder.py new file mode 100644 index 0000000000000000000000000000000000000000..1c98a90daccf4d62302765a5a479b87414a5ad44 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/app_data/via_disk_folder.py @@ -0,0 +1,176 @@ +""" +A rough layout of the current storage goes as: + +virtualenv-app-data +├── py - +│ └── *.json/lock +├── wheel +│ ├── house +│ │ └── *.whl +│ └── -> 3.9 +│ ├── img- +│ │ └── image +│ │ └── -> CopyPipInstall / SymlinkPipInstall +│ │ └── -> pip-20.1.1-py2.py3-none-any +│ └── embed +│ └── 3 -> json format versioning +│ └── *.json -> for every distribution contains data about newer embed versions and releases +└─── unzip + └── + ├── py_info.py + ├── debug.py + └── _virtualenv.py +""" # noqa: D415 + +from __future__ import annotations + +import json +import logging +from abc import ABC +from contextlib import contextmanager, suppress +from hashlib import sha256 + +from virtualenv.util.lock import ReentrantFileLock +from virtualenv.util.path import safe_delete +from virtualenv.util.zipapp import extract +from virtualenv.version import __version__ + +from .base import AppData, ContentStore + +LOGGER = logging.getLogger(__name__) + + +class AppDataDiskFolder(AppData): + """Store the application data on the disk within a folder layout.""" + + transient = False + can_update = True + + def __init__(self, folder) -> None: + self.lock = ReentrantFileLock(folder) + + def __repr__(self) -> str: + return f"{type(self).__name__}({self.lock.path})" + + def __str__(self) -> str: + return str(self.lock.path) + + def reset(self): + LOGGER.debug("reset app data folder %s", self.lock.path) + safe_delete(self.lock.path) + + def close(self): + """Do nothing.""" + + @contextmanager + def locked(self, path): + path_lock = self.lock / path + with path_lock: + yield path_lock.path + + @contextmanager + def extract(self, path, to_folder): + root = ReentrantFileLock(to_folder()) if to_folder is not None else self.lock / "unzip" / __version__ + with root.lock_for_key(path.name): + dest = root.path / path.name + if not dest.exists(): + extract(path, dest) + yield dest + + @property + def py_info_at(self): + return self.lock / "py_info" / "2" + + def py_info(self, path): + return PyInfoStoreDisk(self.py_info_at, path) + + def py_info_clear(self): + """clear py info.""" + py_info_folder = self.py_info_at + with py_info_folder: + for filename in py_info_folder.path.iterdir(): + if filename.suffix == ".json": + with py_info_folder.lock_for_key(filename.stem): + if filename.exists(): + filename.unlink() + + def embed_update_log(self, distribution, for_py_version): + return EmbedDistributionUpdateStoreDisk(self.lock / "wheel" / for_py_version / "embed" / "3", distribution) + + @property + def house(self): + path = self.lock.path / "wheel" / "house" + path.mkdir(parents=True, exist_ok=True) + return path + + def wheel_image(self, for_py_version, name): + return self.lock.path / "wheel" / for_py_version / "image" / "1" / name + + +class JSONStoreDisk(ContentStore, ABC): + def __init__(self, in_folder, key, msg, msg_args) -> None: + self.in_folder = in_folder + self.key = key + self.msg = msg + self.msg_args = (*msg_args, self.file) + + @property + def file(self): + return self.in_folder.path / f"{self.key}.json" + + def exists(self): + return self.file.exists() + + def read(self): + data, bad_format = None, False + try: + data = json.loads(self.file.read_text(encoding="utf-8")) + except ValueError: + bad_format = True + except Exception: # noqa: BLE001, S110 + pass + else: + LOGGER.debug("got %s from %s", self.msg, self.msg_args) + return data + if bad_format: + with suppress(OSError): # reading and writing on the same file may cause race on multiple processes + self.remove() + return None + + def remove(self): + self.file.unlink() + LOGGER.debug("removed %s at %s", self.msg, self.msg_args) + + @contextmanager + def locked(self): + with self.in_folder.lock_for_key(self.key): + yield + + def write(self, content): + folder = self.file.parent + folder.mkdir(parents=True, exist_ok=True) + self.file.write_text(json.dumps(content, sort_keys=True, indent=2), encoding="utf-8") + LOGGER.debug("wrote %s at %s", self.msg, self.msg_args) + + +class PyInfoStoreDisk(JSONStoreDisk): + def __init__(self, in_folder, path) -> None: + key = sha256(str(path).encode("utf-8")).hexdigest() + super().__init__(in_folder, key, "python info of %s", (path,)) + + +class EmbedDistributionUpdateStoreDisk(JSONStoreDisk): + def __init__(self, in_folder, distribution) -> None: + super().__init__( + in_folder, + distribution, + "embed update of distribution %s", + (distribution,), + ) + + +__all__ = [ + "AppDataDiskFolder", + "JSONStoreDisk", + "PyInfoStoreDisk", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/app_data/via_tempdir.py b/.venv/lib/python3.11/site-packages/virtualenv/app_data/via_tempdir.py new file mode 100644 index 0000000000000000000000000000000000000000..884a570ce6daebbd17b057c94305f2497e82f228 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/app_data/via_tempdir.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +import logging +from tempfile import mkdtemp + +from virtualenv.util.path import safe_delete + +from .via_disk_folder import AppDataDiskFolder + +LOGGER = logging.getLogger(__name__) + + +class TempAppData(AppDataDiskFolder): + transient = True + can_update = False + + def __init__(self) -> None: + super().__init__(folder=mkdtemp()) + LOGGER.debug("created temporary app data folder %s", self.lock.path) + + def reset(self): + """This is a temporary folder, is already empty to start with.""" + + def close(self): + LOGGER.debug("remove temporary app data folder %s", self.lock.path) + safe_delete(self.lock.path) + + def embed_update_log(self, distribution, for_py_version): + raise NotImplementedError + + +__all__ = [ + "TempAppData", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/builtin.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/builtin.py new file mode 100644 index 0000000000000000000000000000000000000000..0b0d26b5d305f5ac1bf3c0cb363e82a4fb1f921f --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/builtin.py @@ -0,0 +1,230 @@ +from __future__ import annotations + +import logging +import os +import sys +from contextlib import suppress +from pathlib import Path +from typing import TYPE_CHECKING + +from virtualenv.info import IS_WIN, fs_path_id + +from .discover import Discover +from .py_info import PythonInfo +from .py_spec import PythonSpec + +if TYPE_CHECKING: + from argparse import ArgumentParser + from collections.abc import Callable, Generator, Iterable, Mapping, Sequence + + from virtualenv.app_data.base import AppData +LOGGER = logging.getLogger(__name__) + + +class Builtin(Discover): + python_spec: Sequence[str] + app_data: AppData + try_first_with: Sequence[str] + + def __init__(self, options) -> None: + super().__init__(options) + self.python_spec = options.python or [sys.executable] + if self._env.get("VIRTUALENV_PYTHON"): + self.python_spec = self.python_spec[1:] + self.python_spec[:1] # Rotate the list + self.app_data = options.app_data + self.try_first_with = options.try_first_with + + @classmethod + def add_parser_arguments(cls, parser: ArgumentParser) -> None: + parser.add_argument( + "-p", + "--python", + dest="python", + metavar="py", + type=str, + action="append", + default=[], + help="interpreter based on what to create environment (path/identifier) " + "- by default use the interpreter where the tool is installed - first found wins", + ) + parser.add_argument( + "--try-first-with", + dest="try_first_with", + metavar="py_exe", + type=str, + action="append", + default=[], + help="try first these interpreters before starting the discovery", + ) + + def run(self) -> PythonInfo | None: + for python_spec in self.python_spec: + result = get_interpreter(python_spec, self.try_first_with, self.app_data, self._env) + if result is not None: + return result + return None + + def __repr__(self) -> str: + spec = self.python_spec[0] if len(self.python_spec) == 1 else self.python_spec + return f"{self.__class__.__name__} discover of python_spec={spec!r}" + + +def get_interpreter( + key, try_first_with: Iterable[str], app_data: AppData | None = None, env: Mapping[str, str] | None = None +) -> PythonInfo | None: + spec = PythonSpec.from_string_spec(key) + LOGGER.info("find interpreter for spec %r", spec) + proposed_paths = set() + env = os.environ if env is None else env + for interpreter, impl_must_match in propose_interpreters(spec, try_first_with, app_data, env): + key = interpreter.system_executable, impl_must_match + if key in proposed_paths: + continue + LOGGER.info("proposed %s", interpreter) + if interpreter.satisfies(spec, impl_must_match): + LOGGER.debug("accepted %s", interpreter) + return interpreter + proposed_paths.add(key) + return None + + +def propose_interpreters( # noqa: C901, PLR0912, PLR0915 + spec: PythonSpec, + try_first_with: Iterable[str], + app_data: AppData | None = None, + env: Mapping[str, str] | None = None, +) -> Generator[tuple[PythonInfo, bool], None, None]: + # 0. try with first + env = os.environ if env is None else env + tested_exes: set[str] = set() + for py_exe in try_first_with: + path = os.path.abspath(py_exe) + try: + os.lstat(path) # Windows Store Python does not work with os.path.exists, but does for os.lstat + except OSError: + pass + else: + exe_raw = os.path.abspath(path) + exe_id = fs_path_id(exe_raw) + if exe_id in tested_exes: + continue + tested_exes.add(exe_id) + yield PythonInfo.from_exe(exe_raw, app_data, env=env), True + + # 1. if it's a path and exists + if spec.path is not None: + try: + os.lstat(spec.path) # Windows Store Python does not work with os.path.exists, but does for os.lstat + except OSError: + if spec.is_abs: + raise + else: + exe_raw = os.path.abspath(spec.path) + exe_id = fs_path_id(exe_raw) + if exe_id not in tested_exes: + tested_exes.add(exe_id) + yield PythonInfo.from_exe(exe_raw, app_data, env=env), True + if spec.is_abs: + return + else: + # 2. otherwise try with the current + current_python = PythonInfo.current_system(app_data) + exe_raw = str(current_python.executable) + exe_id = fs_path_id(exe_raw) + if exe_id not in tested_exes: + tested_exes.add(exe_id) + yield current_python, True + + # 3. otherwise fallback to platform default logic + if IS_WIN: + from .windows import propose_interpreters # noqa: PLC0415 + + for interpreter in propose_interpreters(spec, app_data, env): + exe_raw = str(interpreter.executable) + exe_id = fs_path_id(exe_raw) + if exe_id in tested_exes: + continue + tested_exes.add(exe_id) + yield interpreter, True + # finally just find on path, the path order matters (as the candidates are less easy to control by end user) + find_candidates = path_exe_finder(spec) + for pos, path in enumerate(get_paths(env)): + LOGGER.debug(LazyPathDump(pos, path, env)) + for exe, impl_must_match in find_candidates(path): + exe_raw = str(exe) + exe_id = fs_path_id(exe_raw) + if exe_id in tested_exes: + continue + tested_exes.add(exe_id) + interpreter = PathPythonInfo.from_exe(exe_raw, app_data, raise_on_error=False, env=env) + if interpreter is not None: + yield interpreter, impl_must_match + + +def get_paths(env: Mapping[str, str]) -> Generator[Path, None, None]: + path = env.get("PATH", None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + path = os.defpath + if path: + for p in map(Path, path.split(os.pathsep)): + with suppress(OSError): + if p.exists(): + yield p + + +class LazyPathDump: + def __init__(self, pos: int, path: Path, env: Mapping[str, str]) -> None: + self.pos = pos + self.path = path + self.env = env + + def __repr__(self) -> str: + content = f"discover PATH[{self.pos}]={self.path}" + if self.env.get("_VIRTUALENV_DEBUG"): # this is the over the board debug + content += " with =>" + for file_path in self.path.iterdir(): + try: + if file_path.is_dir() or not (file_path.stat().st_mode & os.X_OK): + continue + except OSError: + pass + content += " " + content += file_path.name + return content + + +def path_exe_finder(spec: PythonSpec) -> Callable[[Path], Generator[tuple[Path, bool], None, None]]: + """Given a spec, return a function that can be called on a path to find all matching files in it.""" + pat = spec.generate_re(windows=sys.platform == "win32") + direct = spec.str_spec + if sys.platform == "win32": + direct = f"{direct}.exe" + + def path_exes(path: Path) -> Generator[tuple[Path, bool], None, None]: + # 4. then maybe it's something exact on PATH - if it was direct lookup implementation no longer counts + direct_path = path / direct + if direct_path.exists(): + yield direct_path, False + + # 5. or from the spec we can deduce if a name on path matches + for exe in path.iterdir(): + match = pat.fullmatch(exe.name) + if match: + # the implementation must match when we find “python[ver]” + yield exe.absolute(), match["impl"] == "python" + + return path_exes + + +class PathPythonInfo(PythonInfo): + """python info from path.""" + + +__all__ = [ + "Builtin", + "PathPythonInfo", + "get_interpreter", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/cached_py_info.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/cached_py_info.py new file mode 100644 index 0000000000000000000000000000000000000000..4f3ee5e90ce891a6a85713afccfb42de16647921 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/cached_py_info.py @@ -0,0 +1,183 @@ +""" + +We acquire the python information by running an interrogation script via subprocess trigger. This operation is not +cheap, especially not on Windows. To not have to pay this hefty cost every time we apply multiple levels of +caching. +""" # noqa: D205 + +from __future__ import annotations + +import logging +import os +import random +import sys +from collections import OrderedDict +from pathlib import Path +from shlex import quote +from string import ascii_lowercase, ascii_uppercase, digits +from subprocess import Popen + +from virtualenv.app_data import AppDataDisabled +from virtualenv.discovery.py_info import PythonInfo +from virtualenv.util.subprocess import subprocess + +_CACHE = OrderedDict() +_CACHE[Path(sys.executable)] = PythonInfo() +LOGGER = logging.getLogger(__name__) + + +def from_exe(cls, app_data, exe, env=None, raise_on_error=True, ignore_cache=False): # noqa: FBT002, PLR0913 + env = os.environ if env is None else env + result = _get_from_cache(cls, app_data, exe, env, ignore_cache=ignore_cache) + if isinstance(result, Exception): + if raise_on_error: + raise result + LOGGER.info("%s", result) + result = None + return result + + +def _get_from_cache(cls, app_data, exe, env, ignore_cache=True): # noqa: FBT002 + # note here we cannot resolve symlinks, as the symlink may trigger different prefix information if there's a + # pyenv.cfg somewhere alongside on python3.5+ + exe_path = Path(exe) + if not ignore_cache and exe_path in _CACHE: # check in the in-memory cache + result = _CACHE[exe_path] + else: # otherwise go through the app data cache + py_info = _get_via_file_cache(cls, app_data, exe_path, exe, env) + result = _CACHE[exe_path] = py_info + # independent if it was from the file or in-memory cache fix the original executable location + if isinstance(result, PythonInfo): + result.executable = exe + return result + + +def _get_via_file_cache(cls, app_data, path, exe, env): + path_text = str(path) + try: + path_modified = path.stat().st_mtime + except OSError: + path_modified = -1 + if app_data is None: + app_data = AppDataDisabled() + py_info, py_info_store = None, app_data.py_info(path) + with py_info_store.locked(): + if py_info_store.exists(): # if exists and matches load + data = py_info_store.read() + of_path, of_st_mtime, of_content = data["path"], data["st_mtime"], data["content"] + if of_path == path_text and of_st_mtime == path_modified: + py_info = cls._from_dict(of_content.copy()) + sys_exe = py_info.system_executable + if sys_exe is not None and not os.path.exists(sys_exe): + py_info_store.remove() + py_info = None + else: + py_info_store.remove() + if py_info is None: # if not loaded run and save + failure, py_info = _run_subprocess(cls, exe, app_data, env) + if failure is None: + data = { + "st_mtime": path_modified, + "path": path_text, + "content": py_info._to_dict(), # noqa: SLF001 + } + py_info_store.write(data) + else: + py_info = failure + return py_info + + +COOKIE_LENGTH: int = 32 + + +def gen_cookie(): + return "".join( + random.choice(f"{ascii_lowercase}{ascii_uppercase}{digits}") # noqa: S311 + for _ in range(COOKIE_LENGTH) + ) + + +def _run_subprocess(cls, exe, app_data, env): + py_info_script = Path(os.path.abspath(__file__)).parent / "py_info.py" + # Cookies allow to split the serialized stdout output generated by the script collecting the info from the output + # generated by something else. The right way to deal with it is to create an anonymous pipe and pass its descriptor + # to the child and output to it. But AFAIK all of them are either not cross-platform or too big to implement and are + # not in the stdlib. So the easiest and the shortest way I could mind is just using the cookies. + # We generate pseudorandom cookies because it easy to implement and avoids breakage from outputting modules source + # code, i.e. by debug output libraries. We reverse the cookies to avoid breakages resulting from variable values + # appearing in debug output. + + start_cookie = gen_cookie() + end_cookie = gen_cookie() + with app_data.ensure_extracted(py_info_script) as py_info_script: + cmd = [exe, str(py_info_script), start_cookie, end_cookie] + # prevent sys.prefix from leaking into the child process - see https://bugs.python.org/issue22490 + env = env.copy() + env.pop("__PYVENV_LAUNCHER__", None) + LOGGER.debug("get interpreter info via cmd: %s", LogCmd(cmd)) + try: + process = Popen( + cmd, + universal_newlines=True, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + env=env, + encoding="utf-8", + ) + out, err = process.communicate() + code = process.returncode + except OSError as os_error: + out, err, code = "", os_error.strerror, os_error.errno + result, failure = None, None + if code == 0: + out_starts = out.find(start_cookie[::-1]) + + if out_starts > -1: + pre_cookie = out[:out_starts] + + if pre_cookie: + sys.stdout.write(pre_cookie) + + out = out[out_starts + COOKIE_LENGTH :] + + out_ends = out.find(end_cookie[::-1]) + + if out_ends > -1: + post_cookie = out[out_ends + COOKIE_LENGTH :] + + if post_cookie: + sys.stdout.write(post_cookie) + + out = out[:out_ends] + + result = cls._from_json(out) + result.executable = exe # keep original executable as this may contain initialization code + else: + msg = f"{exe} with code {code}{f' out: {out!r}' if out else ''}{f' err: {err!r}' if err else ''}" + failure = RuntimeError(f"failed to query {msg}") + return failure, result + + +class LogCmd: + def __init__(self, cmd, env=None) -> None: + self.cmd = cmd + self.env = env + + def __repr__(self) -> str: + cmd_repr = " ".join(quote(str(c)) for c in self.cmd) + if self.env is not None: + cmd_repr = f"{cmd_repr} env of {self.env!r}" + return cmd_repr + + +def clear(app_data): + app_data.py_info_clear() + _CACHE.clear() + + +___all___ = [ + "from_exe", + "clear", + "LogCmd", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/discover.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/discover.py new file mode 100644 index 0000000000000000000000000000000000000000..0aaa17c8e3318b33188127bb7312e0619ac3c8ac --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/discover.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod + + +class Discover(ABC): + """Discover and provide the requested Python interpreter.""" + + @classmethod + def add_parser_arguments(cls, parser): + """ + Add CLI arguments for this discovery mechanisms. + + :param parser: the CLI parser + """ + raise NotImplementedError + + def __init__(self, options) -> None: + """ + Create a new discovery mechanism. + + :param options: the parsed options as defined within :meth:`add_parser_arguments` + """ + self._has_run = False + self._interpreter = None + self._env = options.env + + @abstractmethod + def run(self): + """ + Discovers an interpreter. + + :return: the interpreter ready to use for virtual environment creation + """ + raise NotImplementedError + + @property + def interpreter(self): + """:return: the interpreter as returned by :meth:`run`, cached""" + if self._has_run is False: + self._interpreter = self.run() + self._has_run = True + return self._interpreter + + +__all__ = [ + "Discover", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/py_info.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/py_info.py new file mode 100644 index 0000000000000000000000000000000000000000..be978116e133d2facca56159577d55ab8fe865f3 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/py_info.py @@ -0,0 +1,586 @@ +""" +The PythonInfo contains information about a concrete instance of a Python interpreter. + +Note: this file is also used to query target interpreters, so can only use standard library methods +""" + +from __future__ import annotations + +import json +import logging +import os +import platform +import re +import sys +import sysconfig +import warnings +from collections import OrderedDict, namedtuple +from string import digits + +VersionInfo = namedtuple("VersionInfo", ["major", "minor", "micro", "releaselevel", "serial"]) # noqa: PYI024 +LOGGER = logging.getLogger(__name__) + + +def _get_path_extensions(): + return list(OrderedDict.fromkeys(["", *os.environ.get("PATHEXT", "").lower().split(os.pathsep)])) + + +EXTENSIONS = _get_path_extensions() +_CONF_VAR_RE = re.compile(r"\{\w+\}") + + +class PythonInfo: # noqa: PLR0904 + """Contains information for a Python interpreter.""" + + def __init__(self) -> None: # noqa: PLR0915 + def abs_path(v): + return None if v is None else os.path.abspath(v) # unroll relative elements from path (e.g. ..) + + # qualifies the python + self.platform = sys.platform + self.implementation = platform.python_implementation() + if self.implementation == "PyPy": + self.pypy_version_info = tuple(sys.pypy_version_info) + + # this is a tuple in earlier, struct later, unify to our own named tuple + self.version_info = VersionInfo(*sys.version_info) + self.architecture = 64 if sys.maxsize > 2**32 else 32 + + # Used to determine some file names. + # See `CPython3Windows.python_zip()`. + self.version_nodot = sysconfig.get_config_var("py_version_nodot") + + self.version = sys.version + self.os = os.name + self.free_threaded = sysconfig.get_config_var("Py_GIL_DISABLED") == 1 + + # information about the prefix - determines python home + self.prefix = abs_path(getattr(sys, "prefix", None)) # prefix we think + self.base_prefix = abs_path(getattr(sys, "base_prefix", None)) # venv + self.real_prefix = abs_path(getattr(sys, "real_prefix", None)) # old virtualenv + + # information about the exec prefix - dynamic stdlib modules + self.base_exec_prefix = abs_path(getattr(sys, "base_exec_prefix", None)) + self.exec_prefix = abs_path(getattr(sys, "exec_prefix", None)) + + self.executable = abs_path(sys.executable) # the executable we were invoked via + self.original_executable = abs_path(self.executable) # the executable as known by the interpreter + self.system_executable = self._fast_get_system_executable() # the executable we are based of (if available) + + try: + __import__("venv") + has = True + except ImportError: + has = False + self.has_venv = has + self.path = sys.path + self.file_system_encoding = sys.getfilesystemencoding() + self.stdout_encoding = getattr(sys.stdout, "encoding", None) + + scheme_names = sysconfig.get_scheme_names() + + if "venv" in scheme_names: + self.sysconfig_scheme = "venv" + self.sysconfig_paths = { + i: sysconfig.get_path(i, expand=False, scheme=self.sysconfig_scheme) for i in sysconfig.get_path_names() + } + # we cannot use distutils at all if "venv" exists, distutils don't know it + self.distutils_install = {} + # debian / ubuntu python 3.10 without `python3-distutils` will report + # mangled `local/bin` / etc. names for the default prefix + # intentionally select `posix_prefix` which is the unaltered posix-like paths + elif sys.version_info[:2] == (3, 10) and "deb_system" in scheme_names: + self.sysconfig_scheme = "posix_prefix" + self.sysconfig_paths = { + i: sysconfig.get_path(i, expand=False, scheme=self.sysconfig_scheme) for i in sysconfig.get_path_names() + } + # we cannot use distutils at all if "venv" exists, distutils don't know it + self.distutils_install = {} + else: + self.sysconfig_scheme = None + self.sysconfig_paths = {i: sysconfig.get_path(i, expand=False) for i in sysconfig.get_path_names()} + self.distutils_install = self._distutils_install().copy() + + # https://bugs.python.org/issue22199 + makefile = getattr(sysconfig, "get_makefile_filename", getattr(sysconfig, "_get_makefile_filename", None)) + self.sysconfig = { + k: v + for k, v in [ + # a list of content to store from sysconfig + ("makefile_filename", makefile()), + ] + if k is not None + } + + config_var_keys = set() + for element in self.sysconfig_paths.values(): + config_var_keys.update(k[1:-1] for k in _CONF_VAR_RE.findall(element)) + config_var_keys.add("PYTHONFRAMEWORK") + + self.sysconfig_vars = {i: sysconfig.get_config_var(i or "") for i in config_var_keys} + + confs = { + k: (self.system_prefix if v is not None and v.startswith(self.prefix) else v) + for k, v in self.sysconfig_vars.items() + } + self.system_stdlib = self.sysconfig_path("stdlib", confs) + self.system_stdlib_platform = self.sysconfig_path("platstdlib", confs) + self.max_size = getattr(sys, "maxsize", getattr(sys, "maxint", None)) + self._creators = None + + def _fast_get_system_executable(self): + """Try to get the system executable by just looking at properties.""" + if self.real_prefix or ( # noqa: PLR1702 + self.base_prefix is not None and self.base_prefix != self.prefix + ): # if this is a virtual environment + if self.real_prefix is None: + base_executable = getattr(sys, "_base_executable", None) # some platforms may set this to help us + if base_executable is not None: # noqa: SIM102 # use the saved system executable if present + if sys.executable != base_executable: # we know we're in a virtual environment, cannot be us + if os.path.exists(base_executable): + return base_executable + # Python may return "python" because it was invoked from the POSIX virtual environment + # however some installs/distributions do not provide a version-less "python" binary in + # the system install location (see PEP 394) so try to fallback to a versioned binary. + # + # Gate this to Python 3.11 as `sys._base_executable` path resolution is now relative to + # the 'home' key from pyvenv.cfg which often points to the system install location. + major, minor = self.version_info.major, self.version_info.minor + if self.os == "posix" and (major, minor) >= (3, 11): + # search relative to the directory of sys._base_executable + base_dir = os.path.dirname(base_executable) + for base_executable in [ + os.path.join(base_dir, exe) for exe in (f"python{major}", f"python{major}.{minor}") + ]: + if os.path.exists(base_executable): + return base_executable + return None # in this case we just can't tell easily without poking around FS and calling them, bail + # if we're not in a virtual environment, this is already a system python, so return the original executable + # note we must choose the original and not the pure executable as shim scripts might throw us off + return self.original_executable + + def install_path(self, key): + result = self.distutils_install.get(key) + if result is None: # use sysconfig if sysconfig_scheme is set or distutils is unavailable + # set prefixes to empty => result is relative from cwd + prefixes = self.prefix, self.exec_prefix, self.base_prefix, self.base_exec_prefix + config_var = {k: "" if v in prefixes else v for k, v in self.sysconfig_vars.items()} + result = self.sysconfig_path(key, config_var=config_var).lstrip(os.sep) + return result + + @staticmethod + def _distutils_install(): + # use distutils primarily because that's what pip does + # https://github.com/pypa/pip/blob/main/src/pip/_internal/locations.py#L95 + # note here we don't import Distribution directly to allow setuptools to patch it + with warnings.catch_warnings(): # disable warning for PEP-632 + warnings.simplefilter("ignore") + try: + from distutils import dist # noqa: PLC0415 + from distutils.command.install import SCHEME_KEYS # noqa: PLC0415 + except ImportError: # if removed or not installed ignore + return {} + + d = dist.Distribution({"script_args": "--no-user-cfg"}) # conf files not parsed so they do not hijack paths + if hasattr(sys, "_framework"): + sys._framework = None # disable macOS static paths for framework # noqa: SLF001 + + with warnings.catch_warnings(): # disable warning for PEP-632 + warnings.simplefilter("ignore") + i = d.get_command_obj("install", create=True) + + i.prefix = os.sep # paths generated are relative to prefix that contains the path sep, this makes it relative + i.finalize_options() + return {key: (getattr(i, f"install_{key}")[1:]).lstrip(os.sep) for key in SCHEME_KEYS} + + @property + def version_str(self): + return ".".join(str(i) for i in self.version_info[0:3]) + + @property + def version_release_str(self): + return ".".join(str(i) for i in self.version_info[0:2]) + + @property + def python_name(self): + version_info = self.version_info + return f"python{version_info.major}.{version_info.minor}" + + @property + def is_old_virtualenv(self): + return self.real_prefix is not None + + @property + def is_venv(self): + return self.base_prefix is not None + + def sysconfig_path(self, key, config_var=None, sep=os.sep): + pattern = self.sysconfig_paths[key] + if config_var is None: + config_var = self.sysconfig_vars + else: + base = self.sysconfig_vars.copy() + base.update(config_var) + config_var = base + return pattern.format(**config_var).replace("/", sep) + + def creators(self, refresh=False): # noqa: FBT002 + if self._creators is None or refresh is True: + from virtualenv.run.plugin.creators import CreatorSelector # noqa: PLC0415 + + self._creators = CreatorSelector.for_interpreter(self) + return self._creators + + @property + def system_include(self): + path = self.sysconfig_path( + "include", + { + k: (self.system_prefix if v is not None and v.startswith(self.prefix) else v) + for k, v in self.sysconfig_vars.items() + }, + ) + if not os.path.exists(path): # some broken packaging don't respect the sysconfig, fallback to distutils path + # the pattern include the distribution name too at the end, remove that via the parent call + fallback = os.path.join(self.prefix, os.path.dirname(self.install_path("headers"))) + if os.path.exists(fallback): + path = fallback + return path + + @property + def system_prefix(self): + return self.real_prefix or self.base_prefix or self.prefix + + @property + def system_exec_prefix(self): + return self.real_prefix or self.base_exec_prefix or self.exec_prefix + + def __repr__(self) -> str: + return "{}({!r})".format( + self.__class__.__name__, + {k: v for k, v in self.__dict__.items() if not k.startswith("_")}, + ) + + def __str__(self) -> str: + return "{}({})".format( + self.__class__.__name__, + ", ".join( + f"{k}={v}" + for k, v in ( + ("spec", self.spec), + ( + "system" + if self.system_executable is not None and self.system_executable != self.executable + else None, + self.system_executable, + ), + ( + "original" + if self.original_executable not in {self.system_executable, self.executable} + else None, + self.original_executable, + ), + ("exe", self.executable), + ("platform", self.platform), + ("version", repr(self.version)), + ("encoding_fs_io", f"{self.file_system_encoding}-{self.stdout_encoding}"), + ) + if k is not None + ), + ) + + @property + def spec(self): + return "{}{}{}-{}".format( + self.implementation, + ".".join(str(i) for i in self.version_info), + "t" if self.free_threaded else "", + self.architecture, + ) + + @classmethod + def clear_cache(cls, app_data): + # this method is not used by itself, so here and called functions can import stuff locally + from virtualenv.discovery.cached_py_info import clear # noqa: PLC0415 + + clear(app_data) + cls._cache_exe_discovery.clear() + + def satisfies(self, spec, impl_must_match): # noqa: C901, PLR0911 + """Check if a given specification can be satisfied by the this python interpreter instance.""" + if spec.path: + if self.executable == os.path.abspath(spec.path): + return True # if the path is a our own executable path we're done + if not spec.is_abs: + # if path set, and is not our original executable name, this does not match + basename = os.path.basename(self.original_executable) + spec_path = spec.path + if sys.platform == "win32": + basename, suffix = os.path.splitext(basename) + if spec_path.endswith(suffix): + spec_path = spec_path[: -len(suffix)] + if basename != spec_path: + return False + + if ( + impl_must_match + and spec.implementation is not None + and spec.implementation.lower() != self.implementation.lower() + ): + return False + + if spec.architecture is not None and spec.architecture != self.architecture: + return False + + if spec.free_threaded is not None and spec.free_threaded != self.free_threaded: + return False + + for our, req in zip(self.version_info[0:3], (spec.major, spec.minor, spec.micro)): + if req is not None and our is not None and our != req: + return False + return True + + _current_system = None + _current = None + + @classmethod + def current(cls, app_data=None): + """ + This locates the current host interpreter information. This might be different than what we run into in case + the host python has been upgraded from underneath us. + """ # noqa: D205 + if cls._current is None: + cls._current = cls.from_exe(sys.executable, app_data, raise_on_error=True, resolve_to_host=False) + return cls._current + + @classmethod + def current_system(cls, app_data=None) -> PythonInfo: + """ + This locates the current host interpreter information. This might be different than what we run into in case + the host python has been upgraded from underneath us. + """ # noqa: D205 + if cls._current_system is None: + cls._current_system = cls.from_exe(sys.executable, app_data, raise_on_error=True, resolve_to_host=True) + return cls._current_system + + def _to_json(self): + # don't save calculated paths, as these are non primitive types + return json.dumps(self._to_dict(), indent=2) + + def _to_dict(self): + data = {var: (getattr(self, var) if var != "_creators" else None) for var in vars(self)} + + data["version_info"] = data["version_info"]._asdict() # namedtuple to dictionary + return data + + @classmethod + def from_exe( # noqa: PLR0913 + cls, + exe, + app_data=None, + raise_on_error=True, # noqa: FBT002 + ignore_cache=False, # noqa: FBT002 + resolve_to_host=True, # noqa: FBT002 + env=None, + ): + """Given a path to an executable get the python information.""" + # this method is not used by itself, so here and called functions can import stuff locally + from virtualenv.discovery.cached_py_info import from_exe # noqa: PLC0415 + + env = os.environ if env is None else env + proposed = from_exe(cls, app_data, exe, env=env, raise_on_error=raise_on_error, ignore_cache=ignore_cache) + + if isinstance(proposed, PythonInfo) and resolve_to_host: + try: + proposed = proposed._resolve_to_system(app_data, proposed) # noqa: SLF001 + except Exception as exception: + if raise_on_error: + raise + LOGGER.info("ignore %s due cannot resolve system due to %r", proposed.original_executable, exception) + proposed = None + return proposed + + @classmethod + def _from_json(cls, payload): + # the dictionary unroll here is to protect against pypy bug of interpreter crashing + raw = json.loads(payload) + return cls._from_dict(raw.copy()) + + @classmethod + def _from_dict(cls, data): + data["version_info"] = VersionInfo(**data["version_info"]) # restore this to a named tuple structure + result = cls() + result.__dict__ = data.copy() + return result + + @classmethod + def _resolve_to_system(cls, app_data, target): + start_executable = target.executable + prefixes = OrderedDict() + while target.system_executable is None: + prefix = target.real_prefix or target.base_prefix or target.prefix + if prefix in prefixes: + if len(prefixes) == 1: + # if we're linking back to ourselves accept ourselves with a WARNING + LOGGER.info("%r links back to itself via prefixes", target) + target.system_executable = target.executable + break + for at, (p, t) in enumerate(prefixes.items(), start=1): + LOGGER.error("%d: prefix=%s, info=%r", at, p, t) + LOGGER.error("%d: prefix=%s, info=%r", len(prefixes) + 1, prefix, target) + msg = "prefixes are causing a circle {}".format("|".join(prefixes.keys())) + raise RuntimeError(msg) + prefixes[prefix] = target + target = target.discover_exe(app_data, prefix=prefix, exact=False) + if target.executable != target.system_executable: + target = cls.from_exe(target.system_executable, app_data) + target.executable = start_executable + return target + + _cache_exe_discovery = {} # noqa: RUF012 + + def discover_exe(self, app_data, prefix, exact=True, env=None): # noqa: FBT002 + key = prefix, exact + if key in self._cache_exe_discovery and prefix: + LOGGER.debug("discover exe from cache %s - exact %s: %r", prefix, exact, self._cache_exe_discovery[key]) + return self._cache_exe_discovery[key] + LOGGER.debug("discover exe for %s in %s", self, prefix) + # we don't know explicitly here, do some guess work - our executable name should tell + possible_names = self._find_possible_exe_names() + possible_folders = self._find_possible_folders(prefix) + discovered = [] + env = os.environ if env is None else env + for folder in possible_folders: + for name in possible_names: + info = self._check_exe(app_data, folder, name, exact, discovered, env) + if info is not None: + self._cache_exe_discovery[key] = info + return info + if exact is False and discovered: + info = self._select_most_likely(discovered, self) + folders = os.pathsep.join(possible_folders) + self._cache_exe_discovery[key] = info + LOGGER.debug("no exact match found, chosen most similar of %s within base folders %s", info, folders) + return info + msg = "failed to detect {} in {}".format("|".join(possible_names), os.pathsep.join(possible_folders)) + raise RuntimeError(msg) + + def _check_exe(self, app_data, folder, name, exact, discovered, env): # noqa: PLR0913 + exe_path = os.path.join(folder, name) + if not os.path.exists(exe_path): + return None + info = self.from_exe(exe_path, app_data, resolve_to_host=False, raise_on_error=False, env=env) + if info is None: # ignore if for some reason we can't query + return None + for item in ["implementation", "architecture", "version_info"]: + found = getattr(info, item) + searched = getattr(self, item) + if found != searched: + if item == "version_info": + found, searched = ".".join(str(i) for i in found), ".".join(str(i) for i in searched) + executable = info.executable + LOGGER.debug("refused interpreter %s because %s differs %s != %s", executable, item, found, searched) + if exact is False: + discovered.append(info) + break + else: + return info + return None + + @staticmethod + def _select_most_likely(discovered, target): + # no exact match found, start relaxing our requirements then to facilitate system package upgrades that + # could cause this (when using copy strategy of the host python) + def sort_by(info): + # we need to setup some priority of traits, this is as follows: + # implementation, major, minor, micro, architecture, tag, serial + matches = [ + info.implementation == target.implementation, + info.version_info.major == target.version_info.major, + info.version_info.minor == target.version_info.minor, + info.architecture == target.architecture, + info.version_info.micro == target.version_info.micro, + info.version_info.releaselevel == target.version_info.releaselevel, + info.version_info.serial == target.version_info.serial, + ] + return sum((1 << pos if match else 0) for pos, match in enumerate(reversed(matches))) + + sorted_discovered = sorted(discovered, key=sort_by, reverse=True) # sort by priority in decreasing order + return sorted_discovered[0] + + def _find_possible_folders(self, inside_folder): + candidate_folder = OrderedDict() + executables = OrderedDict() + executables[os.path.realpath(self.executable)] = None + executables[self.executable] = None + executables[os.path.realpath(self.original_executable)] = None + executables[self.original_executable] = None + for exe in executables: + base = os.path.dirname(exe) + # following path pattern of the current + if base.startswith(self.prefix): + relative = base[len(self.prefix) :] + candidate_folder[f"{inside_folder}{relative}"] = None + + # or at root level + candidate_folder[inside_folder] = None + return [i for i in candidate_folder if os.path.exists(i)] + + def _find_possible_exe_names(self): + name_candidate = OrderedDict() + for name in self._possible_base(): + for at in (3, 2, 1, 0): + version = ".".join(str(i) for i in self.version_info[:at]) + mods = [""] + if self.free_threaded: + mods.append("t") + for mod in mods: + for arch in [f"-{self.architecture}", ""]: + for ext in EXTENSIONS: + candidate = f"{name}{version}{mod}{arch}{ext}" + name_candidate[candidate] = None + return list(name_candidate.keys()) + + def _possible_base(self): + possible_base = OrderedDict() + basename = os.path.splitext(os.path.basename(self.executable))[0].rstrip(digits) + possible_base[basename] = None + possible_base[self.implementation] = None + # python is always the final option as in practice is used by multiple implementation as exe name + if "python" in possible_base: + del possible_base["python"] + possible_base["python"] = None + for base in possible_base: + lower = base.lower() + yield lower + from virtualenv.info import fs_is_case_sensitive # noqa: PLC0415 + + if fs_is_case_sensitive(): + if base != lower: + yield base + upper = base.upper() + if upper != base: + yield upper + + +if __name__ == "__main__": + # dump a JSON representation of the current python + + argv = sys.argv[1:] + + if len(argv) >= 1: + start_cookie = argv[0] + argv = argv[1:] + else: + start_cookie = "" + + if len(argv) >= 1: + end_cookie = argv[0] + argv = argv[1:] + else: + end_cookie = "" + + sys.argv = sys.argv[:1] + argv + + info = PythonInfo()._to_json() # noqa: SLF001 + sys.stdout.write("".join((start_cookie[::-1], info, end_cookie[::-1]))) diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/py_spec.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/py_spec.py new file mode 100644 index 0000000000000000000000000000000000000000..d8519c23d6179090e8927c8fc2a72046587ef502 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/py_spec.py @@ -0,0 +1,129 @@ +"""A Python specification is an abstract requirement definition of an interpreter.""" + +from __future__ import annotations + +import os +import re + +PATTERN = re.compile(r"^(?P[a-zA-Z]+)?(?P[0-9.]+)?(?Pt)?(?:-(?P32|64))?$") + + +class PythonSpec: + """Contains specification about a Python Interpreter.""" + + def __init__( # noqa: PLR0913 + self, + str_spec: str, + implementation: str | None, + major: int | None, + minor: int | None, + micro: int | None, + architecture: int | None, + path: str | None, + *, + free_threaded: bool | None = None, + ) -> None: + self.str_spec = str_spec + self.implementation = implementation + self.major = major + self.minor = minor + self.micro = micro + self.free_threaded = free_threaded + self.architecture = architecture + self.path = path + + @classmethod + def from_string_spec(cls, string_spec: str): # noqa: C901, PLR0912 + impl, major, minor, micro, threaded, arch, path = None, None, None, None, None, None, None + if os.path.isabs(string_spec): # noqa: PLR1702 + path = string_spec + else: + ok = False + match = re.match(PATTERN, string_spec) + if match: + + def _int_or_none(val): + return None if val is None else int(val) + + try: + groups = match.groupdict() + version = groups["version"] + if version is not None: + versions = tuple(int(i) for i in version.split(".") if i) + if len(versions) > 3: # noqa: PLR2004 + raise ValueError # noqa: TRY301 + if len(versions) == 3: # noqa: PLR2004 + major, minor, micro = versions + elif len(versions) == 2: # noqa: PLR2004 + major, minor = versions + elif len(versions) == 1: + version_data = versions[0] + major = int(str(version_data)[0]) # first digit major + if version_data > 9: # noqa: PLR2004 + minor = int(str(version_data)[1:]) + threaded = bool(groups["threaded"]) + ok = True + except ValueError: + pass + else: + impl = groups["impl"] + if impl in {"py", "python"}: + impl = None + arch = _int_or_none(groups["arch"]) + + if not ok: + path = string_spec + + return cls(string_spec, impl, major, minor, micro, arch, path, free_threaded=threaded) + + def generate_re(self, *, windows: bool) -> re.Pattern: + """Generate a regular expression for matching against a filename.""" + version = r"{}(\.{}(\.{})?)?".format( + *(r"\d+" if v is None else v for v in (self.major, self.minor, self.micro)) + ) + impl = "python" if self.implementation is None else f"python|{re.escape(self.implementation)}" + mod = "t?" if self.free_threaded else "" + suffix = r"\.exe" if windows else "" + version_conditional = ( + "?" + # Windows Python executables are almost always unversioned + if windows + # Spec is an empty string + or self.major is None + else "" + ) + # Try matching `direct` first, so the `direct` group is filled when possible. + return re.compile( + rf"(?P{impl})(?P{version}{mod}){version_conditional}{suffix}$", + flags=re.IGNORECASE, + ) + + @property + def is_abs(self): + return self.path is not None and os.path.isabs(self.path) + + def satisfies(self, spec): + """Called when there's a candidate metadata spec to see if compatible - e.g. PEP-514 on Windows.""" + if spec.is_abs and self.is_abs and self.path != spec.path: + return False + if spec.implementation is not None and spec.implementation.lower() != self.implementation.lower(): + return False + if spec.architecture is not None and spec.architecture != self.architecture: + return False + if spec.free_threaded is not None and spec.free_threaded != self.free_threaded: + return False + + for our, req in zip((self.major, self.minor, self.micro), (spec.major, spec.minor, spec.micro)): + if req is not None and our is not None and our != req: + return False + return True + + def __repr__(self) -> str: + name = type(self).__name__ + params = "implementation", "major", "minor", "micro", "architecture", "path", "free_threaded" + return f"{name}({', '.join(f'{k}={getattr(self, k)}' for k in params if getattr(self, k) is not None)})" + + +__all__ = [ + "PythonSpec", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/windows/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/windows/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b7206406aa0361046c49c22f2a2fdf8ba7383c3d --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/windows/__init__.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from virtualenv.discovery.py_info import PythonInfo +from virtualenv.discovery.py_spec import PythonSpec + +from .pep514 import discover_pythons + +# Map of well-known organizations (as per PEP 514 Company Windows Registry key part) versus Python implementation +_IMPLEMENTATION_BY_ORG = { + "ContinuumAnalytics": "CPython", + "PythonCore": "CPython", +} + + +class Pep514PythonInfo(PythonInfo): + """A Python information acquired from PEP-514.""" + + +def propose_interpreters(spec, cache_dir, env): + # see if PEP-514 entries are good + + # start with higher python versions in an effort to use the latest version available + # and prefer PythonCore over conda pythons (as virtualenv is mostly used by non conda tools) + existing = list(discover_pythons()) + existing.sort( + key=lambda i: (*tuple(-1 if j is None else j for j in i[1:4]), 1 if i[0] == "PythonCore" else 0), + reverse=True, + ) + + for name, major, minor, arch, threaded, exe, _ in existing: + # Map well-known/most common organizations to a Python implementation, use the org name as a fallback for + # backwards compatibility. + implementation = _IMPLEMENTATION_BY_ORG.get(name, name) + + # Pre-filtering based on Windows Registry metadata, for CPython only + skip_pre_filter = implementation.lower() != "cpython" + registry_spec = PythonSpec(None, implementation, major, minor, None, arch, exe, free_threaded=threaded) + if skip_pre_filter or registry_spec.satisfies(spec): + interpreter = Pep514PythonInfo.from_exe(exe, cache_dir, env=env, raise_on_error=False) + if interpreter is not None and interpreter.satisfies(spec, impl_must_match=True): + yield interpreter # Final filtering/matching using interpreter metadata + + +__all__ = [ + "Pep514PythonInfo", + "propose_interpreters", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/discovery/windows/pep514.py b/.venv/lib/python3.11/site-packages/virtualenv/discovery/windows/pep514.py new file mode 100644 index 0000000000000000000000000000000000000000..a75dad36d9260b875702ba47ca71303a4586b66a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/discovery/windows/pep514.py @@ -0,0 +1,165 @@ +"""Implement https://www.python.org/dev/peps/pep-0514/ to discover interpreters - Windows only.""" + +from __future__ import annotations + +import os +import re +import winreg +from logging import basicConfig, getLogger + +LOGGER = getLogger(__name__) + + +def enum_keys(key): + at = 0 + while True: + try: + yield winreg.EnumKey(key, at) + except OSError: + break + at += 1 + + +def get_value(key, value_name): + try: + return winreg.QueryValueEx(key, value_name)[0] + except OSError: + return None + + +def discover_pythons(): + for hive, hive_name, key, flags, default_arch in [ + (winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER", r"Software\Python", 0, 64), + (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_64KEY, 64), + (winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", r"Software\Python", winreg.KEY_WOW64_32KEY, 32), + ]: + yield from process_set(hive, hive_name, key, flags, default_arch) + + +def process_set(hive, hive_name, key, flags, default_arch): + try: + with winreg.OpenKeyEx(hive, key, 0, winreg.KEY_READ | flags) as root_key: + for company in enum_keys(root_key): + if company == "PyLauncher": # reserved + continue + yield from process_company(hive_name, company, root_key, default_arch) + except OSError: + pass + + +def process_company(hive_name, company, root_key, default_arch): + with winreg.OpenKeyEx(root_key, company) as company_key: + for tag in enum_keys(company_key): + spec = process_tag(hive_name, company, company_key, tag, default_arch) + if spec is not None: + yield spec + + +def process_tag(hive_name, company, company_key, tag, default_arch): + with winreg.OpenKeyEx(company_key, tag) as tag_key: + version = load_version_data(hive_name, company, tag, tag_key) + if version is not None: # if failed to get version bail + major, minor, _ = version + arch = load_arch_data(hive_name, company, tag, tag_key, default_arch) + if arch is not None: + exe_data = load_exe(hive_name, company, company_key, tag) + if exe_data is not None: + exe, args = exe_data + threaded = load_threaded(hive_name, company, tag, tag_key) + return company, major, minor, arch, threaded, exe, args + return None + return None + return None + + +def load_exe(hive_name, company, company_key, tag): + key_path = f"{hive_name}/{company}/{tag}" + try: + with winreg.OpenKeyEx(company_key, rf"{tag}\InstallPath") as ip_key, ip_key: + exe = get_value(ip_key, "ExecutablePath") + if exe is None: + ip = get_value(ip_key, None) + if ip is None: + msg(key_path, "no ExecutablePath or default for it") + + else: + exe = os.path.join(ip, "python.exe") + if exe is not None and os.path.exists(exe): + args = get_value(ip_key, "ExecutableArguments") + return exe, args + msg(key_path, f"could not load exe with value {exe}") + except OSError: + msg(f"{key_path}/InstallPath", "missing") + return None + + +def load_arch_data(hive_name, company, tag, tag_key, default_arch): + arch_str = get_value(tag_key, "SysArchitecture") + if arch_str is not None: + key_path = f"{hive_name}/{company}/{tag}/SysArchitecture" + try: + return parse_arch(arch_str) + except ValueError as sys_arch: + msg(key_path, sys_arch) + return default_arch + + +def parse_arch(arch_str): + if isinstance(arch_str, str): + match = re.match(r"^(\d+)bit$", arch_str) + if match: + return int(next(iter(match.groups()))) + error = f"invalid format {arch_str}" + else: + error = f"arch is not string: {arch_str!r}" + raise ValueError(error) + + +def load_version_data(hive_name, company, tag, tag_key): + for candidate, key_path in [ + (get_value(tag_key, "SysVersion"), f"{hive_name}/{company}/{tag}/SysVersion"), + (tag, f"{hive_name}/{company}/{tag}"), + ]: + if candidate is not None: + try: + return parse_version(candidate) + except ValueError as sys_version: + msg(key_path, sys_version) + return None + + +def parse_version(version_str): + if isinstance(version_str, str): + match = re.match(r"^(\d+)(?:\.(\d+))?(?:\.(\d+))?$", version_str) + if match: + return tuple(int(i) if i is not None else None for i in match.groups()) + error = f"invalid format {version_str}" + else: + error = f"version is not string: {version_str!r}" + raise ValueError(error) + + +def load_threaded(hive_name, company, tag, tag_key): + display_name = get_value(tag_key, "DisplayName") + if display_name is not None: + if isinstance(display_name, str): + if "freethreaded" in display_name.lower(): + return True + else: + key_path = f"{hive_name}/{company}/{tag}/DisplayName" + msg(key_path, f"display name is not string: {display_name!r}") + return bool(re.match(r"^\d+(\.\d+){0,2}t$", tag, flags=re.IGNORECASE)) + + +def msg(path, what): + LOGGER.warning("PEP-514 violation in Windows Registry at %s error: %s", path, what) + + +def _run(): + basicConfig() + interpreters = [repr(spec) for spec in discover_pythons()] + print("\n".join(sorted(interpreters))) # noqa: T201 + + +if __name__ == "__main__": + _run() diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c144d8dfd7704d4727472c3695c32dddfb0b8959 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__init__.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from ._permission import make_exe, set_tree +from ._sync import copy, copytree, ensure_dir, safe_delete, symlink +from ._win import get_short_path_name + +__all__ = [ + "copy", + "copytree", + "ensure_dir", + "get_short_path_name", + "make_exe", + "safe_delete", + "set_tree", + "symlink", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17815c654040896e2bcb97f4fe007ca65bd612ff Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/__init__.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_permission.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_permission.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e98462bad50d184c09dcabf1407b2da5c8e5b31 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_permission.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_sync.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_sync.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f1200cd3fa3e830aa5bddd7ee7859f7f64e1d76 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_sync.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_win.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_win.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aba5b09ea18fce2e84050b7be7429ce6d4a25924 Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/util/path/__pycache__/_win.cpython-311.pyc differ diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/_permission.py b/.venv/lib/python3.11/site-packages/virtualenv/util/path/_permission.py new file mode 100644 index 0000000000000000000000000000000000000000..8dcad0ce9c07f6608856731047782a3402475a04 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/util/path/_permission.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import os +from stat import S_IXGRP, S_IXOTH, S_IXUSR + + +def make_exe(filename): + original_mode = filename.stat().st_mode + levels = [S_IXUSR, S_IXGRP, S_IXOTH] + for at in range(len(levels), 0, -1): + try: + mode = original_mode + for level in levels[:at]: + mode |= level + filename.chmod(mode) + break + except OSError: + continue + + +def set_tree(folder, stat): + for root, _, files in os.walk(str(folder)): + for filename in files: + os.chmod(os.path.join(root, filename), stat) + + +__all__ = ( + "make_exe", + "set_tree", +) diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/_sync.py b/.venv/lib/python3.11/site-packages/virtualenv/util/path/_sync.py new file mode 100644 index 0000000000000000000000000000000000000000..02a6f6e9e58c1a83370f421394bb09de0a41fda9 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/util/path/_sync.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import logging +import os +import shutil +import sys +from stat import S_IWUSR + +LOGGER = logging.getLogger(__name__) + + +def ensure_dir(path): + if not path.exists(): + LOGGER.debug("create folder %s", str(path)) + os.makedirs(str(path)) + + +def ensure_safe_to_do(src, dest): + if src == dest: + msg = f"source and destination is the same {src}" + raise ValueError(msg) + if not dest.exists(): + return + if dest.is_dir() and not dest.is_symlink(): + LOGGER.debug("remove directory %s", dest) + safe_delete(dest) + else: + LOGGER.debug("remove file %s", dest) + dest.unlink() + + +def symlink(src, dest): + ensure_safe_to_do(src, dest) + LOGGER.debug("symlink %s", _Debug(src, dest)) + dest.symlink_to(src, target_is_directory=src.is_dir()) + + +def copy(src, dest): + ensure_safe_to_do(src, dest) + is_dir = src.is_dir() + method = copytree if is_dir else shutil.copy + LOGGER.debug("copy %s", _Debug(src, dest)) + method(str(src), str(dest)) + + +def copytree(src, dest): + for root, _, files in os.walk(src): + dest_dir = os.path.join(dest, os.path.relpath(root, src)) + if not os.path.isdir(dest_dir): + os.makedirs(dest_dir) + for name in files: + src_f = os.path.join(root, name) + dest_f = os.path.join(dest_dir, name) + shutil.copy(src_f, dest_f) + + +def safe_delete(dest): + def onerror(func, path, exc_info): # noqa: ARG001 + if not os.access(path, os.W_OK): + os.chmod(path, S_IWUSR) + func(path) + else: + raise # noqa: PLE0704 + + kwargs = {"onexc" if sys.version_info >= (3, 12) else "onerror": onerror} + shutil.rmtree(str(dest), ignore_errors=True, **kwargs) + + +class _Debug: + def __init__(self, src, dest) -> None: + self.src = src + self.dest = dest + + def __str__(self) -> str: + return f"{'directory ' if self.src.is_dir() else ''}{self.src!s} to {self.dest!s}" + + +__all__ = [ + "copy", + "copytree", + "ensure_dir", + "safe_delete", + "symlink", + "symlink", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/path/_win.py b/.venv/lib/python3.11/site-packages/virtualenv/util/path/_win.py new file mode 100644 index 0000000000000000000000000000000000000000..6404cda64cf5ebd6cdf48c6a00643de56e460292 --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/util/path/_win.py @@ -0,0 +1,23 @@ +from __future__ import annotations + + +def get_short_path_name(long_name): + """Gets the short path name of a given long path - http://stackoverflow.com/a/23598461/200291.""" + import ctypes # noqa: PLC0415 + from ctypes import wintypes # noqa: PLC0415 + + GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW # noqa: N806 + GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, wintypes.DWORD] + GetShortPathNameW.restype = wintypes.DWORD + output_buf_size = 0 + while True: + output_buf = ctypes.create_unicode_buffer(output_buf_size) + needed = GetShortPathNameW(long_name, output_buf, output_buf_size) + if output_buf_size >= needed: + return output_buf.value + output_buf_size = needed + + +__all__ = [ + "get_short_path_name", +] diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/subprocess/__init__.py b/.venv/lib/python3.11/site-packages/virtualenv/util/subprocess/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e6d5fc885336288f2ae3bbe11208e0d47f0b054a --- /dev/null +++ b/.venv/lib/python3.11/site-packages/virtualenv/util/subprocess/__init__.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +import subprocess + +CREATE_NO_WINDOW = 0x80000000 + + +def run_cmd(cmd): + try: + process = subprocess.Popen( + cmd, + universal_newlines=True, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding="utf-8", + ) + out, err = process.communicate() # input disabled + code = process.returncode + except OSError as error: + code, out, err = error.errno, "", error.strerror + if code == 2 and "file" in err: # noqa: PLR2004 + err = str(error) # FileNotFoundError in Python >= 3.3 + return code, out, err + + +__all__ = ( + "CREATE_NO_WINDOW", + "run_cmd", +) diff --git a/.venv/lib/python3.11/site-packages/virtualenv/util/subprocess/__pycache__/__init__.cpython-311.pyc b/.venv/lib/python3.11/site-packages/virtualenv/util/subprocess/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5af7313f3453007ec18fac36cbf3a5a8d67af2bd Binary files /dev/null and b/.venv/lib/python3.11/site-packages/virtualenv/util/subprocess/__pycache__/__init__.cpython-311.pyc differ