diff --git a/plugins/python-build/scripts/add_miniconda.py b/plugins/python-build/scripts/add_miniconda.py index 3ecb80d6..32b191f8 100755 --- a/plugins/python-build/scripts/add_miniconda.py +++ b/plugins/python-build/scripts/add_miniconda.py @@ -11,15 +11,18 @@ Also ignores sub-patch releases if that major.minor.patch already exists, but otherwise, takes the latest sub-patch release for given OS/arch. Assumes all miniconda3 releases < 4.7 default to python 3.6, and anything else 3.7. """ +import logging +import re +import string +import sys import textwrap from argparse import ArgumentParser from collections import defaultdict +from dataclasses import dataclass from enum import Enum from functools import total_ordering from pathlib import Path from typing import NamedTuple, List, Optional, DefaultDict, Dict -import logging -import string import requests_html @@ -90,7 +93,7 @@ class Flavor(StrEnum): ANACONDA = "anaconda" MINICONDA = "miniconda" - + class TFlavor(StrEnum): ANACONDA = "Anaconda" MINICONDA = "Miniconda" @@ -102,21 +105,37 @@ class Suffix(StrEnum): NONE = "" -class PyVersion(StrEnum): - PY27 = "py27" - PY36 = "py36" - PY37 = "py37" - PY38 = "py38" - PY39 = "py39" - PY310 = "py310" - PY311 = "py311" +PyVersion = None +class PyVersionMeta(type): + def __getattr__(self, name): + """Generate PyVersion.PYXXX on demand to future-proof it""" + if PyVersion is not None: + return PyVersion(name.lower()) + return super(PyVersionMeta,self).__getattr__(self, name) + + +@dataclass(frozen=True) +class PyVersion(metaclass=PyVersionMeta): + major: str + minor: str + + def __init__(self, value): + (major, minor) = re.match(r"py(\d)(\d+)", value).groups() + object.__setattr__(self, "major", major) + object.__setattr__(self, "minor", minor) + + @property + def value(self): + return f"py{self.major}{self.minor}" def version(self): - first, *others = self.value[2:] - return f"{first}.{''.join(others)}" + return f"{self.major}.{self.minor}" def version_info(self): - return tuple(int(n) for n in self.version().split(".")) + return (self.major, self.minor) + + def __str__(self): + return self.value @total_ordering @@ -190,10 +209,15 @@ class CondaVersion(NamedTuple): # https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-python.html if v < (4, 7): return PyVersion.PY36 - else: + if v < (4, 8): return PyVersion.PY37 + else: + # since 4.8, Miniconda specifies versions explicitly in the file name + return None if self.flavor == "anaconda": # https://docs.anaconda.com/free/anaconda/reference/release-notes/ + if v >= (2024,6): + return PyVersion.PY312 if v >= (2023,7): return PyVersion.PY311 if v >= (2023,3): @@ -247,7 +271,7 @@ class CondaSpec(NamedTuple): md5, repo, ) - if py_version is None: + if py_version is None and py_ver is None and ver != "latest": spec = spec.with_py_version(spec.version.default_py_version()) return spec @@ -296,8 +320,8 @@ def get_existing_condas(name): if v.version_str != "latest": logger.debug("Found existing %(name)s version %(v)s", locals()) yield v - except ValueError: - logger.error("Unable to parse existing version %s", entry_name) + except ValueError as e: + logger.error("Unable to parse existing version %s: %s", entry_name, e) def get_available_condas(name, repo): @@ -348,19 +372,12 @@ if __name__ == "__main__": help="Do not write scripts, just report them to stdout", ) parser.add_argument( - "-v", "--verbose", action="count", default=0, + "-v", "--verbose", action="store_true", default=0, help="Increase verbosity of logging", ) parsed = parser.parse_args() - log_level = { - 0: logging.WARNING, - 1: logging.INFO, - 2: logging.DEBUG, - }.get(parsed.verbose, logging.DEBUG) - logging.basicConfig(level=log_level) - if parsed.verbose < 3: - logging.getLogger("requests").setLevel(logging.WARNING) + logging.basicConfig(level=logging.DEBUG if parsed.verbose else logging.INFO) existing_versions = set() available_specs = set() @@ -377,7 +394,7 @@ if __name__ == "__main__": for s in sorted(available_specs, key=key_fn): key = s.version vv = key.version_str.info() - + reason = None if key in existing_versions: reason = "already exists" @@ -385,7 +402,7 @@ if __name__ == "__main__": reason = "too old" elif len(key.version_str.info()) >= 4 and "-" not in key.version_str: reason = "ignoring hotfix releases" - + if reason: logger.debug("Ignoring version %(s)s (%(reason)s)", locals()) continue