summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAngelos Evripiotis <jevripiotis@bloomberg.net>2019-04-16 15:07:13 +0100
committerAngelos Evripiotis <jevripiotis@bloomberg.net>2019-04-17 11:19:48 +0100
commitc0fd176b901186295582f2afceb4c8194307141a (patch)
tree8ea93df10663087a960838e19087dcea41955d5d
parent5a9b3f64000d6cb6104b579d404bd6ed4918bcd0 (diff)
downloadbuildstream-aevri/plugin_venvs.tar.gz
HACK: unsuccessful multi-venv experimentaevri/plugin_venvs
This is an experiment to test the feasability of supporting a venv per plugin, all running under the same interpreter. It shows: * Installing two separate venvs with different dependency versions * Running an interpretor which loads a plugin in both separate venvs (it could even be the same plugin, and need not be a "BuildStream" plugin, but some python module loaded on demand) * Prove that we infact have separation (perhaps by having the plugin just print the versions of it's dependencies). The approach taken in this experiment is to push all modules required by the plugin into a PluginBase space. There are major problems with this approach, see the README.md.
-rw-r--r--.gitignore1
-rw-r--r--multivenv_experiment/README.md99
-rw-r--r--multivenv_experiment/notbstalphaelement/bstplugin.py10
-rw-r--r--multivenv_experiment/notbstalphaelement/setup.py8
-rw-r--r--multivenv_experiment/notbstbetaelement/bstplugin.py10
-rw-r--r--multivenv_experiment/notbstbetaelement/setup.py8
-rw-r--r--multivenv_experiment/notbstgammaelement/bstplugin.py10
-rw-r--r--multivenv_experiment/notbstgammaelement/setup.py8
-rw-r--r--multivenv_experiment/notbuildstream/notbuildstream.py74
-rw-r--r--multivenv_experiment/notbuildstream/setup.py12
10 files changed, 240 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 5c258fad4..b02de5fa2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,4 @@ doc/source/buildstream.rst
doc/source/buildstream.*.rst
doc/build/
versioneer.pyc
+*.egg-info
diff --git a/multivenv_experiment/README.md b/multivenv_experiment/README.md
new file mode 100644
index 000000000..1ca97f1de
--- /dev/null
+++ b/multivenv_experiment/README.md
@@ -0,0 +1,99 @@
+Multi-venv experiment
+=====================
+
+This is an experiment to test the feasability of supporting a venv per plugin,
+all running under the same interpreter.
+
+It shows:
+
+ * Installing two separate venvs with different dependency versions
+ * Running an interpretor which loads a plugin in both separate
+ venvs (it could even be the same plugin, and need not be a
+ "BuildStream" plugin, but some python module loaded on demand)
+ * Prove that we infact have separation (perhaps by having the plugin
+ just print the versions of it's dependencies).
+
+The approach taken in this experiment is to push all modules required by the
+plugin into a PluginBase space.
+
+Major problems discovered
+-------------------------
+
+- We don't override `sys.modules`, so this in jinja2 v2.10 won't work: `del
+ sys.modules['jinja2._identifier']`. A first attempt to override `sys.modules`
+ resulted in mysterious failures not detailed here.
+
+- Relative-imports work in a way that doesn't seem to be obvious, which is
+ incompatible with the hack to rewrite top-level imports. See the
+ `import_override` function for more details.
+
+- Global imports don't seem to work with pure PluginBase, e.g. jinja2
+ itself will do `from jinja2.environment import Environment, Template` in its
+ `__init__.py`, which fails. This is overcome with the `import_override`
+ hackery in the experiment.
+
+- the `jinja2.__version__` reported by the plugin is actually the version of
+ `pluginbase` instead. Interestingly the `jinja2.__version__` reported in the
+ main app is different.
+
+- the `jinja2.evalcontextfilter` accessed by the plugin is different from the
+ one accessed in the main app. That's probably for the same reason as the
+ above point.
+
+Setup
+-----
+
+Create a Python venv for each subdirectory, e.g.
+
+ python3 -m venv ~/PyVenv/notbuildstream
+ python3 -m venv ~/PyVenv/notbstalphaelement
+ python3 -m venv ~/PyVenv/notbstbetaelement
+ python3 -m venv ~/PyVenv/notbstgammaelement
+
+Then, for each directory:
+
+- Activate the appropriate venv. e.g. `. ~/PyVenv/notbuildstream/bin/activate`.
+- For each subdirectory, `pip install SUBDIRECTORY`. Don't use `-e`, as it
+ seems that `.egg-link`s are a separate case that need special consideration.
+
+Make sure that the reference to `lib/python3.7/site-packages` in
+`notbuildstream.py` is fixed up as appropriate for your venvs.
+
+Running
+-------
+
+Enter the venv for `notbuildstream`, and invoke it with the paths to the other
+venvs, e.g.
+
+ notbst ~/PyVenv/ ~/PyVenv/notbst{alpha,beta,gamma}element
+
+You should see somthing like:
+
+```
+venv: /Users/jevripiotis/PyVenv/notbstalphaelement
+Alpha
+jinja2.__version__: 1.0.0
+jinja2.evalcontextfilter: None
+main: jinja2: <module 'pluginbase._internalspace._sp5d362f8c6220a8c7f6ec3263825004f6.jinja2' from '/Users/jevripiotis/PyVenv/notbstalphaelement/lib/python3.7/site-packages/jinja2/__init__.py'>
+main: jinja2.__version__: 2.8
+main: jinja2.__file__: /Users/jevripiotis/PyVenv/notbstalphaelement/lib/python3.7/site-packages/jinja2/__init__.py
+main: Has evalcontextfilter: <function evalcontextfilter at 0x10b66c9d8>
+
+venv: /Users/jevripiotis/PyVenv/notbstbetaelement
+Beta
+jinja2.__version__: 1.0.0
+jinja2.evalcontextfilter: None
+main: jinja2: <module 'pluginbase._internalspace._sp97ff7e741eeaeaf9c282906561d1b456.jinja2' from '/Users/jevripiotis/PyVenv/notbstbetaelement/lib/python3.7/site-packages/jinja2/__init__.py'>
+main: jinja2.__version__: unknown
+main: jinja2.__file__: /Users/jevripiotis/PyVenv/notbstbetaelement/lib/python3.7/site-packages/jinja2/__init__.py
+main: Has evalcontextfilter: None
+
+venv: /Users/jevripiotis/PyVenv/notbstgammaelement
+Traceback (most recent call last):
+ File "/Users/jevripiotis/PyVenv/notbuildstream/bin/notbst", line 11, in <module>
+ load_entry_point('notbuildstream', 'console_scripts', 'notbst')()
+--- 8< --- snip long stacktrace --- 8< ---
+ File "/Users/jevripiotis/PyVenv/notbstgammaelement/lib/python3.7/site-packages/jinja2/lexer.py", line 50, in <module>
+ del sys.modules['jinja2._identifier']
+KeyError: 'jinja2._identifier'
+```
diff --git a/multivenv_experiment/notbstalphaelement/bstplugin.py b/multivenv_experiment/notbstalphaelement/bstplugin.py
new file mode 100644
index 000000000..e30671f6c
--- /dev/null
+++ b/multivenv_experiment/notbstalphaelement/bstplugin.py
@@ -0,0 +1,10 @@
+import sys
+
+import jinja2
+
+
+class Element:
+ def __init__(self, bst_context):
+ print("Alpha")
+ print(f"jinja2.__version__: {jinja2.__version__}")
+ print("jinja2.evalcontextfilter:", getattr(jinja2, "evalcontextfilter", None))
diff --git a/multivenv_experiment/notbstalphaelement/setup.py b/multivenv_experiment/notbstalphaelement/setup.py
new file mode 100644
index 000000000..cf758fb26
--- /dev/null
+++ b/multivenv_experiment/notbstalphaelement/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup
+
+setup(
+ name="notbstalphaelement",
+ version="0.1",
+ py_modules=["bstplugin"],
+ install_requires=["Jinja2==2.8"],
+)
diff --git a/multivenv_experiment/notbstbetaelement/bstplugin.py b/multivenv_experiment/notbstbetaelement/bstplugin.py
new file mode 100644
index 000000000..5e41f75cf
--- /dev/null
+++ b/multivenv_experiment/notbstbetaelement/bstplugin.py
@@ -0,0 +1,10 @@
+import sys
+
+import jinja2
+
+
+class Element:
+ def __init__(self, bst_context):
+ print("Beta")
+ print(f"jinja2.__version__: {jinja2.__version__}")
+ print("jinja2.evalcontextfilter:", getattr(jinja2, "evalcontextfilter", None))
diff --git a/multivenv_experiment/notbstbetaelement/setup.py b/multivenv_experiment/notbstbetaelement/setup.py
new file mode 100644
index 000000000..da7c2d86f
--- /dev/null
+++ b/multivenv_experiment/notbstbetaelement/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup
+
+setup(
+ name="notbstbetaelement",
+ version="0.1",
+ py_modules=["bstplugin"],
+ install_requires=["Jinja2==2.3"],
+)
diff --git a/multivenv_experiment/notbstgammaelement/bstplugin.py b/multivenv_experiment/notbstgammaelement/bstplugin.py
new file mode 100644
index 000000000..cfd41f260
--- /dev/null
+++ b/multivenv_experiment/notbstgammaelement/bstplugin.py
@@ -0,0 +1,10 @@
+import sys
+
+import jinja2
+
+
+class Element:
+ def __init__(self, bst_context):
+ print("Gamma")
+ print(f"jinja2.__version__: {jinja2.__version__}")
+ print("jinja2.evalcontextfilter:", getattr(jinja2, "evalcontextfilter", None))
diff --git a/multivenv_experiment/notbstgammaelement/setup.py b/multivenv_experiment/notbstgammaelement/setup.py
new file mode 100644
index 000000000..3bf3a2e81
--- /dev/null
+++ b/multivenv_experiment/notbstgammaelement/setup.py
@@ -0,0 +1,8 @@
+from setuptools import setup
+
+setup(
+ name="notbstgammaelement",
+ version="0.1",
+ py_modules=["bstplugin"],
+ install_requires=["Jinja2==2.10"],
+)
diff --git a/multivenv_experiment/notbuildstream/notbuildstream.py b/multivenv_experiment/notbuildstream/notbuildstream.py
new file mode 100644
index 000000000..cce44d6dd
--- /dev/null
+++ b/multivenv_experiment/notbuildstream/notbuildstream.py
@@ -0,0 +1,74 @@
+import builtins
+import sys
+import contextlib
+
+import click
+
+import pluginbase
+
+
+# TODO: Make this work instead by overriding everything except BuildStream and
+# the standard library.
+@contextlib.contextmanager
+def import_override(*override_modules):
+ def myimport(name, globals_=None, locals_=None, fromlist=None, level=None):
+ # XXX: In the case of 'from . import A, B' we run into trouble if
+ # we remap the name. We should fully understand why and what
+ # guarantees there are before considering using any of this.
+ #
+ # When this is the case, it seems that 'fromlist' will be an empty
+ # list. Again, we need to know the guarantees here.
+ #
+ if fromlist != []:
+ for m in override_modules:
+ if name.startswith(m):
+ name = "notbuildstream.plugins." + name
+ return builtins_import(name, globals_, locals_, fromlist, level)
+
+ builtins_import = builtins.__import__
+ try:
+ builtins.__import__ = myimport
+ yield
+ finally:
+ builtins.__import__ = builtins_import
+
+
+@click.command("notbuildstream")
+@click.argument(
+ "plugin_venvs",
+ nargs=-1,
+ metavar="PATH",
+ type=click.Path(exists=True, file_okay=False, dir_okay=True),
+)
+def cli(plugin_venvs):
+
+ pbase = pluginbase.PluginBase(package="notbuildstream.plugins")
+ psource_list = []
+
+ for venv in plugin_venvs:
+ print(f"venv: {venv}")
+
+ # XXX: We should determine this path using some standard mechanism.
+ search_path = [venv + "/lib/python3.7/site-packages", venv]
+
+ psource = pbase.make_plugin_source(searchpath=search_path, identifier=venv)
+ psource_list.append(psource)
+
+ with import_override("jinja2", "markupsafe"):
+ with psource:
+ # TODO: use entrypoints and lookup plugins with pkgconfig.
+ plugin = psource.load_plugin("bstplugin")
+ element = plugin.Element("a")
+ jinja2 = psource.load_plugin("jinja2")
+ print(f"main: jinja2: {jinja2}")
+ print(f"main: jinja2.__version__: {jinja2.__version__}")
+ print(f"main: jinja2.__file__: {jinja2.__file__}")
+ print(
+ "main: Has evalcontextfilter:",
+ getattr(jinja2, "evalcontextfilter", None),
+ )
+ print()
+
+
+if __name__ == "__main__":
+ sys.exit(cli())
diff --git a/multivenv_experiment/notbuildstream/setup.py b/multivenv_experiment/notbuildstream/setup.py
new file mode 100644
index 000000000..f0a79823d
--- /dev/null
+++ b/multivenv_experiment/notbuildstream/setup.py
@@ -0,0 +1,12 @@
+from setuptools import setup
+
+setup(
+ name="notbuildstream",
+ version="0.1",
+ py_modules=["notbuildstream"],
+ install_requires=["Click", "pluginbase"],
+ entry_points="""
+ [console_scripts]
+ notbst=notbuildstream:cli
+ """,
+)