Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
ARIA-215 Refactor plugin-related code into PluginManager
Refactored plugin-related code from ProcessExecutor into PluginManager.
Additionally, renamed plugin_prefix to plugin_dir in PluginManager.
  • Loading branch information
Ran Ziv committed May 7, 2017
1 parent 45c158e commit 3e1ed14c00ea2c83fafdca8ec0e37817bea1d5e8
Showing 4 changed files with 100 additions and 49 deletions.
@@ -23,6 +23,9 @@
import wagon

from . import exceptions
from ..utils import process as process_utils

_IS_WIN = os.name == 'nt'


class PluginManager(object):
@@ -62,11 +65,40 @@ def install(self, source):
raise exceptions.PluginAlreadyExistsError(
'Plugin {0}, version {1} already exists'.format(plugin.package_name,
plugin.package_version))
self._install_wagon(source=source, prefix=self.get_plugin_prefix(plugin))
self._install_wagon(source=source, prefix=self.get_plugin_dir(plugin))
self._model.plugin.put(plugin)
return plugin

def get_plugin_prefix(self, plugin):
def load_plugin(self, plugin, env=None):
"""
Load the plugin into an environment.
Loading the plugin means the plugin's code and binaries paths will be appended to the
environment's PATH and PYTHONPATH, thereby allowing usage of the plugin.
:param plugin: The plugin to load
:param env: The environment to load the plugin into; If `None`, os.environ will be used.
"""
env = env or os.environ
plugin_dir = self.get_plugin_dir(plugin)

# Update PATH environment variable to include plugin's bin dir
bin_dir = 'Scripts' if _IS_WIN else 'bin'
process_utils.append_to_path(os.path.join(plugin_dir, bin_dir), env=env)

# Update PYTHONPATH environment variable to include plugin's site-packages
# directories
if _IS_WIN:
pythonpath_dirs = [os.path.join(plugin_dir, 'Lib', 'site-packages')]
else:
# In some linux environments, there will be both a lib and a lib64 directory
# with the latter, containing compiled packages.
pythonpath_dirs = [os.path.join(
plugin_dir, 'lib{0}'.format(b),
'python{0}.{1}'.format(sys.version_info[0], sys.version_info[1]),
'site-packages') for b in ('', '64')]

process_utils.append_to_pythonpath(*pythonpath_dirs, env=env)

def get_plugin_dir(self, plugin):
return os.path.join(
self._plugins_dir,
'{0}-{1}'.format(plugin.package_name, plugin.package_version))
@@ -47,13 +47,12 @@
from aria.extension import process_executor
from aria.utils import (
imports,
exceptions
exceptions,
process as process_utils
)
from aria.modeling import types as modeling_types


_IS_WIN = os.name == 'nt'

_INT_FMT = 'I'
_INT_SIZE = struct.calcsize(_INT_FMT)
UPDATE_TRACKED_CHANGES_FAILED_STR = \
@@ -127,13 +126,7 @@ def execute(self, task):
with open(arguments_json_path, 'wb') as f:
f.write(pickle.dumps(self._create_arguments_dict(task)))

env = os.environ.copy()
# See _update_env for plugin_prefix usage
if task.plugin_fk and self._plugin_manager:
plugin_prefix = self._plugin_manager.get_plugin_prefix(task.plugin)
else:
plugin_prefix = None
self._update_env(env=env, plugin_prefix=plugin_prefix)
env = self._construct_subprocess_env(task=task)
# Asynchronously start the operation in a subprocess
subprocess.Popen(
'{0} {1} {2}'.format(sys.executable, __file__, arguments_json_path),
@@ -156,40 +149,19 @@ def _create_arguments_dict(self, task):
'context': task.context.serialization_dict,
}

def _update_env(self, env, plugin_prefix):
pythonpath_dirs = []
# If this is a plugin operation, plugin prefix will point to where
# This plugin is installed.
# We update the environment variables that the subprocess will be started with based on it
if plugin_prefix:

# Update PATH environment variable to include plugin's bin dir
bin_dir = 'Scripts' if _IS_WIN else 'bin'
env['PATH'] = '{0}{1}{2}'.format(
os.path.join(plugin_prefix, bin_dir),
os.pathsep,
env.get('PATH', ''))

# Update PYTHONPATH environment variable to include plugin's site-packages
# directories
if _IS_WIN:
pythonpath_dirs = [os.path.join(plugin_prefix, 'Lib', 'site-packages')]
else:
# In some linux environments, there will be both a lib and a lib64 directory
# with the latter, containing compiled packages.
pythonpath_dirs = [os.path.join(
plugin_prefix, 'lib{0}'.format(b),
'python{0}.{1}'.format(sys.version_info[0], sys.version_info[1]),
'site-packages') for b in ('', '64')]

# Add used supplied directories to injected PYTHONPATH
pythonpath_dirs.extend(self._python_path)

if pythonpath_dirs:
env['PYTHONPATH'] = '{0}{1}{2}'.format(
os.pathsep.join(pythonpath_dirs),
os.pathsep,
env.get('PYTHONPATH', ''))
def _construct_subprocess_env(self, task):
env = os.environ.copy()

if task.plugin_fk and self._plugin_manager:
# If this is a plugin operation,
# load the plugin on the subprocess env we're constructing
self._plugin_manager.load_plugin(task.plugin, env=env)

# Add user supplied directories to injected PYTHONPATH
if self._python_path:
process_utils.append_to_pythonpath(*self._python_path, env=env)

return env

def _listener(self):
# Notify __init__ method this thread has actually started
@@ -0,0 +1,47 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os


def append_to_path(*args, **kwargs):
"""
Appends one or more paths to the system path of an environment.
The environment will be that of the current process unless another is passed using the
'env' keyword argument.
:param args: paths to append
:param kwargs: 'env' may be used to pass a custom environment to use
"""
_append_to_path('PATH', *args, **kwargs)


def append_to_pythonpath(*args, **kwargs):
"""
Appends one or more paths to the python path of an environment.
The environment will be that of the current process unless another is passed using the
'env' keyword argument.
:param args: paths to append
:param kwargs: 'env' may be used to pass a custom environment to use
"""
_append_to_path('PYTHONPATH', *args, **kwargs)


def _append_to_path(path, *args, **kwargs):
env = kwargs.get('env') or os.environ
env[path] = '{0}{1}{2}'.format(
os.pathsep.join(args),
os.pathsep,
env.get(path, '')
)
@@ -38,9 +38,9 @@ def test_install(self, plugin_manager, mock_plugin, model, plugins_dir):
assert plugin.package_name == PACKAGE_NAME
assert plugin.package_version == PACKAGE_VERSION
assert plugin == model.plugin.get(plugin.id)
plugin_prefix = os.path.join(plugins_dir, '{0}-{1}'.format(PACKAGE_NAME, PACKAGE_VERSION))
assert os.path.isdir(plugin_prefix)
assert plugin_prefix == plugin_manager.get_plugin_prefix(plugin)
plugin_dir = os.path.join(plugins_dir, '{0}-{1}'.format(PACKAGE_NAME, PACKAGE_VERSION))
assert os.path.isdir(plugin_dir)
assert plugin_dir == plugin_manager.get_plugin_dir(plugin)

def test_install_already_exits(self, plugin_manager, mock_plugin):
plugin_manager.install(mock_plugin)

0 comments on commit 3e1ed14

Please sign in to comment.