Example #1
0
    def start(self):
        self.zmq_out_socket = self.context.socket(zmq.PAIR)
        self.zmq_out_port = self.zmq_out_socket.bind_to_random_port('tcp://*')
        self.zmq_in_socket = self.context.socket(zmq.PAIR)
        self.zmq_in_socket.set_hwm(0)
        self.zmq_in_port = self.zmq_in_socket.bind_to_random_port('tcp://*')
        self.transport_args += ['--zmq-in-port', self.zmq_out_port,
                                '--zmq-out-port', self.zmq_in_port]

        self.lsp_server_log = subprocess.PIPE
        if get_debug_level() > 0:
            lsp_server_file = 'lsp_server_logfile.log'
            log_file = get_conf_path(osp.join('lsp_logs', lsp_server_file))
            if not osp.exists(osp.dirname(log_file)):
                os.makedirs(osp.dirname(log_file))
            self.lsp_server_log = open(log_file, 'w')

        if not self.external_server:
            logger.info('Starting server: {0}'.format(
                ' '.join(self.server_args)))
            creation_flags = 0
            if WINDOWS:
                creation_flags = subprocess.CREATE_NEW_PROCESS_GROUP
            self.lsp_server = subprocess.Popen(
                self.server_args,
                stdout=self.lsp_server_log,
                stderr=subprocess.STDOUT,
                creationflags=creation_flags)
            # self.transport_args += self.server_args

        self.stdout_log = subprocess.PIPE
        self.stderr_log = subprocess.PIPE
        if get_debug_level() > 0:
            stderr_log_file = 'lsp_client_{0}.log'.format(self.language)
            log_file = get_conf_path(osp.join('lsp_logs', stderr_log_file))
            if not osp.exists(osp.dirname(log_file)):
                os.makedirs(osp.dirname(log_file))
            self.stderr_log = open(log_file, 'w')

        new_env = dict(os.environ)
        python_path = os.pathsep.join(sys.path)[1:]
        new_env['PYTHONPATH'] = python_path
        self.transport_args = map(str, self.transport_args)
        self.transport_client = subprocess.Popen(self.transport_args,
                                                 stdout=self.stdout_log,
                                                 stderr=self.stderr_log,
                                                 env=new_env)

        fid = self.zmq_in_socket.getsockopt(zmq.FD)
        self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self)
        # self.notifier.activated.connect(self.debug_print)
        self.notifier.activated.connect(self.on_msg_received)
Example #2
0
def test_get_autosave_filename(editor_bot):
    """
    Test filename returned by `get_autosave_filename`.

    Test a consistent and unique name for the autosave file is returned.
    """
    editor_stack, editor = editor_bot
    autosave = editor_stack.autosave
    expected = os.path.join(get_conf_path('autosave'), 'foo.py')
    assert autosave.get_autosave_filename('foo.py') == expected
    editor_stack.new('ham/foo.py', 'utf-8', '')
    expected2 = os.path.join(get_conf_path('autosave'), 'foo-1.py')
    assert autosave.get_autosave_filename('foo.py') == expected
    assert autosave.get_autosave_filename('ham/foo.py') == expected2
Example #3
0
    def get_root_path(self, language):
        """
        Get root path to pass to the LSP servers.

        This can be the current project path or the output of
        getcwd_or_home (except for Python, see below).
        """
        path = None

        # Get path of the current project
        if self.main and self.main.projects:
            path = self.main.projects.get_active_project_path()

        # If there's no project, use the output of getcwd_or_home.
        if not path:
            # We can't use getcwd_or_home for Python because if it
            # returns home and you have a lot of Python files on it
            # then computing Rope completions takes a long time
            # and blocks the PyLS server.
            # Instead we use an empty directory inside our config one,
            # just like we did for Rope in Spyder 3.
            if language == 'python':
                path = get_conf_path('lsp_root_path')
                if not osp.exists(path):
                    os.mkdir(path)
            else:
                path = getcwd_or_home()

        return path
Example #4
0
    def get_autosave_filename(self, filename):
        """
        Get name of autosave file for specified file name.

        This function uses the dict in `self.name_mapping`. If `filename` is
        in the mapping, then return the corresponding autosave file name.
        Otherwise, construct a unique file name and update the mapping.

        Args:
            filename (str): original file name
        """
        try:
            autosave_filename = self.name_mapping[filename]
        except KeyError:
            autosave_dir = get_conf_path('autosave')
            if not osp.isdir(autosave_dir):
                try:
                    os.mkdir(autosave_dir)
                except EnvironmentError as error:
                    action = _('Error while creating autosave directory')
                    msgbox = AutosaveErrorDialog(action, error)
                    msgbox.exec_if_enabled()
            autosave_filename = self.create_unique_autosave_filename(
                    filename, autosave_dir)
            self.name_mapping[filename] = autosave_filename
            self.stack.sig_option_changed.emit(
                    'autosave_mapping', self.name_mapping)
            logger.debug('New autosave file name')
        return autosave_filename
Example #5
0
def main():
    """
    Run pytest tests.
    """
    # Remove temp conf_dir before starting the tests
    from spyder.config.base import get_conf_path
    conf_dir = get_conf_path()
    if osp.isdir(conf_dir):
        shutil.rmtree(conf_dir)

    pytest_args = ['spyder',
                   'spyder_profiler',
                   '-x',
                   '-vv',
                   '-rw',
                   '--durations=10',
                   '--cov=spyder',
                   '--cov=spyder_profiler',
                   '--cov-report=term-missing']

    if run_slow:
        pytest_args.append('--run-slow')

    errno = pytest.main(pytest_args)

    # sys.exit doesn't work here because some things could be running
    # in the background (e.g. closing the main window) when this point
    # is reached. And if that's the case, sys.exit does't stop the
    # script (as you would expected).
    if errno != 0:
        raise SystemExit(errno)
Example #6
0
 def try_recover_from_autosave(self):
     """Offer to recover files from autosave."""
     autosave_dir = get_conf_path('autosave')
     autosave_mapping = CONF.get('editor', 'autosave_mapping', {})
     dialog = RecoveryDialog(autosave_dir, autosave_mapping,
                             parent=self.editor)
     dialog.exec_if_nonempty()
     self.recover_files_to_open = dialog.files_to_open[:]
Example #7
0
def test_autosave_updates_name_mapping(editor_bot, mocker, qtbot):
    """Test that autosave() updates name_mapping."""
    editor_stack, editor = editor_bot
    assert editor_stack.autosave.name_mapping == {}
    mocker.patch.object(editor_stack, '_write_to_file')
    with qtbot.wait_signal(editor_stack.sig_option_changed) as blocker:
        editor_stack.autosave.autosave(0)
    expected = {'foo.py': os.path.join(get_conf_path('autosave'), 'foo.py')}
    assert editor_stack.autosave.name_mapping == expected
    assert blocker.args == ['autosave_mapping', expected]
Example #8
0
    def __init__(self, plugin, name, history_filename, config_options,
                 additional_options, interpreter_versions,
                 connection_file=None, hostname=None,
                 menu_actions=None, slave=False):
        super(ClientWidget, self).__init__(plugin)
        SaveHistoryMixin.__init__(self)

        # --- Init attrs
        self.name = name
        self.history_filename = get_conf_path(history_filename)
        self.connection_file = connection_file
        self.hostname = hostname
        self.menu_actions = menu_actions
        self.slave = slave

        # --- Other attrs
        self.options_button = None
        self.stop_button = None
        self.stop_icon = ima.icon('stop')
        self.history = []

        # --- Widgets
        self.shellwidget = ShellWidget(config=config_options,
                                       additional_options=additional_options,
                                       interpreter_versions=interpreter_versions,
                                       local_kernel=True)
        self.shellwidget.hide()
        self.infowidget = WebView(self)
        self.set_infowidget_font()
        self.loading_page = self._create_loading_page()
        self.infowidget.setHtml(self.loading_page,
                                QUrl.fromLocalFile(CSS_PATH))

        # --- Layout
        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        hlayout = QHBoxLayout()
        for button in toolbar_buttons:
            hlayout.addWidget(button)
        vlayout.addLayout(hlayout)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.shellwidget)
        vlayout.addWidget(self.infowidget)
        self.setLayout(vlayout)

        # --- Exit function
        self.exit_callback = lambda: plugin.close_client(client=self)

        # --- Signals
        # As soon as some content is printed in the console, stop
        # our loading animation
        document = self.get_control().document()
        document.contentsChange.connect(self._stop_loading_animation)
Example #9
0
    def __init__(
        self,
        parent=None,
        namespace=None,
        commands=[],
        message=None,
        max_line_count=300,
        font=None,
        exitfunc=None,
        profile=False,
        multithreaded=True,
        light_background=True,
    ):
        PythonShellWidget.__init__(self, parent, get_conf_path("history_internal.py"), profile)

        self.set_light_background(light_background)
        self.multithreaded = multithreaded
        self.setMaximumBlockCount(max_line_count)

        if font is not None:
            self.set_font(font)

        # Allow raw_input support:
        self.input_loop = None
        self.input_mode = False

        # KeyboardInterrupt support
        self.interrupted = False  # used only for not-multithreaded mode
        self.sig_keyboard_interrupt.connect(self.keyboard_interrupt)

        # Code completion / calltips
        getcfg = lambda option: CONF.get("internal_console", option)
        case_sensitive = getcfg("codecompletion/case_sensitive")
        self.set_codecompletion_case(case_sensitive)

        # keyboard events management
        self.eventqueue = []

        # Init interpreter
        self.exitfunc = exitfunc
        self.commands = commands
        self.message = message
        self.interpreter = None
        self.start_interpreter(namespace)

        # Clear status bar
        self.status.emit("")

        # Embedded shell -- requires the monitor (which installs the
        # 'open_in_spyder' function in builtins)
        if hasattr(builtins, "open_in_spyder"):
            self.go_to_error.connect(self.open_with_external_spyder)
Example #10
0
def get_spyderplugins_mods(io=False):
    """Import modules from plugins package and return the list"""
    # Create user directory
    user_plugin_path = osp.join(get_conf_path(), USER_PLUGIN_DIR)
    if not osp.isdir(user_plugin_path):
        os.makedirs(user_plugin_path)

    modlist, modnames = [], []

    # The user plugins directory is given the priority when looking for modules
    for plugin_path in [user_plugin_path] + sys.path:
        _get_spyderplugins(plugin_path, io, modnames, modlist)
    return modlist
Example #11
0
    def __init__(self, parent, history_filename, profile=False,
                 initial_message=None, default_foreground_color=None,
                 error_foreground_color=None, traceback_foreground_color=None,
                 prompt_foreground_color=None, background_color=None):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self, history_filename)
        BrowseHistoryMixin.__init__(self)
                
        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True
        
        # History
        assert is_text_string(history_filename)
        self.history = self.load_history()
        
        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))
        
        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile
        
        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        if initial_message:
            self.__buffer.append(initial_message)

        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()

        # Cursor width
        self.setCursorWidth(CONF.get('main', 'cursor/width'))

        # Adjustments to completion_widget to use it here
        self.completion_widget.currentRowChanged.disconnect()
Example #12
0
 def load_plugin(self):
     """Load the Rope introspection plugin"""
     if not programs.is_module_installed('rope', ROPE_REQVER):
         raise ImportError('Requires Rope %s' % ROPE_REQVER)
     self.project = None
     self.create_rope_project(root_path=get_conf_path())
     submods = get_preferred_submodules()
     actual = []
     for submod in submods:
         try:
             imp.find_module(submod)
             actual.append(submod)
         except ImportError:
             pass
     if self.project is not None:
         self.project.prefs.set('extension_modules', actual)
Example #13
0
 def _filename_global(self):
     """Create a .ini filename located in user home directory.
     This .ini files stores the global spyder preferences.
     """
     if self.subfolder is None:
         config_file = osp.join(get_home_dir(), '.%s.ini' % self.name)
         return config_file
     else:
         folder = get_conf_path()
         # Save defaults in a "defaults" dir of .spyder2 to not pollute it
         if 'defaults' in self.name:
             folder = osp.join(folder, 'defaults')
             if not osp.isdir(folder):
                 os.mkdir(folder)
         config_file = osp.join(folder, '%s.ini' % self.name)
         return config_file
Example #14
0
    def __init__(self,
                 parent,
                 history_filename,
                 profile=False,
                 initial_message=None):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self)
        BrowseHistoryMixin.__init__(self)

        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True

        # History
        assert is_text_string(history_filename)
        self.history_filename = history_filename
        self.history = self.load_history()

        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))

        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile

        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        if initial_message:
            self.__buffer.append(initial_message)

        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()

        # Cursor width
        self.setCursorWidth(CONF.get('main', 'cursor/width'))
Example #15
0
    def start_server(self):
        """Start server."""
        # This is not necessary if we're trying to connect to an
        # external server
        if self.external_server or self.stdio:
            return

        logger.info('Starting server: {0}'.format(' '.join(self.server_args)))

        # Create server process
        self.server = QProcess(self)
        env = self.server.processEnvironment()

        if DEV:
            # Use local pyls instead of site-packages one
            env.insert('PYTHONPATH', os.pathsep.join(sys.path)[:])

        # Adjustments for the Python language server.
        if self.language == 'python':
            # Set the PyLS current working to an empty dir inside
            # our config one. This avoids the server to pick up user
            # files such as random.py or string.py instead of the
            # standard library modules named the same.
            cwd = osp.join(get_conf_path(), 'lsp_paths', 'cwd')
            if not osp.exists(cwd):
                os.makedirs(cwd)
        else:
            # There's no need to define a cwd for other servers.
            cwd = None

            # Most LSP servers spawn other processes, which may require
            # some environment variables.
            for var in os.environ:
                env.insert(var, os.environ[var])
            logger.info('Server process env variables: {0}'.format(env.keys()))

        # Setup server
        self.server.setProcessEnvironment(env)
        self.server.errorOccurred.connect(self.handle_process_errors)
        self.server.setWorkingDirectory(cwd)
        self.server.setProcessChannelMode(QProcess.MergedChannels)
        if self.server_log_file is not None:
            self.server.setStandardOutputFile(self.server_log_file)

        # Start server
        self.server.start(self.server_args[0], self.server_args[1:])
Example #16
0
    def __init__(self, parent, history_filename, profile=False):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self)
                
        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True
        
        # History
        self.histidx = None
        self.hist_wholeline = False
        assert is_text_string(history_filename)
        self.history_filename = history_filename
        self.history = self.load_history()
        
        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))
        
        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile
        
        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        self.__buffer.append("NOTE: The Python console is going to "
                             "be REMOVED in Spyder 3.2. Please start "
                             "to migrate your work to the "
                             "IPython console instead.\n\n")
        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()

        # Cursor width
        self.setCursorWidth( CONF.get('main', 'cursor/width') )
Example #17
0
    def run_tests(self, config=None):
        """
        Run unit tests.

        First, run `self.pre_test_hook` if it is set, and abort if its return
        value is `False`.

        Then, run the unit tests.

        The process's output is consumed by `read_output()`.
        When the process finishes, the `finish` signal is emitted.

        Parameters
        ----------
        config : Config or None
            configuration for unit tests. If None, use `self.config`.
            In either case, configuration should be valid.
        """
        if self.pre_test_hook:
            if self.pre_test_hook() is False:
                return

        if config is None:
            config = self.config
        pythonpath = self.pythonpath
        self.testdatamodel.testresults = []
        self.testdetails = []
        tempfilename = get_conf_path('unittest.results')
        self.testrunner = self.framework_registry.create_runner(
            config.framework, self, tempfilename)
        self.testrunner.sig_finished.connect(self.process_finished)
        self.testrunner.sig_collected.connect(self.tests_collected)
        self.testrunner.sig_collecterror.connect(self.tests_collect_error)
        self.testrunner.sig_starttest.connect(self.tests_started)
        self.testrunner.sig_testresult.connect(self.tests_yield_result)
        self.testrunner.sig_stop.connect(self.tests_stopped)

        try:
            self.testrunner.start(config, pythonpath)
        except RuntimeError:
            QMessageBox.critical(self,
                                 _("Error"), _("Process failed to start"))
        else:
            self.set_running_state(True)
            self.set_status_label(_('Running tests ...'))
Example #18
0
    def get_defaults_path_name_from_version(self, old_version=None):
        """
        Override method.

        Get defaults location based on version.
        """
        if old_version:
            if check_version(old_version, '3.0.0', '<='):
                name = '{}-{}'.format('defaults', old_version)
                path = self._module_source_path
            elif check_version(old_version, '52.0.0', '<'):
                name = '{}-{}'.format(self._defaults_name_prefix, old_version)
                path = osp.join(get_conf_path(), 'defaults')
        else:
            super_class = super(SpyderUserConfig, self)
            path, name = super_class.get_defaults_path_name_from_version()

        return path, name
Example #19
0
    def get_previous_config_fpath(self):
        """
        Override method.

        Return the last configuration file used if found.
        """
        fpath = self.get_config_fpath()
        previous_paths = [
            # >= 52.0.0
            fpath,
            # < 52.0.0
            os.path.join(get_conf_path(), 'spyder.ini'),
        ]
        for fpath in previous_paths:
            if osp.isfile(fpath):
                break

        return fpath
Example #20
0
    def __init__(self, parent=None, namespace=None, commands=[], message=None,
                 max_line_count=300, font=None, exitfunc=None, profile=False,
                 multithreaded=True, light_background=True):
        PythonShellWidget.__init__(self, parent,
                                   get_conf_path('history_internal.py'),
                                   profile)
        
        self.set_light_background(light_background)
        self.multithreaded = multithreaded
        self.setMaximumBlockCount(max_line_count)

        if font is not None:
            self.set_font(font)

        # Allow raw_input support:
        self.input_loop = None
        self.input_mode = False
        
        # KeyboardInterrupt support
        self.interrupted = False # used only for not-multithreaded mode
        self.sig_keyboard_interrupt.connect(self.keyboard_interrupt)
        
        # Code completion / calltips
        getcfg = lambda option: CONF.get('internal_console', option)
        case_sensitive = getcfg('codecompletion/case_sensitive')
        self.set_codecompletion_case(case_sensitive)
        
        # keyboard events management
        self.eventqueue = []

        # Init interpreter
        self.exitfunc = exitfunc
        self.commands = commands
        self.message = message
        self.interpreter = None
        self.start_interpreter(namespace)
        
        # Clear status bar
        self.status.emit('')
        
        # Embedded shell -- requires the monitor (which installs the
        # 'open_in_spyder' function in builtins)
        if hasattr(builtins, 'open_in_spyder'):
            self.go_to_error.connect(self.open_with_external_spyder)
Example #21
0
    def save_autosave_mapping(self):
        """
        Writes current autosave mapping to a pidNNN.txt file.

        This function should be called after updating `self.autosave_mapping`.
        The NNN in the file name is the pid of the Spyder process. If the
        current autosave mapping is empty, then delete the file if it exists.
        """
        autosave_dir = get_conf_path('autosave')
        my_pid = os.getpid()
        pidfile_name = osp.join(autosave_dir, 'pid{}.txt'.format(my_pid))
        if self.name_mapping:
            with open(pidfile_name, 'w') as pidfile:
                pidfile.write(pprint.pformat(self.name_mapping))
        else:
            try:
                os.remove(pidfile_name)
            except (IOError, OSError):
                pass
Example #22
0
    def __init__(self, plugin, name, history_filename, connection_file=None, 
                 hostname=None, sshkey=None, password=None, 
                 kernel_widget_id=None, menu_actions=None):
        super(IPythonClient, self).__init__(plugin)
        SaveHistoryMixin.__init__(self)
        self.options_button = None
        
        # stop button and icon
        self.stop_button = None
        self.stop_icon = ima.icon('stop')        
        self.connection_file = connection_file
        self.kernel_widget_id = kernel_widget_id
        self.hostname = hostname
        self.sshkey = sshkey
        self.password = password
        self.name = name
        self.get_option = plugin.get_option
        self.shellwidget = IPythonShellWidget(config=self.shellwidget_config(),
                                              local_kernel=False)
        self.shellwidget.hide()
        self.infowidget = WebView(self)
        self.menu_actions = menu_actions
        self.history_filename = get_conf_path(history_filename)
        self.history = []
        self.namespacebrowser = None

        self.set_infowidget_font()
        self.loading_page = self._create_loading_page()
        self.infowidget.setHtml(self.loading_page,
                                QUrl.fromLocalFile(CSS_PATH))

        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        hlayout = QHBoxLayout()
        for button in toolbar_buttons:
            hlayout.addWidget(button)
        vlayout.addLayout(hlayout)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.shellwidget)
        vlayout.addWidget(self.infowidget)
        self.setLayout(vlayout)
        
        self.exit_callback = lambda: plugin.close_client(client=self)
Example #23
0
    def start_transport(self):
        """Start transport layer."""
        self.transport_args = list(map(str, self.transport_args))
        logger.info('Starting transport: {0}'.format(' '.join(
            self.transport_args)))

        self.transport = QProcess(self)
        self.transport.errorOccurred.connect(self.handle_process_errors)

        # Modifying PYTHONPATH to run transport in development mode or
        # tests
        if DEV or running_under_pytest():
            env = QProcessEnvironment()
            if running_under_pytest():
                env.insert('PYTHONPATH', os.pathsep.join(sys.path)[:])
            else:
                env.insert('PYTHONPATH', os.pathsep.join(sys.path)[1:])
            self.transport.setProcessEnvironment(env)

        # Set transport log file
        transport_log_file = None
        if get_debug_level() > 0:
            transport_log_fname = 'transport_{0}_{1}.log'.format(
                self.language, os.getpid())
            transport_log_file = get_conf_path(
                osp.join('lsp_logs', transport_log_fname))
            if not osp.exists(osp.dirname(transport_log_file)):
                os.makedirs(osp.dirname(transport_log_file))

        # Set channel properties
        if self.stdio:
            self.transport_args += self.server_args
            self.transport.setProcessChannelMode(QProcess.SeparateChannels)
            if transport_log_file is not None:
                self.transport.setStandardErrorFile(transport_log_file)
        else:
            self.transport.setProcessChannelMode(QProcess.MergedChannels)
            if transport_log_file is not None:
                self.transport.setStandardOutputFile(transport_log_file)

        # Start transport
        self.transport.start(self.transport_args[0], self.transport_args[1:])
Example #24
0
    def __init__(self,
                 parent=None,
                 namespace=None,
                 commands=[],
                 message=None,
                 max_line_count=300,
                 exitfunc=None,
                 profile=False,
                 multithreaded=True):
        super().__init__(parent,
                         get_conf_path('history_internal.py'),
                         profile=profile)

        self.multithreaded = multithreaded
        self.setMaximumBlockCount(max_line_count)

        # Allow raw_input support:
        self.input_loop = None
        self.input_mode = False

        # KeyboardInterrupt support
        self.interrupted = False  # used only for not-multithreaded mode
        self.sig_keyboard_interrupt.connect(self.keyboard_interrupt)

        # Code completion / calltips
        # keyboard events management
        self.eventqueue = []

        # Init interpreter
        self.exitfunc = exitfunc
        self.commands = commands
        self.message = message
        self.interpreter = None

        # Clear status bar
        self.sig_show_status_requested.emit('')

        # Embedded shell -- requires the monitor (which installs the
        # 'open_in_spyder' function in builtins)
        if hasattr(builtins, 'open_in_spyder'):
            self.sig_go_to_error_requested.connect(
                self.open_with_external_spyder)
Example #25
0
    def get_backup_fpath_from_version(self, version=None, old_version=None):
        """
        Override method.

        Make a backup of the configuration file.
        """
        if old_version and check_version(old_version, '51.0.0', '<'):
            name = 'spyder.ini'
            fpath = os.path.join(get_conf_path(), name)
            if version is None:
                backup_fpath = "{}{}".format(fpath, self._backup_suffix)
            else:
                backup_fpath = "{}-{}{}".format(fpath, version,
                                                self._backup_suffix)
        else:
            super_class = super(SpyderUserConfig, self)
            backup_fpath = super_class.get_backup_fpath_from_version(
                version, old_version)

        return backup_fpath
Example #26
0
    def __init__(self, parent, history_filename, profile=False):
        """
        parent : specifies the parent widget
        """
        ConsoleBaseWidget.__init__(self, parent)
        SaveHistoryMixin.__init__(self)
                
        # Prompt position: tuple (line, index)
        self.current_prompt_pos = None
        self.new_input_line = True
        
        # History
        self.histidx = None
        self.hist_wholeline = False
        assert is_text_string(history_filename)
        self.history_filename = history_filename
        self.history = self.load_history()
        
        # Session
        self.historylog_filename = CONF.get('main', 'historylog_filename',
                                            get_conf_path('history.log'))
        
        # Context menu
        self.menu = None
        self.setup_context_menu()

        # Simple profiling test
        self.profile = profile
        
        # Buffer to increase performance of write/flush operations
        self.__buffer = []
        self.__timestamp = 0.0
        self.__flushtimer = QTimer(self)
        self.__flushtimer.setSingleShot(True)
        self.__flushtimer.timeout.connect(self.flush)

        # Give focus to widget
        self.setFocus()

        # Cursor width
        self.setCursorWidth( CONF.get('main', 'cursor/width') )
Example #27
0
    def get_root_path(self, language):
        """
        Get root path to pass to the LSP servers.

        This can be the current project path or the output of
        getcwd_or_home (except for Python, see below).
        """
        path = self.current_project_path

        if not path:
            # We can't use getcwd_or_home for LSP servers because if it
            # returns home and you have a lot of files on it
            # then computing completions takes a long time
            # and blocks the server.
            # Instead we use an empty directory inside our config one,
            # just like we did for Rope in Spyder 3.
            path = osp.join(get_conf_path(), 'lsp_paths', 'root_path')
            if not osp.exists(path):
                os.makedirs(path)

        return path
Example #28
0
def test_remove_autosave_file(editor_bot, mocker, qtbot):
    """
    Test that remove_autosave_file() removes the autosave file.

    Also, test that it updates `name_mapping`.
    """
    editor_stack, editor = editor_bot
    autosave = editor_stack.autosave
    editor.set_text('spam\n')
    with qtbot.wait_signal(editor_stack.sig_option_changed) as blocker:
        autosave.maybe_autosave(0)
    autosave_filename = os.path.join(get_conf_path('autosave'), 'foo.py')
    assert os.access(autosave_filename, os.R_OK)
    expected = {'foo.py': autosave_filename}
    assert autosave.name_mapping == expected
    assert blocker.args == ['autosave_mapping', expected]
    with qtbot.wait_signal(editor_stack.sig_option_changed) as blocker:
        autosave.remove_autosave_file(editor_stack.data[0].filename)
    assert not os.access(autosave_filename, os.R_OK)
    assert autosave.name_mapping == {}
    assert blocker.args == ['autosave_mapping', {}]
Example #29
0
def test_remove_autosave_file(editor_bot, mocker, qtbot):
    """
    Test that remove_autosave_file() removes the autosave file.

    Also, test that it updates `name_mapping`.
    """
    editor_stack, editor = editor_bot
    autosave = editor_stack.autosave
    editor.set_text('spam\n')

    autosave.maybe_autosave(0)

    autosave_filename = os.path.join(get_conf_path('autosave'), 'foo.py')
    assert os.access(autosave_filename, os.R_OK)
    expected = {'foo.py': autosave_filename}
    assert autosave.name_mapping == expected

    autosave.remove_autosave_file(editor_stack.data[0].filename)

    assert not os.access(autosave_filename, os.R_OK)
    assert autosave.name_mapping == {}
Example #30
0
def load_session(filename):
    """Load Spyder session"""
    filename = osp.abspath(filename)
    old_cwd = getcwd()
    os.chdir(osp.dirname(filename))
    error_message = None
    renamed = False
    try:
        tar = tarfile.open(filename, "r")
        extracted_files = tar.getnames()

        # Rename original config files
        for fname in extracted_files:
            orig_name = get_conf_path(fname)
            bak_name = get_conf_path(fname + '.bak')
            if osp.isfile(bak_name):
                os.remove(bak_name)
            if osp.isfile(orig_name):
                os.rename(orig_name, bak_name)
        renamed = True

        tar.extractall()

        for fname in extracted_files:
            shutil.move(fname, get_conf_path(fname))

    except Exception as error:
        error_message = to_text_string(error)
        if renamed:
            # Restore original config files
            for fname in extracted_files:
                orig_name = get_conf_path(fname)
                bak_name = get_conf_path(fname + '.bak')
                if osp.isfile(orig_name):
                    os.remove(orig_name)
                if osp.isfile(bak_name):
                    os.rename(bak_name, orig_name)

    finally:
        # Removing backup config files
        for fname in extracted_files:
            bak_name = get_conf_path(fname + '.bak')
            if osp.isfile(bak_name):
                os.remove(bak_name)

    os.chdir(old_cwd)
    return error_message
Example #31
0
    def _get_log_filename(self, kind):
        """
        Get filename to redirect server or transport logs to in
        debugging mode.

        Parameters
        ----------
        kind: str
            It can be "server" or "transport".
        """
        if get_debug_level() == 0:
            return None

        fname = '{0}_{1}_{2}.log'.format(kind, self.language, os.getpid())
        location = get_conf_path(osp.join('lsp_logs', fname))

        # Create directory that contains the file, in case it doesn't
        # exist
        if not osp.exists(osp.dirname(location)):
            os.makedirs(osp.dirname(location))

        return location
Example #32
0
def get_preferred_submodules():
    """
    Get all submodules of the main scientific modules and others of our
    interest
    """
    # Path to the modules database
    modules_path = get_conf_path('db')

    # Modules database
    modules_db = PickleShareDB(modules_path)

    if 'submodules' in modules_db:
        return modules_db['submodules']

    submodules = []

    for m in PREFERRED_MODULES:
        submods = get_submodules(m)
        submodules += submods
    
    modules_db['submodules'] = submodules
    return submodules
Example #33
0
def get_preferred_submodules():
    """
    Get all submodules of the main scientific modules and others of our
    interest
    """
    # Path to the modules database
    modules_path = get_conf_path('db')

    # Modules database
    modules_db = PickleShareDB(modules_path)

    if 'submodules' in modules_db:
        return modules_db['submodules']

    submodules = []

    for m in PREFERRED_MODULES:
        submods = get_submodules(m)
        submodules += submods

    modules_db['submodules'] = submodules
    return submodules
Example #34
0
def test_remove_autosave_file(editor_bot, mocker, qtbot):
    """
    Test that remove_autosave_file() removes the autosave file

    Also, test that it updates `name_mapping`.
    """
    editor_stack, editor = editor_bot
    autosave = editor_stack.autosave
    with qtbot.wait_signal(editor_stack.sig_option_changed) as blocker:
        autosave.autosave(0)
    autosave_filename = os.path.join(get_conf_path('autosave'), 'foo.py')
    assert os.access(autosave_filename, os.R_OK)
    expected = {'foo.py': autosave_filename}
    assert autosave.name_mapping == expected
    assert blocker.args == ['autosave_mapping', expected]
    with qtbot.wait_signal(editor_stack.sig_option_changed) as blocker:
        autosave.remove_autosave_file(editor_stack.data[0])
    assert not os.access(autosave_filename, os.R_OK)
    assert autosave.name_mapping == {}
    assert blocker.args == ['autosave_mapping', {}]
    with qtbot.assert_not_emitted(editor_stack.sig_option_changed):
        autosave.autosave(0)
    assert not os.access(autosave_filename, os.R_OK)
    assert autosave.name_mapping == {}
Example #35
0
    def create_debug_log_actions(self):
        """Create an action for each lsp and debug log file."""
        self.menu_debug_logs.clear_actions()

        files = [os.environ['SPYDER_DEBUG_FILE']]
        files += glob.glob(os.path.join(get_conf_path('lsp_logs'), '*.log'))

        debug_logs_actions = []
        for file in files:
            action = self.create_action(
                file,
                os.path.basename(file),
                tip=file,
                triggered=lambda _, file=file: self.load_log_file(file),
                overwrite=True,
                register_action=False
            )
            debug_logs_actions.append(action)

        # Add Spyder log on its own section
        self.add_item_to_menu(
            debug_logs_actions[0],
            self.menu_debug_logs,
            section=LogsMenuSections.SpyderLogSection
        )

        # Add LSP logs
        for action in debug_logs_actions[1:]:
            self.add_item_to_menu(
                action,
                self.menu_debug_logs,
                section=LogsMenuSections.LSPLogsSection
            )

        # Render menu
        self.menu_debug_logs._render()
Example #36
0
def test_autosave(editor_bot):
    """Test that autosave() saves text to correct file."""
    editor_stack, editor = editor_bot
    editor_stack.autosave.autosave(0)
    contents = open(os.path.join(get_conf_path('autosave'), 'foo.py')).read()
    assert contents.startswith('a = 1')
Example #37
0
import shutil

import pytest

# Local imports
from spyder.tests.fixtures.file_fixtures import create_folders_files
from spyder.tests.fixtures.bookmark_fixtures import (code_editor_bot,
                                                     setup_editor)


# To activate/deactivate certain things for pytest's only
os.environ['SPYDER_PYTEST'] = 'True'

# Remove temp conf_dir before starting the tests
from spyder.config.base import get_conf_path
conf_dir = get_conf_path()
if osp.isdir(conf_dir):
    shutil.rmtree(conf_dir)


def pytest_addoption(parser):
    """Add option to run slow tests."""
    parser.addoption("--run-slow", action="store_true",
                     default=False, help="Run slow tests")


def pytest_collection_modifyitems(config, items):
    """Skip tests with the slow marker"""
    if config.getoption("--run-slow"):
        # --run-slow given in cli: do not skip slow tests
        return
Example #38
0
def test_add_history(historylog):
    """
    Test the add_history method.

    Test adding a history file to the history log widget and the
    code editor properties that are enabled/disabled.
    """
    hl = historylog
    hw = historylog.get_widget()

    # No editors yet.
    assert len(hw.editors) == 1

    # Add one file.
    name1 = 'test_history.py'
    text1 = 'a = 5\nb= 10\na + b\n'
    path1 = create_file(name1, text1)
    tab1 = os.path.basename(path1)
    hw.set_conf('line_numbers', False)
    hw.set_conf('wrap', False)
    hl.add_history(path1)

    # Check tab and editor were created correctly.
    assert len(hw.editors) == 2
    assert hw.filenames == [get_conf_path('history.py'), path1]
    assert hw.tabwidget.currentIndex() == 1
    assert not hw.editors[0].linenumberarea.isVisible()
    assert hw.editors[0].wordWrapMode() == QTextOption.NoWrap
    assert hw.tabwidget.tabText(1) == tab1
    assert hw.tabwidget.tabToolTip(1) == path1

    # Try to add same file -- does not process filename again, so
    # linenumbers and wrap doesn't change.
    hw.add_history(path1)
    assert hw.tabwidget.currentIndex() == 1
    # assert not hw.editors[0].linenumberarea.isVisible()

    # Add another file.
    name2 = 'history2.js'
    text2 = 'random text\nspam line\n\n\n\n'
    path2 = create_file(name2, text2)
    tab2 = os.path.basename(path2)
    hw.set_conf('line_numbers', True)
    hw.set_conf('wrap', True)
    hw.add_history(path2)

    # Check second tab and editor were created correctly.
    assert len(hw.editors) == 3
    assert hw.filenames == [get_conf_path('history.py'), path1, path2]
    assert hw.tabwidget.currentIndex() == 2
    assert hw.editors[2].wordWrapMode() == (
        QTextOption.WrapAtWordBoundaryOrAnywhere)
    assert hw.editors[2].linenumberarea.isVisible()
    assert hw.tabwidget.tabText(2) == tab2
    assert hw.tabwidget.tabToolTip(2) == path2

    # Check differences between tabs based on setup.
    assert hw.editors[1].supported_language
    assert hw.editors[1].isReadOnly()
    assert not hw.editors[1].isVisible()
    assert hw.editors[1].toPlainText() == text1

    assert not hw.editors[2].supported_language
    assert hw.editors[2].isReadOnly()
    assert hw.editors[2].isVisible()
    assert hw.editors[2].toPlainText() == text2
Example #39
0
class ProfilerWidget(QWidget):
    """
    Profiler widget
    """
    DATAPATH = get_conf_path('profiler.results')
    VERSION = '0.0.1'
    redirect_stdio = Signal(bool)

    def __init__(self, parent, max_entries=100):
        QWidget.__init__(self, parent)

        self.setWindowTitle("Profiler")

        self.output = None
        self.error_output = None

        self._last_wdir = None
        self._last_args = None
        self._last_pythonpath = None

        self.filecombo = PythonModulesComboBox(self)

        self.start_button = create_toolbutton(self,
                                              icon=ima.icon('run'),
                                              text=_("Profile"),
                                              tip=_("Run profiler"),
                                              triggered=lambda: self.start(),
                                              text_beside_icon=True)
        self.stop_button = create_toolbutton(self,
                                             icon=ima.icon('stop'),
                                             text=_("Stop"),
                                             tip=_("Stop current profiling"),
                                             text_beside_icon=True)
        self.filecombo.valid.connect(self.start_button.setEnabled)
        #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data)
        # FIXME: The combobox emits this signal on almost any event
        #        triggering show_data() too early, too often.

        browse_button = create_toolbutton(self,
                                          icon=ima.icon('fileopen'),
                                          tip=_('Select Python script'),
                                          triggered=self.select_file)

        self.datelabel = QLabel()

        self.log_button = create_toolbutton(self,
                                            icon=ima.icon('log'),
                                            text=_("Output"),
                                            text_beside_icon=True,
                                            tip=_("Show program's output"),
                                            triggered=self.show_log)

        self.datatree = ProfilerDataTree(self)

        self.collapse_button = create_toolbutton(
            self,
            icon=ima.icon('collapse'),
            triggered=lambda dD: self.datatree.change_view(-1),
            tip=_('Collapse one level up'))
        self.expand_button = create_toolbutton(
            self,
            icon=ima.icon('expand'),
            triggered=lambda dD: self.datatree.change_view(1),
            tip=_('Expand one level down'))

        self.save_button = create_toolbutton(self,
                                             text_beside_icon=True,
                                             text=_("Save data"),
                                             icon=ima.icon('filesave'),
                                             triggered=self.save_data,
                                             tip=_('Save profiling data'))
        self.load_button = create_toolbutton(
            self,
            text_beside_icon=True,
            text=_("Load data"),
            icon=ima.icon('fileimport'),
            triggered=self.compare,
            tip=_('Load profiling data for comparison'))
        self.clear_button = create_toolbutton(self,
                                              text_beside_icon=True,
                                              text=_("Clear comparison"),
                                              icon=ima.icon('editdelete'),
                                              triggered=self.clear)

        hlayout1 = QHBoxLayout()
        hlayout1.addWidget(self.filecombo)
        hlayout1.addWidget(browse_button)
        hlayout1.addWidget(self.start_button)
        hlayout1.addWidget(self.stop_button)

        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(self.collapse_button)
        hlayout2.addWidget(self.expand_button)
        hlayout2.addStretch()
        hlayout2.addWidget(self.datelabel)
        hlayout2.addStretch()
        hlayout2.addWidget(self.log_button)
        hlayout2.addWidget(self.save_button)
        hlayout2.addWidget(self.load_button)
        hlayout2.addWidget(self.clear_button)

        layout = QVBoxLayout()
        layout.addLayout(hlayout1)
        layout.addLayout(hlayout2)
        layout.addWidget(self.datatree)
        self.setLayout(layout)

        self.process = None
        self.set_running_state(False)
        self.start_button.setEnabled(False)
        self.clear_button.setEnabled(False)

        if not is_profiler_installed():
            # This should happen only on certain GNU/Linux distributions
            # or when this a home-made Python build because the Python
            # profilers are included in the Python standard library
            for widget in (self.datatree, self.filecombo, self.start_button,
                           self.stop_button):
                widget.setDisabled(True)
            url = 'http://docs.python.org/library/profile.html'
            text = '%s <a href=%s>%s</a>' % (_('Please install'), url,
                                             _("the Python profiler modules"))
            self.datelabel.setText(text)
        else:
            pass  # self.show_data()

    def save_data(self):
        """Save data"""
        title = _("Save profiler result")
        filename, _selfilter = getsavefilename(
            self, title, getcwd_or_home(),
            _("Profiler result") + " (*.Result)")
        if filename:
            self.datatree.save_data(filename)

    def compare(self):
        filename, _selfilter = getopenfilename(
            self, _("Select script to compare"), getcwd_or_home(),
            _("Profiler result") + " (*.Result)")
        if filename:
            self.datatree.compare(filename)
            self.show_data()
            self.clear_button.setEnabled(True)

    def clear(self):
        self.datatree.compare(None)
        self.datatree.hide_diff_cols(True)
        self.show_data()
        self.clear_button.setEnabled(False)

    def analyze(self, filename, wdir=None, args=None, pythonpath=None):
        if not is_profiler_installed():
            return
        self.kill_if_running()
        #index, _data = self.get_data(filename)
        index = None  # FIXME: storing data is not implemented yet
        if index is None:
            self.filecombo.addItem(filename)
            self.filecombo.setCurrentIndex(self.filecombo.count() - 1)
        else:
            self.filecombo.setCurrentIndex(self.filecombo.findText(filename))
        self.filecombo.selected()
        if self.filecombo.is_valid():
            if wdir is None:
                wdir = osp.dirname(filename)
            self.start(wdir, args, pythonpath)

    def select_file(self):
        self.redirect_stdio.emit(False)
        filename, _selfilter = getopenfilename(
            self, _("Select Python script"), getcwd_or_home(),
            _("Python scripts") + " (*.py ; *.pyw)")
        self.redirect_stdio.emit(True)
        if filename:
            self.analyze(filename)

    def show_log(self):
        if self.output:
            TextEditor(self.output,
                       title=_("Profiler output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    def show_errorlog(self):
        if self.error_output:
            TextEditor(self.error_output,
                       title=_("Profiler output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    def start(self, wdir=None, args=None, pythonpath=None):
        filename = to_text_string(self.filecombo.currentText())
        if wdir is None:
            wdir = self._last_wdir
            if wdir is None:
                wdir = osp.basename(filename)
        if args is None:
            args = self._last_args
            if args is None:
                args = []
        if pythonpath is None:
            pythonpath = self._last_pythonpath
        self._last_wdir = wdir
        self._last_args = args
        self._last_pythonpath = pythonpath

        self.datelabel.setText(_('Profiling, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(wdir)
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(
            lambda: self.read_output(error=True))
        self.process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self.finished(ec, es))
        self.stop_button.clicked.connect(self.process.kill)

        if pythonpath is not None:
            env = [
                to_text_string(_pth)
                for _pth in self.process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, separator, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)
            self.process.setProcessEnvironment(processEnvironment)

        self.output = ''
        self.error_output = ''

        p_args = ['-m', 'cProfile', '-o', self.DATAPATH]
        if os.name == 'nt':
            # On Windows, one has to replace backslashes by slashes to avoid
            # confusion with escape characters (otherwise, for example, '\t'
            # will be interpreted as a tabulation):
            p_args.append(osp.normpath(filename).replace(os.sep, '/'))
        else:
            p_args.append(filename)
        if args:
            p_args.extend(shell_split(args))
        executable = sys.executable
        if executable.endswith("spyder.exe"):
            # py2exe distribution
            executable = "python.exe"
        self.process.start(executable, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))

    def set_running_state(self, state=True):
        self.start_button.setEnabled(not state)
        self.stop_button.setEnabled(state)

    def read_output(self, error=False):
        if error:
            self.process.setReadChannel(QProcess.StandardError)
        else:
            self.process.setReadChannel(QProcess.StandardOutput)
        qba = QByteArray()
        while self.process.bytesAvailable():
            if error:
                qba += self.process.readAllStandardError()
            else:
                qba += self.process.readAllStandardOutput()
        text = to_text_string(locale_codec.toUnicode(qba.data()))
        if error:
            self.error_output += text
        else:
            self.output += text

    def finished(self, exit_code, exit_status):
        self.set_running_state(False)
        self.show_errorlog()  # If errors occurred, show them.
        self.output = self.error_output + self.output
        # FIXME: figure out if show_data should be called here or
        #        as a signal from the combobox
        self.show_data(justanalyzed=True)

    def kill_if_running(self):
        if self.process is not None:
            if self.process.state() == QProcess.Running:
                self.process.kill()
                self.process.waitForFinished()

    def show_data(self, justanalyzed=False):
        if not justanalyzed:
            self.output = None
        self.log_button.setEnabled(self.output is not None \
                                   and len(self.output) > 0)
        self.kill_if_running()
        filename = to_text_string(self.filecombo.currentText())
        if not filename:
            return

        self.datelabel.setText(_('Sorting data, please wait...'))
        QApplication.processEvents()

        self.datatree.load_data(self.DATAPATH)
        self.datatree.show_tree()

        text_style = "<span style=\'color: #444444\'><b>%s </b></span>"
        date_text = text_style % time.strftime("%d %b %Y %H:%M",
                                               time.localtime())
        self.datelabel.setText(date_text)
Example #40
0
class WorkingDirectory(SpyderPluginV2):
    """
    Working directory changer plugin.
    """

    NAME = 'workingdir'
    REQUIRES = [Plugins.Console]
    OPTIONAL = [
        Plugins.Editor, Plugins.Explorer, Plugins.IPythonConsole,
        Plugins.Projects
    ]
    CONTAINER_CLASS = WorkingDirectoryContainer
    CONF_SECTION = NAME
    CONF_WIDGET_CLASS = WorkingDirectoryConfigPage
    CONF_FILE = False
    LOG_PATH = get_conf_path(CONF_SECTION)

    # --- Signals
    # ------------------------------------------------------------------------
    sig_current_directory_changed = Signal(str)
    """
    This signal is emitted when the current directory has changed.

    Parameters
    ----------
    new_working_directory: str
        The new new working directory path.
    """

    # --- SpyderPluginV2 API
    # ------------------------------------------------------------------------
    def get_name(self):
        return _('Current working directory')

    def get_description(self):
        return _('Set the current working directory for various plugins.')

    def get_icon(self):
        return self.create_icon('DirOpenIcon')

    def register(self):
        container = self.get_container()
        editor = self.get_plugin(Plugins.Editor)
        explorer = self.get_plugin(Plugins.Explorer)
        ipyconsole = self.get_plugin(Plugins.IPythonConsole)

        self.add_application_toolbar(self.NAME, container.toolbar)
        container.sig_current_directory_changed.connect(
            self.sig_current_directory_changed)
        self.sig_current_directory_changed.connect(
            lambda path, plugin=None: self.chdir(path, plugin))
        container.set_history(self.load_history())

        if editor:
            editor.sig_dir_opened.connect(
                lambda path, plugin=editor: self.chdir(path, editor))

        if ipyconsole:
            self.sig_current_directory_changed.connect(
                ipyconsole.set_current_client_working_directory)
            # TODO: chdir_current_client might follow a better naming
            # convention
            ipyconsole.sig_current_directory_changed.connect(
                lambda path, plugin=ipyconsole: self.chdir(path, plugin))

        if explorer:
            self.sig_current_directory_changed.connect(
                lambda path: explorer.chdir(path, emit=False))
            explorer.sig_dir_opened.connect(
                lambda path, plugin=explorer: self.chdir(path, plugin))

    # --- Public API
    # ------------------------------------------------------------------------
    def chdir(self, directory, sender_plugin=None):
        """
        Change current working directory.

        Parameters
        ----------
        directory: str
            The new working directory to set.
        sender_plugin: spyder.api.plugins.SpyderPluginsV2
            The plugin that requested this change: Default is None.
        """
        explorer = self.get_plugin(Plugins.Explorer)
        ipyconsole = self.get_plugin(Plugins.IPythonConsole)

        if explorer and sender_plugin != explorer:
            explorer.chdir(directory, emit=False)
            explorer.refresh_plugin(directory, force_current=True)

        if ipyconsole and sender_plugin != ipyconsole:
            ipyconsole.set_current_client_working_directory(directory)

        if sender_plugin is not None:
            container = self.get_container()
            container.chdir(directory, emit=False)

        self.save_history()

    def load_history(self, workdir=None):
        """
        Load history from a text file located in Spyder configuration folder
        or use `workdir` if there are no directories saved yet.

        Parameters
        ----------
        workdir: str
            The working directory to return. Default is None.
        """
        if osp.isfile(self.LOG_PATH):
            history, _ = encoding.readlines(self.LOG_PATH)
            history = [name for name in history if osp.isdir(name)]
        else:
            if workdir is None:
                workdir = self.get_workdir()

            history = [workdir]

        return history

    def save_history(self):
        """
        Save history to a text file located in Spyder configuration folder.
        """
        history = self.get_container().get_history()
        try:
            encoding.writelines(history, self.LOG_PATH)
        except EnvironmentError:
            pass

    def get_workdir(self):
        """
        Get current working directory.

        Returns
        -------
        str
            Current working directory.
        """
        return self.get_container().get_workdir()
Example #41
0
def main():
    """
    Start Spyder application.

    If single instance mode is turned on (default behavior) and an instance of
    Spyder is already running, this will just parse and send command line
    options to the application.
    """
    # Parse command line options
    if running_under_pytest():
        try:
            from unittest.mock import Mock
        except ImportError:
            from mock import Mock # Python 2

        options = Mock()
        options.new_instance = False
        options.reset_config_files = False
        options.debug_info = None
        args = None
    else:
        options, args = get_options()

    # Store variable to be used in self.restart (restart spyder instance)
    os.environ['SPYDER_ARGS'] = str(sys.argv[1:])

    #==========================================================================
    # Proper high DPI scaling is available in Qt >= 5.6.0. This attibute must
    # be set before creating the application.
    #==========================================================================
    if CONF.get('main', 'high_dpi_custom_scale_factor'):
        factors = str(CONF.get('main', 'high_dpi_custom_scale_factors'))
        f = list(filter(None, factors.split(';')))
        if len(f) == 1:
            os.environ['QT_SCALE_FACTOR'] = f[0]
        else:
            os.environ['QT_SCREEN_SCALE_FACTORS'] = factors
    else:
        os.environ['QT_SCALE_FACTOR'] = ''
        os.environ['QT_SCREEN_SCALE_FACTORS'] = ''

    if sys.platform == 'darwin':
        # Prevent Spyder from crashing in macOS if locale is not defined
        LANG = os.environ.get('LANG')
        LC_ALL = os.environ.get('LC_ALL')
        if bool(LANG) and not bool(LC_ALL):
            LC_ALL = LANG
        elif not bool(LANG) and bool(LC_ALL):
            LANG = LC_ALL
        else:
            LANG = LC_ALL = 'en_US.UTF-8'

        os.environ['LANG'] = LANG
        os.environ['LC_ALL'] = LC_ALL

        # Don't show useless warning in the terminal where Spyder
        # was started
        # See issue 3730
        os.environ['EVENT_NOKQUEUE'] = '1'
    else:
        # Prevent our kernels to crash when Python fails to identify
        # the system locale.
        # Fixes issue 7051.
        try:
            from locale import getlocale
            getlocale()
        except ValueError:
            # This can fail on Windows. See issue 6886
            try:
                os.environ['LANG'] = 'C'
                os.environ['LC_ALL'] = 'C'
            except Exception:
                pass

    if options.debug_info:
        levels = {'minimal': '2', 'verbose': '3'}
        os.environ['SPYDER_DEBUG'] = levels[options.debug_info]

    if CONF.get('main', 'single_instance') and not options.new_instance \
      and not options.reset_config_files and not running_in_mac_app():
        # Minimal delay (0.1-0.2 secs) to avoid that several
        # instances started at the same time step in their
        # own foots while trying to create the lock file
        time.sleep(random.randrange(1000, 2000, 90)/10000.)

        # Lock file creation
        lock_file = get_conf_path('spyder.lock')
        lock = lockfile.FilesystemLock(lock_file)

        # Try to lock spyder.lock. If it's *possible* to do it, then
        # there is no previous instance running and we can start a
        # new one. If *not*, then there is an instance already
        # running, which is locking that file
        try:
            lock_created = lock.lock()
        except:
            # If locking fails because of errors in the lockfile
            # module, try to remove a possibly stale spyder.lock.
            # This is reported to solve all problems with
            # lockfile (See issue 2363)
            try:
                if os.name == 'nt':
                    if osp.isdir(lock_file):
                        import shutil
                        shutil.rmtree(lock_file, ignore_errors=True)
                else:
                    if osp.islink(lock_file):
                        os.unlink(lock_file)
            except:
                pass

            # Then start Spyder as usual and *don't* continue
            # executing this script because it doesn't make
            # sense
            from spyder.app import mainwindow
            if running_under_pytest():
                return mainwindow.main()
            else:
                mainwindow.main()
                return

        if lock_created:
            # Start a new instance
            from spyder.app import mainwindow
            if running_under_pytest():
                return mainwindow.main()
            else:
                mainwindow.main()
        else:
            # Pass args to Spyder or print an informative
            # message
            if args:
                send_args_to_spyder(args)
            else:
                print("Spyder is already running. If you want to open a new \n"
                      "instance, please pass to it the --new-instance option")
    else:
        from spyder.app import mainwindow
        if running_under_pytest():
            return mainwindow.main()
        else:
            mainwindow.main()
Example #42
0
class PylintWidget(PluginMainWidget):
    """
    Pylint widget.
    """
    DEFAULT_OPTIONS = {
        "history_filenames": [],
        "max_entries": 30,
        "project_dir": None,
    }
    ENABLE_SPINNER = True

    DATAPATH = get_conf_path("pylint.results")
    VERSION = "1.1.0"

    # --- Signals
    sig_edit_goto_requested = Signal(str, int, str)
    """
    This signal will request to open a file in a given row and column
    using a code editor.

    Parameters
    ----------
    path: str
        Path to file.
    row: int
        Cursor starting row position.
    word: str
        Word to select on given row.
    """

    sig_start_analysis_requested = Signal()
    """
    This signal will request the plugin to start the analysis. This is to be
    able to interact with other plugins, which can only be done at the plugin
    level.
    """
    def __init__(self,
                 name=None,
                 plugin=None,
                 parent=None,
                 options=DEFAULT_OPTIONS):
        super().__init__(name, plugin, parent, options)

        # Attributes
        self._process = None
        self.output = None
        self.error_output = None
        self.filename = None
        self.rdata = []
        self.curr_filenames = self.get_option("history_filenames")
        self.code_analysis_action = None
        self.browse_action = None

        # Widgets
        self.filecombo = PythonModulesComboBox(self)
        self.ratelabel = QLabel(self)
        self.datelabel = QLabel(self)
        self.treewidget = ResultsTree(self)

        if osp.isfile(self.DATAPATH):
            try:
                with open(self.DATAPATH, "rb") as fh:
                    data = pickle.loads(fh.read())

                if data[0] == self.VERSION:
                    self.rdata = data[1:]
            except (EOFError, ImportError):
                pass

        # Widget setup
        self.filecombo.setInsertPolicy(self.filecombo.InsertAtTop)
        for fname in self.curr_filenames[::-1]:
            self.set_filename(fname)

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.treewidget)
        self.setLayout(layout)

        # Signals
        self.filecombo.valid.connect(self._check_new_file)
        self.treewidget.sig_edit_goto_requested.connect(
            self.sig_edit_goto_requested)

    # --- Private API
    # ------------------------------------------------------------------------
    @Slot()
    def _start(self):
        """Start the code analysis."""
        self.start_spinner()
        self.output = ""
        self.error_output = ""
        self._process = process = QProcess(self)

        process.setProcessChannelMode(QProcess.SeparateChannels)
        process.setWorkingDirectory(getcwd_or_home())
        process.readyReadStandardOutput.connect(self._read_output)
        process.readyReadStandardError.connect(
            lambda: self._read_output(error=True))
        process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self._finished(ec, es))

        command_args = self.get_command(self.get_filename())
        processEnvironment = QProcessEnvironment()
        processEnvironment.insert("PYTHONIOENCODING", "utf8")

        # resolve spyder-ide/spyder#14262
        if running_in_mac_app():
            pyhome = os.environ.get("PYTHONHOME")
            processEnvironment.insert("PYTHONHOME", pyhome)

        process.setProcessEnvironment(processEnvironment)
        process.start(sys.executable, command_args)
        running = process.waitForStarted()
        if not running:
            self.stop_spinner()
            QMessageBox.critical(
                self,
                _("Error"),
                _("Process failed to start"),
            )

    def _read_output(self, error=False):
        process = self._process
        if error:
            process.setReadChannel(QProcess.StandardError)
        else:
            process.setReadChannel(QProcess.StandardOutput)

        qba = QByteArray()
        while process.bytesAvailable():
            if error:
                qba += process.readAllStandardError()
            else:
                qba += process.readAllStandardOutput()

        text = str(qba.data(), "utf-8")
        if error:
            self.error_output += text
        else:
            self.output += text

        self.update_actions()

    def _finished(self, exit_code, exit_status):
        if not self.output:
            self.stop_spinner()
            if self.error_output:
                QMessageBox.critical(
                    self,
                    _("Error"),
                    self.error_output,
                )
                print("pylint error:\n\n" + self.error_output, file=sys.stderr)
            return

        filename = self.get_filename()
        rate, previous, results = self.parse_output(self.output)
        self._save_history()
        self.set_data(filename, (time.localtime(), rate, previous, results))
        self.output = self.error_output + self.output
        self.show_data(justanalyzed=True)
        self.update_actions()
        self.stop_spinner()

    def _check_new_file(self):
        fname = self.get_filename()
        if fname != self.filename:
            self.filename = fname
            self.show_data()

    def _is_running(self):
        process = self._process
        return process is not None and process.state() == QProcess.Running

    def _kill_process(self):
        self._process.kill()
        self._process.waitForFinished()
        self.stop_spinner()

    def _update_combobox_history(self):
        """Change the number of files listed in the history combobox."""
        max_entries = self.get_option("max_entries")
        if self.filecombo.count() > max_entries:
            num_elements = self.filecombo.count()
            diff = num_elements - max_entries
            for __ in range(diff):
                num_elements = self.filecombo.count()
                self.filecombo.removeItem(num_elements - 1)
            self.filecombo.selected()
        else:
            num_elements = self.filecombo.count()
            diff = max_entries - num_elements
            for i in range(num_elements, num_elements + diff):
                if i >= len(self.curr_filenames):
                    break
                act_filename = self.curr_filenames[i]
                self.filecombo.insertItem(i, act_filename)

    def _save_history(self):
        """Save the current history filenames."""
        if self.parent:
            list_save_files = []
            for fname in self.curr_filenames:
                if _("untitled") not in fname:
                    list_save_files.append(fname)

            self.curr_filenames = list_save_files[:MAX_HISTORY_ENTRIES]
            self.set_option("history_filenames", self.curr_filenames)
        else:
            self.curr_filenames = []

    # --- PluginMainWidget API
    # ------------------------------------------------------------------------
    def get_title(self):
        return _("Code Analysis")

    def get_focus_widget(self):
        return self.treewidget

    def setup(self, options):
        change_history_depth_action = self.create_action(
            PylintWidgetActions.ChangeHistory,
            text=_("History..."),
            tip=_("Set history maximum entries"),
            icon=self.create_icon("history"),
            triggered=self.change_history_depth,
        )
        self.code_analysis_action = self.create_action(
            PylintWidgetActions.RunCodeAnalysis,
            icon_text=_("Analyze"),
            text=_("Run code analysis"),
            tip=_("Run code analysis"),
            icon=self.create_icon("run"),
            triggered=lambda: self.sig_start_analysis_requested.emit(),
            context=Qt.ApplicationShortcut,
            register_shortcut=True)
        self.browse_action = self.create_action(
            PylintWidgetActions.BrowseFile,
            text=_("Select Python file"),
            tip=_("Select Python file"),
            icon=self.create_icon("fileopen"),
            triggered=self.select_file,
        )
        self.log_action = self.create_action(
            PylintWidgetActions.ShowLog,
            text=_("Output"),
            icon_text=_("Output"),
            tip=_("Complete output"),
            icon=self.create_icon("log"),
            triggered=self.show_log,
        )

        options_menu = self.get_options_menu()
        self.add_item_to_menu(
            self.treewidget.get_action(OneColumnTreeActions.CollapseAllAction),
            menu=options_menu,
            section=PylintWidgetOptionsMenuSections.Global,
        )
        self.add_item_to_menu(
            self.treewidget.get_action(OneColumnTreeActions.ExpandAllAction),
            menu=options_menu,
            section=PylintWidgetOptionsMenuSections.Global,
        )
        self.add_item_to_menu(
            self.treewidget.get_action(
                OneColumnTreeActions.CollapseSelectionAction),
            menu=options_menu,
            section=PylintWidgetOptionsMenuSections.Section,
        )
        self.add_item_to_menu(
            self.treewidget.get_action(
                OneColumnTreeActions.ExpandSelectionAction),
            menu=options_menu,
            section=PylintWidgetOptionsMenuSections.Section,
        )
        self.add_item_to_menu(
            change_history_depth_action,
            menu=options_menu,
            section=PylintWidgetOptionsMenuSections.History,
        )

        # Update OneColumnTree contextual menu
        self.add_item_to_menu(
            change_history_depth_action,
            menu=self.treewidget.menu,
            section=PylintWidgetOptionsMenuSections.History,
        )
        self.treewidget.restore_action.setVisible(False)

        toolbar = self.get_main_toolbar()
        for item in [
                self.filecombo, self.browse_action, self.code_analysis_action
        ]:
            self.add_item_to_toolbar(
                item,
                toolbar,
                section=PylintWidgetMainToolBarSections.Main,
            )

        secondary_toolbar = self.create_toolbar("secondary")
        for item in [
                self.ratelabel,
                self.create_stretcher(), self.datelabel,
                self.create_stretcher(), self.log_action
        ]:
            self.add_item_to_toolbar(
                item,
                secondary_toolbar,
                section=PylintWidgetMainToolBarSections.Main,
            )

        self.show_data()

        if self.rdata:
            self.remove_obsolete_items()
            self.filecombo.insertItems(0, self.get_filenames())
            self.code_analysis_action.setEnabled(self.filecombo.is_valid())
        else:
            self.code_analysis_action.setEnabled(False)

        # Signals
        self.filecombo.valid.connect(self.code_analysis_action.setEnabled)

    def on_option_update(self, option, value):
        if option == "max_entries":
            self._update_combobox_history()
        elif option == "history_filenames":
            self.curr_filenames = value
            self._update_combobox_history()

    def update_actions(self):
        fm = self.ratelabel.fontMetrics()
        toolbar = self.get_main_toolbar()
        width = max([fm.width(_("Stop")), fm.width(_("Analyze"))])
        widget = toolbar.widgetForAction(self.code_analysis_action)
        if widget:
            widget.setMinimumWidth(width * 1.5)

        if self._is_running():
            self.code_analysis_action.setIconText(_("Stop"))
            self.code_analysis_action.setIcon(self.create_icon("stop"))
        else:
            self.code_analysis_action.setIconText(_("Analyze"))
            self.code_analysis_action.setIcon(self.create_icon("run"))

        self.remove_obsolete_items()

    # --- Public API
    # ------------------------------------------------------------------------
    @Slot()
    @Slot(int)
    def change_history_depth(self, value=None):
        """
        Set history maximum entries.

        Parameters
        ----------
        value: int or None, optional
            The valur to set  the maximum history depth. If no value is
            provided, an input dialog will be launched. Default is None.
        """
        if value is None:
            dialog = QInputDialog(self)

            # Set dialog properties
            dialog.setModal(False)
            dialog.setWindowTitle(_("History"))
            dialog.setLabelText(_("Maximum entries"))
            dialog.setInputMode(QInputDialog.IntInput)
            dialog.setIntRange(MIN_HISTORY_ENTRIES, MAX_HISTORY_ENTRIES)
            dialog.setIntStep(1)
            dialog.setIntValue(self.get_option("max_entries"))

            # Connect slot
            dialog.intValueSelected.connect(
                lambda value: self.set_option("max_entries", value))

            dialog.show()
        else:
            self.set_option("max_entries", value)

    def get_filename(self):
        """
        Get current filename in combobox.
        """
        return str(self.filecombo.currentText())

    @Slot(str)
    def set_filename(self, filename):
        """
        Set current filename in combobox.
        """
        if self._is_running():
            self._kill_process()

        # Don't try to reload saved analysis for filename, if filename
        # is the one currently displayed.
        # Fixes spyder-ide/spyder#13347
        if self.get_filename() == filename:
            return

        filename = str(filename)
        index, _data = self.get_data(filename)

        if filename not in self.curr_filenames:
            self.filecombo.insertItem(0, filename)
            self.curr_filenames.insert(0, filename)
            self.filecombo.setCurrentIndex(0)
        else:
            try:
                index = self.filecombo.findText(filename)
                self.filecombo.removeItem(index)
                self.curr_filenames.pop(index)
            except IndexError:
                self.curr_filenames.remove(filename)
            self.filecombo.insertItem(0, filename)
            self.curr_filenames.insert(0, filename)
            self.filecombo.setCurrentIndex(0)

        num_elements = self.filecombo.count()
        if num_elements > self.get_option("max_entries"):
            self.filecombo.removeItem(num_elements - 1)

        self.filecombo.selected()

    def start_code_analysis(self, filename=None):
        """
        Perform code analysis for given `filename`.

        If `filename` is None default to current filename in combobox.

        If this method is called while still running it will stop the code
        analysis.
        """
        if self._is_running():
            self._kill_process()
        else:
            if filename is not None:
                self.set_filename(filename)

            if self.filecombo.is_valid():
                self._start()

        self.update_actions()

    def stop_code_analysis(self):
        """
        Stop the code analysis process.
        """
        if self._is_running():
            self._kill_process()

    def remove_obsolete_items(self):
        """
        Removing obsolete items.
        """
        self.rdata = [(filename, data) for filename, data in self.rdata
                      if is_module_or_package(filename)]

    def get_filenames(self):
        """
        Return all filenames for which there is data available.
        """
        return [filename for filename, _data in self.rdata]

    def get_data(self, filename):
        """
        Get and load code analysis data for given `filename`.
        """
        filename = osp.abspath(filename)
        for index, (fname, data) in enumerate(self.rdata):
            if fname == filename:
                return index, data
        else:
            return None, None

    def set_data(self, filename, data):
        """
        Set and save code analysis `data` for given `filename`.
        """
        filename = osp.abspath(filename)
        index, _data = self.get_data(filename)
        if index is not None:
            self.rdata.pop(index)

        self.rdata.insert(0, (filename, data))

        while len(self.rdata) > self.get_option("max_entries"):
            self.rdata.pop(-1)

        with open(self.DATAPATH, "wb") as fh:
            pickle.dump([self.VERSION] + self.rdata, fh, 2)

    def show_data(self, justanalyzed=False):
        """
        Show data in treewidget.
        """
        text_color = MAIN_TEXT_COLOR
        prevrate_color = MAIN_PREVRATE_COLOR

        if not justanalyzed:
            self.output = None

        self.log_action.setEnabled(self.output is not None
                                   and len(self.output) > 0)

        if self._is_running():
            self._kill_process()

        filename = self.get_filename()
        if not filename:
            return

        _index, data = self.get_data(filename)
        if data is None:
            text = _("Source code has not been rated yet.")
            self.treewidget.clear_results()
            date_text = ""
        else:
            datetime, rate, previous_rate, results = data
            if rate is None:
                text = _("Analysis did not succeed "
                         "(see output for more details).")
                self.treewidget.clear_results()
                date_text = ""
            else:
                text_style = "<span style=\"color: %s\"><b>%s </b></span>"
                rate_style = "<span style=\"color: %s\"><b>%s</b></span>"
                prevrate_style = "<span style=\"color: %s\">%s</span>"
                color = DANGER_COLOR
                if float(rate) > 5.:
                    color = SUCCESS_COLOR
                elif float(rate) > 3.:
                    color = WARNING_COLOR

                text = _("Global evaluation:")
                text = ((text_style % (text_color, text)) +
                        (rate_style % (color, ("%s/10" % rate))))
                if previous_rate:
                    text_prun = _("previous run:")
                    text_prun = " (%s %s/10)" % (text_prun, previous_rate)
                    text += prevrate_style % (prevrate_color, text_prun)

                self.treewidget.set_results(filename, results)
                date = time.strftime("%Y-%m-%d %H:%M:%S", datetime)
                date_text = text_style % (text_color, date)

        self.ratelabel.setText(text)
        self.datelabel.setText(date_text)

    @Slot()
    def show_log(self):
        """
        Show output log dialog.
        """
        if self.output:
            output_dialog = TextEditor(self.output,
                                       title=_("Code analysis output"),
                                       parent=self,
                                       readonly=True)
            output_dialog.resize(700, 500)
            output_dialog.exec_()

    # --- Python Specific
    # ------------------------------------------------------------------------
    def get_pylintrc_path(self, filename):
        """
        Get the path to the most proximate pylintrc config to the file.
        """
        search_paths = [
            # File"s directory
            osp.dirname(filename),
            # Working directory
            getcwd_or_home(),
            # Project directory
            self.get_option("project_dir"),
            # Home directory
            osp.expanduser("~"),
        ]

        return get_pylintrc_path(search_paths=search_paths)

    @Slot()
    def select_file(self, filename=None):
        """
        Select filename using a open file dialog and set as current filename.

        If `filename` is provided, the dialog is not used.
        """
        if filename is None:
            self.sig_redirect_stdio_requested.emit(False)
            filename, _selfilter = getopenfilename(
                self,
                _("Select Python file"),
                getcwd_or_home(),
                _("Python files") + " (*.py ; *.pyw)",
            )
            self.sig_redirect_stdio_requested.emit(True)

        if filename:
            self.set_filename(filename)
            self.start_code_analysis()

    def get_command(self, filename):
        """
        Return command to use to run code analysis on given filename
        """
        command_args = []
        if PYLINT_VER is not None:
            command_args = [
                "-m",
                "pylint",
                "--output-format=text",
                "--msg-template="
                '{msg_id}:{symbol}:{line:3d},{column}: {msg}"',
            ]

        pylintrc_path = self.get_pylintrc_path(filename=filename)
        if pylintrc_path is not None:
            command_args += ["--rcfile={}".format(pylintrc_path)]

        command_args.append(filename)
        return command_args

    def parse_output(self, output):
        """
        Parse output and return current revious rate and results.
        """
        # Convention, Refactor, Warning, Error
        results = {"C:": [], "R:": [], "W:": [], "E:": []}
        txt_module = "************* Module "

        module = ""  # Should not be needed - just in case something goes wrong
        for line in output.splitlines():
            if line.startswith(txt_module):
                # New module
                module = line[len(txt_module):]
                continue
            # Supporting option include-ids: ("R3873:" instead of "R:")
            if not re.match(r"^[CRWE]+([0-9]{4})?:", line):
                continue

            items = {}
            idx_0 = 0
            idx_1 = 0
            key_names = ["msg_id", "message_name", "line_nb", "message"]
            for key_idx, key_name in enumerate(key_names):
                if key_idx == len(key_names) - 1:
                    idx_1 = len(line)
                else:
                    idx_1 = line.find(":", idx_0)

                if idx_1 < 0:
                    break

                item = line[(idx_0):idx_1]
                if not item:
                    break

                if key_name == "line_nb":
                    item = int(item.split(",")[0])

                items[key_name] = item
                idx_0 = idx_1 + 1
            else:
                pylint_item = (module, items["line_nb"], items["message"],
                               items["msg_id"], items["message_name"])
                results[line[0] + ":"].append(pylint_item)

        # Rate
        rate = None
        txt_rate = "Your code has been rated at "
        i_rate = output.find(txt_rate)
        if i_rate > 0:
            i_rate_end = output.find("/10", i_rate)
            if i_rate_end > 0:
                rate = output[i_rate + len(txt_rate):i_rate_end]

        # Previous run
        previous = ""
        if rate is not None:
            txt_prun = "previous run: "
            i_prun = output.find(txt_prun, i_rate_end)
            if i_prun > 0:
                i_prun_end = output.find("/10", i_prun)
                previous = output[i_prun + len(txt_prun):i_prun_end]

        return rate, previous, results
Example #43
0
import errno
import os
import socket
import threading

# Third party imports
from qtpy.QtCore import QThread, Signal

# Local imports
from spyder.config.base import get_conf_path, DEBUG
from spyder.utils.debug import log_last_error
from spyder.utils.bsdsocket import read_packet, write_packet
from spyder.utils.misc import select_port


LOG_FILENAME = get_conf_path('introspection.log')

DEBUG_INTROSPECTION = DEBUG >= 2

if DEBUG_INTROSPECTION:
    import logging
    logging.basicConfig(filename=get_conf_path('introspection_debug.log'),
                        level=logging.DEBUG)

SPYDER_PORT = 20128


class IntrospectionServer(threading.Thread):
    """Introspection server"""
    def __init__(self):
        threading.Thread.__init__(self)
Example #44
0
class Help(SpyderDockablePlugin):
    """
    Docstrings viewer widget.
    """
    NAME = 'help'
    REQUIRES = [Plugins.Preferences, Plugins.Console, Plugins.Editor]
    OPTIONAL = [Plugins.IPythonConsole, Plugins.Shortcuts, Plugins.MainMenu]
    TABIFY = Plugins.VariableExplorer
    WIDGET_CLASS = HelpWidget
    CONF_SECTION = NAME
    CONF_WIDGET_CLASS = HelpConfigPage
    CONF_FILE = False
    LOG_PATH = get_conf_path(CONF_SECTION)
    FONT_SIZE_DELTA = DEFAULT_SMALL_DELTA
    DISABLE_ACTIONS_WHEN_HIDDEN = False
    CONF_FROM_OPTIONS = {
        'editor_shortcut': ('shortcuts', 'editor/inspect current object'),
        'console_shortcut': ('shortcuts', 'console/inspect current object'),
    }

    # Signals
    sig_focus_changed = Signal()  # TODO: What triggers this?

    sig_render_started = Signal()
    """This signal is emitted to inform a help text rendering has started."""

    sig_render_finished = Signal()
    """This signal is emitted to inform a help text rendering has finished."""

    # --- SpyderDocakblePlugin API
    #  -----------------------------------------------------------------------
    def get_name(self):
        return _('Help')

    def get_description(self):
        return _('Get rich text documentation from the editor and the console')

    def get_icon(self):
        return self.create_icon('help')

    def register(self):
        widget = self.get_widget()

        # Plugins
        internal_console = self.get_plugin(Plugins.Console)
        editor = self.get_plugin(Plugins.Editor)
        ipyconsole = self.get_plugin(Plugins.IPythonConsole)
        shortcuts = self.get_plugin(Plugins.Shortcuts)
        preferences = self.get_plugin(Plugins.Preferences)

        preferences.register_plugin_preferences(self)

        # Expose widget signals on the plugin
        widget.sig_render_started.connect(self.sig_render_started)
        widget.sig_render_finished.connect(self.sig_render_finished)

        # self.sig_focus_changed.connect(self.main.plugin_focus_changed)
        widget.set_history(self.load_history())
        widget.set_internal_console(internal_console)
        widget.sig_item_found.connect(self.save_history)

        editor.sig_help_requested.connect(self.set_editor_doc)
        internal_console.sig_help_requested.connect(self.set_object_text)

        if ipyconsole:
            ipyconsole.sig_shellwidget_changed.connect(self.set_shellwidget)
            ipyconsole.sig_shellwidget_process_started.connect(
                self.set_shellwidget)
            ipyconsole.sig_render_plain_text_requested.connect(
                self.show_plain_text)
            ipyconsole.sig_render_rich_text_requested.connect(
                self.show_rich_text)

            ipyconsole.sig_help_requested.connect(self.set_object_text)

        if shortcuts:
            # See: spyder-ide/spyder#6992
            shortcuts.sig_shortcuts_updated.connect(
                lambda: self.show_intro_message())

        self.tutorial_action = self.create_action(
            HelpActions.ShowSpyderTutorialAction,
            text=_("Spyder tutorial"),
            triggered=self.show_tutorial,
            register_shortcut=False,
        )

        # Add actions to main menu (Help menu)
        self._setup_menus()

    def update_font(self):
        color_scheme = self.get_color_scheme()
        font = self.get_font()
        rich_font = self.get_font(rich_text=True)

        widget = self.get_widget()
        widget.set_plain_text_font(font, color_scheme=color_scheme)
        widget.set_rich_text_font(rich_font, font)
        widget.set_plain_text_color_scheme(color_scheme)

    def on_close(self, cancelable=False):
        self.save_history()
        return True

    def apply_conf(self, options_set):
        super().apply_conf(options_set)
        widget = self.get_widget()

        if 'color_scheme_name' in options_set:
            widget.set_plain_text_color_scheme(self.get_color_scheme())

        # To make auto-connection changes take place instantly
        editor = self.get_plugin(Plugins.Editor)
        editor.apply_plugin_settings({'connect_to_oi'})

        ipyconsole = self.get_plugin(Plugins.IPythonConsole)
        if ipyconsole:
            ipyconsole.apply_plugin_settings({'connect_to_oi'})

    # --- Private API
    # ------------------------------------------------------------------------
    def _setup_menus(self):
        mainmenu = self.get_plugin(Plugins.MainMenu)
        shortcuts = self.get_plugin(Plugins.Shortcuts)
        shortcuts_summary_action = None
        if shortcuts:
            from spyder.plugins.shortcuts.plugin import ShortcutActions
            shortcuts_summary_action = shortcuts.get_action(
                ShortcutActions.ShortcutSummaryAction)
        if mainmenu:
            from spyder.plugins.mainmenu.api import (ApplicationMenus,
                                                     HelpMenuSections)
            # Documentation actions
            mainmenu.add_item_to_application_menu(
                self.tutorial_action,
                menu_id=ApplicationMenus.Help,
                section=HelpMenuSections.Documentation,
                before=shortcuts_summary_action,
                before_section=HelpMenuSections.Support)

    # --- Public API
    # ------------------------------------------------------------------------
    def set_shellwidget(self, shellwidget):
        """
        Set IPython Console `shelwidget` as the current shellwidget.

        Parameters
        ----------
        shellwidget: spyder.plugins.ipyconsole.widgets.shell.ShellWidget
            The shell widget that is going to be connected to Help.
        """
        shellwidget._control.set_help_enabled(
            self.get_conf('connect/ipython_console'))
        self.get_widget().set_shell(shellwidget)

    def load_history(self, obj=None):
        """
        Load history from a text file in the user configuration directory.
        """
        if os.path.isfile(self.LOG_PATH):
            with open(self.LOG_PATH, 'r') as fh:
                lines = fh.read().split('\n')

            history = [line.replace('\n', '') for line in lines]
        else:
            history = []

        return history

    def save_history(self):
        """
        Save history to a text file in the user configuration directory.
        """
        # Don't fail when saving search history to disk
        # See spyder-ide/spyder#8878 and spyder-ide/spyder#6864
        try:
            search_history = '\n'.join(self.get_widget().get_history())
            with open(self.LOG_PATH, 'w') as fh:
                fh.write(search_history)
        except (UnicodeEncodeError, UnicodeDecodeError, EnvironmentError):
            pass

    def show_tutorial(self):
        """Show the Spyder tutorial."""
        self.switch_to_plugin()
        self.get_widget().show_tutorial()

    def show_intro_message(self):
        """Show the IPython introduction message."""
        self.switch_to_plugin()
        self.get_widget().show_intro_message()

    def show_rich_text(self, text, collapse=False, img_path=''):
        """
        Show help in rich mode.

        Parameters
        ----------
        text: str
            Plain text to display.
        collapse: bool, optional
            Show collapsable sections as collapsed/expanded. Default is False.
        img_path: str, optional
            Path to folder with additional images needed to correctly
            display the rich text help. Default is ''.
        """
        self.switch_to_plugin()
        self.get_widget().show_rich_text(text,
                                         collapse=collapse,
                                         img_path=img_path)

    def show_plain_text(self, text):
        """
        Show help in plain mode.

        Parameters
        ----------
        text: str
            Plain text to display.
        """
        self.switch_to_plugin()
        self.get_widget().show_plain_text(text)

    def set_object_text(self, options_dict):
        """
        Set object's name in Help's combobox.

        Parameters
        ----------
        options_dict: dict
            Dictionary of data. See the example for the expected keys.

        Examples
        --------
        >>> help_data = {
            'name': str,
            'force_refresh': bool,
        }

        See Also
        --------
        :py:meth:spyder.widgets.mixins.GetHelpMixin.show_object_info
        """
        self.switch_to_plugin()
        self.get_widget().set_object_text(
            options_dict['name'],
            ignore_unknown=options_dict['ignore_unknown'],
        )

    def set_editor_doc(self, help_data):
        """
        Set content for help data sent from the editor.

        Parameters
        ----------
        help_data: dict
            Dictionary of data. See the example for the expected keys.

        Examples
        --------
        >>> help_data = {
            'obj_text': str,
            'name': str,
            'argspec': str,
            'note': str,
            'docstring': str,
            'force_refresh': bool,
            'path': str,
        }

        See Also
        --------
        :py:meth:spyder.plugins.editor.widgets.editor.EditorStack.send_to_help
        """
        force_refresh = help_data.pop('force_refresh', False)
        self.switch_to_plugin()
        self.get_widget().set_editor_doc(
            help_data,
            force_refresh=force_refresh,
        )
class MemoryProfilerWidget(QWidget):
    """
    Memory profiler widget.
    """
    DATAPATH = get_conf_path('memoryprofiler.results')
    VERSION = '0.0.1'
    redirect_stdio = Signal(bool)
    sig_finished = Signal()

    def __init__(self, parent):
        QWidget.__init__(self, parent)

        self.setWindowTitle("Memory profiler")

        self.output = None
        self.error_output = None

        self.use_colors = True

        self._last_wdir = None
        self._last_args = None
        self._last_pythonpath = None

        self.filecombo = PythonModulesComboBox(self)

        self.start_button = create_toolbutton(self,
                                              icon=get_icon('run.png'),
                                              text=_("Profile memory usage"),
                                              tip=_("Run memory profiler"),
                                              triggered=self.start,
                                              text_beside_icon=True)
        self.stop_button = create_toolbutton(self,
                                             icon=get_icon('terminate.png'),
                                             text=_("Stop"),
                                             tip=_("Stop current profiling"),
                                             text_beside_icon=True)
        self.filecombo.valid.connect(self.start_button.setEnabled)
        #self.connect(self.filecombo, SIGNAL('valid(bool)'), self.show_data)
        # FIXME: The combobox emits this signal on almost any event
        #        triggering show_data() too early, too often.

        browse_button = create_toolbutton(self,
                                          icon=get_icon('fileopen.png'),
                                          tip=_('Select Python script'),
                                          triggered=self.select_file)

        self.datelabel = QLabel()

        self.log_button = create_toolbutton(self,
                                            icon=get_icon('log.png'),
                                            text=_("Output"),
                                            text_beside_icon=True,
                                            tip=_("Show program's output"),
                                            triggered=self.show_log)

        self.datatree = MemoryProfilerDataTree(self)

        self.collapse_button = create_toolbutton(
            self,
            icon=get_icon('collapse.png'),
            triggered=lambda dD=-1: self.datatree.collapseAll(),
            tip=_('Collapse all'))
        self.expand_button = create_toolbutton(
            self,
            icon=get_icon('expand.png'),
            triggered=lambda dD=1: self.datatree.expandAll(),
            tip=_('Expand all'))

        hlayout1 = QHBoxLayout()
        hlayout1.addWidget(self.filecombo)
        hlayout1.addWidget(browse_button)
        hlayout1.addWidget(self.start_button)
        hlayout1.addWidget(self.stop_button)

        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(self.collapse_button)
        hlayout2.addWidget(self.expand_button)
        hlayout2.addStretch()
        hlayout2.addWidget(self.datelabel)
        hlayout2.addStretch()
        hlayout2.addWidget(self.log_button)

        layout = QVBoxLayout()
        layout.addLayout(hlayout1)
        layout.addLayout(hlayout2)
        layout.addWidget(self.datatree)
        self.setLayout(layout)

        self.process = None
        self.set_running_state(False)
        self.start_button.setEnabled(False)

        if not is_memoryprofiler_installed():
            for widget in (self.datatree, self.filecombo, self.log_button,
                           self.start_button, self.stop_button, browse_button,
                           self.collapse_button, self.expand_button):
                widget.setDisabled(True)
            text = _(
                '<b>Please install the <a href="%s">memory_profiler module</a></b>'
            ) % WEBSITE_URL
            self.datelabel.setText(text)
            self.datelabel.setOpenExternalLinks(True)
        else:
            pass  # self.show_data()

    def analyze(self,
                filename,
                wdir=None,
                args=None,
                pythonpath=None,
                use_colors=True):
        self.use_colors = use_colors
        if not is_memoryprofiler_installed():
            return
        self.kill_if_running()
        #index, _data = self.get_data(filename)
        index = None  # FIXME: storing data is not implemented yet
        if index is None:
            self.filecombo.addItem(filename)
            self.filecombo.setCurrentIndex(self.filecombo.count() - 1)
        else:
            self.filecombo.setCurrentIndex(self.filecombo.findText(filename))
        self.filecombo.selected()
        if self.filecombo.is_valid():
            if wdir is None:
                wdir = osp.dirname(filename)
            self.start(wdir, args, pythonpath)

    def select_file(self):
        self.redirect_stdio.emit(False)
        filename, _selfilter = getopenfilename(
            self, _("Select Python script"), getcwd(),
            _("Python scripts") + " (*.py ; *.pyw)")
        self.redirect_stdio.emit(False)
        if filename:
            self.analyze(filename)

    def show_log(self):
        if self.output:
            TextEditor(self.output,
                       title=_("Memory profiler output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    def show_errorlog(self):
        if self.error_output:
            TextEditor(self.error_output,
                       title=_("Memory profiler output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    def start(self, wdir=None, args=None, pythonpath=None):
        filename = to_text_string(self.filecombo.currentText())
        if wdir is None:
            wdir = self._last_wdir
            if wdir is None:
                wdir = osp.basename(filename)
        if args is None:
            args = self._last_args
            if args is None:
                args = []
        if pythonpath is None:
            pythonpath = self._last_pythonpath
        self._last_wdir = wdir
        self._last_args = args
        self._last_pythonpath = pythonpath

        self.datelabel.setText(_('Profiling, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(wdir)
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(
            lambda: self.read_output(error=True))
        self.process.finished.connect(self.finished)
        self.stop_button.clicked.connect(self.process.kill)

        if pythonpath is not None:
            env = [
                to_text_string(_pth)
                for _pth in self.process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, separator, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)
            self.process.setProcessEnvironment(processEnvironment)

        self.output = ''
        self.error_output = ''

        # remove previous results, since memory_profiler appends to output file
        # instead of replacing
        if osp.isfile(self.DATAPATH):
            os.remove(self.DATAPATH)

        if os.name == 'nt':
            # On Windows, one has to replace backslashes by slashes to avoid
            # confusion with escape characters (otherwise, for example, '\t'
            # will be interpreted as a tabulation):
            filename = osp.normpath(filename).replace(os.sep, '/')
            p_args = [
                '-m', 'memory_profiler', '-o', '"' + self.DATAPATH + '"',
                '"' + filename + '"'
            ]
            if args:
                p_args.extend(programs.shell_split(args))
            executable = get_python_executable()
            executable += ' ' + ' '.join(p_args)
            executable = executable.replace(os.sep, '/')
            self.process.start(executable)
        else:
            p_args = ['-m', 'memory_profiler', '-o', self.DATAPATH, filename]
            if args:
                p_args.extend(programs.shell_split(args))
            executable = get_python_executable()
            self.process.start(executable, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))

    def set_running_state(self, state=True):
        self.start_button.setEnabled(not state)
        self.stop_button.setEnabled(state)

    def read_output(self, error=False):
        if error:
            self.process.setReadChannel(QProcess.StandardError)
        else:
            self.process.setReadChannel(QProcess.StandardOutput)
        qba = QByteArray()
        while self.process.bytesAvailable():
            if error:
                qba += self.process.readAllStandardError()
            else:
                qba += self.process.readAllStandardOutput()
        text = to_text_string(locale_codec.toUnicode(qba.data()))
        if error:
            self.error_output += text
        else:
            self.output += text

    def finished(self):
        self.set_running_state(False)
        self.show_errorlog()  # If errors occurred, show them.
        self.output = self.error_output + self.output
        # FIXME: figure out if show_data should be called here or
        #        as a signal from the combobox
        self.show_data(justanalyzed=True)
        self.sig_finished.emit()

    def kill_if_running(self):
        if self.process is not None:
            if self.process.state() == QProcess.Running:
                self.process.kill()
                self.process.waitForFinished()

    def show_data(self, justanalyzed=False):
        if not justanalyzed:
            self.output = None
        self.log_button.setEnabled(self.output is not None
                                   and len(self.output) > 0)
        self.kill_if_running()
        filename = to_text_string(self.filecombo.currentText())
        if not filename:
            return

        self.datatree.load_data(self.DATAPATH)
        self.datelabel.setText(_('Sorting data, please wait...'))
        QApplication.processEvents()
        self.datatree.show_tree()

        text_style = "<span style=\'color: #444444\'><b>%s </b></span>"
        date_text = text_style % time.strftime("%d %b %Y %H:%M",
                                               time.localtime())
        self.datelabel.setText(date_text)
Example #46
0
    def __init__(self, parent=None, fname=None, wdir=None,
                 history_filename=None, show_icontext=True,
                 light_background=True, menu_actions=None,
                 show_buttons_inside=True, show_elapsed_time=True):
        QWidget.__init__(self, parent)
        
        self.menu_actions = menu_actions
        self.write_lock = QMutex()
        self.buffer_lock = QMutex()
        self.buffer = []
        
        self.run_button = None
        self.kill_button = None
        self.options_button = None
        self.icontext_action = None

        self.show_elapsed_time = show_elapsed_time
        
        self.fname = fname
        if wdir is None:
            wdir = osp.dirname(osp.abspath(fname))
        self.wdir = wdir if osp.isdir(wdir) else None
        self.arguments = ""
        
        self.shell = self.SHELL_CLASS(parent, get_conf_path(history_filename))
        self.shell.set_light_background(light_background)
        self.shell.execute.connect(self.send_to_process)
        self.shell.sig_keyboard_interrupt.connect(self.keyboard_interrupt)
        # Redirecting some SIGNALs:
        self.shell.redirect_stdio.connect(
                     lambda state: self.redirect_stdio.emit(state))
        
        self.state_label = None
        self.time_label = None
                
        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        if show_buttons_inside:
            self.state_label = QLabel()
            hlayout = QHBoxLayout()
            hlayout.addWidget(self.state_label)
            hlayout.addStretch(0)
            hlayout.addWidget(self.create_time_label())
            hlayout.addStretch(0)
            for button in toolbar_buttons:
                hlayout.addWidget(button)
            vlayout.addLayout(hlayout)
        else:
            vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.get_shell_widget())
        self.setLayout(vlayout)
        self.resize(640, 480)
        if parent is None:
            self.setWindowIcon(self.get_icon())
            self.setWindowTitle(_("Console"))

        self.t0 = None
        self.timer = QTimer(self)

        self.process = None
        
        self.is_closing = False

        if show_buttons_inside:
            self.update_time_label_visibility()
Example #47
0
# Distributed under the terms of the Modified BSD License
# (BSD 3-clause; see NOTICE.txt in the Spyder root directory for details).
# -----------------------------------------------------------------------------

"""
Module completion auxiliary functions.
"""

import pkgutil

from pickleshare import PickleShareDB

from spyder.config.base import get_conf_path

# Path to the modules database
MODULES_PATH = get_conf_path('db')

# Modules database
modules_db = PickleShareDB(MODULES_PATH)


def get_submodules(mod):
    """Get all submodules of a given module"""
    def catch_exceptions(module):
        pass
    try:
        m = __import__(mod)
        submodules = [mod]
        submods = pkgutil.walk_packages(m.__path__, m.__name__ + '.',
                                        catch_exceptions)
        for sm in submods:
Example #48
0
# Local imports
from spyder.utils.misc import fix_reference_name
from spyder.utils.debug import log_last_error
from spyder.utils.dochelpers import (getargtxt, getdoc, getsource,
                                     getobjdir, isdefined)
from spyder.utils.bsdsocket import (communicate, read_packet, write_packet,
                                    PACKET_NOT_RECEIVED, PICKLE_HIGHEST_PROTOCOL)
from spyder.utils.introspection.module_completion import module_completion
from spyder.config.base import get_conf_path, get_supported_types, DEBUG
from spyder.py3compat import getcwd, is_text_string, pickle, _thread


SUPPORTED_TYPES = {}

LOG_FILENAME = get_conf_path('monitor.log')

DEBUG_MONITOR = DEBUG >= 2

if DEBUG_MONITOR:
    import logging
    logging.basicConfig(filename=get_conf_path('monitor_debug.log'),
                        level=logging.DEBUG)

REMOTE_SETTINGS = ('check_all', 'exclude_private', 'exclude_uppercase',
                   'exclude_capitalized', 'exclude_unsupported',
                   'excluded_names', 'truncate', 'minmax',
                   'remote_editing', 'autorefresh')

def get_remote_data(data, settings, mode, more_excluded_names=None):
    """
Example #49
0
def main():
    """
    Start Spyder application.

    If single instance mode is turned on (default behavior) and an instance of
    Spyder is already running, this will just parse and send command line
    options to the application.
    """
    # Parse command line options
    options, args = get_options()

    # Store variable to be used in self.restart (restart spyder instance)
    os.environ['SPYDER_ARGS'] = str(sys.argv[1:])

    if CONF.get('main', 'single_instance') and not options.new_instance \
      and not options.reset_config_files and not running_in_mac_app():
        # Minimal delay (0.1-0.2 secs) to avoid that several
        # instances started at the same time step in their
        # own foots while trying to create the lock file
        time.sleep(random.randrange(1000, 2000, 90)/10000.)

        # Lock file creation
        lock_file = get_conf_path('spyder.lock')
        lock = lockfile.FilesystemLock(lock_file)

        # Try to lock spyder.lock. If it's *possible* to do it, then
        # there is no previous instance running and we can start a
        # new one. If *not*, then there is an instance already
        # running, which is locking that file
        try:
            lock_created = lock.lock()
        except:
            # If locking fails because of errors in the lockfile
            # module, try to remove a possibly stale spyder.lock.
            # This is reported to solve all problems with
            # lockfile (See issue 2363)
            try:
                if os.name == 'nt':
                    if osp.isdir(lock_file):
                        import shutil
                        shutil.rmtree(lock_file, ignore_errors=True)
                else:
                    if osp.islink(lock_file):
                        os.unlink(lock_file)
            except:
                pass

            # Then start Spyder as usual and *don't* continue
            # executing this script because it doesn't make
            # sense
            from spyder.app import mainwindow
            mainwindow.main()
            return

        if lock_created:
            # Start a new instance
            from spyder.app import mainwindow
            mainwindow.main()
        else:
            # Pass args to Spyder or print an informative
            # message
            if args:
                send_args_to_spyder(args)
            else:
                print("Spyder is already running. If you want to open a new \n"
                      "instance, please pass to it the --new-instance option")
    else:
        from spyder.app import mainwindow
        mainwindow.main()
Example #50
0
class PylintWidget(QWidget):
    """
    Pylint widget
    """
    DATAPATH = get_conf_path('pylint.results')
    VERSION = '1.1.0'
    redirect_stdio = Signal(bool)

    def __init__(self, parent, max_entries=100):
        QWidget.__init__(self, parent)

        self.setWindowTitle("Pylint")

        self.output = None
        self.error_output = None

        self.max_entries = max_entries
        self.rdata = []
        if osp.isfile(self.DATAPATH):
            try:
                data = pickle.loads(open(self.DATAPATH, 'rb').read())
                if data[0] == self.VERSION:
                    self.rdata = data[1:]
            except (EOFError, ImportError):
                pass

        self.filecombo = PythonModulesComboBox(self)

        self.start_button = create_toolbutton(self,
                                              icon=ima.icon('run'),
                                              text=_("Analyze"),
                                              tip=_("Run analysis"),
                                              triggered=self.start,
                                              text_beside_icon=True)
        self.stop_button = create_toolbutton(self,
                                             icon=ima.icon('stop'),
                                             text=_("Stop"),
                                             tip=_("Stop current analysis"),
                                             text_beside_icon=True)
        self.filecombo.valid.connect(self.start_button.setEnabled)
        self.filecombo.valid.connect(self.show_data)

        browse_button = create_toolbutton(self,
                                          icon=ima.icon('fileopen'),
                                          tip=_('Select Python file'),
                                          triggered=self.select_file)

        self.ratelabel = QLabel()
        self.datelabel = QLabel()
        self.log_button = create_toolbutton(self,
                                            icon=ima.icon('log'),
                                            text=_("Output"),
                                            text_beside_icon=True,
                                            tip=_("Complete output"),
                                            triggered=self.show_log)
        self.treewidget = ResultsTree(self)

        hlayout1 = QHBoxLayout()
        hlayout1.addWidget(self.filecombo)
        hlayout1.addWidget(browse_button)
        hlayout1.addWidget(self.start_button)
        hlayout1.addWidget(self.stop_button)

        hlayout2 = QHBoxLayout()
        hlayout2.addWidget(self.ratelabel)
        hlayout2.addStretch()
        hlayout2.addWidget(self.datelabel)
        hlayout2.addStretch()
        hlayout2.addWidget(self.log_button)

        layout = QVBoxLayout()
        layout.addLayout(hlayout1)
        layout.addLayout(hlayout2)
        layout.addWidget(self.treewidget)
        self.setLayout(layout)

        self.process = None
        self.set_running_state(False)
        self.show_data()

        if self.rdata:
            self.remove_obsolete_items()
            self.filecombo.addItems(self.get_filenames())
        else:
            self.start_button.setEnabled(False)

    def analyze(self, filename):
        filename = to_text_string(filename)  # filename is a QString instance
        self.kill_if_running()
        index, _data = self.get_data(filename)
        if index is None:
            self.filecombo.addItem(filename)
            self.filecombo.setCurrentIndex(self.filecombo.count() - 1)
        else:
            self.filecombo.setCurrentIndex(self.filecombo.findText(filename))
        self.filecombo.selected()
        if self.filecombo.is_valid():
            self.start()

    @Slot()
    def select_file(self):
        self.redirect_stdio.emit(False)
        filename, _selfilter = getopenfilename(
            self, _("Select Python file"), getcwd_or_home(),
            _("Python files") + " (*.py ; *.pyw)")
        self.redirect_stdio.emit(True)
        if filename:
            self.analyze(filename)

    def remove_obsolete_items(self):
        """Removing obsolete items"""
        self.rdata = [(filename, data) for filename, data in self.rdata
                      if is_module_or_package(filename)]

    def get_filenames(self):
        return [filename for filename, _data in self.rdata]

    def get_data(self, filename):
        filename = osp.abspath(filename)
        for index, (fname, data) in enumerate(self.rdata):
            if fname == filename:
                return index, data
        else:
            return None, None

    def set_data(self, filename, data):
        filename = osp.abspath(filename)
        index, _data = self.get_data(filename)
        if index is not None:
            self.rdata.pop(index)
        self.rdata.insert(0, (filename, data))
        self.save()

    def save(self):
        while len(self.rdata) > self.max_entries:
            self.rdata.pop(-1)
        pickle.dump([self.VERSION] + self.rdata, open(self.DATAPATH, 'wb'), 2)

    @Slot()
    def show_log(self):
        if self.output:
            TextEditor(self.output,
                       title=_("Pylint output"),
                       readonly=True,
                       size=(700, 500)).exec_()

    @Slot()
    def start(self):
        filename = to_text_string(self.filecombo.currentText())

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(osp.dirname(filename))
        self.process.readyReadStandardOutput.connect(self.read_output)
        self.process.readyReadStandardError.connect(
            lambda: self.read_output(error=True))
        self.process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self.finished(ec, es))
        self.stop_button.clicked.connect(self.process.kill)

        self.output = ''
        self.error_output = ''

        plver = PYLINT_VER
        if plver is not None:
            p_args = ['-m', 'pylint', '--output-format=text']
            if plver.split('.')[0] == '0':
                p_args += ['-i', 'yes']
            else:
                # Option '-i' (alias for '--include-ids') was removed in pylint
                # 1.0
                p_args += ["--msg-template='{msg_id}:{line:3d},"\
                           "{column}: {obj}: {msg}"]
            p_args += [osp.basename(filename)]
        else:
            p_args = [osp.basename(filename)]
        self.process.start(sys.executable, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))

    def set_running_state(self, state=True):
        self.start_button.setEnabled(not state)
        self.stop_button.setEnabled(state)

    def read_output(self, error=False):
        if error:
            self.process.setReadChannel(QProcess.StandardError)
        else:
            self.process.setReadChannel(QProcess.StandardOutput)
        qba = QByteArray()
        while self.process.bytesAvailable():
            if error:
                qba += self.process.readAllStandardError()
            else:
                qba += self.process.readAllStandardOutput()
        text = to_text_string(locale_codec.toUnicode(qba.data()))
        if error:
            self.error_output += text
        else:
            self.output += text

    def finished(self, exit_code, exit_status):
        self.set_running_state(False)
        if not self.output:
            if self.error_output:
                QMessageBox.critical(self, _("Error"), self.error_output)
                print("pylint error:\n\n" + self.error_output, file=sys.stderr)
            return

        # Convention, Refactor, Warning, Error
        results = {'C:': [], 'R:': [], 'W:': [], 'E:': []}
        txt_module = '************* Module '

        module = ''  # Should not be needed - just in case something goes wrong
        for line in self.output.splitlines():
            if line.startswith(txt_module):
                # New module
                module = line[len(txt_module):]
                continue
            # Supporting option include-ids: ('R3873:' instead of 'R:')
            if not re.match('^[CRWE]+([0-9]{4})?:', line):
                continue
            i1 = line.find(':')
            if i1 == -1:
                continue
            msg_id = line[:i1]
            i2 = line.find(':', i1 + 1)
            if i2 == -1:
                continue
            line_nb = line[i1 + 1:i2].strip()
            if not line_nb:
                continue
            line_nb = int(line_nb.split(',')[0])
            message = line[i2 + 1:]
            item = (module, line_nb, message, msg_id)
            results[line[0] + ':'].append(item)

        # Rate
        rate = None
        txt_rate = 'Your code has been rated at '
        i_rate = self.output.find(txt_rate)
        if i_rate > 0:
            i_rate_end = self.output.find('/10', i_rate)
            if i_rate_end > 0:
                rate = self.output[i_rate + len(txt_rate):i_rate_end]

        # Previous run
        previous = ''
        if rate is not None:
            txt_prun = 'previous run: '
            i_prun = self.output.find(txt_prun, i_rate_end)
            if i_prun > 0:
                i_prun_end = self.output.find('/10', i_prun)
                previous = self.output[i_prun + len(txt_prun):i_prun_end]

        filename = to_text_string(self.filecombo.currentText())
        self.set_data(filename, (time.localtime(), rate, previous, results))
        self.output = self.error_output + self.output
        self.show_data(justanalyzed=True)

    def kill_if_running(self):
        if self.process is not None:
            if self.process.state() == QProcess.Running:
                self.process.kill()
                self.process.waitForFinished()

    def show_data(self, justanalyzed=False):
        if not justanalyzed:
            self.output = None
        self.log_button.setEnabled(self.output is not None \
                                   and len(self.output) > 0)
        self.kill_if_running()
        filename = to_text_string(self.filecombo.currentText())
        if not filename:
            return

        _index, data = self.get_data(filename)
        if data is None:
            text = _('Source code has not been rated yet.')
            self.treewidget.clear_results()
            date_text = ''
        else:
            datetime, rate, previous_rate, results = data
            if rate is None:
                text = _('Analysis did not succeed '
                         '(see output for more details).')
                self.treewidget.clear_results()
                date_text = ''
            else:
                text_style = "<span style=\'color: #444444\'><b>%s </b></span>"
                rate_style = "<span style=\'color: %s\'><b>%s</b></span>"
                prevrate_style = "<span style=\'color: #666666\'>%s</span>"
                color = "#FF0000"
                if float(rate) > 5.:
                    color = "#22AA22"
                elif float(rate) > 3.:
                    color = "#EE5500"
                text = _('Global evaluation:')
                text = (text_style % text) + (rate_style % (color,
                                                            ('%s/10' % rate)))
                if previous_rate:
                    text_prun = _('previous run:')
                    text_prun = ' (%s %s/10)' % (text_prun, previous_rate)
                    text += prevrate_style % text_prun
                self.treewidget.set_results(filename, results)
                date = to_text_string(time.strftime("%d %b %Y %H:%M",
                                                    datetime),
                                      encoding='utf8')
                date_text = text_style % date

        self.ratelabel.setText(text)
        self.datelabel.setText(date_text)
Example #51
0
class ProfilerWidget(PluginMainWidget):
    """
    Profiler widget.
    """
    ENABLE_SPINNER = True
    DATAPATH = get_conf_path('profiler.results')

    # --- Signals
    # ------------------------------------------------------------------------
    sig_edit_goto_requested = Signal(str, int, str)
    """
    This signal will request to open a file in a given row and column
    using a code editor.

    Parameters
    ----------
    path: str
        Path to file.
    row: int
        Cursor starting row position.
    word: str
        Word to select on given row.
    """

    sig_redirect_stdio_requested = Signal(bool)
    """
    This signal is emitted to request the main application to redirect
    standard output/error when using Open/Save/Browse dialogs within widgets.

    Parameters
    ----------
    redirect: bool
        Start redirect (True) or stop redirect (False).
    """

    sig_started = Signal()
    """This signal is emitted to inform the profiling process has started."""

    sig_finished = Signal()
    """This signal is emitted to inform the profile profiling has finished."""
    def __init__(self, name=None, plugin=None, parent=None):
        super().__init__(name, plugin, parent)
        self.set_conf('text_color', MAIN_TEXT_COLOR)

        # Attributes
        self._last_wdir = None
        self._last_args = None
        self._last_pythonpath = None
        self.error_output = None
        self.output = None
        self.running = False
        self.text_color = self.get_conf('text_color')

        # Widgets
        self.process = None
        self.filecombo = PythonModulesComboBox(
            self, id_=ProfilerWidgetMainToolbarItems.FileCombo)
        self.datatree = ProfilerDataTree(self)
        self.datelabel = QLabel()
        self.datelabel.ID = ProfilerWidgetInformationToolbarItems.DateLabel

        # Layout
        layout = QVBoxLayout()
        layout.addWidget(self.datatree)
        self.setLayout(layout)

        # Signals
        self.datatree.sig_edit_goto_requested.connect(
            self.sig_edit_goto_requested)

    # --- PluginMainWidget API
    # ------------------------------------------------------------------------
    def get_title(self):
        return _('Profiler')

    def get_focus_widget(self):
        return self.datatree

    def setup(self):
        self.start_action = self.create_action(
            ProfilerWidgetActions.Run,
            text=_("Run profiler"),
            tip=_("Run profiler"),
            icon=self.create_icon('run'),
            triggered=self.run,
        )
        browse_action = self.create_action(
            ProfilerWidgetActions.Browse,
            text='',
            tip=_('Select Python script'),
            icon=self.create_icon('fileopen'),
            triggered=lambda x: self.select_file(),
        )
        self.log_action = self.create_action(
            ProfilerWidgetActions.ShowOutput,
            text=_("Output"),
            tip=_("Show program's output"),
            icon=self.create_icon('log'),
            triggered=self.show_log,
        )
        self.collapse_action = self.create_action(
            ProfilerWidgetActions.Collapse,
            text=_('Collapse'),
            tip=_('Collapse one level up'),
            icon=self.create_icon('collapse'),
            triggered=lambda x=None: self.datatree.change_view(-1),
        )
        self.expand_action = self.create_action(
            ProfilerWidgetActions.Expand,
            text=_('Expand'),
            tip=_('Expand one level down'),
            icon=self.create_icon('expand'),
            triggered=lambda x=None: self.datatree.change_view(1),
        )
        self.save_action = self.create_action(
            ProfilerWidgetActions.SaveData,
            text=_("Save data"),
            tip=_('Save profiling data'),
            icon=self.create_icon('filesave'),
            triggered=self.save_data,
        )
        self.load_action = self.create_action(
            ProfilerWidgetActions.LoadData,
            text=_("Load data"),
            tip=_('Load profiling data for comparison'),
            icon=self.create_icon('fileimport'),
            triggered=self.compare,
        )
        self.clear_action = self.create_action(
            ProfilerWidgetActions.Clear,
            text=_("Clear comparison"),
            tip=_("Clear comparison"),
            icon=self.create_icon('editdelete'),
            triggered=self.clear,
        )
        self.clear_action.setEnabled(False)

        # Main Toolbar
        toolbar = self.get_main_toolbar()
        for item in [self.filecombo, browse_action, self.start_action]:
            self.add_item_to_toolbar(
                item,
                toolbar=toolbar,
                section=ProfilerWidgetMainToolbarSections.Main,
            )

        # Secondary Toolbar
        secondary_toolbar = self.create_toolbar(
            ProfilerWidgetToolbars.Information)
        for item in [
                self.collapse_action, self.expand_action,
                self.create_stretcher(
                    id_=ProfilerWidgetInformationToolbarItems.Stretcher1),
                self.datelabel,
                self.create_stretcher(
                    id_=ProfilerWidgetInformationToolbarItems.Stretcher2),
                self.log_action, self.save_action, self.load_action,
                self.clear_action
        ]:
            self.add_item_to_toolbar(
                item,
                toolbar=secondary_toolbar,
                section=ProfilerWidgetInformationToolbarSections.Main,
            )

        # Setup
        if not is_profiler_installed():
            # This should happen only on certain GNU/Linux distributions
            # or when this a home-made Python build because the Python
            # profilers are included in the Python standard library
            for widget in (self.datatree, self.filecombo, self.start_action):
                widget.setDisabled(True)
            url = 'https://docs.python.org/3/library/profile.html'
            text = '%s <a href=%s>%s</a>' % (_('Please install'), url,
                                             _("the Python profiler modules"))
            self.datelabel.setText(text)

    def update_actions(self):
        if self.running:
            icon = self.create_icon('stop')
        else:
            icon = self.create_icon('run')
        self.start_action.setIcon(icon)

        self.start_action.setEnabled(bool(self.filecombo.currentText()))

    # --- Private API
    # ------------------------------------------------------------------------
    def _kill_if_running(self):
        """Kill the profiling process if it is running."""
        if self.process is not None:
            if self.process.state() == QProcess.Running:
                self.process.close()
                self.process.waitForFinished(1000)

        self.update_actions()

    def _finished(self, exit_code, exit_status):
        """
        Parse results once the profiling process has ended.

        Parameters
        ----------
        exit_code: int
            QProcess exit code.
        exit_status: str
            QProcess exit status.
        """
        self.running = False
        self.show_errorlog()  # If errors occurred, show them.
        self.output = self.error_output + self.output
        self.datelabel.setText('')
        self.show_data(justanalyzed=True)
        self.update_actions()

    def _read_output(self, error=False):
        """
        Read otuput from QProcess.

        Parameters
        ----------
        error: bool, optional
            Process QProcess output or error channels. Default is False.
        """
        if error:
            self.process.setReadChannel(QProcess.StandardError)
        else:
            self.process.setReadChannel(QProcess.StandardOutput)

        qba = QByteArray()
        while self.process.bytesAvailable():
            if error:
                qba += self.process.readAllStandardError()
            else:
                qba += self.process.readAllStandardOutput()

        text = to_text_string(qba.data(), encoding='utf-8')
        if error:
            self.error_output += text
        else:
            self.output += text

    # --- Public API
    # ------------------------------------------------------------------------
    def save_data(self):
        """Save data."""
        title = _("Save profiler result")
        filename, _selfilter = getsavefilename(
            self,
            title,
            getcwd_or_home(),
            _("Profiler result") + " (*.Result)",
        )

        if filename:
            self.datatree.save_data(filename)

    def compare(self):
        """Compare previous saved run with last run."""
        filename, _selfilter = getopenfilename(
            self,
            _("Select script to compare"),
            getcwd_or_home(),
            _("Profiler result") + " (*.Result)",
        )

        if filename:
            self.datatree.compare(filename)
            self.show_data()
            self.clear_action.setEnabled(True)

    def clear(self):
        """Clear data in tree."""
        self.datatree.compare(None)
        self.datatree.hide_diff_cols(True)
        self.show_data()
        self.clear_action.setEnabled(False)

    def analyze(self, filename, wdir=None, args=None, pythonpath=None):
        """
        Start the profiling process.

        Parameters
        ----------
        wdir: str
            Working directory path string. Default is None.
        args: list
            Arguments to pass to the profiling process. Default is None.
        pythonpath: str
            Python path string. Default is None.
        """
        if not is_profiler_installed():
            return

        self._kill_if_running()

        # TODO: storing data is not implemented yet
        # index, _data = self.get_data(filename)
        combo = self.filecombo
        items = [combo.itemText(idx) for idx in range(combo.count())]
        index = None
        if index is None and filename not in items:
            self.filecombo.addItem(filename)
            self.filecombo.setCurrentIndex(self.filecombo.count() - 1)
        else:
            self.filecombo.setCurrentIndex(self.filecombo.findText(filename))

        self.filecombo.selected()
        if self.filecombo.is_valid():
            if wdir is None:
                wdir = osp.dirname(filename)

            self.start(wdir, args, pythonpath)

    def select_file(self, filename=None):
        """
        Select filename to profile.

        Parameters
        ----------
        filename: str, optional
            Path to filename to profile. default is None.

        Notes
        -----
        If no `filename` is provided an open filename dialog will be used.
        """
        if filename is None:
            self.sig_redirect_stdio_requested.emit(False)
            filename, _selfilter = getopenfilename(
                self, _("Select Python script"), getcwd_or_home(),
                _("Python scripts") + " (*.py ; *.pyw)")
            self.sig_redirect_stdio_requested.emit(True)

        if filename:
            self.analyze(filename)

    def show_log(self):
        """Show process output log."""
        if self.output:
            output_dialog = TextEditor(
                self.output,
                title=_("Profiler output"),
                readonly=True,
                parent=self,
            )
            output_dialog.resize(700, 500)
            output_dialog.exec_()

    def show_errorlog(self):
        """Show process error log."""
        if self.error_output:
            output_dialog = TextEditor(
                self.error_output,
                title=_("Profiler output"),
                readonly=True,
                parent=self,
            )
            output_dialog.resize(700, 500)
            output_dialog.exec_()

    def start(self, wdir=None, args=None, pythonpath=None):
        """
        Start the profiling process.

        Parameters
        ----------
        wdir: str
            Working directory path string. Default is None.
        args: list
            Arguments to pass to the profiling process. Default is None.
        pythonpath: str
            Python path string. Default is None.
        """
        filename = to_text_string(self.filecombo.currentText())
        if wdir is None:
            wdir = self._last_wdir
            if wdir is None:
                wdir = osp.basename(filename)

        if args is None:
            args = self._last_args
            if args is None:
                args = []

        if pythonpath is None:
            pythonpath = self._last_pythonpath

        self._last_wdir = wdir
        self._last_args = args
        self._last_pythonpath = pythonpath

        self.datelabel.setText(_('Profiling, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(wdir)
        self.process.readyReadStandardOutput.connect(self._read_output)
        self.process.readyReadStandardError.connect(
            lambda: self._read_output(error=True))
        self.process.finished.connect(
            lambda ec, es=QProcess.ExitStatus: self._finished(ec, es))
        self.process.finished.connect(self.stop_spinner)

        if pythonpath is not None:
            env = [
                to_text_string(_pth)
                for _pth in self.process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, __, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)

            processEnvironment.insert("PYTHONIOENCODING", "utf8")
            self.process.setProcessEnvironment(processEnvironment)

        self.output = ''
        self.error_output = ''
        self.running = True
        self.start_spinner()

        p_args = ['-m', 'cProfile', '-o', self.DATAPATH]
        if os.name == 'nt':
            # On Windows, one has to replace backslashes by slashes to avoid
            # confusion with escape characters (otherwise, for example, '\t'
            # will be interpreted as a tabulation):
            p_args.append(osp.normpath(filename).replace(os.sep, '/'))
        else:
            p_args.append(filename)

        if args:
            p_args.extend(shell_split(args))

        executable = self.get_conf('executable', section='main_interpreter')

        self.process.start(executable, p_args)
        running = self.process.waitForStarted()
        if not running:
            QMessageBox.critical(
                self,
                _("Error"),
                _("Process failed to start"),
            )
        self.update_actions()

    def stop(self):
        """Stop the running process."""
        self.running = False
        self.process.close()
        self.process.waitForFinished(1000)
        self.stop_spinner()
        self.update_actions()

    def run(self):
        """Toggle starting or running the profiling process."""
        if self.running:
            self.stop()
        else:
            self.start()

    def show_data(self, justanalyzed=False):
        """
        Show analyzed data on results tree.

        Parameters
        ----------
        justanalyzed: bool, optional
            Default is False.
        """
        if not justanalyzed:
            self.output = None

        self.log_action.setEnabled(self.output is not None
                                   and len(self.output) > 0)
        self._kill_if_running()
        filename = to_text_string(self.filecombo.currentText())
        if not filename:
            return

        self.datelabel.setText(_('Sorting data, please wait...'))
        QApplication.processEvents()

        self.datatree.load_data(self.DATAPATH)
        self.datatree.show_tree()

        text_style = "<span style=\'color: %s\'><b>%s </b></span>"
        date_text = text_style % (self.text_color,
                                  time.strftime("%Y-%m-%d %H:%M:%S",
                                                time.localtime()))
        self.datelabel.setText(date_text)
Example #52
0
    def __init__(self,
                 plugin,
                 name,
                 history_filename,
                 config_options,
                 additional_options,
                 interpreter_versions,
                 connection_file=None,
                 hostname=None,
                 menu_actions=None,
                 slave=False,
                 external_kernel=False):
        super(ClientWidget, self).__init__(plugin)
        SaveHistoryMixin.__init__(self)

        # --- Init attrs
        self.name = name
        self.history_filename = get_conf_path(history_filename)
        self.connection_file = connection_file
        self.hostname = hostname
        self.menu_actions = menu_actions
        self.slave = slave

        # --- Other attrs
        self.options_button = None
        self.stop_button = None
        self.stop_icon = ima.icon('stop')
        self.history = []

        # --- Widgets
        self.shellwidget = ShellWidget(
            config=config_options,
            ipyclient=self,
            additional_options=additional_options,
            interpreter_versions=interpreter_versions,
            external_kernel=external_kernel,
            local_kernel=True)
        self.infowidget = WebView(self)
        self.set_infowidget_font()
        self.loading_page = self._create_loading_page()
        self._show_loading_page()

        # --- Layout
        vlayout = QVBoxLayout()
        toolbar_buttons = self.get_toolbar_buttons()
        hlayout = QHBoxLayout()
        for button in toolbar_buttons:
            hlayout.addWidget(button)
        vlayout.addLayout(hlayout)
        vlayout.setContentsMargins(0, 0, 0, 0)
        vlayout.addWidget(self.shellwidget)
        vlayout.addWidget(self.infowidget)
        self.setLayout(vlayout)

        # --- Exit function
        self.exit_callback = lambda: plugin.close_client(client=self)

        # --- Signals
        # As soon as some content is printed in the console, stop
        # our loading animation
        document = self.get_control().document()
        document.contentsChange.connect(self._hide_loading_page)
Example #53
0
# Add external dependencies subrepo paths to sys.path
# NOTE: Please don't move this from here!
HERE = osp.dirname(os.path.realpath(__file__))
DEPS_PATH = osp.join(HERE, 'external-deps')
i = 0
for path in os.listdir(DEPS_PATH):
    external_dep_path = osp.join(DEPS_PATH, path)
    sys.path.insert(i, external_dep_path)
    i += 1

import pytest

# Remove temp conf_dir before starting the tests
from spyder.config.base import get_conf_path
conf_dir = get_conf_path()
if osp.isdir(conf_dir):
    shutil.rmtree(conf_dir)


def pytest_addoption(parser):
    """Add option to run slow tests."""
    parser.addoption("--run-slow",
                     action="store_true",
                     default=False,
                     help="Run slow tests")


def pytest_collection_modifyitems(config, items):
    """
    Decide what tests to run (slow or fast) according to the --run-slow
Example #54
0
class WorkingDirectory(QToolBar, SpyderPluginMixin):
    """
    Working directory changer widget
    """
    CONF_SECTION = 'workingdir'
    CONFIGWIDGET_CLASS = WorkingDirectoryConfigPage
    LOG_PATH = get_conf_path(CONF_SECTION)

    sig_option_changed = Signal(str, object)
    set_previous_enabled = Signal(bool)
    set_next_enabled = Signal(bool)
    redirect_stdio = Signal(bool)
    set_explorer_cwd = Signal(str)
    refresh_findinfiles = Signal()
    set_current_console_wd = Signal(str)

    def __init__(self, parent, workdir=None, **kwds):
        if PYQT5:
            super(WorkingDirectory, self).__init__(parent, **kwds)
        else:
            QToolBar.__init__(self, parent)
            SpyderPluginMixin.__init__(self, parent)

        # Initialize plugin
        self.initialize_plugin()

        self.setWindowTitle(self.get_plugin_title())  # Toolbar title
        self.setObjectName(
            self.get_plugin_title())  # Used to save Window state

        # Previous dir action
        self.history = []
        self.histindex = None
        self.previous_action = create_action(self,
                                             "previous",
                                             None,
                                             ima.icon('previous'),
                                             _('Back'),
                                             triggered=self.previous_directory)
        self.addAction(self.previous_action)

        # Next dir action
        self.history = []
        self.histindex = None
        self.next_action = create_action(self,
                                         "next",
                                         None,
                                         ima.icon('next'),
                                         _('Next'),
                                         triggered=self.next_directory)
        self.addAction(self.next_action)

        # Enable/disable previous/next actions
        self.set_previous_enabled.connect(self.previous_action.setEnabled)
        self.set_next_enabled.connect(self.next_action.setEnabled)

        # Path combo box
        adjust = self.get_option('working_dir_adjusttocontents')
        self.pathedit = PathComboBox(self, adjust_to_contents=adjust)
        self.pathedit.setToolTip(
            _("This is the working directory for newly\n"
              "opened consoles (Python/IPython consoles and\n"
              "terminals), for the file explorer, for the\n"
              "find in files plugin and for new files\n"
              "created in the editor"))
        self.pathedit.open_dir.connect(self.chdir)
        self.pathedit.activated[str].connect(self.chdir)
        self.pathedit.setMaxCount(self.get_option('working_dir_history'))
        wdhistory = self.load_wdhistory(workdir)
        if workdir is None:
            if self.get_option('console/use_project_or_home_directory'):
                workdir = get_home_dir()
            else:
                workdir = self.get_option('console/fixed_directory',
                                          default='')
                if not osp.isdir(workdir):
                    workdir = get_home_dir()
        self.chdir(workdir)
        self.pathedit.addItems(wdhistory)
        self.pathedit.selected_text = self.pathedit.currentText()
        self.refresh_plugin()
        self.addWidget(self.pathedit)

        # Browse action
        browse_action = create_action(self,
                                      "browse",
                                      None,
                                      ima.icon('DirOpenIcon'),
                                      _('Browse a working directory'),
                                      triggered=self.select_directory)
        self.addAction(browse_action)

        # Parent dir action
        parent_action = create_action(self,
                                      "parent",
                                      None,
                                      ima.icon('up'),
                                      _('Change to parent directory'),
                                      triggered=self.parent_directory)
        self.addAction(parent_action)

    #------ SpyderPluginWidget API ---------------------------------------------
    def get_plugin_title(self):
        """Return widget title"""
        return _('Current working directory')

    def get_plugin_icon(self):
        """Return widget icon"""
        return ima.icon('DirOpenIcon')

    def get_plugin_actions(self):
        """Setup actions"""
        return (None, None)

    def register_plugin(self):
        """Register plugin in Spyder's main window"""
        self.redirect_stdio.connect(self.main.redirect_internalshell_stdio)
        self.main.console.shell.refresh.connect(self.refresh_plugin)
        iconsize = 24
        self.setIconSize(QSize(iconsize, iconsize))
        self.main.addToolBar(self)

    def refresh_plugin(self):
        """Refresh widget"""
        curdir = getcwd_or_home()
        self.pathedit.add_text(curdir)
        self.save_wdhistory()
        self.set_previous_enabled.emit(self.histindex is not None
                                       and self.histindex > 0)
        self.set_next_enabled.emit(self.histindex is not None and \
                                   self.histindex < len(self.history)-1)

    def apply_plugin_settings(self, options):
        """Apply configuration file's plugin settings"""
        pass

    def closing_plugin(self, cancelable=False):
        """Perform actions before parent main window is closed"""
        return True

    #------ Public API ---------------------------------------------------------
    def load_wdhistory(self, workdir=None):
        """Load history from a text file in user home directory"""
        if osp.isfile(self.LOG_PATH):
            wdhistory, _ = encoding.readlines(self.LOG_PATH)
            wdhistory = [name for name in wdhistory if os.path.isdir(name)]
        else:
            if workdir is None:
                workdir = get_home_dir()
            wdhistory = [workdir]
        return wdhistory

    def save_wdhistory(self):
        """Save history to a text file in user home directory"""
        text = [ to_text_string( self.pathedit.itemText(index) ) \
                 for index in range(self.pathedit.count()) ]
        encoding.writelines(text, self.LOG_PATH)

    @Slot()
    def select_directory(self):
        """Select directory"""
        self.redirect_stdio.emit(False)
        directory = getexistingdirectory(self.main, _("Select directory"),
                                         getcwd_or_home())
        if directory:
            self.chdir(directory)
        self.redirect_stdio.emit(True)

    @Slot()
    def previous_directory(self):
        """Back to previous directory"""
        self.histindex -= 1
        self.chdir(directory='', browsing_history=True)

    @Slot()
    def next_directory(self):
        """Return to next directory"""
        self.histindex += 1
        self.chdir(directory='', browsing_history=True)

    @Slot()
    def parent_directory(self):
        """Change working directory to parent directory"""
        self.chdir(os.path.join(getcwd_or_home(), os.path.pardir))

    @Slot(str)
    @Slot(str, bool)
    @Slot(str, bool, bool)
    @Slot(str, bool, bool, bool)
    def chdir(self,
              directory,
              browsing_history=False,
              refresh_explorer=True,
              refresh_console=True):
        """Set directory as working directory"""
        if directory:
            directory = osp.abspath(to_text_string(directory))

        # Working directory history management
        if browsing_history:
            directory = self.history[self.histindex]
        elif directory in self.history:
            self.histindex = self.history.index(directory)
        else:
            if self.histindex is None:
                self.history = []
            else:
                self.history = self.history[:self.histindex + 1]
            self.history.append(directory)
            self.histindex = len(self.history) - 1

        # Changing working directory
        try:
            os.chdir(directory)
            if refresh_explorer:
                self.set_explorer_cwd.emit(directory)
            if refresh_console:
                self.set_current_console_wd.emit(directory)
            self.refresh_findinfiles.emit()
        except OSError:
            self.history.pop(self.histindex)
        self.refresh_plugin()
Example #55
0
# Third party imports
from qtpy.QtCore import QObject, QTimer, Signal
from qtpy.QtWidgets import QApplication

# Local imports
from spyder import dependencies
from spyder.config.base import _, DEBUG, debug_print, get_conf_path
from spyder.utils import sourcecode
from spyder.utils.introspection.plugin_client import PluginClient
from spyder.utils.introspection.utils import CodeInfo


PLUGINS = ['rope', 'jedi', 'fallback']

LOG_FILENAME = get_conf_path('introspection.log')
DEBUG_EDITOR = DEBUG >= 3
LEAD_TIME_SEC = 0.25


ROPE_REQVER = '>=0.9.4'
dependencies.add('rope',
                 _("Editor's code completion, go-to-definition and help"),
                 required_version=ROPE_REQVER)

JEDI_REQVER = '>=0.8.1'
dependencies.add('jedi',
                 _("Editor's code completion, go-to-definition and help"),
                 required_version=JEDI_REQVER)

Example #56
0
    def start(self):
        self.zmq_out_socket = self.context.socket(zmq.PAIR)
        self.zmq_out_port = self.zmq_out_socket.bind_to_random_port('tcp://*')
        self.zmq_in_socket = self.context.socket(zmq.PAIR)
        self.zmq_in_socket.set_hwm(0)
        self.zmq_in_port = self.zmq_in_socket.bind_to_random_port('tcp://*')
        self.transport_args += ['--zmq-in-port', self.zmq_out_port,
                                '--zmq-out-port', self.zmq_in_port]

        server_log = subprocess.PIPE
        if get_debug_level() > 0:
            # Create server log file
            server_log_fname = 'server_{0}.log'.format(self.language)
            server_log_file = get_conf_path(osp.join('lsp_logs',
                                                     server_log_fname))
            if not osp.exists(osp.dirname(server_log_file)):
                os.makedirs(osp.dirname(server_log_file))
            server_log = open(server_log_file, 'w')

            # Start server with logging options
            if get_debug_level() == 2:
                self.server_args.append('-v')
            elif get_debug_level() == 3:
                self.server_args.append('-vv')

        if not self.external_server:
            logger.info('Starting server: {0}'.format(
                ' '.join(self.server_args)))
            creation_flags = 0
            if os.name == 'nt':
                creation_flags = (subprocess.CREATE_NEW_PROCESS_GROUP
                                  | 0x08000000)  # CREATE_NO_WINDOW

            if os.environ.get('CI') and os.name == 'nt':
                # The following patching avoids:
                #
                # OSError: [WinError 6] The handle is invalid
                #
                # while running our tests in CI services on Windows
                # (they run fine locally).
                # See this comment for an explanation:
                # https://stackoverflow.com/q/43966523/
                # 438386#comment74964124_43966523
                def patched_cleanup():
                    pass
                subprocess._cleanup = patched_cleanup

            self.lsp_server = subprocess.Popen(
                self.server_args,
                stdout=server_log,
                stderr=subprocess.STDOUT,
                creationflags=creation_flags)

        client_log = subprocess.PIPE
        if get_debug_level() > 0:
            # Client log file
            client_log_fname = 'client_{0}.log'.format(self.language)
            client_log_file = get_conf_path(osp.join('lsp_logs',
                                                     client_log_fname))
            if not osp.exists(osp.dirname(client_log_file)):
                os.makedirs(osp.dirname(client_log_file))
            client_log = open(client_log_file, 'w')

        new_env = dict(os.environ)
        python_path = os.pathsep.join(sys.path)[1:]
        new_env['PYTHONPATH'] = python_path
        self.transport_args = list(map(str, self.transport_args))
        logger.info('Starting transport: {0}'
                    .format(' '.join(self.transport_args)))
        self.transport_client = subprocess.Popen(self.transport_args,
                                                 stdout=subprocess.PIPE,
                                                 stderr=client_log,
                                                 env=new_env)

        fid = self.zmq_in_socket.getsockopt(zmq.FD)
        self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self)
        self.notifier.activated.connect(self.on_msg_received)

        # This is necessary for tests to pass locally!
        logger.debug('LSP {} client started!'.format(self.language))