summaryrefslogtreecommitdiff
path: root/src/virtualenv/run/plugin
diff options
context:
space:
mode:
authorBernat Gabor <bgabor8@bloomberg.net>2020-01-10 09:18:59 +0000
committerBernat Gabor <bgabor8@bloomberg.net>2020-01-10 15:38:39 +0000
commit5f3580ee96ad948e5f4c66fc8358d0b81903da57 (patch)
tree55312be9e4d62ec60464d5ae15e3606d6eb1fc34 /src/virtualenv/run/plugin
parent7d964e3ce7bf13326a6b15497d8294fd8830e4a5 (diff)
downloadvirtualenv-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__.py0
-rw-r--r--src/virtualenv/run/plugin/activators.py49
-rw-r--r--src/virtualenv/run/plugin/base.py60
-rw-r--r--src/virtualenv/run/plugin/creators.py41
-rw-r--r--src/virtualenv/run/plugin/discovery.py25
-rw-r--r--src/virtualenv/run/plugin/seeders.py31
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)