Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add Spyder PYTHONPATH to PluginClient #4263

Merged
merged 5 commits into from
Mar 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion spyder/plugins/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,8 @@ def __init__(self, parent, ignore_last_opened_files=False):
self.toolbar_list = None
self.menu_list = None

self.introspector = IntrospectionManager()
self.introspector = IntrospectionManager(
extra_path=self.main.get_spyder_pythonpath())

# Setup new windows:
self.main.all_actions_defined.connect(self.setup_other_windows)
Expand Down Expand Up @@ -1462,6 +1463,9 @@ def set_current_filename(self, filename, editorwindow=None):
def set_path(self):
for finfo in self.editorstacks[0].data:
finfo.path = self.main.get_spyder_pythonpath()
if self.introspector:
self.introspector.change_extra_path(
self.main.get_spyder_pythonpath())

#------ Refresh methods
def refresh_file_dependent_actions(self):
Expand Down
27 changes: 21 additions & 6 deletions spyder/utils/introspection/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ class PluginManager(QObject):

introspection_complete = Signal(object)

def __init__(self, executable):
def __init__(self, executable, extra_path=None):

super(PluginManager, self).__init__()
plugins = OrderedDict()
for name in PLUGINS:
try:
plugin = PluginClient(name, executable)
plugin = PluginClient(name, executable, extra_path=extra_path)
plugin.run()
except Exception as e:
debug_print('Introspection Plugin Failed: %s' % name)
Expand Down Expand Up @@ -129,7 +130,9 @@ def handle_response(self, response):
self.pending = response

def close(self):
[plugin.close() for plugin in self.plugins]
for name, plugin in self.plugins.items():
plugin.close()
debug_print("Introspection Plugin Closed: {}".format(name))

def _finalize(self, response):
self.waiting = False
Expand Down Expand Up @@ -160,17 +163,29 @@ class IntrospectionManager(QObject):
send_to_help = Signal(str, str, str, str, bool)
edit_goto = Signal(str, int, str)

def __init__(self, executable=None):
def __init__(self, executable=None, extra_path=None):
super(IntrospectionManager, self).__init__()
self.editor_widget = None
self.pending = None
self.plugin_manager = PluginManager(executable)
self.extra_path = extra_path
self.executable = executable
self.plugin_manager = PluginManager(executable, extra_path)
self.plugin_manager.introspection_complete.connect(
self._introspection_complete)

def change_executable(self, executable):
self.executable = executable
self._restart_plugin()

def change_extra_path(self, extra_path):
if extra_path != self.extra_path:
self.extra_path = extra_path
self._restart_plugin()

def _restart_plugin(self):
self.plugin_manager.close()
self.plugin_manager = PluginManager(executable)
self.plugin_manager = PluginManager(self.executable,
extra_path=self.extra_path)
self.plugin_manager.introspection_complete.connect(
self._introspection_complete)

Expand Down
38 changes: 28 additions & 10 deletions spyder/utils/introspection/plugin_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import zmq

# Local imports
from spyder.config.base import debug_print, DEV, get_module_path
from spyder.config.base import debug_print, get_module_path


# Heartbeat timer in milliseconds
Expand All @@ -41,7 +41,8 @@ class AsyncClient(QObject):
received = Signal(object)

def __init__(self, target, executable=None, name=None,
extra_args=None, libs=None, cwd=None, env=None):
extra_args=None, libs=None, cwd=None, env=None,
extra_path=None):
super(AsyncClient, self).__init__()
self.executable = executable or sys.executable
self.extra_args = extra_args
Expand All @@ -50,8 +51,11 @@ def __init__(self, target, executable=None, name=None,
self.libs = libs
self.cwd = cwd
self.env = env
self.extra_path = extra_path
self.is_initialized = False
self.closing = False
self.notifier = None
self.process = None
self.context = zmq.Context()
QApplication.instance().aboutToQuit.connect(self.close)

Expand All @@ -77,7 +81,7 @@ def run(self):
# Set up environment variables.
processEnvironment = QProcessEnvironment()
env = self.process.systemEnvironment()
if (self.env and 'PYTHONPATH' not in self.env) or DEV:
if (self.env and 'PYTHONPATH' not in self.env) or self.env is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore DEV here. I think it's needed to make completions work when Spyder is started with bootstrap.py

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm no, I tested and this condition evaluate to True when using ./bootstrap.py

I changed because previously it only evaluates to True when DEV was True (i. e. libraries, and spyder path only will be added in dev mode)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks for the clarification!

python_path = osp.dirname(get_module_path('spyder'))
# Add the libs to the python path.
for lib in self.libs:
Expand All @@ -86,6 +90,13 @@ def run(self):
python_path = osp.pathsep.join([python_path, path])
except ImportError:
pass
if self.extra_path:
try:
python_path = osp.pathsep.join([python_path] +
self.extra_path)
except Exception as e:
debug_print("Error when adding extra_path to plugin env")
debug_print(e)
env.append("PYTHONPATH=%s" % python_path)
if self.env:
env.update(self.env)
Expand Down Expand Up @@ -129,12 +140,17 @@ def close(self):
self.closing = True
self.is_initialized = False
self.timer.stop()
self.notifier.activated.disconnect(self._on_msg_received)
self.notifier.setEnabled(False)
del self.notifier

if self.notifier is not None:
self.notifier.activated.disconnect(self._on_msg_received)
self.notifier.setEnabled(False)
self.notifier = None

self.request('server_quit')
self.process.waitForFinished(1000)
self.process.close()

if self.process is not None:
self.process.waitForFinished(1000)
self.process.close()
self.context.destroy()

def _on_finished(self):
Expand Down Expand Up @@ -192,11 +208,13 @@ def _send(self, obj):

class PluginClient(AsyncClient):

def __init__(self, plugin_name, executable=None, env=None):
def __init__(self, plugin_name, executable=None, env=None,
extra_path=None):
cwd = os.path.dirname(__file__)
super(PluginClient, self).__init__('plugin_server.py',
executable=executable, cwd=cwd, env=env,
extra_args=[plugin_name], libs=[plugin_name])
extra_args=[plugin_name], libs=[plugin_name],
extra_path=extra_path)
self.name = plugin_name


Expand Down
42 changes: 42 additions & 0 deletions spyder/utils/introspection/tests/test_plugin_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
#
"""Tests for plugin_client.py."""

# Standard library imports
import os.path as osp

# Test library imports
import pytest

# Local imports
from spyder.utils.introspection.plugin_client import PluginClient
from spyder.utils.introspection.manager import PLUGINS


@pytest.mark.parametrize("plugin_name", PLUGINS)
def test_plugin_client(qtbot, plugin_name):
"""Test creation of the diferent plugin clients."""
plugin = PluginClient(plugin_name=plugin_name)

assert plugin


@pytest.mark.parametrize("plugin_name", PLUGINS)
def test_plugin_client_extra_path(qtbot, plugin_name):
"""Test adding of extra path.

Extra path is used for adding spyder_path to plugin clients.
"""
extra_path = '/some/dummy/path'

plugin = PluginClient(plugin_name=plugin_name, extra_path=[extra_path])
plugin.run()
python_path = plugin.process.processEnvironment().value('PYTHONPATH')
assert extra_path in python_path.split(osp.pathsep)


if __name__ == "__main__":
pytest.main()