diff options
| author | Bernat Gabor <bgabor8@bloomberg.net> | 2020-01-10 09:18:59 +0000 |
|---|---|---|
| committer | Bernat Gabor <bgabor8@bloomberg.net> | 2020-01-10 15:38:39 +0000 |
| commit | 5f3580ee96ad948e5f4c66fc8358d0b81903da57 (patch) | |
| tree | 55312be9e4d62ec60464d5ae15e3606d6eb1fc34 /src/virtualenv/run/plugin | |
| parent | 7d964e3ce7bf13326a6b15497d8294fd8830e4a5 (diff) | |
| download | virtualenv-5f3580ee96ad948e5f4c66fc8358d0b81903da57.tar.gz | |
reorganize run.py - prefer inheritence based API over generators
Signed-off-by: Bernat Gabor <bgabor8@bloomberg.net>
Diffstat (limited to 'src/virtualenv/run/plugin')
| -rw-r--r-- | src/virtualenv/run/plugin/__init__.py | 0 | ||||
| -rw-r--r-- | src/virtualenv/run/plugin/activators.py | 49 | ||||
| -rw-r--r-- | src/virtualenv/run/plugin/base.py | 60 | ||||
| -rw-r--r-- | src/virtualenv/run/plugin/creators.py | 41 | ||||
| -rw-r--r-- | src/virtualenv/run/plugin/discovery.py | 25 | ||||
| -rw-r--r-- | src/virtualenv/run/plugin/seeders.py | 31 |
6 files changed, 206 insertions, 0 deletions
diff --git a/src/virtualenv/run/plugin/__init__.py b/src/virtualenv/run/plugin/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/virtualenv/run/plugin/__init__.py diff --git a/src/virtualenv/run/plugin/activators.py b/src/virtualenv/run/plugin/activators.py new file mode 100644 index 0000000..8cd217f --- /dev/null +++ b/src/virtualenv/run/plugin/activators.py @@ -0,0 +1,49 @@ +from __future__ import absolute_import, unicode_literals + +from argparse import ArgumentTypeError + +from .base import ComponentBuilder + + +class ActivationSelector(ComponentBuilder): + def __init__(self, interpreter, parser): + self.default = None + super(ActivationSelector, self).__init__(interpreter, parser, "virtualenv.activate", "activators", True) + self.active = None + + def add_selector_arg_parse(self, name, choices): + self.default = ",".join(choices) + + self.parser.add_argument( + "--{}".format(name), + default=self.default, + metavar="comma_separated_list", + required=False, + help="activators to generate together with virtual environment - default is all available and compatible", + type=self._extract_activators, + ) + + def _extract_activators(self, entered_str): + elements = [e.strip() for e in entered_str.split(",") if e.strip()] + missing = [e for e in elements if e not in self.possible] + if missing: + raise ArgumentTypeError("the following activators are not available {}".format(",".join(missing))) + return elements + + def handle_selected_arg_parse(self, options): + selected_activators = ( + self._extract_activators(self.default) if options.activators is self.default else options.activators + ) + self.active = {k: v for k, v in self.possible.items() if k in selected_activators} + self.parser.add_argument( + "--prompt", + dest="prompt", + metavar="prompt", + help="provides an alternative prompt prefix for this environment", + default=None, + ) + for activator in self.active.values(): + activator.add_parser_arguments(self.parser, self.interpreter) + + def create(self, options): + return [activator_class(options) for activator_class in self.active.values()] diff --git a/src/virtualenv/run/plugin/base.py b/src/virtualenv/run/plugin/base.py new file mode 100644 index 0000000..5232b39 --- /dev/null +++ b/src/virtualenv/run/plugin/base.py @@ -0,0 +1,60 @@ +from __future__ import absolute_import, unicode_literals + +import sys +from collections import OrderedDict + +if sys.version_info >= (3, 8): + from importlib.metadata import entry_points +else: + from importlib_metadata import entry_points + + +class PluginLoader(object): + _OPTIONS = None + _ENTRY_POINTS = None + + @classmethod + def entry_points_for(cls, key): + return OrderedDict((e.name, e.load()) for e in cls.entry_points().get(key, {})) + + @staticmethod + def entry_points(): + if PluginLoader._ENTRY_POINTS is None: + PluginLoader._ENTRY_POINTS = entry_points() + return PluginLoader._ENTRY_POINTS + + +class ComponentBuilder(PluginLoader): + def __init__(self, interpreter, parser, key, name, needs_support): + self.interpreter = interpreter + self.name = name + self._impl_class = None + opts = self.options(key) + self.possible = self._build_options( + OrderedDict((k, v) for k, v in opts.items() if v.supports(interpreter)) if needs_support else opts + ) + self.parser = parser.add_argument_group("{} options".format(name)) + self.add_selector_arg_parse(name, list(self.possible)) + + @classmethod + def options(cls, key): + if cls._OPTIONS is None: + cls._OPTIONS = cls.entry_points_for(key) + return cls._OPTIONS + + def add_selector_arg_parse(self, name, choices): + raise NotImplementedError + + def _build_options(self, options): + return options + + def handle_selected_arg_parse(self, options): + selected = getattr(options, self.name) + if selected not in self.possible: + raise RuntimeError("No implementation for {}".format(self.interpreter)) + self._impl_class = self.possible[selected] + self._impl_class.add_parser_arguments(self.parser, self.interpreter) + return selected + + def create(self, options): + return self._impl_class(options, self.interpreter) diff --git a/src/virtualenv/run/plugin/creators.py b/src/virtualenv/run/plugin/creators.py new file mode 100644 index 0000000..a6d3cd3 --- /dev/null +++ b/src/virtualenv/run/plugin/creators.py @@ -0,0 +1,41 @@ +from __future__ import absolute_import, unicode_literals + +from virtualenv.interpreters.create.venv import Venv + +from .base import ComponentBuilder + + +class CreatorSelector(ComponentBuilder): + def __init__(self, interpreter, parser): + super(CreatorSelector, self).__init__(interpreter, parser, "virtualenv.create", "creator", True) + + def _build_options(self, options): + if not options: + raise RuntimeError("No virtualenv implementation for {}".format(self.interpreter)) + + from virtualenv.interpreters.create.builtin_way import VirtualenvBuiltin + + self.builtin_way = next((i for i, v in options.items() if issubclass(v, VirtualenvBuiltin)), None) + if self.builtin_way is not None: + options["builtin"] = options[self.builtin_way] # make the first builtin method the builtin alias + return options + + def add_selector_arg_parse(self, name, choices): + # prefer the built-in venv if present, otherwise fallback to first defined type + choices = sorted(choices, key=lambda a: 0 if a == "venv" else 1) + self.parser.add_argument( + "--{}".format(name), + choices=choices, + default=next(iter(choices)), + required=False, + help="create environment via{}".format( + "" if self.builtin_way is None else " (builtin = {})".format(self.builtin_way) + ), + ) + + def create(self, options): + if issubclass(self._impl_class, Venv): + options.builtin_way = ( + None if self.builtin_way is None else self.possible[self.builtin_way](options, self.interpreter) + ) + return super(CreatorSelector, self).create(options) diff --git a/src/virtualenv/run/plugin/discovery.py b/src/virtualenv/run/plugin/discovery.py new file mode 100644 index 0000000..aaed452 --- /dev/null +++ b/src/virtualenv/run/plugin/discovery.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import, unicode_literals + +from .base import PluginLoader + + +class Discovery(PluginLoader): + """""" + + +def get_discover(parser, args, options): + discover_types = Discovery.entry_points_for("virtualenv.discovery") + discovery_parser = parser.add_argument_group("target interpreter identifier") + discovery_parser.add_argument( + "--discovery", + choices=list(discover_types.keys()), + default=next(i for i in discover_types.keys()), + required=False, + help="interpreter discovery method", + ) + options, _ = parser.parse_known_args(args, namespace=options) + discover_class = discover_types[options.discovery] + discover_class.add_parser_arguments(discovery_parser) + options, _ = parser.parse_known_args(args, namespace=options) + discover = discover_class(options) + return discover diff --git a/src/virtualenv/run/plugin/seeders.py b/src/virtualenv/run/plugin/seeders.py new file mode 100644 index 0000000..75cfad8 --- /dev/null +++ b/src/virtualenv/run/plugin/seeders.py @@ -0,0 +1,31 @@ +from __future__ import absolute_import, unicode_literals + +from .base import ComponentBuilder + + +class SeederSelector(ComponentBuilder): + def __init__(self, interpreter, parser): + super(SeederSelector, self).__init__(interpreter, parser, "virtualenv.seed", "seeder", False) + + def add_selector_arg_parse(self, name, choices): + self.parser.add_argument( + "--{}".format(name), + choices=choices, + default="app-data", + required=False, + help="seed packages install method", + ) + self.parser.add_argument( + "--without-pip", + help="if set forces the none seeder, used for compatibility with venv", + action="store_true", + dest="without_pip", + ) + + def handle_selected_arg_parse(self, options): + if options.without_pip is True: + setattr(options, self.name, "none") + return super(SeederSelector, self).handle_selected_arg_parse(options) + + def create(self, options): + return self._impl_class(options) |
