diff --git a/src/appenv.py b/src/appenv.py index 15b1478..58974ef 100755 --- a/src/appenv.py +++ b/src/appenv.py @@ -28,6 +28,14 @@ import re +class TColors: + """Terminal colors for pretty output.""" + RED = '\033[91m' + GREEN = '\033[92m' + YELLOW = '\033[93m' + RESET = '\033[0m' + + def cmd(c, merge_stderr=True, quiet=False): # TODO revisit the cmd() architecture w/ python 3 # XXX better IO management for interactive output and seeing original @@ -134,8 +142,7 @@ def ensure_venv(target): pip(target, ["install", "--upgrade", "pip"]) -def ensure_minimal_python(): - current_python = os.path.realpath(sys.executable) +def parse_preferences(): preferences = None if os.path.exists('requirements.txt'): with open('requirements.txt') as f: @@ -148,9 +155,15 @@ def ensure_minimal_python(): preferences = [x.strip() for x in preferences.split(',')] preferences = list(filter(None, preferences)) break + return preferences + + +def ensure_minimal_python(): + current_python = os.path.realpath(sys.executable) + preferences = parse_preferences() if not preferences: # We have no preferences defined, use the current python. - print("Update lockfile with with {}.".format(current_python)) + print("Updating lockfile with with {}.".format(current_python)) print("If you want to use a different version, set it via") print(" `# appenv-python-preference:` in requirements.txt.") return @@ -194,20 +207,18 @@ def ensure_best_python(base): return import shutil - # use newest Python available if nothing else is requested - preferences = ['3.{}'.format(x) for x in reversed(range(4, 20))] + preferences = parse_preferences() - if os.path.exists('requirements.txt'): - with open('requirements.txt') as f: - for line in f: - # Expected format: - # # appenv-python-preference: 3.1,3.9,3.4 - if not line.startswith("# appenv-python-preference: "): - continue - preferences = line.split(':')[1] - preferences = [x.strip() for x in preferences.split(',')] - preferences = list(filter(None, preferences)) - break + if preferences is None: + if sys.version_info >= (3, 12): + print("You are using a Python version >= 3.12.") + print( + "Please specify a Python version in the requirements.txt file." + ) + print("Lockfiles created with a Python version lower than 3.12") + print("may create a broken venv with a Python version >= 3.12.") + # use newest Python available if nothing else is requested + preferences = ['3.{}'.format(x) for x in reversed(range(4, 20))] current_python = os.path.realpath(sys.executable) for version in preferences: @@ -505,6 +516,12 @@ def reset(self, args=None, remaining=None): def update_lockfile(self, args=None, remaining=None): ensure_minimal_python() + preferences = parse_preferences() + python312_mixed_setuptools_workaround = False + if preferences is not None: + if any(f'3.{x}' in preferences for x in range(4, 12)): + if any(f'3.{x}' in preferences for x in range(12, 20)): + python312_mixed_setuptools_workaround = True os.chdir(self.base) print("Updating lockfile") tmpdir = os.path.join(self.appenv_dir, "updatelock") @@ -515,7 +532,12 @@ def update_lockfile(self, args=None, remaining=None): pip(tmpdir, ["install", "-r", "requirements.txt"]) extra_specs = [] - result = pip(tmpdir, ["freeze"], merge_stderr=False).decode('ascii') + pip_freeze_args = ["freeze"] + if python312_mixed_setuptools_workaround: + pip_freeze_args.extend(["--all", "--exclude", "pip"]) + result = pip( + tmpdir, pip_freeze_args, merge_stderr=False).decode('ascii') + # They changed this behaviour in https://github.com/pypa/pip/pull/12032 pinned_versions = {} for line in result.splitlines(): if line.strip().startswith('-e '):