codekingpro/portable-devtools
114k
1from __future__ import annotations2 3from collections import OrderedDict, defaultdict4from typing import TYPE_CHECKING, NamedTuple5 6from virtualenv.create.describe import Describe7from virtualenv.create.via_global_ref.builtin.builtin_way import VirtualenvBuiltin8 9from .base import ComponentBuilder10 11if TYPE_CHECKING:12 from virtualenv.create.creator import Creator, CreatorMeta13 14 15class CreatorInfo(NamedTuple):16 key_to_class: dict[str, type[Creator]]17 key_to_meta: dict[str, CreatorMeta]18 describe: type[Describe] | None19 builtin_key: str20 21 22class CreatorSelector(ComponentBuilder):23 def __init__(self, interpreter, parser) -> None:24 creators, self.key_to_meta, self.describe, self.builtin_key = self.for_interpreter(interpreter)25 super().__init__(interpreter, parser, "creator", creators)26 27 @classmethod28 def for_interpreter(cls, interpreter):29 key_to_class, key_to_meta, builtin_key, describe = OrderedDict(), {}, None, None30 errors = defaultdict(list)31 for key, creator_class in cls.options("virtualenv.create").items():32 if key == "builtin":33 msg = "builtin creator is a reserved name"34 raise RuntimeError(msg)35 meta = creator_class.can_create(interpreter)36 if meta:37 if meta.error:38 errors[meta.error].append(creator_class)39 else:40 if "builtin" not in key_to_class and issubclass(creator_class, VirtualenvBuiltin):41 builtin_key = key42 key_to_class["builtin"] = creator_class43 key_to_meta["builtin"] = meta44 key_to_class[key] = creator_class45 key_to_meta[key] = meta46 if describe is None and issubclass(creator_class, Describe) and creator_class.can_describe(interpreter):47 describe = creator_class48 if not key_to_meta:49 if errors:50 rows = [f"{k} for creators {', '.join(i.__name__ for i in v)}" for k, v in errors.items()]51 raise RuntimeError("\n".join(rows))52 msg = f"No virtualenv implementation for {interpreter}"53 raise RuntimeError(msg)54 return CreatorInfo(55 key_to_class=key_to_class,56 key_to_meta=key_to_meta,57 describe=describe,58 builtin_key=builtin_key,59 )60 61 def add_selector_arg_parse(self, name, choices):62 # prefer the built-in venv if present, otherwise fallback to first defined type63 choices = sorted(choices, key=lambda a: 0 if a == "builtin" else 1)64 default_value = self._get_default(choices)65 self.parser.add_argument(66 f"--{name}",67 choices=choices,68 default=default_value,69 required=False,70 help=f"create environment via{'' if self.builtin_key is None else f' (builtin = {self.builtin_key})'}",71 )72 73 @staticmethod74 def _get_default(choices):75 return next(iter(choices))76 77 def populate_selected_argparse(self, selected, app_data):78 self.parser.description = f"options for {self.name} {selected}"79 self._impl_class.add_parser_arguments(self.parser, self.interpreter, self.key_to_meta[selected], app_data)80 81 def create(self, options):82 options.meta = self.key_to_meta[getattr(options, self.name)]83 if not issubclass(self._impl_class, Describe):84 options.describe = self.describe(options, self.interpreter)85 return super().create(options)86 87 88__all__ = [89 "CreatorInfo",90 "CreatorSelector",91]92 