예제 #1
0
    def __init__(self, cmd_list, environ=None):
        """
        Process worker based on a QProcess for non blocking UI.

        Parameters
        ----------
        cmd_list : list of str
            Command line arguments to execute.
        environ : dict
            Process environment,
        """
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._partial_stderr = None
        self._started = False

        self._timer = QTimer()
        self._process = QProcess()
        self._set_environment(environ)

        self._timer.setInterval(150)
        self._timer.timeout.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)
예제 #2
0
    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 = ''

        clver = COALA_VER
        if clver is not None:
            c_args = ['-m', 'run_coala']
        self.process.start(sys.executable, c_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
예제 #3
0
    def update_environment(self, filename, name=None, prefix=None, emit=False):
        """
        Set environment at 'prefix' or 'name' to match 'filename' spec as yaml.
        """
        if name and prefix:
            raise TypeError("Exactly one of 'name' or 'prefix' is required.")

        if self._is_not_running:
            if name:
                temporal_envname = name

            if prefix:
                temporal_envname = 'tempenv' + int(random.random()*10000000)
                envs_dir = self.info()['envs_dirs'][0]
                os.symlink(prefix, os.sep.join([envs_dir, temporal_envname]))

            cmd = self._abspath(True)
            cmd.extend(['env', 'update', '--name', temporal_envname, '--file',
                        os.path.abspath(filename)])
            qprocess = QProcess()
            qprocess.start(cmd[0], cmd[1:])
            qprocess.waitForFinished()

            if prefix:
                os.unlink(os.sep.join([envs_dir, 'tempenv']))
예제 #4
0
    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:
            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(PYLINT_PATH, p_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
예제 #5
0
    def __init__(self,
                 cmd_list,
                 parse=False,
                 pip=False,
                 callback=None,
                 extra_kwargs={}):
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._parse = parse
        self._pip = pip
        self._conda = not pip
        self._callback = callback
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._extra_kwargs = extra_kwargs

        self._timer = QTimer()
        self._process = QProcess()

        self._timer.setInterval(150)

        self._timer.timeout.connect(self._communicate)
        # self._process.finished.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)
예제 #6
0
    def _create_process(
        self,
        installer: InstallerTypes = "pip",
    ):
        process = QProcess()
        if installer != "pip":
            process.setProgram(installer)
        else:
            process.setProgram(self._sys_executable_or_bundled_python())

        process.setProcessChannelMode(QProcess.MergedChannels)
        process.readyReadStandardOutput.connect(
            lambda process=process: self._on_stdout_ready(process))

        # setup process path
        env = QProcessEnvironment()
        combined_paths = os.pathsep.join([
            user_site_packages(),
            env.systemEnvironment().value("PYTHONPATH")
        ])
        env.insert("PYTHONPATH", combined_paths)
        # use path of parent process
        env.insert("PATH",
                   QProcessEnvironment.systemEnvironment().value("PATH"))
        process.setProcessEnvironment(env)
        self.set_output_widget(self._output_widget)
        process.finished.connect(
            lambda ec, es: self._on_process_finished(process, ec, es)
        )  # FIXME connecting lambda to finished signal is bug creating and may end with segfault when garbage
        # collection will consume Installer object before process end.
        return process
예제 #7
0
    def _create_process(
        self,
        installer: InstallerTypes = "pip",
    ):
        process = QProcess()
        process.setProcessChannelMode(QProcess.MergedChannels)
        process.readyReadStandardOutput.connect(
            lambda process=process: self._on_stdout_ready(process))
        env = QProcessEnvironment.systemEnvironment()

        if installer == "pip":
            process.setProgram(self._sys_executable_or_bundled_python())
            # patch process path
            combined_paths = os.pathsep.join([
                user_site_packages(),
                env.systemEnvironment().value("PYTHONPATH"),
            ])
            env.insert("PYTHONPATH", combined_paths)
        else:
            process.setProgram(installer)

        if installer == "mamba":
            from ..._version import version_tuple

            # To avoid napari version changing when installing a plugin, we
            # add a pin to the current napari version, that way we can
            # restrict any changes to the actual napari application.
            # Conda/mamba also pin python by default, so we effectively
            # constrain python and napari versions from changing, when
            # installing plugins inside the constructor bundled application.
            # See: https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-pkgs.html#preventing-packages-from-updating-pinning
            napari_version = ".".join(str(v) for v in version_tuple[:3])
            if env.contains("CONDA_PINNED_PACKAGES"):
                # str delimiter is '&'
                system_pins = f"&{env.value('CONDA_PINNED_PACKAGES')}"
            else:
                system_pins = ""
            env.insert(
                "CONDA_PINNED_PACKAGES",
                f"napari={napari_version}{system_pins}",
            )
            if os.name == "nt":
                # workaround https://github.com/napari/napari/issues/4247, 4484
                if not env.contains("TEMP"):
                    temp = gettempdir()
                    env.insert("TMP", temp)
                    env.insert("TEMP", temp)
                if not env.contains("USERPROFILE"):
                    env.insert("HOME", os.path.expanduser("~"))
                    env.insert("USERPROFILE", os.path.expanduser("~"))

        process.setProcessEnvironment(env)
        self.set_output_widget(self._output_widget)
        process.finished.connect(
            lambda ec, es: self._on_process_finished(process, ec, es)
        )  # FIXME connecting lambda to finished signal is bug creating and may end with segfault when garbage
        # collection will consume Installer object before process end.
        return process
예제 #8
0
파일: client.py 프로젝트: timgates42/spyder
    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()

        # 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)

            if os.name == "nt":
                # On Windows, some modules (notably Matplotlib)
                # cause exceptions if they cannot get the user home.
                # So, we need to pass the USERPROFILE env variable to
                # the PyLSP.
                if "USERPROFILE" in os.environ:
                    env.insert("USERPROFILE", os.environ["USERPROFILE"])

                # The PyLSP can't start on pip installations if APPDATA
                # is missing and the user has installed their packages on
                # that directory.
                # Fixes spyder-ide/spyder#17661
                if (not (is_anaconda() or is_pynsist())
                        and "APPDATA" in os.environ):
                    env.insert("APPDATA", os.environ["APPDATA"])
        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:])
예제 #9
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()

        # Use local PyLS instead of site-packages one.
        if DEV or running_under_pytest():
            running_in_ci = bool(os.environ.get('CI'))
            if os.name != 'nt' or os.name == 'nt' and not running_in_ci:
                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)

            # On Windows, some modules (notably Matplotlib)
            # cause exceptions if they cannot get the user home.
            # So, we need to pass the USERPROFILE env variable to
            # the PyLS.
            if os.name == "nt" and "USERPROFILE" in os.environ:
                env.insert("USERPROFILE", os.environ["USERPROFILE"])
        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:])
예제 #10
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:
            return

        # Set server log file
        server_log_file = None
        if get_debug_level() > 0:
            # Create server log file
            server_log_fname = 'server_{0}_{1}.log'.format(
                self.language, os.getpid())
            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))

            if self.stdio:
                if self.language == 'python':
                    self.server_args += ['--log-file', server_log_file]
                self.transport_args += ['--server-log-file', server_log_file]

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

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

        # 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.
        if self.language == 'python':
            cwd = get_conf_path('empty_cwd')
            if not osp.exists(cwd):
                os.mkdir(cwd)
        else:
            cwd = None

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

        # Start server
        self.server.start(self.server_args[0], self.server_args[1:])
예제 #11
0
    def create_process(self):
        self.shell.clear()

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)

        # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe)
        env = [
            to_text_string(_path)
            for _path in self.process.systemEnvironment()
        ]

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)

        add_pathlist_to_PYTHONPATH(env, self.path)
        self.process.setProcessEnvironment(processEnvironment)

        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)

        # Shell arguments
        if os.name == 'nt':
            p_args = ['/Q']
        else:
            p_args = ['-i']

        if self.arguments:
            p_args.extend(shell_split(self.arguments))

        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.finished.connect(self.finished)
        self.kill_button.clicked.connect(self.process.kill)

        if os.name == 'nt':
            self.process.start('cmd.exe', p_args)
        else:
            # Using bash:
            self.process.start('bash', p_args)
            self.send_to_process('PS1="\\u@\\h:\\w> "\n')

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

        return self.process
예제 #12
0
    def run(self):
        """Handle the connection with the server.
        """
        # Set up the zmq port.
        self.socket = self.context.socket(zmq.PAIR)
        self.port = self.socket.bind_to_random_port('tcp://*')

        # Set up the process.
        self.process = QProcess(self)
        if self.cwd:
            self.process.setWorkingDirectory(self.cwd)
        p_args = ['-u', self.target, str(self.port)]
        if self.extra_args is not None:
            p_args += self.extra_args

        # Set up environment variables.
        processEnvironment = QProcessEnvironment()
        env = self.process.systemEnvironment()
        if (self.env and 'PYTHONPATH' not in self.env) or self.env is None:
            python_path = osp.dirname(get_module_path('spyder'))
            # Add the libs to the python path.
            for lib in self.libs:
                try:
                    path = osp.dirname(imp.find_module(lib)[1])
                    python_path = osp.pathsep.join([python_path, path])
                except ImportError:
                    pass
            if self.extra_path:
                try:
                    python_path = osp.pathsep.join([python_path] +
                                                   self.extra_path)
                except Exception as e:
                    debug_print("Error when adding extra_path to plugin env")
                    debug_print(e)
            env.append("PYTHONPATH=%s" % python_path)
        if self.env:
            env.update(self.env)
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)
        self.process.setProcessEnvironment(processEnvironment)

        # Start the process and wait for started.
        self.process.start(self.executable, p_args)
        self.process.finished.connect(self._on_finished)
        running = self.process.waitForStarted()
        if not running:
            raise IOError('Could not start %s' % self)

        # Set up the socket notifer.
        fid = self.socket.getsockopt(zmq.FD)
        self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self)
        self.notifier.activated.connect(self._on_msg_received)
예제 #13
0
    def start(self, config, pythonpath):
        """
        Start process which will run the unit test suite.

        The process is run in the working directory specified in 'config',
        with the directories in `pythonpath` added to the Python path for the
        test process. The test results are written to the file
        `self.resultfilename`. The standard output and error are also recorded.
        Once the process is finished, `self.finished()` will be called.

        Parameters
        ----------
        config : TestConfig
            Unit test configuration.
        pythonpath : list of str
            List of directories to be added to the Python path

        Raises
        ------
        RuntimeError
            If process failed to start.
        """
        wdir = config.wdir

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.setWorkingDirectory(wdir)
        self.process.finished.connect(self.finished)

        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)

        executable = get_python_executable()
        p_args = ['-m', self.module] + self.create_argument_list()

        try:
            os.remove(self.resultfilename)
        except OSError:
            pass

        self.process.start(executable, p_args)
        running = self.process.waitForStarted()
        if not running:
            raise RuntimeError
예제 #14
0
    def start_process(self, process, args=None, cwd=None, env=None):
        """
        Starts a process interactively.

        :param process: Process to run
        :type process: str

        :param args: List of arguments (list of str)
        :type args: list

        :param cwd: Working directory
        :type cwd: str

        :param env: environment variables (dict).
        """
        self.setReadOnly(False)
        if env is None:
            env = {}
        if args is None:
            args = []
        if not self._running:
            self.process = QProcess()
            self.process.finished.connect(self._on_process_finished)
            self.process.started.connect(self.process_started.emit)
            self.process.error.connect(self._write_error)
            self.process.readyReadStandardError.connect(self._on_stderr)
            self.process.readyReadStandardOutput.connect(self._on_stdout)
            if cwd:
                self.process.setWorkingDirectory(cwd)
            e = self.process.systemEnvironment()
            ev = QProcessEnvironment()
            for v in e:
                values = v.split('=')
                ev.insert(values[0], '='.join(values[1:]))
            for k, v in env.items():
                ev.insert(k, v)
            self.process.setProcessEnvironment(ev)
            self._running = True
            self._process_name = process
            self._args = args
            if self._clear_on_start:
                self.clear()
            self._user_stop = False
            self._write_started()
            self.process.start(process, args)
            self.process.waitForStarted()
        else:
            _logger().warning('a process is already running')
예제 #15
0
파일: workers.py 프로젝트: 0xBADCA7/spyder
    def __init__(self, cmd_list, environ=None):
        """
        Process worker based on a QProcess for non blocking UI.

        Parameters
        ----------
        cmd_list : list of str
            Command line arguments to execute.
        environ : dict
            Process environment,
        """
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._started = False

        self._timer = QTimer()
        self._process = QProcess()
        self._set_environment(environ)

        self._timer.setInterval(150)
        self._timer.timeout.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)
예제 #16
0
    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"),
            )
예제 #17
0
 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:
         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(PYLINT_PATH, p_args)
     
     running = self.process.waitForStarted()
     self.set_running_state(running)
     if not running:
         QMessageBox.critical(self, _("Error"),
                              _("Process failed to start"))
예제 #18
0
파일: pylintgui.py 프로젝트: wpfeder/spyder
    def start(self):
        """Start the code analysis."""
        filename = str(self.filecombo.currentText())

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(getcwd_or_home())
        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}"
                ]

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

        p_args += [filename]
        processEnvironment = QProcessEnvironment()
        processEnvironment.insert("PYTHONIOENCODING", "utf8")
        self.process.setProcessEnvironment(processEnvironment)

        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"))
예제 #19
0
파일: client.py 프로젝트: cimeq/spyder
    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:])
예제 #20
0
    def restart(self):
        """Restart the napari application in a detached process."""
        process = QProcess()
        process.setProgram(sys.executable)

        if not running_as_bundled_app():
            process.setArguments(sys.argv)

        process.startDetached()
        self.close(quit_app=True)
예제 #21
0
    def start_server(self, filename, interpreter):
        """
        Start a notebook server asynchronously.

        Start a server which can render the given notebook and return
        immediately. Assume the server uses the given interpreter. The manager
        will check periodically whether the server is accepting requests and
        emit `sig_server_started` or `sig_server_timed_out` when appropriate.

        Parameters
        ----------
        filename : str
            File name of notebook to be rendered by the server.
        interpreter : str
            File name of Python interpreter to be used.
        """
        home_dir = get_home_dir()
        if filename.startswith(home_dir):
            nbdir = home_dir
        else:
            nbdir = osp.dirname(filename)

        logger.debug('Starting new notebook server for %s', nbdir)
        process = QProcess(None)
        serverscript = osp.join(osp.dirname(__file__), '../server/main.py')
        serverscript = osp.normpath(serverscript)
        arguments = [
            serverscript, '--no-browser', '--notebook-dir={}'.format(nbdir),
            '--NotebookApp.password='******'--KernelSpecManager.kernel_spec_class={}'.format(KERNELSPEC)
        ]
        if self.dark_theme:
            arguments.append('--dark')
        logger.debug('Arguments: %s', repr(arguments))

        if DEV:
            env = QProcessEnvironment.systemEnvironment()
            env.insert('PYTHONPATH', osp.dirname(get_module_path('spyder')))
            process.setProcessEnvironment(env)

        server_process = ServerProcess(process,
                                       notebook_dir=nbdir,
                                       interpreter=interpreter)
        process.setProcessChannelMode(QProcess.MergedChannels)
        process.readyReadStandardOutput.connect(
            lambda: self.read_server_output(server_process))
        process.errorOccurred.connect(
            lambda error: self.handle_error(server_process, error))
        process.finished.connect(lambda code, status: self.handle_finished(
            server_process, code, status))

        process.start(sys.executable, arguments)
        self.servers.append(server_process)

        self._check_server_started(server_process)
예제 #22
0
    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)
        print(filename)
        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, '/')
        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(_('Running tests, please wait...'))

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(filename)
        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()]
            baseshell.add_pathlist_to_PYTHONPATH(env, pythonpath)
            self.process.setEnvironment(env)

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

        executable = "py.test"
        p_args = ['--junit-xml', self.DATAPATH]

#        executable = "nosetests"
#        p_args = ['--with-xunit', "--xunit-file=%s" % self.DATAPATH]

        if args:
            p_args.extend(programs.shell_split(args))
        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"))
예제 #23
0
파일: client.py 프로젝트: bnavigator/spyder
    def start_transport(self):
        """Start transport layer."""
        logger.info('Starting transport for {1}: {0}'.format(
            ' '.join(self.transport_args), self.language))

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

        # Most LSP servers spawn other processes other than Python, which may
        # require some environment variables
        if self.language != 'python' and self.stdio:
            for var in os.environ:
                env.insert(var, os.environ[var])
            logger.info('Transport process env variables: {0}'.format(
                env.keys()))

        self.transport.setProcessEnvironment(env)

        # Modifying PYTHONPATH to run transport in development mode or
        # tests
        if (DEV or running_under_pytest()) and not running_in_ci():
            sys_path = self._clean_sys_path()
            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 up transport
        self.transport.errorOccurred.connect(self.handle_process_errors)
        if self.stdio:
            self.transport.setProcessChannelMode(QProcess.SeparateChannels)
            if self.transport_log_file is not None:
                self.transport.setStandardErrorFile(self.transport_log_file)
        else:
            self.transport.setProcessChannelMode(QProcess.MergedChannels)
            if self.transport_log_file is not None:
                self.transport.setStandardOutputFile(self.transport_log_file)

        # Start transport
        self.transport.start(self.transport_args[0], self.transport_args[1:])
예제 #24
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:])
예제 #25
0
    def __init__(self, output_widget: QTextEdit = None):
        from ...plugins import plugin_manager

        # create install process
        self._output_widget = None
        self.process = QProcess()
        self.process.setProgram(sys.executable)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        self.process.readyReadStandardOutput.connect(self._on_stdout_ready)
        # setup process path
        env = QProcessEnvironment()
        combined_paths = os.pathsep.join([
            user_site_packages(),
            env.systemEnvironment().value("PYTHONPATH")
        ])
        env.insert("PYTHONPATH", combined_paths)
        self.process.setProcessEnvironment(env)
        self.process.finished.connect(lambda: plugin_manager.discover())
        self.process.finished.connect(lambda: plugin_manager.prune())
        self.set_output_widget(output_widget)
예제 #26
0
    def run_command(self, command_list):
        for qwidget in self.qwidgets_to_disable:
            qwidget.setEnabled(False)

        self._busy.setRange(0, 0)
        self._busy.setVisible(True)
        self._busy.update()
        self._timer_label.setVisible(True)
        self._time0 = time.time()

        self.process = QProcess(self)
        self.process.readyReadStandardOutput.connect(self.stdout_ready)
        self.process.finished.connect(self.on_finished)
        #self.process.started.connect(lambda: print_func('Started!'))
        #self.process.finished.connect(lambda: print_func('Finished!'))
        #self.process.startDetached(command_list[0], command_list[1:])
        self.process.start(command_list[0], command_list[1:])

        for qwidget in self.qwidgets_to_disable:
            qwidget.setEnabled(True)
예제 #27
0
    def __init__(self,
                 cmd_list,
                 parse=False,
                 pip=False,
                 callback=None,
                 extra_kwargs=None):
        """Conda worker based on a QProcess for non blocking UI.

        Parameters
        ----------
        cmd_list : list of str
            Command line arguments to execute.
        parse : bool (optional)
            Parse json from output.
        pip : bool (optional)
            Define as a pip command.
        callback : func (optional)
            If the process has a callback to process output from comd_list.
        extra_kwargs : dict
            Arguments for the callback.
        """
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._parse = parse
        self._pip = pip
        self._conda = not pip
        self._callback = callback
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._extra_kwargs = extra_kwargs if extra_kwargs else {}

        self._timer = QTimer()
        self._process = QProcess()

        self._timer.setInterval(150)

        self._timer.timeout.connect(self._communicate)
        # self._process.finished.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)
예제 #28
0
 def __init__(self, parent=None, window_flags=None):
     super(PyChopGui, self).__init__(parent)
     if window_flags:
         self.setWindowFlags(window_flags)
     self.folder = os.path.dirname(sys.modules[self.__module__].__file__)
     for fname in os.listdir(self.folder):
         if fname.endswith('.yaml'):
             instobj = Instrument(os.path.join(self.folder, fname))
             self.instruments[instobj.name] = instobj
             self.choppers[instobj.name] = instobj.getChopperNames()
             self.minE[instobj.name] = max([instobj.emin, 0.01])
             self.maxE[instobj.name] = instobj.emax
     self.drawLayout()
     self.setInstrument(list(self.instruments.keys())[0])
     self.resaxes_xlim = 0
     self.qeaxes_xlim = 0
     self.isFramePlotted = 0
     # help
     self.assistant_process = QProcess(self)
     # pylint: disable=protected-access
     self.mantidplot_name = 'PyChop'
예제 #29
0
    def create_process(self):
        self.shell.clear()

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)

        # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe)
        env = [to_text_string(_path)
               for _path in self.process.systemEnvironment()]

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)

        add_pathlist_to_PYTHONPATH(env, self.path)
        self.process.setProcessEnvironment(processEnvironment)                   

        
        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)
            
        # Shell arguments
        if os.name == 'nt':
            p_args = ['/Q']
        else:
            p_args = ['-i']
            
        if self.arguments:
            p_args.extend( shell_split(self.arguments) )
        
        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.finished.connect(self.finished)
        self.kill_button.clicked.connect(self.process.kill)
        
        if os.name == 'nt':
            self.process.start('cmd.exe', p_args)
        else:
            # Using bash:
            self.process.start('bash', p_args)
            self.send_to_process('PS1="\\u@\\h:\\w> "\n')
            
        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
        else:
            self.shell.setFocus()
            self.started.emit()
            
        return self.process
예제 #30
0
def launch(project_path, command):
    """
    Handle launching commands from projects.
    """
    logger.debug(str((project_path, command)))
    command = command.format(PREFIX=project_path)
    command = command.replace('\\', '/')
    if os.name == 'nt':
        command = command.replace('/bin', '/Scripts')

    if '.ipynb' in command:
        filename = command.replace('ipython notebook ', '')
        filename = filename.replace('jupyter notebook ', '')
        run_notebook(project_path=project_path, filename=filename)
    elif command.startswith('python '):
        filename = command.replace('python ', '')
        run_python_file(project_path, filename=filename)
    else:
        proc = QProcess()
        logger.debug(command)
        proc.startDetached(command)
예제 #31
0
    def environment_exists(self, name=None, prefix=None, abspath=True,
                           emit=False):
        """
        Check if an environment exists by 'name' or by 'prefix'. If query is
        by 'name' only the default conda environments directory is searched.
        """
        if name and prefix:
            raise TypeError("Exactly one of 'name' or 'prefix' is required.")

        qprocess = QProcess()
        cmd_list = self._abspath(abspath)
        cmd_list.extend(['list', '--json'])

        if name:
            cmd_list.extend(['--name', name])
        else:
            cmd_list.extend(['--prefix', prefix])

        qprocess.start(cmd_list[0], cmd_list[1:])
        qprocess.waitForFinished()
        output = qprocess.readAllStandardOutput()
        output = handle_qbytearray(output, CondaProcess.ENCODING)
        info = json.loads(output)

        if emit:
            self.sig_finished.emit("info", unicode(info), "")

        return 'error' not in info
예제 #32
0
    def pip_list(self, name=None, prefix=None, abspath=True, emit=False):
        """
        Get list of pip installed packages.
        """
        if (name and prefix) or not (name or prefix):
            raise TypeError("conda pip: exactly one of 'name' ""or 'prefix' "
                            "required.")

        if self._is_not_running:
            cmd_list = self._abspath(abspath)
            if name:
                cmd_list.extend(['list', '--name', name])
            if prefix:
                cmd_list.extend(['list', '--prefix', prefix])

            qprocess = QProcess()
            qprocess.start(cmd_list[0], cmd_list[1:])
            qprocess.waitForFinished()
            output = qprocess.readAllStandardOutput()
            output = handle_qbytearray(output, CondaProcess.ENCODING)

            result = []
            lines = output.split('\n')

            for line in lines:
                if '<pip>' in line:
                    temp = line.split()[:-1] + ['pip']
                    result.append('-'.join(temp))

            if emit:
                self.sig_finished.emit("pip", str(result), "")

        return result
예제 #33
0
    def start(self):
        """Start the code analysis."""
        filename = str(self.filecombo.currentText())

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.process.setWorkingDirectory(getcwd_or_home())
        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 = ''

        if PYLINT_VER is not None:
            pylint_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:
            pylint_args += ['--rcfile={}'.format(pylintrc_path)]

        pylint_args.append(filename)
        processEnvironment = QProcessEnvironment()
        processEnvironment.insert("PYTHONIOENCODING", "utf8")
        self.process.setProcessEnvironment(processEnvironment)

        self.process.start(sys.executable, pylint_args)

        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
예제 #34
0
def _main(args, exec=True):
    # import pydm
    # app = QApplication([])
    # app = pydm.PyDMApplication()
    app = QApplication.instance() or QApplication([])
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    from xicam.gui.windows import splash
    from xicam.core import msg

    if getattr(args, 'verbose', False):
        QErrorMessage.qtHandler()

    # start splash in subprocess
    splash_proc = QProcess()
    # splash_proc.started.connect(lambda: print('started splash'))
    # splash_proc.finished.connect(lambda: print('finished splashing'))
    log_file = msg.file_handler.baseFilename
    initial_length = os.path.getsize(log_file)
    splash_proc.start(sys.executable,
                      [splash.__file__, log_file,
                       str(initial_length)])

    from xicam.gui.windows.mainwindow import XicamMainWindow

    mainwindow = XicamMainWindow()
    while splash_proc.state() != QProcess.NotRunning:
        app.processEvents()
    # splash_proc.waitForFinished()
    mainwindow.show()
    mainwindow.activateWindow()

    # splash = splash.XicamSplashScreen(args=args)
    if exec:
        return app.exec_()
    else:
        return mainwindow
예제 #35
0
def _main(args, exec=True):
    global mainwindow, splash_proc, show_check_timer
    # import pydm
    # app = QApplication([])
    # app = pydm.PyDMApplication()
    app = QApplication.instance() or QApplication([])
    signal.signal(signal.SIGINT, signal.SIG_DFL)

    from xicam.gui.windows import splash
    from xicam.core import msg

    if getattr(args, 'verbose', False):
        QErrorMessage.qtHandler()

    # start splash in subprocess
    splash_proc = QProcess()
    # splash_proc.started.connect(lambda: print('started splash'))
    # splash_proc.finished.connect(lambda: print('finished splashing'))
    log_file = msg.file_handler.baseFilename
    initial_length = os.path.getsize(log_file)
    splash_proc.start(sys.executable,
                      [splash.__file__, log_file,
                       str(initial_length)])

    show_check_timer = QTimer()
    show_check_timer.timeout.connect(check_show_mainwindow)
    show_check_timer.start(100)

    from xicam.gui.windows.mainwindow import XicamMainWindow

    mainwindow = XicamMainWindow()

    if exec:
        return app.exec_()
    else:
        return mainwindow
예제 #36
0
    def _prepare_process(self, config, pythonpath):
        """
        Prepare and return process for running the unit test suite.

        This sets the working directory and environment.
        """
        process = QProcess(self)
        process.setProcessChannelMode(QProcess.MergedChannels)
        process.setWorkingDirectory(config.wdir)
        process.finished.connect(self.finished)
        if pythonpath is not None:
            env = [
                to_text_string(_pth) for _pth in process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, separator, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)
            process.setProcessEnvironment(processEnvironment)
        return process
예제 #37
0
    def __init__(self, parent):
        QObject.__init__(self, parent)
        self._parent = parent
        self._output = None
        self._partial = None
        self._stdout = None
        self._error = None
        self._parse = False
        self._function_called = ""
        self._name = None
        self._process = QProcess()
        self.set_root_prefix()

        # Signals
        self._process.finished.connect(self._call_conda_ready)
        self._process.readyReadStandardOutput.connect(self._call_conda_partial)
예제 #38
0
    def run(self):
        """Handle the connection with the server.
        """
        # Set up the zmq port.
        self.socket = self.context.socket(zmq.PAIR)
        self.port = self.socket.bind_to_random_port('tcp://*')

        # Set up the process.
        self.process = QProcess(self)
        if self.cwd:
            self.process.setWorkingDirectory(self.cwd)
        p_args = ['-u', self.target, str(self.port)]
        if self.extra_args is not None:
            p_args += self.extra_args

        # Set up environment variables.
        processEnvironment = QProcessEnvironment()
        env = self.process.systemEnvironment()
        if (self.env and 'PYTHONPATH' not in self.env) or DEV:
            python_path = osp.dirname(get_module_path('spyderlib'))
            # Add the libs to the python path.
            for lib in self.libs:
                try:
                    path = osp.dirname(imp.find_module(lib)[1])
                    python_path = osp.pathsep.join([python_path, path])
                except ImportError:
                    pass
            env.append("PYTHONPATH=%s" % python_path)
        if self.env:
            env.update(self.env)
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)
        self.process.setProcessEnvironment(processEnvironment)

        # Start the process and wait for started.
        self.process.start(self.executable, p_args)
        self.process.finished.connect(self._on_finished)
        running = self.process.waitForStarted()
        if not running:
            raise IOError('Could not start %s' % self)

        # Set up the socket notifer.
        fid = self.socket.getsockopt(zmq.FD)
        self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self)
        self.notifier.activated.connect(self._on_msg_received)
예제 #39
0
    def _prepare_process(self, config, pythonpath):
        """
        Prepare and return process for running the unit test suite.

        This sets the working directory and environment.
        """
        process = QProcess(self)
        process.setProcessChannelMode(QProcess.MergedChannels)
        process.setWorkingDirectory(config.wdir)
        process.finished.connect(self.finished)
        if pythonpath:
            env = QProcessEnvironment.systemEnvironment()
            old_python_path = env.value('PYTHONPATH', None)
            python_path_str = os.pathsep.join(pythonpath)
            if old_python_path:
                python_path_str += os.pathsep + old_python_path
            env.insert('PYTHONPATH', python_path_str)
            process.setProcessEnvironment(env)
        return process
예제 #40
0
    def _prepare_process(self, config, pythonpath):
        """
        Prepare and return process for running the unit test suite.

        This sets the working directory and environment.
        """
        process = QProcess(self)
        process.setProcessChannelMode(QProcess.MergedChannels)
        process.setWorkingDirectory(config.wdir)
        process.finished.connect(self.finished)
        if pythonpath is not None:
            env = [
                to_text_string(_pth)
                for _pth in process.systemEnvironment()
            ]
            add_pathlist_to_PYTHONPATH(env, pythonpath)
            processEnvironment = QProcessEnvironment()
            for envItem in env:
                envName, separator, envValue = envItem.partition('=')
                processEnvironment.insert(envName, envValue)
            process.setProcessEnvironment(processEnvironment)
        return process
예제 #41
0
    def __init__(self, parent, on_finished=None, on_partial=None):
        QObject.__init__(self, parent)
        self._parent = parent
        self.output = None
        self.partial = None
        self.stdout = None
        self.error = None
        self._parse = False
        self._function_called = ''
        self._name = None
        self._process = QProcess()
        self._on_finished = on_finished

        self._process.finished.connect(self._call_conda_ready)
        self._process.readyReadStandardOutput.connect(self._call_conda_partial)

        if on_finished is not None:
            self._process.finished.connect(on_finished)
        if on_partial is not None:
            self._process.readyReadStandardOutput.connect(on_partial)

        self.set_root_prefix()
예제 #42
0
    def __init__(self, cmd_list, parse=False, pip=False, callback=None,
                 extra_kwargs={}):
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._parse = parse
        self._pip = pip
        self._conda = not pip
        self._callback = callback
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._extra_kwargs = extra_kwargs

        self._timer = QTimer()
        self._process = QProcess()

        self._timer.setInterval(50)

        self._timer.timeout.connect(self._communicate)
        self._process.finished.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)
예제 #43
0
    def info(self, abspath=True):
        """
        Return a dictionary with configuration information.
        No guarantee is made about which keys exist.  Therefore this function
        should only be used for testing and debugging.
        """
#        return self._call_and_parse(['info', '--json'], abspath=abspath)

        qprocess = QProcess()
        cmd_list = self._abspath(abspath)
        cmd_list.extend(['info', '--json'])
        qprocess.start(cmd_list[0], cmd_list[1:])
        qprocess.waitForFinished()
        output = qprocess.readAllStandardOutput()
        output = handle_qbytearray(output, CondaProcess.ENCODING)
        info = json.loads(output)
        return info
예제 #44
0
    def __init__(self, cmd_list, parse=False, pip=False, callback=None,
                 extra_kwargs=None):
        """Conda worker based on a QProcess for non blocking UI.

        Parameters
        ----------
        cmd_list : list of str
            Command line arguments to execute.
        parse : bool (optional)
            Parse json from output.
        pip : bool (optional)
            Define as a pip command.
        callback : func (optional)
            If the process has a callback to process output from comd_list.
        extra_kwargs : dict
            Arguments for the callback.
        """
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._parse = parse
        self._pip = pip
        self._conda = not pip
        self._callback = callback
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._extra_kwargs = extra_kwargs if extra_kwargs else {}

        self._timer = QTimer()
        self._process = QProcess()

        self._timer.setInterval(150)

        self._timer.timeout.connect(self._communicate)
        # self._process.finished.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)
예제 #45
0
    def info(self, abspath=True):
        """
        Return a dictionary with configuration information.

        No guarantee is made about which keys exist.  Therefore this function
        should only be used for testing and debugging.
        """
        if self._is_not_running():
            qprocess = QProcess()
            cmd_list = self._abspath(abspath)
            cmd_list.extend(["info", "--json"])
            qprocess.start(cmd_list[0], cmd_list[1:])
            qprocess.waitForFinished()
            output = qprocess.readAllStandardOutput()
            output = handle_qbytearray(output, CondaProcess.ENCODING)
            info = json.loads(output)

            self.sig_finished.emit("info", str(info), "")
            return info
예제 #46
0
class ExternalPythonShell(ExternalShellBase):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = ExtPythonShellWidget
    sig_pdb = Signal(str, int)
    open_file = Signal(str, int)
    ipython_kernel_start_error = Signal(str)
    create_ipython_client = Signal(str)
    started = Signal()
    sig_finished = Signal()
    
    def __init__(self, parent=None, fname=None, wdir=None,
                 interact=False, debug=False, post_mortem=False,
                 path=[], python_args='',
                 ipykernel=False, arguments='', stand_alone=None,
                 umr_enabled=True, umr_namelist=[], umr_verbose=True,
                 pythonstartup=None, pythonexecutable=None,
                 external_interpreter=False,
                 monitor_enabled=True, mpl_backend=None, ets_backend='qt4',
                 qt_api=None, merge_output_channels=False,
                 colorize_sys_stderr=False, autorefresh_timeout=3000,
                 autorefresh_state=True, light_background=True,
                 menu_actions=None, show_buttons_inside=True,
                 show_elapsed_time=True):

        assert qt_api in (None, 'pyqt', 'pyside', 'pyqt5')

        self.namespacebrowser = None # namespace browser widget!
        self.dialog_manager = DialogManager()

        self.stand_alone = stand_alone # stand alone settings (None: plugin)
        self.interact = interact
        self.is_ipykernel = ipykernel
        self.pythonstartup = pythonstartup
        self.pythonexecutable = pythonexecutable
        self.external_interpreter = external_interpreter
        self.monitor_enabled = monitor_enabled
        self.mpl_backend = mpl_backend
        self.ets_backend = ets_backend
        self.qt_api = qt_api
        self.merge_output_channels = merge_output_channels
        self.colorize_sys_stderr = colorize_sys_stderr
        self.umr_enabled = umr_enabled
        self.umr_namelist = umr_namelist
        self.umr_verbose = umr_verbose
        self.autorefresh_timeout = autorefresh_timeout
        self.autorefresh_state = autorefresh_state
                
        self.namespacebrowser_button = None
        self.cwd_button = None
        self.env_button = None
        self.syspath_button = None
        self.terminate_button = None

        self.notification_thread = None
        
        ExternalShellBase.__init__(self, parent=parent, fname=fname, wdir=wdir,
                                   history_filename='history.py',
                                   light_background=light_background,
                                   menu_actions=menu_actions,
                                   show_buttons_inside=show_buttons_inside,
                                   show_elapsed_time=show_elapsed_time)

        if self.pythonexecutable is None:
            self.pythonexecutable = get_python_executable()

        self.python_args = None
        if python_args:
            assert is_text_string(python_args)
            self.python_args = python_args
        
        assert is_text_string(arguments)
        self.arguments = arguments
        
        self.connection_file = None

        if self.is_ipykernel:
            self.interact = False
            # Running our custom startup script for IPython kernels:
            # (see spyderlib/widgets/externalshell/start_ipython_kernel.py)
            self.fname = get_module_source_path(
                'spyderlib.widgets.externalshell', 'start_ipython_kernel.py')
        
        self.shell.set_externalshell(self)

        self.toggle_globals_explorer(False)
        self.interact_action.setChecked(self.interact)
        self.debug_action.setChecked(debug)
        
        
        self.introspection_socket = None
        self.is_interpreter = fname is None
        
        if self.is_interpreter:
            self.terminate_button.hide()
            
        self.post_mortem_action.setChecked(post_mortem and not self.is_interpreter)
        
        # Additional python path list
        self.path = path
        self.shell.path = path
        
    def set_introspection_socket(self, introspection_socket):
        self.introspection_socket = introspection_socket
        if self.namespacebrowser is not None:
            settings = self.namespacebrowser.get_view_settings()
            communicate(introspection_socket,
                        'set_remote_view_settings()', settings=[settings])
        
    def set_autorefresh_timeout(self, interval):
        if self.introspection_socket is not None:
            try:
                communicate(self.introspection_socket,
                            "set_monitor_timeout(%d)" % interval)
            except socket.error:
                pass
        
    def closeEvent(self, event):
        self.quit_monitor()
        ExternalShellBase.closeEvent(self, event)
        
    def get_toolbar_buttons(self):
        ExternalShellBase.get_toolbar_buttons(self)
        if self.namespacebrowser_button is None \
           and self.stand_alone is not None:
            self.namespacebrowser_button = create_toolbutton(self,
                  text=_("Variables"), icon=ima.icon('dictedit'),
                  tip=_("Show/hide global variables explorer"),
                  toggled=self.toggle_globals_explorer, text_beside_icon=True)
        if self.terminate_button is None:
            self.terminate_button = create_toolbutton(self,
                  text=_("Terminate"), icon=ima.icon('stop'),
                  tip=_("Attempts to stop the process. The process\n"
                        "may not exit as a result of clicking this\n"
                        "button (it is given the chance to prompt\n"
                        "the user for any unsaved files, etc)."))
        buttons = []
        if self.namespacebrowser_button is not None:
            buttons.append(self.namespacebrowser_button)
        buttons += [self.run_button, self.terminate_button, self.kill_button,
                    self.options_button]
        return buttons

    def get_options_menu(self):
        ExternalShellBase.get_options_menu(self)
        self.interact_action = create_action(self, _("Interact"))
        self.interact_action.setCheckable(True)
        self.debug_action = create_action(self, _("Debug"))
        self.debug_action.setCheckable(True)
        self.args_action = create_action(self, _("Arguments..."),
                                         triggered=self.get_arguments)
        self.post_mortem_action = create_action(self, _("Post Mortem Debug"))
        self.post_mortem_action.setCheckable(True)
        run_settings_menu = QMenu(_("Run settings"), self)
        add_actions(run_settings_menu,
                    (self.interact_action, self.debug_action, self.args_action,
                     self.post_mortem_action))
        self.cwd_button = create_action(self, _("Working directory"),
                                icon=ima.icon('DirOpenIcon'),
                                tip=_("Set current working directory"),
                                triggered=self.set_current_working_directory)
        self.env_button = create_action(self, _("Environment variables"),
                                        icon=ima.icon('environ'),
                                        triggered=self.show_env)
        self.syspath_button = create_action(self,
                                            _("Show sys.path contents"),
                                            icon=ima.icon('syspath'),
                                            triggered=self.show_syspath)
        actions = [run_settings_menu, self.show_time_action, None,
                   self.cwd_button, self.env_button, self.syspath_button]
        if self.menu_actions is not None:
            actions += [None]+self.menu_actions
        return actions

    def is_interpreter(self):
        """Return True if shellwidget is a Python interpreter"""
        return self.is_interpreter
        
    def get_shell_widget(self):
        if self.stand_alone is None:
            return self.shell
        else:
            self.namespacebrowser = NamespaceBrowser(self)
            settings = self.stand_alone
            self.namespacebrowser.set_shellwidget(self)
            self.namespacebrowser.setup(**settings)
            self.namespacebrowser.sig_collapse.connect(
                         lambda: self.toggle_globals_explorer(False))
            # Shell splitter
            self.splitter = splitter = QSplitter(Qt.Vertical, self)
            self.splitter.splitterMoved.connect(self.splitter_moved)
            splitter.addWidget(self.shell)
            splitter.setCollapsible(0, False)
            splitter.addWidget(self.namespacebrowser)
            splitter.setStretchFactor(0, 1)
            splitter.setStretchFactor(1, 0)
            splitter.setHandleWidth(5)
            splitter.setSizes([2, 1])
            return splitter
    
    def get_icon(self):
        return ima.icon('python')

    def set_buttons_runnning_state(self, state):
        ExternalShellBase.set_buttons_runnning_state(self, state)
        self.interact_action.setEnabled(not state and not self.is_interpreter)
        self.debug_action.setEnabled(not state and not self.is_interpreter)
        self.args_action.setEnabled(not state and not self.is_interpreter)
        self.post_mortem_action.setEnabled(not state and not self.is_interpreter)
        if state:
            if self.arguments:
                argstr = _("Arguments: %s") % self.arguments
            else:
                argstr = _("No argument")
        else:
            argstr = _("Arguments...")
        self.args_action.setText(argstr)
        self.terminate_button.setVisible(not self.is_interpreter and state)
        if not state:
            self.toggle_globals_explorer(False)
        for btn in (self.cwd_button, self.env_button, self.syspath_button):
            btn.setEnabled(state and self.monitor_enabled)
        if self.namespacebrowser_button is not None:
            self.namespacebrowser_button.setEnabled(state)
    
    def set_namespacebrowser(self, namespacebrowser):
        """
        Set namespace browser *widget*
        Note: this method is not used in stand alone mode
        """
        self.namespacebrowser = namespacebrowser
        self.configure_namespacebrowser()
        
    def configure_namespacebrowser(self):
        """Connect the namespace browser to the notification thread"""
        if self.notification_thread is not None:
            self.notification_thread.refresh_namespace_browser.connect(
                         self.namespacebrowser.refresh_table)
            signal = self.notification_thread.sig_process_remote_view
            signal.connect(lambda data:
                           self.namespacebrowser.process_remote_view(data))
    
    def create_process(self):
        self.shell.clear()
            
        self.process = QProcess(self)
        if self.merge_output_channels:
            self.process.setProcessChannelMode(QProcess.MergedChannels)
        else:
            self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.shell.wait_for_ready_read.connect(
                     lambda: self.process.waitForReadyRead(250))
        
        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)

        #-------------------------Python specific------------------------------
        # Python arguments
        p_args = ['-u']
        if DEBUG >= 3:
            p_args += ['-v']
        p_args += get_python_args(self.fname, self.python_args,
                                  self.interact_action.isChecked(),
                                  self.debug_action.isChecked(),
                                  self.arguments)
        
        env = [to_text_string(_path)
               for _path in self.process.systemEnvironment()]
        if self.pythonstartup:
            env.append('PYTHONSTARTUP=%s' % self.pythonstartup)
        
        #-------------------------Python specific-------------------------------
        # Post mortem debugging
        if self.post_mortem_action.isChecked():
            env.append('SPYDER_EXCEPTHOOK=True')

        # Set standard input/output encoding for Python consoles
        # (IPython handles it on its own)
        # See http://stackoverflow.com/q/26312400/438386, specifically
        # the comments of Martijn Pieters
        if not self.is_ipykernel:
            env.append('PYTHONIOENCODING=UTF-8')

        # Monitor
        if self.monitor_enabled:
            env.append('SPYDER_SHELL_ID=%s' % id(self))
            env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout)
            env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state)
            from spyderlib.widgets.externalshell import introspection
            introspection_server = introspection.start_introspection_server()
            introspection_server.register(self)
            notification_server = introspection.start_notification_server()
            self.notification_thread = notification_server.register(self)
            self.notification_thread.sig_pdb.connect(
                                              lambda fname, lineno:
                                              self.sig_pdb.emit(fname, lineno))
            self.notification_thread.new_ipython_kernel.connect(
                                         lambda args:
                                         self.create_ipython_client.emit(args))
            self.notification_thread.open_file.connect(
                                            lambda fname, lineno:
                                            self.open_file.emit(fname, lineno))
            if self.namespacebrowser is not None:
                self.configure_namespacebrowser()
            env.append('SPYDER_I_PORT=%d' % introspection_server.port)
            env.append('SPYDER_N_PORT=%d' % notification_server.port)

        # External modules options
        if not self.is_ipykernel:
            env.append('ETS_TOOLKIT=%s' % self.ets_backend)
        if self.mpl_backend is not None:
            backends = {0: 'Automatic', 1: 'None', 2: 'TkAgg'}
            env.append('SPY_MPL_BACKEND=%s' % backends[self.mpl_backend])
        if self.qt_api and not self.is_ipykernel:
            env.append('QT_API=%s' % self.qt_api)
        env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr)
#        # Socket-based alternative (see input hook in sitecustomize.py):
#        if self.install_qt_inputhook:
#            from PyQt4.QtNetwork import QLocalServer
#            self.local_server = QLocalServer()
#            self.local_server.listen(str(id(self)))

        # User Module Deleter
        if self.is_interpreter:
            env.append('UMR_ENABLED=%r' % self.umr_enabled)
            env.append('UMR_NAMELIST=%s' % ','.join(self.umr_namelist))
            env.append('UMR_VERBOSE=%r' % self.umr_verbose)
            env.append('MATPLOTLIB_ION=True')
        else:
            if self.interact:
                env.append('MATPLOTLIB_ION=True')
            else:
                env.append('MATPLOTLIB_ION=False')

        # IPython kernel
        env.append('IPYTHON_KERNEL=%r' % self.is_ipykernel)

        # External interpreter
        env.append('EXTERNAL_INTERPRETER=%r' % self.external_interpreter)

        # Add sitecustomize path to path list
        pathlist = []
        scpath = osp.dirname(osp.abspath(__file__))
        pathlist.append(scpath)
        
        # Adding Spyder path
        pathlist += self.path
        
        # Adding path list to PYTHONPATH environment variable
        add_pathlist_to_PYTHONPATH(env, pathlist)

        #-------------------------Python specific------------------------------
                        
        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.readyReadStandardError.connect(self.write_error)
        self.process.finished.connect(lambda ec, es=QProcess.ExitStatus:
                                      self.finished(ec, es))
        self.sig_finished.connect(self.dialog_manager.close_all)
        self.terminate_button.clicked.connect(self.process.terminate)
        self.kill_button.clicked.connect(self.process.kill)
        
        #-------------------------Python specific------------------------------
        # Fixes for our Mac app:
        # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app,
        #    but their values are messing sys.path for external interpreters
        #    (e.g. EPD) so we need to remove them from the environment.
        # 2. Set PYTHONPATH again but without grabbing entries defined in the
        #    environment (Fixes Issue 1321)
        # 3. Remove PYTHONOPTIMIZE from env so that we can have assert
        #    statements working with our interpreters (See Issue 1281)
        if running_in_mac_app():
            if MAC_APP_NAME not in self.pythonexecutable:
                env = [p for p in env if not (p.startswith('PYTHONPATH') or \
                                              p.startswith('PYTHONHOME'))] # 1.

                add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=True)   # 2.
            env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')]   # 3.

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)
        self.process.setProcessEnvironment(processEnvironment)                   
        self.process.start(self.pythonexecutable, p_args)
        #-------------------------Python specific------------------------------
            
        running = self.process.waitForStarted(3000)
        self.set_running_state(running)
        if not running:
            if self.is_ipykernel:
                self.ipython_kernel_start_error.emit(
                          _("The kernel failed to start!! That's all we know... "
                            "Please close this console and open a new one."))
            else:
                QMessageBox.critical(self, _("Error"),
                                     _("A Python console failed to start!"))
        else:
            self.shell.setFocus()
            self.started.emit()
        return self.process

    def finished(self, exit_code, exit_status):
        """Reimplement ExternalShellBase method"""
        if self.is_ipykernel and exit_code == 1:
            self.ipython_kernel_start_error.emit(self.shell.get_text_with_eol())
        ExternalShellBase.finished(self, exit_code, exit_status)
        self.introspection_socket = None

    
#==============================================================================
#    Input/Output
#==============================================================================
    def write_error(self):
        if os.name == 'nt':
            #---This is apparently necessary only on Windows (not sure though):
            #   emptying standard output buffer before writing error output
            self.process.setReadChannel(QProcess.StandardOutput)
            if self.process.waitForReadyRead(1):
                self.write_output()
        self.shell.write_error(self.get_stderr())
        QApplication.processEvents()
        
    def send_to_process(self, text):
        if not self.is_running():
            return
        if not is_text_string(text):
            text = to_text_string(text)
        if self.mpl_backend == 0 and os.name == 'nt' and \
          self.introspection_socket is not None:
            communicate(self.introspection_socket, "toggle_inputhook_flag(True)")
#            # Socket-based alternative (see input hook in sitecustomize.py):
#            while self.local_server.hasPendingConnections():
#                self.local_server.nextPendingConnection().write('go!')
        if any([text == cmd for cmd in ['%ls', '%pwd', '%scientific']]) or \
          any([text.startswith(cmd) for cmd in ['%cd ', '%clear ']]):
            text = 'evalsc(r"%s")\n' % text
        if not text.endswith('\n'):
            text += '\n'
        self.process.write(to_binary_string(text, 'utf8'))
        self.process.waitForBytesWritten(-1)
        
        # Eventually write prompt faster (when hitting Enter continuously)
        # -- necessary/working on Windows only:
        if os.name == 'nt':
            self.write_error()
        
    def keyboard_interrupt(self):
        if self.introspection_socket is not None:
            communicate(self.introspection_socket, "thread.interrupt_main()")
        
    def quit_monitor(self):
        if self.introspection_socket is not None:
            try:
                write_packet(self.introspection_socket, "thread.exit()")
            except socket.error:
                pass
            
#==============================================================================
#    Globals explorer
#==============================================================================
    @Slot(bool)
    def toggle_globals_explorer(self, state):
        if self.stand_alone is not None:
            self.splitter.setSizes([1, 1 if state else 0])
            self.namespacebrowser_button.setChecked(state)
            if state and self.namespacebrowser is not None:
                self.namespacebrowser.refresh_table()
        
    def splitter_moved(self, pos, index):
        self.namespacebrowser_button.setChecked( self.splitter.sizes()[1] )

#==============================================================================
#    Misc.
#==============================================================================
    @Slot()
    def set_current_working_directory(self):
        """Set current working directory"""
        cwd = self.shell.get_cwd()
        self.redirect_stdio.emit(False)
        directory = getexistingdirectory(self, _("Select directory"), cwd)
        if directory:
            self.shell.set_cwd(directory)
        self.redirect_stdio.emit(True)

    @Slot()
    def show_env(self):
        """Show environment variables"""
        get_func = self.shell.get_env
        set_func = self.shell.set_env
        self.dialog_manager.show(RemoteEnvDialog(get_func, set_func))

    @Slot()
    def show_syspath(self):
        """Show sys.path contents"""
        editor = CollectionsEditor()
        editor.setup(self.shell.get_syspath(), title="sys.path", readonly=True,
                     width=600, icon=ima.icon('syspath'))
        self.dialog_manager.show(editor)
예제 #47
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)
        if self.rdata:
            self.remove_obsolete_items()
            self.filecombo.addItems(self.get_filenames())
        
        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()
        
    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(), _("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)
class LineProfilerWidget(QWidget):
    """
    Line profiler widget.
    """
    DATAPATH = get_conf_path('lineprofiler.results')
    VERSION = '0.0.1'
    redirect_stdio = Signal(bool)

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

        self.setWindowTitle("Line 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 by line"),
            tip=_("Run line 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.filecombo.valid.connect(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 = LineProfilerDataTree(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_lineprofiler_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">line_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_lineprofiler_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=_("Line profiler output"),
                       readonly=True, size=(700, 500)).exec_()

    def show_errorlog(self):
        if self.error_output:
            TextEditor(self.error_output, title=_("Line 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 = ''

        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 = ['-lvb', '-o', '"' + self.DATAPATH + '"',
                      '"' + filename + '"']
            if args:
                p_args.extend(programs.shell_split(args))
            executable = '"' + programs.find_program('kernprof') + '"'
            executable += ' ' + ' '.join(p_args)
            executable = executable.replace(os.sep, '/')
            self.process.start(executable)
        else:
            p_args = ['-lvb', '-o', self.DATAPATH, filename]
            if args:
                p_args.extend(programs.shell_split(args))
            executable = 'kernprof'
            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)

    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)
    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 = ''

        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 = ['-lvb', '-o', '"' + self.DATAPATH + '"',
                      '"' + filename + '"']
            if args:
                p_args.extend(programs.shell_split(args))
            executable = '"' + programs.find_program('kernprof') + '"'
            executable += ' ' + ' '.join(p_args)
            executable = executable.replace(os.sep, '/')
            self.process.start(executable)
        else:
            p_args = ['-lvb', '-o', self.DATAPATH, filename]
            if args:
                p_args.extend(programs.shell_split(args))
            executable = 'kernprof'
            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"))
예제 #50
0
파일: workers.py 프로젝트: 0xBADCA7/spyder
class ProcessWorker(QObject):
    """Process worker based on a QProcess for non blocking UI."""

    sig_started = Signal(object)        
    sig_finished = Signal(object, object, object)
    sig_partial = Signal(object, object, object)

    def __init__(self, cmd_list, environ=None):
        """
        Process worker based on a QProcess for non blocking UI.

        Parameters
        ----------
        cmd_list : list of str
            Command line arguments to execute.
        environ : dict
            Process environment,
        """
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._started = False

        self._timer = QTimer()
        self._process = QProcess()
        self._set_environment(environ)

        self._timer.setInterval(150)
        self._timer.timeout.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)

    def _get_encoding(self):
        """Return the encoding/codepage to use."""
        enco = 'utf-8'

        #  Currently only cp1252 is allowed?
        if WIN:
            import ctypes
            codepage = to_text_string(ctypes.cdll.kernel32.GetACP())
            # import locale
            # locale.getpreferredencoding()  # Differences?
            enco = 'cp' + codepage
        return enco

    def _set_environment(self, environ):
        """Set the environment on the QProcess."""
        if environ:
            q_environ = self._process.processEnvironment()
            for k, v in environ.items():
                q_environ.insert(k, v)
            self._process.setProcessEnvironment(q_environ)

    def _partial(self):
        """Callback for partial output."""
        raw_stdout = self._process.readAllStandardOutput()
        stdout = handle_qbytearray(raw_stdout, self._get_encoding())

        if self._partial_stdout is None:
            self._partial_stdout = stdout
        else:
            self._partial_stdout += stdout

        self.sig_partial.emit(self, stdout, None)

    def _communicate(self):
        """Callback for communicate."""
        if (not self._communicate_first and
                self._process.state() == QProcess.NotRunning):
            self.communicate()
        elif self._fired:
            self._timer.stop()

    def communicate(self):
        """Retrieve information."""
        self._communicate_first = True
        self._process.waitForFinished()

        enco = self._get_encoding()
        if self._partial_stdout is None:
            raw_stdout = self._process.readAllStandardOutput()
            stdout = handle_qbytearray(raw_stdout, enco)
        else:
            stdout = self._partial_stdout

        raw_stderr = self._process.readAllStandardError()
        stderr = handle_qbytearray(raw_stderr, enco)
        result = [stdout.encode(enco), stderr.encode(enco)]

        if PY2:
            stderr = stderr.decode()
        result[-1] = ''

        self._result = result

        if not self._fired:
            self.sig_finished.emit(self, result[0], result[-1])

        self._fired = True

        return result

    def close(self):
        """Close the running process."""
        self._process.close()

    def is_finished(self):
        """Return True if worker has finished processing."""
        return self._process.state() == QProcess.NotRunning and self._fired

    def _start(self):
        """Start process."""
        if not self._fired:
            self._partial_ouput = None
            self._process.start(self._cmd_list[0], self._cmd_list[1:])
            self._timer.start()

    def terminate(self):
        """Terminate running processes."""
        if self._process.state() == QProcess.Running:
            try:
                self._process.terminate()
            except Exception:
                pass
        self._fired = True

    def start(self):
        """Start worker."""
        if not self._started:
            self.sig_started.emit(self)
            self._started = True
예제 #51
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(),
                                               _("Profiler result")+" (*.Result)")
        if filename:
            self.datatree.save_data(filename)
            
    def compare(self):
        filename, _selfilter = getopenfilename(self, _("Select script to compare"),
                                               getcwd(), _("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(), _("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()]
            baseshell.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)
예제 #52
0
class CondaProcess(QObject):
    """Conda API modified to work with QProcess instead of popen."""

    # Signals
    sig_finished = Signal(str, object, str)
    sig_partial = Signal(str, object, str)
    sig_started = Signal()

    ENCODING = 'ascii'
    ROOT_PREFIX = None

    def __init__(self, parent):
        QObject.__init__(self, parent)
        self._parent = parent
        self._output = None
        self._partial = None
        self._stdout = None
        self._error = None
        self._parse = False
        self._function_called = ''
        self._name = None
        self._process = QProcess()
        self.set_root_prefix()

        # Signals
        self._process.finished.connect(self._call_conda_ready)
        self._process.readyReadStandardOutput.connect(self._call_conda_partial)

    # --- Helpers
    # -------------------------------------------------------------------------
    def _is_running(self):
        return self._process.state() != QProcess.NotRunning

    def _is_not_running(self):
        return self._process.state() == QProcess.NotRunning

    def _call_conda_partial(self):
        """ """
        stdout = self._process.readAllStandardOutput()
        stdout = handle_qbytearray(stdout, CondaProcess.ENCODING)

        stderr = self._process.readAllStandardError()
        stderr = handle_qbytearray(stderr, CondaProcess.ENCODING)

        if self._parse:
            try:
                self._output = json.loads(stdout)
            except Exception:
                # Result is a partial json. Can only be parsed when finished
                self._output = stdout
        else:
            self._output = stdout

        self._partial = self._output
        self._stdout = self._output
        self._error = stderr

        self.sig_partial.emit(self._function_called, self._partial,
                              self._error)

    def _call_conda_ready(self):
        """Function called when QProcess in _call_conda finishes task."""
        function = self._function_called

        if self._stdout is None:
            stdout = to_text_string(self._process.readAllStandardOutput(),
                                    encoding=CondaProcess.ENCODING)
        else:
            stdout = self._stdout

        if self._error is None:
            stderr = to_text_string(self._process.readAllStandardError(),
                                    encoding=CondaProcess.ENCODING)
        else:
            stderr = self._error

        if function == 'get_conda_version':
            pat = re.compile(r'conda:?\s+(\d+\.\d\S+|unknown)')
            m = pat.match(stderr.strip())
            if m is None:
                m = pat.match(stdout.strip())
            if m is None:
                raise Exception('output did not match: {0}'.format(stderr))
            self._output = m.group(1)
        elif function == 'config_path':
            result = self._output
            self._output = result['rc_path']
        elif function == 'config_get':
            result = self._output
            self._output = result['get']
        elif (function == 'config_delete' or function == 'config_add' or
              function == 'config_set' or function == 'config_remove'):
            result = self._output
            self._output = result.get('warnings', [])
        elif function == 'pip':
            result = []
            lines = self._output.split('\n')
            for line in lines:
                if '<pip>' in line:
                    temp = line.split()[:-1] + ['pip']
                    result.append('-'.join(temp))
            self._output = result

        if stderr.strip():
            self._error = stderr

        self._parse = False

        self.sig_finished.emit(self._function_called, self._output,
                               self._error)

    def _abspath(self, abspath):
        """ """
        if abspath:
            if sys.platform == 'win32':
                python = join(CondaProcess.ROOT_PREFIX, 'python.exe')
                conda = join(CondaProcess.ROOT_PREFIX,
                             'Scripts', 'conda-script.py')
            else:
                python = join(CondaProcess.ROOT_PREFIX, 'bin/python')
                conda = join(CondaProcess.ROOT_PREFIX, 'bin/conda')
            cmd_list = [python, conda]
        else:
            # Just use whatever conda/pip is on the path
            cmd_list = ['conda']

        return cmd_list

    def _call_conda(self, extra_args, abspath=True):
        """ """
        if self._is_not_running():
            cmd_list = self._abspath(abspath)
            cmd_list.extend(extra_args)
            self._error, self._output = None, None
            self._process.start(cmd_list[0], cmd_list[1:])
            self.sig_started.emit()

    def _call_pip(self, name=None, prefix=None, extra_args=None):
        """ """
        if self._is_not_running():
            cmd_list = self._pip_cmd(name=name, prefix=prefix)
            cmd_list.extend(extra_args)
            self._error, self._output = None, None
            self._parse = False
            self._process.start(cmd_list[0], cmd_list[1:])
            self.sig_started.emit()

    def _call_and_parse(self, extra_args, abspath=True):
        """ """
        self._parse = True
        self._call_conda(extra_args, abspath=abspath)

    def _setup_install_commands_from_kwargs(self, kwargs, keys=tuple()):
        """ """
        cmd_list = []
        if kwargs.get('override_channels', False) and 'channel' not in kwargs:
            raise TypeError('conda search: override_channels requires channel')

        if 'env' in kwargs:
            cmd_list.extend(['--name', kwargs.pop('env')])
        if 'prefix' in kwargs:
            cmd_list.extend(['--prefix', kwargs.pop('prefix')])
        if 'channel' in kwargs:
            channel = kwargs.pop('channel')
            if isinstance(channel, str):
                cmd_list.extend(['--channel', channel])
            else:
                cmd_list.append('--channel')
                cmd_list.extend(channel)

        for key in keys:
            if key in kwargs and kwargs[key]:
                cmd_list.append('--' + key.replace('_', '-'))

        return cmd_list

    # --- Public api
    # ------------------------------------------------------------------------
    @staticmethod
    def linked(prefix, as_spec=False):
        """
        Return the (set of canonical names) of linked packages in `prefix`.
        """
        if not isdir(prefix):
            raise Exception('no such directory: {0}'.format(prefix))

        meta_dir = join(prefix, 'conda-meta')

        if not isdir(meta_dir):
            # We might have nothing in linked (and no conda-meta directory)
            result = set()

        result = set(fn[:-5] for fn in os.listdir(meta_dir)
                     if fn.endswith('.json'))

        new_result = []
        if as_spec:
            for r in result:
                n, v, b = CondaProcess.split_canonical_name(r)
                new_result.append("{0}={1}".format(n, v))
            result = "\n".join(new_result)

        return result

    @staticmethod
    def split_canonical_name(cname):
        """
        Split a canonical package name into (name, version, build) strings.
        """
        result = tuple(cname.rsplit('-', 2))
        return result

    def set_root_prefix(self, prefix=None):
        """
        Set the prefix to the root environment (default is /opt/anaconda).
        """
        if prefix:
            CondaProcess.ROOT_PREFIX = prefix
        else:
            # Find conda instance, and then use info() to get 'root_prefix'
            if CondaProcess.ROOT_PREFIX is None:
                info = self.info(abspath=False)
                CondaProcess.ROOT_PREFIX = info['root_prefix']

    def get_conda_version(self):
        """
        Return the version of conda being used (invoked) as a string.
        """
        if self._is_not_running():
            self._function_called = 'get_conda_version'
            self._call_conda(['--version'])

    def get_envs(self, emit=False):
        """
        Return all of the (named) environments (this does not include the root
        environment), as a list of absolute paths to their prefixes.
        """
        if self._is_not_running():
            info = self.info()
            result = info['envs']

            if emit:
                self.sig_finished.emit('get_envs', result, "")

            return result

    def get_prefix_envname(self, name, emit=False):
        """
        Given the name of an environment return its full prefix path, or None
        if it cannot be found.
        """
        if self._is_not_running():
            if name == 'root':
                prefix = CondaProcess.ROOT_PREFIX
            for env_prefix in self.get_envs():
                if basename(env_prefix) == name:
                    prefix = env_prefix
                    break
            if emit:
                self.sig_finished.emit('get_prefix_envname', prefix, "")

            return prefix

    def info(self, abspath=True, emit=False):
        """
        Return a dictionary with configuration information.

        No guarantee is made about which keys exist.  Therefore this function
        should only be used for testing and debugging.
        """
        if self._is_not_running():
            qprocess = QProcess()
            cmd_list = self._abspath(abspath)
            cmd_list.extend(['info', '--json'])
            qprocess.start(cmd_list[0], cmd_list[1:])
            qprocess.waitForFinished()
            output = qprocess.readAllStandardOutput()
            output = handle_qbytearray(output, CondaProcess.ENCODING)
            info = json.loads(output)

            if emit:
                self.sig_finished.emit("info", str(info), "")

            return info

    def package_info(self, package, abspath=True):
        """
        Return a dictionary with package information.

        Structure is {
            'package_name': [{
                'depends': list,
                'version': str,
                'name': str,
                'license': str,
                'fn': ...,
                ...
            }]
        }
        """
        if self._is_not_running():
            self._function_called = 'package_info'
            self._call_and_parse(['info', package, '--json'], abspath=abspath)

    def search(self, regex=None, spec=None, **kwargs):
        """
        Search for packages.
        """
        cmd_list = ['search', '--json']

        if regex and spec:
            raise TypeError('conda search: only one of regex or spec allowed')

        if regex:
            cmd_list.append(regex)

        if spec:
            cmd_list.extend(['--spec', spec])

        if 'platform' in kwargs:
            platform = kwargs.pop('platform')
            platforms = ('win-32', 'win-64', 'osx-64', 'linux-32', 'linux-64')
            if platform not in platforms:
                raise TypeError('conda search: platform must be one of ' +
                                ', '.join(platforms))
            cmd_list.extend(['--platform', platform])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('canonical', 'unknown', 'use_index_cache', 'outdated',
                 'override_channels')))

        if self._is_not_running():
            self._function_called = 'search'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def share(self, prefix):
        """
        Create a "share package" of the environment located in `prefix`,
        and return a dictionary with (at least) the following keys:
          - 'prefix': the full path to the created package
          - 'warnings': a list of warnings

        This file is created in a temp directory, and it is the callers
        responsibility to remove this directory (after the file has been
        handled in some way).
        """
        if self._is_not_running:
            self._function_called = 'share'
            self._call_and_parse(['share', '--json', '--prefix', prefix])

    def create(self, name=None, prefix=None, pkgs=None):
        """
        Create an environment either by 'name' or 'prefix' with a specified set
        of packages.
        """
        # TODO: Fix temporal hack
        if not pkgs or not isinstance(pkgs, (list, tuple, str)):
            raise TypeError('must specify a list of one or more packages to '
                            'install into new environment')

        cmd_list = ['create', '--yes', '--quiet', '--json', '--mkdir']
        if name:
            ref = name
            search = [os.path.join(d, name) for d in self.info()['envs_dirs']]
            cmd_list.extend(['--name', name])
        elif prefix:
            ref = prefix
            search = [prefix]
            cmd_list.extend(['--prefix', prefix])
        else:
            raise TypeError("Must specify either an environment 'name' or a "
                            "'prefix' for new environment.")

        if any(os.path.exists(path) for path in search):
            raise CondaEnvExistsError('Conda environment [%s] already exists'
                                      % ref)

        # TODO: Fix temporal hack
        if isinstance(pkgs, (list, tuple)):
            cmd_list.extend(pkgs)
        elif isinstance(pkgs, str):
            cmd_list.extend(['--file', pkgs])

        if self._is_not_running:
            self._function_called = 'create'
            self._call_conda(cmd_list)

    def install(self, name=None, prefix=None, pkgs=None, dep=True):
        """
        Install packages into an environment either by 'name' or 'prefix' with
        a specified set of packages
        """        
        # TODO: Fix temporal hack
        if not pkgs or not isinstance(pkgs, (list, tuple, str)):
            raise TypeError('must specify a list of one or more packages to '
                            'install into existing environment')

        cmd_list = ['install', '--yes', '--json', '--force-pscheck']
        if name:
            cmd_list.extend(['--name', name])
        elif prefix:
            cmd_list.extend(['--prefix', prefix])
        else:  # just install into the current environment, whatever that is
            pass

        # TODO: Fix temporal hack
        if isinstance(pkgs, (list, tuple)):
            cmd_list.extend(pkgs)
        elif isinstance(pkgs, str):
            cmd_list.extend(['--file', pkgs])

        if not dep:
            cmd_list.extend(['--no-deps'])

        if self._is_not_running:
            self._function_called = 'install'
            self._call_conda(cmd_list)

    def update(self, *pkgs, **kwargs):
        """
        Update package(s) (in an environment) by name.
        """
        cmd_list = ['update', '--json', '--quiet', '--yes']

        if not pkgs and not kwargs.get('all'):
            raise TypeError("Must specify at least one package to update, \
                            or all=True.")

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('dry_run', 'no_deps', 'override_channels',
                 'no_pin', 'force', 'all', 'use_index_cache', 'use_local',
                 'alt_hint')))

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = 'update'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def remove(self, *pkgs, **kwargs):
        """
        Remove a package (from an environment) by name.

        Returns {
            success: bool, (this is always true),
            (other information)
        }
        """
        cmd_list = ['remove', '--json', '--quiet', '--yes', '--force-pscheck']

        if not pkgs and not kwargs.get('all'):
            raise TypeError("Must specify at least one package to remove, \
                            or all=True.")

        if kwargs.get('name') and kwargs.get('prefix'):
            raise TypeError("conda remove: At most one of 'name', 'prefix' "
                            "allowed")

        if kwargs.get('name'):
            cmd_list.extend(['--name', kwargs.pop('name')])

        if kwargs.get('prefix'):
            cmd_list.extend(['--prefix', kwargs.pop('prefix')])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('dry_run', 'features', 'override_channels',
                 'no_pin', 'force', 'all')))

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = 'remove'
            self._call_and_parse(cmd_list,
                                 abspath=kwargs.get('abspath', True))

    def remove_environment(self, name=None, prefix=None, **kwargs):
        """
        Remove an environment entirely.

        See ``remove``.
        """
        if self._is_not_running:
            self._function_called = 'remove_environment'
            self.remove(name=name, prefix=prefix, all=True, **kwargs)

    def clone_environment(self, clone, name=None, prefix=None, **kwargs):
        """
        Clone the environment ``clone`` into ``name`` or ``prefix``.
        """
        cmd_list = ['create', '--json', '--quiet']

        if (name and prefix) or not (name or prefix):
            raise TypeError("conda clone_environment: exactly one of 'name' "
                            "or 'prefix' required.")

        if name:
            cmd_list.extend(['--name', name])

        if prefix:
            cmd_list.extend(['--prefix', prefix])

        cmd_list.extend(['--clone', clone])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('dry_run', 'unknown', 'use_index_cache', 'use_local',
                 'no_pin', 'force', 'all', 'channel', 'override_channels',
                 'no_default_packages')))

        if self._is_not_running():
            self._function_called = 'clone_environment'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def clone(self, path, prefix):
        """
        Clone a "share package" (located at `path`) by creating a new
        environment at `prefix`, and return a dictionary with (at least) the
        following keys:
          - 'warnings': a list of warnings

        The directory `path` is located in, should be some temp directory or
        some other directory OUTSIDE /opt/anaconda.  After calling this
        function, the original file (at `path`) may be removed (by the caller
        of this function).
        The return object is a list of warnings.
        """
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'clone'
            self._call_and_parse(['clone', '--json', '--prefix', prefix, path])

    def _setup_config_from_kwargs(kwargs):
        cmd_list = ['--json', '--force']

        if 'file' in kwargs:
            cmd_list.extend(['--file', kwargs['file']])

        if 'system' in kwargs:
            cmd_list.append('--system')

        return cmd_list

    def config_path(self, **kwargs):
        """
        Get the path to the config file.
        """
        cmd_list = ['config', '--get']
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = 'config_path'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_get(self, *keys, **kwargs):
        """
        Get the values of configuration keys.

        Returns a dictionary of values. Note, the key may not be in the
        dictionary if the key wasn't set in the configuration file.
        """
        cmd_list = ['config', '--get']
        cmd_list.extend(keys)
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = 'config_get'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_set(self, key, value, **kwargs):
        """
        Set a key to a (bool) value.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--set', key, str(value)]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = 'config_set'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_add(self, key, value, **kwargs):
        """
        Add a value to a key.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--add', key, value]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = 'config_add'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_remove(self, key, value, **kwargs):
        """
        Remove a value from a key.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--remove', key, value]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = 'config_remove'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_delete(self, key, **kwargs):
        """
        Remove a key entirely.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--remove-key', key]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = 'config_delete'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def run(self, command, abspath=True):
        """
        Launch the specified app by name or full package name.

        Returns a dictionary containing the key "fn", whose value is the full
        package (ending in ``.tar.bz2``) of the app.
        """
        cmd_list = ['run', '--json', command]

        if self._is_not_running:
            self._function_called = 'run'
            self._call_and_parse(cmd_list, abspath=abspath)

    # --- Additional methods not in conda-api
    # ------------------------------------------------------------------------
    def dependencies(self, name=None, prefix=None, pkgs=None, dep=True):
        """
        Get dependenciy list for packages to be installed into an environment
        defined either by 'name' or 'prefix'.
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError('must specify a list of one or more packages to '
                            'install into existing environment')

        cmd_list = ['install', '--dry-run', '--json']
        cmd_list = ['install', '--dry-run', '--json', '--force-pscheck']

        if not dep:
            cmd_list.extend(['--no-deps'])

        if name:
            cmd_list.extend(['--name', name])
        elif prefix:
            cmd_list.extend(['--prefix', prefix])
        else:
            pass

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = 'install_dry'
            self._call_and_parse(cmd_list)

    def environment_exists(self, name=None, prefix=None, abspath=True,
                           emit=False):
        """
        Check if an environment exists by 'name' or by 'prefix'. If query is
        by 'name' only the default conda environments directory is searched.
        """
        if name and prefix:
            raise TypeError("Exactly one of 'name' or 'prefix' is required.")

        qprocess = QProcess()
        cmd_list = self._abspath(abspath)
        cmd_list.extend(['list', '--json'])

        if name:
            cmd_list.extend(['--name', name])
        else:
            cmd_list.extend(['--prefix', prefix])

        qprocess.start(cmd_list[0], cmd_list[1:])
        qprocess.waitForFinished()
        output = qprocess.readAllStandardOutput()
        output = handle_qbytearray(output, CondaProcess.ENCODING)
        info = json.loads(output)

        if emit:
            self.sig_finished.emit("info", unicode(info), "")

        return 'error' not in info

    def export(self, filename, name=None, prefix=None, emit=False):
        """
        Export environment by 'prefix' or 'name' as yaml 'filename'.
        """
        if name and prefix:
            raise TypeError("Exactly one of 'name' or 'prefix' is required.")

        if self._is_not_running:
            if name:
                temporal_envname = name

            if prefix:
                temporal_envname = 'tempenv' + int(random.random()*10000000)
                envs_dir = self.info()['envs_dirs'][0]
                os.symlink(prefix, os.sep.join([envs_dir, temporal_envname]))

            cmd = self._abspath(True)
            cmd.extend(['env', 'export', '--name', temporal_envname, '--file',
                        os.path.abspath(filename)])
            qprocess = QProcess()
            qprocess.start(cmd[0], cmd[1:])
            qprocess.waitForFinished()

            if prefix:
                os.unlink(os.sep.join([envs_dir, temporal_envname]))

    def update_environment(self, filename, name=None, prefix=None, emit=False):
        """
        Set environment at 'prefix' or 'name' to match 'filename' spec as yaml.
        """
        if name and prefix:
            raise TypeError("Exactly one of 'name' or 'prefix' is required.")

        if self._is_not_running:
            if name:
                temporal_envname = name

            if prefix:
                temporal_envname = 'tempenv' + int(random.random()*10000000)
                envs_dir = self.info()['envs_dirs'][0]
                os.symlink(prefix, os.sep.join([envs_dir, temporal_envname]))

            cmd = self._abspath(True)
            cmd.extend(['env', 'update', '--name', temporal_envname, '--file',
                        os.path.abspath(filename)])
            qprocess = QProcess()
            qprocess.start(cmd[0], cmd[1:])
            qprocess.waitForFinished()

            if prefix:
                os.unlink(os.sep.join([envs_dir, 'tempenv']))

    @property
    def error(self):
        return self._error

    @property
    def output(self):
        return self._output

    # --- Pip commands
    # -------------------------------------------------------------------------
    def _pip_cmd(self, name=None, prefix=None):
        """
        Get pip location based on environment `name` or `prefix`.
        """
        if (name and prefix) or not (name or prefix):
            raise TypeError("conda pip: exactly one of 'name' ""or 'prefix' "
                            "required.")

        if name and self.environment_exists(name=name):
            prefix = self.get_prefix_envname(name)

        if sys.platform == 'win32':
            python = join(prefix, 'python.exe')  # FIXME:
            pip = join(prefix, 'pip.exe')    # FIXME:
        else:
            python = join(prefix, 'bin/python')
            pip = join(prefix, 'bin/pip')

        cmd_list = [python, pip]

        return cmd_list

    def pip_list(self, name=None, prefix=None, abspath=True, emit=False):
        """
        Get list of pip installed packages.
        """
        if (name and prefix) or not (name or prefix):
            raise TypeError("conda pip: exactly one of 'name' ""or 'prefix' "
                            "required.")

        if self._is_not_running:
            cmd_list = self._abspath(abspath)
            if name:
                cmd_list.extend(['list', '--name', name])
            if prefix:
                cmd_list.extend(['list', '--prefix', prefix])

            qprocess = QProcess()
            qprocess.start(cmd_list[0], cmd_list[1:])
            qprocess.waitForFinished()
            output = qprocess.readAllStandardOutput()
            output = handle_qbytearray(output, CondaProcess.ENCODING)

            result = []
            lines = output.split('\n')

            for line in lines:
                if '<pip>' in line:
                    temp = line.split()[:-1] + ['pip']
                    result.append('-'.join(temp))

            if emit:
                self.sig_finished.emit("pip", str(result), "")

        return result

    def pip_remove(self, name=None, prefix=None, pkgs=None):
        """
        Remove a pip pacakge in given environment by 'name' or 'prefix'.
        """
        if isinstance(pkgs, list) or isinstance(pkgs, tuple):
            pkg = ' '.join(pkgs)
        else:
            pkg = pkgs

        extra_args = ['uninstall', '--yes', pkg]

        if self._is_not_running():
            self._function_called = 'pip_remove'
            self._call_pip(name=name, prefix=prefix, extra_args=extra_args)
예제 #53
0
class AsyncClient(QObject):

    """
    A class which handles a connection to a client through a QProcess.
    """

    # Emitted when the client has initialized.
    initialized = Signal()

    # Emitted when the client errors.
    errored = Signal()

    # Emitted when a request response is received.
    received = Signal(object)

    def __init__(self, target, executable=None, name=None,
                 extra_args=None, libs=None, cwd=None, env=None):
        super(AsyncClient, self).__init__()
        self.executable = executable or sys.executable
        self.extra_args = extra_args
        self.target = target
        self.name = name or self
        self.libs = libs
        self.cwd = cwd
        self.env = env
        self.is_initialized = False
        self.closing = False
        self.context = zmq.Context()
        QApplication.instance().aboutToQuit.connect(self.close)

        # Set up the heartbeat timer.
        self.timer = QTimer(self)
        self.timer.timeout.connect(self._heartbeat)

    def run(self):
        """Handle the connection with the server.
        """
        # Set up the zmq port.
        self.socket = self.context.socket(zmq.PAIR)
        self.port = self.socket.bind_to_random_port('tcp://*')

        # Set up the process.
        self.process = QProcess(self)
        if self.cwd:
            self.process.setWorkingDirectory(self.cwd)
        p_args = ['-u', self.target, str(self.port)]
        if self.extra_args is not None:
            p_args += self.extra_args

        # Set up environment variables.
        processEnvironment = QProcessEnvironment()
        env = self.process.systemEnvironment()
        if (self.env and 'PYTHONPATH' not in self.env) or DEV:
            python_path = osp.dirname(get_module_path('spyderlib'))
            # Add the libs to the python path.
            for lib in self.libs:
                try:
                    path = osp.dirname(imp.find_module(lib)[1])
                    python_path = osp.pathsep.join([python_path, path])
                except ImportError:
                    pass
            env.append("PYTHONPATH=%s" % python_path)
        if self.env:
            env.update(self.env)
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)
        self.process.setProcessEnvironment(processEnvironment)

        # Start the process and wait for started.
        self.process.start(self.executable, p_args)
        self.process.finished.connect(self._on_finished)
        running = self.process.waitForStarted()
        if not running:
            raise IOError('Could not start %s' % self)

        # Set up the socket notifer.
        fid = self.socket.getsockopt(zmq.FD)
        self.notifier = QSocketNotifier(fid, QSocketNotifier.Read, self)
        self.notifier.activated.connect(self._on_msg_received)

    def request(self, func_name, *args, **kwargs):
        """Send a request to the server.

        The response will be a dictionary the 'request_id' and the
        'func_name' as well as a 'result' field with the object returned by
        the function call or or an 'error' field with a traceback.
        """
        if not self.is_initialized:
            return
        request_id = uuid.uuid4().hex
        request = dict(func_name=func_name,
                       args=args,
                       kwargs=kwargs,
                       request_id=request_id)
        self._send(request)
        return request_id

    def close(self):
        """Cleanly close the connection to the server.
        """
        self.closing = True
        self.is_initialized = False
        self.timer.stop()
        self.notifier.activated.disconnect(self._on_msg_received)
        self.notifier.setEnabled(False)
        del self.notifier
        self.request('server_quit')
        self.process.waitForFinished(1000)
        self.process.close()
        self.context.destroy()

    def _on_finished(self):
        """Handle a finished signal from the process.
        """
        if self.closing:
            return
        if self.is_initialized:
            debug_print('Restarting %s' % self.name)
            debug_print(self.process.readAllStandardOutput())
            debug_print(self.process.readAllStandardError())
            self.is_initialized = False
            self.notifier.setEnabled(False)
            self.run()
        else:
            debug_print('Errored %s' % self.name)
            debug_print(self.process.readAllStandardOutput())
            debug_print(self.process.readAllStandardError())
            self.errored.emit()

    def _on_msg_received(self):
        """Handle a message trigger from the socket.
        """
        self.notifier.setEnabled(False)
        while 1:
            try:
                resp = self.socket.recv_pyobj(flags=zmq.NOBLOCK)
            except zmq.ZMQError:
                self.notifier.setEnabled(True)
                return
            if not self.is_initialized:
                self.is_initialized = True
                debug_print('Initialized %s' % self.name)
                self.initialized.emit()
                self.timer.start(HEARTBEAT)
                continue
            resp['name'] = self.name
            self.received.emit(resp)

    def _heartbeat(self):
        """Send a heartbeat to keep the server alive.
        """
        self._send(dict(func_name='server_heartbeat'))

    def _send(self, obj):
        """Send an object to the server.
        """
        try:
            self.socket.send_pyobj(obj)
        except Exception as e:
            debug_print(e)
            self.is_initialized = False
            self._on_finished()
예제 #54
0
class CondaProcess(QObject):
    """conda-api modified to work with QProcess instead of popen"""
    ENCODING = 'ascii'

    def __init__(self, parent, on_finished=None, on_partial=None):
        QObject.__init__(self, parent)
        self._parent = parent
        self.output = None
        self.partial = None
        self.stdout = None
        self.error = None
        self._parse = False
        self._function_called = ''
        self._name = None
        self._process = QProcess()
        self._on_finished = on_finished

        self._process.finished.connect(self._call_conda_ready)
        self._process.readyReadStandardOutput.connect(self._call_conda_partial)

        if on_finished is not None:
            self._process.finished.connect(on_finished)
        if on_partial is not None:
            self._process.readyReadStandardOutput.connect(on_partial)

        self.set_root_prefix()

    def _call_conda_partial(self):
        """ """
        stdout = self._process.readAllStandardOutput()
        stdout = handle_qbytearray(stdout, CondaProcess.ENCODING)

        stderr = self._process.readAllStandardError()
        stderr = handle_qbytearray(stderr, CondaProcess.ENCODING)

        if self._parse:
            self.output = json.loads(stdout)
        else:
            self.output = stdout

        self.partial = self.output
        self.stdout = self.output
        self.error = stderr

#        print(self.partial)
#        print(self.error)

    def _call_conda_ready(self):
        """function called when QProcess in _call_conda finishes task"""
        function = self._function_called

        if self.stdout is None:
            stdout = to_text_string(self._process.readAllStandardOutput(),
                                    encoding=CondaProcess.ENCODING)
        else:
            stdout = self.stdout

        if self.error is None:
            stderr = to_text_string(self._process.readAllStandardError(),
                                    encoding=CondaProcess.ENCODING)
        else:
            stderr = self.error

        if function == 'get_conda_version':
            pat = re.compile(r'conda:?\s+(\d+\.\d\S+|unknown)')
            m = pat.match(stderr.strip())
            if m is None:
                m = pat.match(stdout.strip())
            if m is None:
                raise Exception('output did not match: %r' % stderr)
            self.output = m.group(1)
#        elif function == 'get_envs':
#            info = self.output
#            self.output = info['envs']
#        elif function == 'get_prefix_envname':
#            name = self._name
#            envs = self.output
#            self.output = self._get_prefix_envname_helper(name, envs)
#            self._name = None
        elif function == 'config_path':
            result = self.output
            self.output = result['rc_path']
        elif function == 'config_get':
            result = self.output
            self.output = result['get']
        elif (function == 'config_delete' or function == 'config_add' or
                function == 'config_set' or function == 'config_remove'):
            result = self.output
            self.output = result.get('warnings', [])
        elif function == 'pip':
            result = []
            lines = self.output.split('\n')
            for line in lines:
                if '<pip>' in line:
                    temp = line.split()[:-1] + ['pip']
                    result.append('-'.join(temp))
            self.output = result

        if stderr.strip():
            self.error = stderr
#            raise Exception('conda %r:\nSTDERR:\n%s\nEND' % (extra_args,
#                                                             stderr.decode()))
        self._parse = False

    def _get_prefix_envname_helper(self, name, envs):
        """ """
        global ROOTPREFIX
        if name == 'root':
            return ROOT_PREFIX
        for prefix in envs:
            if basename(prefix) == name:
                return prefix
        return None

    def _abspath(self, abspath):
        """ """
        if abspath:
            if sys.platform == 'win32':
                python = join(ROOT_PREFIX, 'python.exe')
                conda = join(ROOT_PREFIX,
                             'Scripts', 'conda-script.py')
            else:
                python = join(ROOT_PREFIX, 'bin/python')
                conda = join(ROOT_PREFIX, 'bin/conda')
            cmd_list = [python, conda]
        else:  # just use whatever conda is on the path
            cmd_list = ['conda']
        return cmd_list

    def _call_conda(self, extra_args, abspath=True):
        """ """
        # call conda with the list of extra arguments, and return the tuple
        # stdout, stderr
        global ROOT_PREFIX
#        if abspath:
#            if sys.platform == 'win32':
#                python = join(ROOT_PREFIX, 'python.exe')
#                conda = join(ROOT_PREFIX,
#                             'Scripts', 'conda-script.py')
#            else:
#                python = join(ROOT_PREFIX, 'bin/python')
#                conda = join(ROOT_PREFIX, 'bin/conda')
#            cmd_list = [python, conda]
#        else:  # just use whatever conda is on the path
#            cmd_list = ['conda']
        cmd_list = self._abspath(abspath)
        cmd_list.extend(extra_args)

#        try:
#            p = Popen(cmd_list, stdout=PIPE, stderr=PIPE)
#        except OSError:
#            raise Exception("could not invoke %r\n" % args)
#        return p.communicate()

        # adapted code
        # ------------
        self.error, self.output = None, None
        self._process.start(cmd_list[0], cmd_list[1:])

    def _call_and_parse(self, extra_args, abspath=True):
        """ """
#        stdout, stderr = _call_conda(extra_args, abspath=abspath)
#        if stderr.decode().strip():
#            raise Exception('conda %r:\nSTDERR:\n%s\nEND' % (extra_args,
#                                                             stderr.decode()))
#    return json.loads(stdout.decode())

        # adapted code
        # ------------
        self._parse = True
        self._call_conda(extra_args, abspath=abspath)

    def _setup_install_commands_from_kwargs(self, kwargs, keys=tuple()):
        """ """
        cmd_list = []
        if kwargs.get('override_channels', False) and 'channel' not in kwargs:
            raise TypeError('conda search: override_channels requires channel')

        if 'env' in kwargs:
            cmd_list.extend(['--name', kwargs.pop('env')])
        if 'prefix' in kwargs:
            cmd_list.extend(['--prefix', kwargs.pop('prefix')])
        if 'channel' in kwargs:
            channel = kwargs.pop('channel')
            if isinstance(channel, str):
                cmd_list.extend(['--channel', channel])
            else:
                cmd_list.append('--channel')
                cmd_list.extend(channel)

        for key in keys:
            if key in kwargs and kwargs[key]:
                cmd_list.append('--' + key.replace('_', '-'))

        return cmd_list

    def set_root_prefix(self, prefix=None):
        """
        Set the prefix to the root environment (default is /opt/anaconda).
        This function should only be called once (right after importing
        conda_api).
        """
        global ROOT_PREFIX

        if prefix:
            ROOT_PREFIX = prefix
        # find *some* conda instance, and then use info() to get 'root_prefix'
        else:
            pass
#            i = self.info(abspath=False)
#            self.ROOT_PREFIX = i['root_prefix']
            '''
            plat = 'posix'
            if sys.platform.lower().startswith('win'):
                listsep = ';'
                plat = 'win'
            else:
                listsep = ':'

            for p in os.environ['PATH'].split(listsep):
                if (os.path.exists(os.path.join(p, 'conda')) or
                    os.path.exists(os.path.join(p, 'conda.exe')) or
                    os.path.exists(os.path.join(p, 'conda.bat'))):

                    # TEMPORARY:
                    ROOT_PREFIX = os.path.dirname(p) # root prefix is 1 dir up
                    i = info()
                    # REAL:
                    ROOT_PREFIX = i['root_prefix']
                    break
            else: # fall back to standard install location, which may be wrong
                if plat == 'win':
                    ROOT_PREFIX = 'C:\Anaconda'
                else:
                    ROOT_PREFIX = '/opt/anaconda'
            '''
            # adapted code
            # ------------
            if ROOT_PREFIX is None:
#                qprocess = QProcess()
#                cmd_list = ['conda', 'info', '--json']
#                qprocess.start(cmd_list[0], cmd_list[1:])
#                qprocess.waitForFinished()

#                output = qprocess.readAllStandardOutput()
#                output = handle_qbytearray(output, CondaProcess.ENCODING)
#                info = json.loads(output)
#                ROOT_PREFIX = info['root_prefix']
                info = self.info(abspath=False)
                ROOT_PREFIX = info['root_prefix']



    def get_conda_version(self):
        """
        return the version of conda being used (invoked) as a string
        """
#        pat = re.compile(r'conda:?\s+(\d+\.\d\S+|unknown)')
#        stdout, stderr = self._call_conda(['--version'])
#        # argparse outputs version to stderr in Python < 3.4.
#        # http://bugs.python.org/issue18920
#        m = pat.match(stderr.decode().strip())
#        if m is None:
#            m = pat.match(stdout.decode().strip())
#
#        if m is None:
#            raise Exception('output did not match: %r' % stderr)
#        return m.group(1)

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'get_conda_version'
            self._call_conda(['--version'])

    def get_envs(self):
        """
        Return all of the (named) environment (this does not include the root
        environment), as a list of absolute path to their prefixes.
        """
#        info = self._call_and_parse(['info', '--json'])
#        return info['envs']

        info = self.info()
        return info['envs']

        # adapted code
        # ------------
#        if self._process.state() == QProcess.NotRunning:
#            self._function_called = 'get_envs'
#            self._call_and_parse(['info', '--json'])

    def get_prefix_envname(self, name):
        """
        Given the name of an environment return its full prefix path, or None
        if it cannot be found.
        """
        if name == 'root':
            return ROOT_PREFIX
        for prefix in self.get_envs():
            if basename(prefix) == name:
                return prefix
        return None

        # adapted code
        # ------------
#        if self._process.state() == QProcess.NotRunning:
#            self._name = name
#            self._function_called = 'get_prefix_envname'
#            self._call_and_parse(['info', '--json'])

    def info(self, abspath=True):
        """
        Return a dictionary with configuration information.
        No guarantee is made about which keys exist.  Therefore this function
        should only be used for testing and debugging.
        """
#        return self._call_and_parse(['info', '--json'], abspath=abspath)

        qprocess = QProcess()
        cmd_list = self._abspath(abspath)
        cmd_list.extend(['info', '--json'])
        qprocess.start(cmd_list[0], cmd_list[1:])
        qprocess.waitForFinished()
        output = qprocess.readAllStandardOutput()
        output = handle_qbytearray(output, CondaProcess.ENCODING)
        info = json.loads(output)
        return info

        # adapted code
        # ------------
#        if self._process.state() == QProcess.NotRunning:
#            self._function_called = 'info'
#            self._call_and_parse(['info', '--json'], abspath=abspath)

    def package_info(self, package, abspath=True):
        """
        Return a dictionary with package information.

        Structure is {
            'package_name': [{
                'depends': list,
                'version': str,
                'name': str,
                'license': str,
                'fn': ...,
                ...
            }]
        }
        """
#        return self._call_and_parse(['info', package, '--json'],
#                                    abspath=abspath)

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'package_info'
            self._call_and_parse(['info', package, '--json'], abspath=abspath)

    def search(self, regex=None, spec=None, **kwargs):
        """
        Search for packages.
        """
        cmd_list = ['search', '--json']

        if regex and spec:
            raise TypeError('conda search: only one of regex or spec allowed')

        if regex:
            cmd_list.append(regex)

        if spec:
            cmd_list.extend(['--spec', spec])

        if 'platform' in kwargs:
            platform = kwargs.pop('platform')
            platforms = ('win-32', 'win-64', 'osx-64', 'linux-32', 'linux-64')
            if platform not in platforms:
                raise TypeError('conda search: platform must be one of ' +
                                ', '.join(platforms))
            cmd_list.extend(['--platform', platform])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('canonical', 'unknown', 'use_index_cache', 'outdated',
                 'override_channels')))

#        return self._call_and_parse(cmd_list,
#                                    abspath=kwargs.get('abspath', True))
        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'search'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def share(self, prefix):
        """
        Create a "share package" of the environment located in `prefix`,
        and return a dictionary with (at least) the following keys:
          - 'path': the full path to the created package
          - 'warnings': a list of warnings

        This file is created in a temp directory, and it is the callers
        responsibility to remove this directory (after the file has been
        handled in some way).
        """
#        return self._call_and_parse(['share', '--json', '--prefix', prefix])

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'share'
            self._call_and_parse(['share', '--json', '--prefix', prefix])

    def create(self, name=None, path=None, pkgs=None):
        """
        Create an environment either by name or path with a specified set of
        packages
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError('must specify a list of one or more packages to '
                            'install into new environment')

        cmd_list = ['create', '--yes', '--quiet']
        if name:
            ref = name
            search = [os.path.join(d, name) for d in self.info()['envs_dirs']]
            cmd_list = ['create', '--yes', '--quiet', '--name', name]
        elif path:
            ref = path
            search = [path]
            cmd_list = ['create', '--yes', '--quiet', '--prefix', path]
        else:
            raise TypeError('must specify either an environment name or a path'
                            ' for new environment')

        if any(os.path.exists(path) for path in search):
            raise CondaEnvExistsError('Conda environment [%s] already exists'
                                      % ref)

        cmd_list.extend(pkgs)
#        (out, err) = self._call_conda(cmd_list)
#        if err.decode().strip():
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               err.decode()))
#        return out

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'create'
            self._call_conda(cmd_list)

    def install(self, name=None, path=None, pkgs=None, dep=True):
        """
        Install packages into an environment either by name or path with a
        specified set of packages
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError('must specify a list of one or more packages to '
                            'install into existing environment')

        cmd_list = ['install', '--yes', '--json', '--force-pscheck']
#        cmd_list = ['install', '--yes', '--quiet']
        if name:
            cmd_list.extend(['--name', name])
        elif path:
            cmd_list.extend(['--prefix', path])
        else:  # just install into the current environment, whatever that is
            pass

        cmd_list.extend(pkgs)

#        (out, err) = self._call_conda(cmd_list)
#        if err.decode().strip():
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               err.decode()))
#        return out

        # adapted code
        # ------------
        if not dep:
            cmd_list.extend(['--no-deps'])

        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'install'
            self._call_conda(cmd_list)

    def update(self, *pkgs, **kwargs):
        """
        Update package(s) (in an environment) by name.
        """
        cmd_list = ['update', '--json', '--quiet', '--yes']

        if not pkgs and not kwargs.get('all'):
            raise TypeError("Must specify at least one package to update, \
                            or all=True.")

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('dry_run', 'no_deps', 'override_channels',
                 'no_pin', 'force', 'all', 'use_index_cache', 'use_local',
                 'alt_hint')))

        cmd_list.extend(pkgs)

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#
#        return result

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'update'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def remove(self, *pkgs, **kwargs):
        """
        Remove a package (from an environment) by name.

        Returns {
            success: bool, (this is always true),
            (other information)
        }
        """
        cmd_list = ['remove', '--json', '--quiet', '--yes', '--force-pscheck']
#        cmd_list = ['remove', '--json', '--quiet', '--yes']

        if not pkgs and not kwargs.get('all'):
            raise TypeError("Must specify at least one package to remove, \
                            or all=True.")

        if kwargs.get('name') and kwargs.get('path'):
            raise TypeError('conda remove: At most one of name, path allowed')

        if kwargs.get('name'):
            cmd_list.extend(['--name', kwargs.pop('name')])

        if kwargs.get('path'):
            cmd_list.extend(['--prefix', kwargs.pop('path')])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('dry_run', 'features', 'override_channels',
                 'no_pin', 'force', 'all')))

        cmd_list.extend(pkgs)

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#
#        return result

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'remove'
            self._call_and_parse(cmd_list,
                                 abspath=kwargs.get('abspath', True))

    def remove_environment(self, name=None, path=None, **kwargs):
        """
        Remove an environment entirely.

        See ``remove``.
        """
#        return self.remove(name=name, path=path, all=True, **kwargs)

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'remove_environment'
            self.remove(name=name, path=path, all=True, **kwargs)

    def clone_environment(self, clone, name=None, path=None, **kwargs):
        """
        Clone the environment ``clone`` into ``name`` or ``path``.
        """
        cmd_list = ['create', '--json', '--quiet']

        if (name and path) or not (name or path):
            raise TypeError("conda clone_environment: exactly one of name or \
                            path required")

        if name:
            cmd_list.extend(['--name', name])

        if path:
            cmd_list.extend(['--prefix', path])

        cmd_list.extend(['--clone', clone])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                ('dry_run', 'unknown', 'use_index_cache', 'use_local',
                 'no_pin', 'force', 'all', 'channel', 'override_channels',
                 'no_default_packages')))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#
#        return result

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'clone_environment'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

#    def process(self, name=None, path=None, cmd=None, args=None, stdin=None,
#                stdout=None, stderr=None, timeout=None):
#        """
#        Create a Popen process for cmd using the specified args but in the
#        conda environment specified by name or path.
#
#        The returned object will need to be invoked with p.communicate() or
#        similar.
#
#        :param name: name of conda environment
#        :param path: path to conda environment (if no name specified)
#        :param cmd:  command to invoke
#        :param args: argument
#        :param stdin: stdin
#        :param stdout: stdout
#        :param stderr: stderr
#        :return: Popen object
#        """
#
#        if bool(name) == bool(path):
#            raise TypeError('exactly one of name or path must be specified')
#
#        if not cmd:
#            raise TypeError('cmd to execute must be specified')
#
#        if not args:
#            args = []
#
#        if name:
#            path = self.get_prefix_envname(name)
#
#        plat = 'posix'
#        if sys.platform.lower().startswith('win'):
#            listsep = ';'
#            plat = 'win'
#        else:
#            listsep = ':'
#
#        conda_env = dict(os.environ)
#
#        if plat == 'posix':
#            conda_env['PATH'] = path + os.path.sep + 'bin' + listsep + \
#                conda_env['PATH']
#        else: # win
#            conda_env['PATH'] = path + os.path.sep + 'Scripts' + listsep + \
#                conda_env['PATH']
#
#        conda_env['PATH'] = path + listsep + conda_env['PATH']
#
#        cmd_list = [cmd]
#        cmd_list.extend(args)
#
#        try:
#            p = Popen(cmd_list, env=conda_env, stdin=stdin, stdout=stdout,
#                      stderr=stderr)
#        except OSError:
#            raise Exception("could not invoke %r\n" % cmd_list)
#        return p

    def clone(self, path, prefix):
        """
        Clone a "share package" (located at `path`) by creating a new
        environment at `prefix`, and return a dictionary with (at least) the
        following keys:
          - 'warnings': a list of warnings

        The directory `path` is located in, should be some temp directory or
        some other directory OUTSIDE /opt/anaconda.  After calling this
        function, the original file (at `path`) may be removed (by the caller
        of this function).
        The return object is a list of warnings.
        """
#        return self._call_and_parse(['clone', '--json', '--prefix', prefix,
#                                     path])
        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'clone'
            self._call_and_parse(['clone', '--json', '--prefix', prefix, path])

    def _setup_config_from_kwargs(kwargs):
        cmd_list = ['--json', '--force']

        if 'file' in kwargs:
            cmd_list.extend(['--file', kwargs['file']])

        if 'system' in kwargs:
            cmd_list.append('--system')

        return cmd_list

    def config_path(self, **kwargs):
        """
        Get the path to the config file.
        """
        cmd_list = ['config', '--get']
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result['rc_path']

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'config_path'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_get(self, *keys, **kwargs):
        """
        Get the values of configuration keys.

        Returns a dictionary of values. Note, the key may not be in the
        dictionary if the key wasn't set in the configuration file.
        """
        cmd_list = ['config', '--get']
        cmd_list.extend(keys)
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result['get']

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'config_get'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_set(self, key, value, **kwargs):
        """
        Set a key to a (bool) value.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--set', key, str(value)]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result.get('warnings', [])

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'config_set'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_add(self, key, value, **kwargs):
        """
        Add a value to a key.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--add', key, value]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result.get('warnings', [])

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'config_add'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_remove(self, key, value, **kwargs):
        """
        Remove a value from a key.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--remove', key, value]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result.get('warnings', [])

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'config_remove'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def config_delete(self, key, **kwargs):
        """
        Remove a key entirely.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ['config', '--remove-key', key]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

#        result = self._call_and_parse(cmd_list,
#                                      abspath=kwargs.get('abspath', True))
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result.get('warnings', [])

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'config_delete'
            self._call_and_parse(cmd_list, abspath=kwargs.get('abspath', True))

    def run(self, command, abspath=True):
        """
        Launch the specified app by name or full package name.

        Returns a dictionary containing the key "fn", whose value is the full
        package (ending in ``.tar.bz2``) of the app.
        """
        cmd_list = ['run', '--json', command]

#        result = self._call_and_parse(cmd_list, abspath=abspath)
#
#        if 'error' in result:
#            raise CondaError('conda %s: %s' % (" ".join(cmd_list),
#                                               result['error']))
#        return result

        # adapted code
        # ------------
        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'run'
            self._call_and_parse(cmd_list, abspath=abspath)

#    def test():
#        """
#        Self-test function, which prints useful debug information.
#        This function returns None on success, and will crash the interpreter
#        on failure.
#        """
#        print('sys.version: %r' % sys.version)
#        print('sys.prefix : %r' % sys.prefix)
#        print('conda_api.__version__: %r' % __version__)
#        print('conda_api.ROOT_PREFIX: %r' % ROOT_PREFIX)
#        if isdir(ROOT_PREFIX):
#            conda_version = get_conda_version()
#            print('conda version: %r' % conda_version)
#            print('conda info:')
#            d = info()
#            for kv in d.items():
#                print('\t%s=%r' % kv)
#            assert d['conda_version'] == conda_version
#        else:
#            print('Warning: no such directory: %r' % ROOT_PREFIX)
#        print('OK')

    # ---- Additional methods not in conda-api
    def pip(self, name):
        """Get list of pip installed packages."""
        cmd_list = ['list', '-n', name]

        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'pip'
            self._call_conda(cmd_list)

    def dependencies(self, name=None, path=None, pkgs=None, dep=True):
        """
        Install packages into an environment either by name or path with a
        specified set of packages
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError('must specify a list of one or more packages to '
                            'install into existing environment')

        cmd_list = ['install', '--dry-run', '--json']
        cmd_list = ['install', '--dry-run', '--json', '--force-pscheck']

        if not dep:
            cmd_list.extend(['--no-deps'])

        if name:
            cmd_list.extend(['--name', name])
        elif path:
            cmd_list.extend(['--prefix', path])
        else:  # just install into the current environment, whatever that is
            pass

        cmd_list.extend(pkgs)

        if self._process.state() == QProcess.NotRunning:
            self._function_called = 'install_dry'
            self._call_and_parse(cmd_list)
예제 #55
0
    def create_process(self):
        self.shell.clear()
            
        self.process = QProcess(self)
        if self.merge_output_channels:
            self.process.setProcessChannelMode(QProcess.MergedChannels)
        else:
            self.process.setProcessChannelMode(QProcess.SeparateChannels)
        self.shell.wait_for_ready_read.connect(
                     lambda: self.process.waitForReadyRead(250))
        
        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)

        #-------------------------Python specific------------------------------
        # Python arguments
        p_args = ['-u']
        if DEBUG >= 3:
            p_args += ['-v']
        p_args += get_python_args(self.fname, self.python_args,
                                  self.interact_action.isChecked(),
                                  self.debug_action.isChecked(),
                                  self.arguments)
        
        env = [to_text_string(_path)
               for _path in self.process.systemEnvironment()]
        if self.pythonstartup:
            env.append('PYTHONSTARTUP=%s' % self.pythonstartup)
        
        #-------------------------Python specific-------------------------------
        # Post mortem debugging
        if self.post_mortem_action.isChecked():
            env.append('SPYDER_EXCEPTHOOK=True')

        # Set standard input/output encoding for Python consoles
        # (IPython handles it on its own)
        # See http://stackoverflow.com/q/26312400/438386, specifically
        # the comments of Martijn Pieters
        if not self.is_ipykernel:
            env.append('PYTHONIOENCODING=UTF-8')

        # Monitor
        if self.monitor_enabled:
            env.append('SPYDER_SHELL_ID=%s' % id(self))
            env.append('SPYDER_AR_TIMEOUT=%d' % self.autorefresh_timeout)
            env.append('SPYDER_AR_STATE=%r' % self.autorefresh_state)
            from spyderlib.widgets.externalshell import introspection
            introspection_server = introspection.start_introspection_server()
            introspection_server.register(self)
            notification_server = introspection.start_notification_server()
            self.notification_thread = notification_server.register(self)
            self.notification_thread.sig_pdb.connect(
                                              lambda fname, lineno:
                                              self.sig_pdb.emit(fname, lineno))
            self.notification_thread.new_ipython_kernel.connect(
                                         lambda args:
                                         self.create_ipython_client.emit(args))
            self.notification_thread.open_file.connect(
                                            lambda fname, lineno:
                                            self.open_file.emit(fname, lineno))
            if self.namespacebrowser is not None:
                self.configure_namespacebrowser()
            env.append('SPYDER_I_PORT=%d' % introspection_server.port)
            env.append('SPYDER_N_PORT=%d' % notification_server.port)

        # External modules options
        if not self.is_ipykernel:
            env.append('ETS_TOOLKIT=%s' % self.ets_backend)
        if self.mpl_backend is not None:
            backends = {0: 'Automatic', 1: 'None', 2: 'TkAgg'}
            env.append('SPY_MPL_BACKEND=%s' % backends[self.mpl_backend])
        if self.qt_api and not self.is_ipykernel:
            env.append('QT_API=%s' % self.qt_api)
        env.append('COLORIZE_SYS_STDERR=%s' % self.colorize_sys_stderr)
#        # Socket-based alternative (see input hook in sitecustomize.py):
#        if self.install_qt_inputhook:
#            from PyQt4.QtNetwork import QLocalServer
#            self.local_server = QLocalServer()
#            self.local_server.listen(str(id(self)))

        # User Module Deleter
        if self.is_interpreter:
            env.append('UMR_ENABLED=%r' % self.umr_enabled)
            env.append('UMR_NAMELIST=%s' % ','.join(self.umr_namelist))
            env.append('UMR_VERBOSE=%r' % self.umr_verbose)
            env.append('MATPLOTLIB_ION=True')
        else:
            if self.interact:
                env.append('MATPLOTLIB_ION=True')
            else:
                env.append('MATPLOTLIB_ION=False')

        # IPython kernel
        env.append('IPYTHON_KERNEL=%r' % self.is_ipykernel)

        # External interpreter
        env.append('EXTERNAL_INTERPRETER=%r' % self.external_interpreter)

        # Add sitecustomize path to path list
        pathlist = []
        scpath = osp.dirname(osp.abspath(__file__))
        pathlist.append(scpath)
        
        # Adding Spyder path
        pathlist += self.path
        
        # Adding path list to PYTHONPATH environment variable
        add_pathlist_to_PYTHONPATH(env, pathlist)

        #-------------------------Python specific------------------------------
                        
        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.readyReadStandardError.connect(self.write_error)
        self.process.finished.connect(lambda ec, es=QProcess.ExitStatus:
                                      self.finished(ec, es))
        self.sig_finished.connect(self.dialog_manager.close_all)
        self.terminate_button.clicked.connect(self.process.terminate)
        self.kill_button.clicked.connect(self.process.kill)
        
        #-------------------------Python specific------------------------------
        # Fixes for our Mac app:
        # 1. PYTHONPATH and PYTHONHOME are set while bootstrapping the app,
        #    but their values are messing sys.path for external interpreters
        #    (e.g. EPD) so we need to remove them from the environment.
        # 2. Set PYTHONPATH again but without grabbing entries defined in the
        #    environment (Fixes Issue 1321)
        # 3. Remove PYTHONOPTIMIZE from env so that we can have assert
        #    statements working with our interpreters (See Issue 1281)
        if running_in_mac_app():
            if MAC_APP_NAME not in self.pythonexecutable:
                env = [p for p in env if not (p.startswith('PYTHONPATH') or \
                                              p.startswith('PYTHONHOME'))] # 1.

                add_pathlist_to_PYTHONPATH(env, pathlist, drop_env=True)   # 2.
            env = [p for p in env if not p.startswith('PYTHONOPTIMIZE')]   # 3.

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)
        self.process.setProcessEnvironment(processEnvironment)                   
        self.process.start(self.pythonexecutable, p_args)
        #-------------------------Python specific------------------------------
            
        running = self.process.waitForStarted(3000)
        self.set_running_state(running)
        if not running:
            if self.is_ipykernel:
                self.ipython_kernel_start_error.emit(
                          _("The kernel failed to start!! That's all we know... "
                            "Please close this console and open a new one."))
            else:
                QMessageBox.critical(self, _("Error"),
                                     _("A Python console failed to start!"))
        else:
            self.shell.setFocus()
            self.started.emit()
        return self.process
예제 #56
0
                messages.append(message)
        except zmq.ZMQError:
            pass
        finally:
            self.notifier.setEnabled(True)
        if messages:
            self.sig_received.emit(messages)

    def close(self):
        """Read any remaining messages and close stream."""
        self.received_message()  # Flush remaining messages
        self.notifier.setEnabled(False)
        self.socket.close()
        self.context.destroy()


if __name__ == '__main__':
    # For testing, construct a ZMQ stream between two processes and send
    # the number 42 over the stream
    if len(sys.argv) == 1:
        app = QApplication(sys.argv)
        manager = ZmqStreamReader()
        manager.sig_received.connect(print)
        process = QProcess()
        process.start('python', [sys.argv[0], str(manager.port)])
        process.finished.connect(app.quit)
        sys.exit(app.exec_())
    else:
        worker = ZmqStreamWriter(sys.argv[1])
        worker.write(42)
예제 #57
0
class ProcessWorker(QObject):
    """
    """
    sig_finished = Signal(object, object, object)
    sig_partial = Signal(object, object, object)

    def __init__(self, cmd_list, parse=False, pip=False, callback=None,
                 extra_kwargs={}):
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._parse = parse
        self._pip = pip
        self._conda = not pip
        self._callback = callback
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._extra_kwargs = extra_kwargs

        self._timer = QTimer()
        self._process = QProcess()

        self._timer.setInterval(50)

        self._timer.timeout.connect(self._communicate)
        self._process.finished.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)

    def _partial(self):
        raw_stdout = self._process.readAllStandardOutput()
        stdout = handle_qbytearray(raw_stdout, _CondaAPI.UTF8)

        json_stdout = stdout.replace('\n\x00', '')
        try:
            json_stdout = json.loads(json_stdout)
        except Exception:
            json_stdout = stdout

        if self._partial_stdout is None:
            self._partial_stdout = stdout
        else:
            self._partial_stdout += stdout

        self.sig_partial.emit(self, json_stdout, None)

    def _communicate(self):
        """
        """
        if not self._communicate_first:
            if self._process.state() == QProcess.NotRunning:
                self.communicate()
        elif self._fired:
            self._timer.stop()

    def communicate(self):
        """
        """
        self._communicate_first = True
        self._process.waitForFinished()

        if self._partial_stdout is None:
            raw_stdout = self._process.readAllStandardOutput()
            stdout = handle_qbytearray(raw_stdout, _CondaAPI.UTF8)
        else:
            stdout = self._partial_stdout

        raw_stderr = self._process.readAllStandardError()
        stderr = handle_qbytearray(raw_stderr, _CondaAPI.UTF8)
        result = [stdout.encode(_CondaAPI.UTF8), stderr.encode(_CondaAPI.UTF8)]

        # FIXME: Why does anaconda client print to stderr???
        if PY2:
            stderr = stderr.decode()
        if 'using anaconda cloud api site' not in stderr.lower():
            if stderr.strip() and self._conda:
                raise Exception('{0}:\n'
                                'STDERR:\n{1}\nEND'
                                ''.format(' '.join(self._cmd_list),
                                          stderr))
#            elif stderr.strip() and self._pip:
#                raise PipError(self._cmd_list)
        else:
            result[-1] = ''

        if self._parse and stdout:
            try:
                result = json.loads(stdout), result[-1]
            except ValueError as error:
                result = stdout, error

            if 'error' in result[0]:
                error = '{0}: {1}'.format(" ".join(self._cmd_list),
                                          result[0]['error'])
                result = result[0], error

        if self._callback:
            result = self._callback(result[0], result[-1],
                                    **self._extra_kwargs), result[-1]

        self._result = result
        self.sig_finished.emit(self, result[0], result[-1])

        if result[-1]:
            logger.error(str(('error', result[-1])))

        self._fired = True

        return result

    def close(self):
        """
        """
        self._process.close()

    def is_finished(self):
        """
        """
        return self._process.state() == QProcess.NotRunning and self._fired

    def start(self):
        """
        """
        logger.debug(str(' '.join(self._cmd_list)))

        if not self._fired:
            self._partial_ouput = None
            self._process.start(self._cmd_list[0], self._cmd_list[1:])
            self._timer.start()
        else:
            raise CondaProcessWorker('A Conda ProcessWorker can only run once '
                                     'per method call.')
예제 #58
0
class ExternalSystemShell(ExternalShellBase):
    """External Shell widget: execute Python script in a separate process"""
    SHELL_CLASS = TerminalWidget
    started = Signal()
    
    def __init__(self, parent=None, wdir=None, path=[], light_background=True,
                 menu_actions=None, show_buttons_inside=True,
                 show_elapsed_time=True):
        ExternalShellBase.__init__(self, parent=parent, fname=None, wdir=wdir,
                                   history_filename='.history',
                                   light_background=light_background,
                                   menu_actions=menu_actions,
                                   show_buttons_inside=show_buttons_inside,
                                   show_elapsed_time=show_elapsed_time)
        
        # Additional python path list
        self.path = path
        
        # For compatibility with the other shells that can live in the external
        # console
        self.is_ipykernel = False
        self.connection_file = None

    def get_icon(self):
        return ima.icon('cmdprompt')

    def finish_process(self):
        while not self.process.waitForFinished(100):
            self.process.kill();

    def create_process(self):
        self.shell.clear()

        self.process = QProcess(self)
        self.process.setProcessChannelMode(QProcess.MergedChannels)

        # PYTHONPATH (in case we use Python in this terminal, e.g. py2exe)
        env = [to_text_string(_path)
               for _path in self.process.systemEnvironment()]

        processEnvironment = QProcessEnvironment()
        for envItem in env:
            envName, separator, envValue = envItem.partition('=')
            processEnvironment.insert(envName, envValue)

        add_pathlist_to_PYTHONPATH(env, self.path)
        self.process.setProcessEnvironment(processEnvironment)                   

        
        # Working directory
        if self.wdir is not None:
            self.process.setWorkingDirectory(self.wdir)
            
        # Shell arguments
        if os.name == 'nt':
            p_args = ['/Q']
        else:
            p_args = ['-i']
            
        if self.arguments:
            p_args.extend( shell_split(self.arguments) )
        
        self.process.readyReadStandardOutput.connect(self.write_output)
        self.process.finished.connect(self.finished)
        self.kill_button.clicked.connect(self.process.kill)
        
        if os.name == 'nt':
            self.process.start('cmd.exe', p_args)
        else:
            # Using bash:
            self.process.start('bash', p_args)
            self.send_to_process('PS1="\\u@\\h:\\w> "\n')
            
        running = self.process.waitForStarted()
        self.set_running_state(running)
        if not running:
            QMessageBox.critical(self, _("Error"),
                                 _("Process failed to start"))
        else:
            self.shell.setFocus()
            self.started.emit()
            
        return self.process
    
#===============================================================================
#    Input/Output
#===============================================================================
    def transcode(self, qba):
        if os.name == 'nt':
            return to_text_string( CP850_CODEC.toUnicode(qba.data()) )
        else:
            return ExternalShellBase.transcode(self, qba)
    
    def send_to_process(self, text):
        if not is_text_string(text):
            text = to_text_string(text)
        if text[:-1] in ["clear", "cls", "CLS"]:
            self.shell.clear()
            self.send_to_process(os.linesep)
            return
        if not text.endswith('\n'):
            text += '\n'
        if os.name == 'nt':
            self.process.write(text.encode('cp850'))
        else:
            self.process.write(LOCALE_CODEC.fromUnicode(text))
        self.process.waitForBytesWritten(-1)
        
    def keyboard_interrupt(self):
        # This does not work on Windows:
        # (unfortunately there is no easy way to send a Ctrl+C to cmd.exe)
        self.send_ctrl_to_process('c')
예제 #59
0
class ProcessWorker(QObject):
    """Conda worker based on a QProcess for non blocking UI."""

    sig_finished = Signal(object, object, object)
    sig_partial = Signal(object, object, object)

    def __init__(self, cmd_list, parse=False, pip=False, callback=None,
                 extra_kwargs=None):
        """Conda worker based on a QProcess for non blocking UI.

        Parameters
        ----------
        cmd_list : list of str
            Command line arguments to execute.
        parse : bool (optional)
            Parse json from output.
        pip : bool (optional)
            Define as a pip command.
        callback : func (optional)
            If the process has a callback to process output from comd_list.
        extra_kwargs : dict
            Arguments for the callback.
        """
        super(ProcessWorker, self).__init__()
        self._result = None
        self._cmd_list = cmd_list
        self._parse = parse
        self._pip = pip
        self._conda = not pip
        self._callback = callback
        self._fired = False
        self._communicate_first = False
        self._partial_stdout = None
        self._extra_kwargs = extra_kwargs if extra_kwargs else {}

        self._timer = QTimer()
        self._process = QProcess()

        self._timer.setInterval(150)

        self._timer.timeout.connect(self._communicate)
        # self._process.finished.connect(self._communicate)
        self._process.readyReadStandardOutput.connect(self._partial)

    def _partial(self):
        """Callback for partial output."""
        raw_stdout = self._process.readAllStandardOutput()
        stdout = handle_qbytearray(raw_stdout, _CondaAPI.UTF8)

        json_stdout = stdout.replace('\n\x00', '')
        try:
            json_stdout = json.loads(json_stdout)
        except Exception:
            json_stdout = stdout

        if self._partial_stdout is None:
            self._partial_stdout = stdout
        else:
            self._partial_stdout += stdout

        self.sig_partial.emit(self, json_stdout, None)

    def _communicate(self):
        """Callback for communicate."""
        if (not self._communicate_first and
                self._process.state() == QProcess.NotRunning):
            self.communicate()
        elif self._fired:
            self._timer.stop()

    def communicate(self):
        """Retrieve information."""
        self._communicate_first = True
        self._process.waitForFinished()

        if self._partial_stdout is None:
            raw_stdout = self._process.readAllStandardOutput()
            stdout = handle_qbytearray(raw_stdout, _CondaAPI.UTF8)
        else:
            stdout = self._partial_stdout

        raw_stderr = self._process.readAllStandardError()
        stderr = handle_qbytearray(raw_stderr, _CondaAPI.UTF8)
        result = [stdout.encode(_CondaAPI.UTF8), stderr.encode(_CondaAPI.UTF8)]

        # FIXME: Why does anaconda client print to stderr???
        if PY2:
            stderr = stderr.decode()
        if 'using anaconda' not in stderr.lower():
            if stderr.strip() and self._conda:
                logger.error('{0}:\nSTDERR:\n{1}\nEND'.format(
                        ' '.join(self._cmd_list), stderr))
            elif stderr.strip() and self._pip:
                logger.error("pip error: {}".format(self._cmd_list))
        result[-1] = ''

        if self._parse and stdout:
            try:
                result = json.loads(stdout), result[-1]
            except Exception as error:
                result = stdout, str(error)

            if 'error' in result[0]:
                if not isinstance(result[0], dict):
                    result = {'error': str(result[0])}, None
                error = '{0}: {1}'.format(" ".join(self._cmd_list),
                                          result[0]['error'])
                result = result[0], error

        if self._callback:
            result = self._callback(result[0], result[-1],
                                    **self._extra_kwargs), result[-1]

        self._result = result
        self.sig_finished.emit(self, result[0], result[-1])

        if result[-1]:
            logger.error(str(('error', result[-1])))

        self._fired = True

        return result

    def close(self):
        """Close the running process."""
        self._process.close()

    def is_finished(self):
        """Return True if worker has finished processing."""
        return self._process.state() == QProcess.NotRunning and self._fired

    def start(self):
        """Start process."""
        logger.debug(str(' '.join(self._cmd_list)))

        if not self._fired:
            self._partial_ouput = None
            self._process.start(self._cmd_list[0], self._cmd_list[1:])
            self._timer.start()
        else:
            raise CondaProcessWorker('A Conda ProcessWorker can only run once '
                                     'per method call.')
예제 #60
0
class CondaProcess(QObject):
    """Conda API modified to work with QProcess instead of popen."""

    # Signals
    sig_finished = Signal(str, object, str)
    sig_partial = Signal(str, object, str)
    sig_started = Signal()

    ENCODING = "ascii"
    ROOT_PREFIX = None

    def __init__(self, parent):
        QObject.__init__(self, parent)
        self._parent = parent
        self._output = None
        self._partial = None
        self._stdout = None
        self._error = None
        self._parse = False
        self._function_called = ""
        self._name = None
        self._process = QProcess()
        self.set_root_prefix()

        # Signals
        self._process.finished.connect(self._call_conda_ready)
        self._process.readyReadStandardOutput.connect(self._call_conda_partial)

    # --- Helpers
    # -------------------------------------------------------------------------
    def _is_running(self):
        return self._process.state() != QProcess.NotRunning

    def _is_not_running(self):
        return self._process.state() == QProcess.NotRunning

    def _call_conda_partial(self):
        """ """
        stdout = self._process.readAllStandardOutput()
        stdout = handle_qbytearray(stdout, CondaProcess.ENCODING)

        stderr = self._process.readAllStandardError()
        stderr = handle_qbytearray(stderr, CondaProcess.ENCODING)

        if self._parse:
            try:
                self._output = json.loads(stdout)
            except Exception:
                # Result is a partial json. Can only be parsed when finished
                self._output = stdout
        else:
            self._output = stdout

        self._partial = self._output
        self._stdout = self._output
        self._error = stderr

        self.sig_partial.emit(self._function_called, self._partial, self._error)

    def _call_conda_ready(self):
        """Function called when QProcess in _call_conda finishes task."""
        function = self._function_called

        if self._stdout is None:
            stdout = to_text_string(self._process.readAllStandardOutput(), encoding=CondaProcess.ENCODING)
        else:
            stdout = self._stdout

        if self._error is None:
            stderr = to_text_string(self._process.readAllStandardError(), encoding=CondaProcess.ENCODING)
        else:
            stderr = self._error

        if function == "get_conda_version":
            pat = re.compile(r"conda:?\s+(\d+\.\d\S+|unknown)")
            m = pat.match(stderr.strip())
            if m is None:
                m = pat.match(stdout.strip())
            if m is None:
                raise Exception("output did not match: {0}".format(stderr))
            self._output = m.group(1)
        elif function == "config_path":
            result = self._output
            self._output = result["rc_path"]
        elif function == "config_get":
            result = self._output
            self._output = result["get"]
        elif (
            function == "config_delete"
            or function == "config_add"
            or function == "config_set"
            or function == "config_remove"
        ):
            result = self._output
            self._output = result.get("warnings", [])
        elif function == "pip":
            result = []
            lines = self._output.split("\n")
            for line in lines:
                if "<pip>" in line:
                    temp = line.split()[:-1] + ["pip"]
                    result.append("-".join(temp))
            self._output = result

        if stderr.strip():
            self._error = stderr

        self._parse = False

        self.sig_finished.emit(self._function_called, self._output, self._error)

    def _get_prefix_envname_helper(self, name, envs):
        """ """
        if name == "root":
            return CondaProcess.ROOT_PREFIX

        for prefix in envs:
            if basename(prefix) == name:
                return prefix
        return None

    def _abspath(self, abspath):
        """ """
        if abspath:
            if sys.platform == "win32":
                python = join(CondaProcess.ROOT_PREFIX, "python.exe")
                conda = join(CondaProcess.ROOT_PREFIX, "Scripts", "conda-script.py")
            else:
                python = join(CondaProcess.ROOT_PREFIX, "bin/python")
                conda = join(CondaProcess.ROOT_PREFIX, "bin/conda")
            cmd_list = [python, conda]
        else:
            # Just use whatever conda is on the path
            cmd_list = ["conda"]

        return cmd_list

    def _call_conda(self, extra_args, abspath=True):
        """ """
        if self._is_not_running():
            cmd_list = self._abspath(abspath)
            cmd_list.extend(extra_args)
            self._error, self._output = None, None
            self._process.start(cmd_list[0], cmd_list[1:])
            self.sig_started.emit()

    def _call_and_parse(self, extra_args, abspath=True):
        """ """
        self._parse = True
        self._call_conda(extra_args, abspath=abspath)

    def _setup_install_commands_from_kwargs(self, kwargs, keys=tuple()):
        """ """
        cmd_list = []
        if kwargs.get("override_channels", False) and "channel" not in kwargs:
            raise TypeError("conda search: override_channels requires channel")

        if "env" in kwargs:
            cmd_list.extend(["--name", kwargs.pop("env")])
        if "prefix" in kwargs:
            cmd_list.extend(["--prefix", kwargs.pop("prefix")])
        if "channel" in kwargs:
            channel = kwargs.pop("channel")
            if isinstance(channel, str):
                cmd_list.extend(["--channel", channel])
            else:
                cmd_list.append("--channel")
                cmd_list.extend(channel)

        for key in keys:
            if key in kwargs and kwargs[key]:
                cmd_list.append("--" + key.replace("_", "-"))

        return cmd_list

    # --- Public api
    # ------------------------------------------------------------------------
    def linked(self, prefix):
        """
        Return the (set of canonical names) of linked packages in `prefix`.
        """
        if not isdir(prefix):
            raise Exception("no such directory: {0}".format(prefix))

        meta_dir = join(prefix, "conda-meta")

        if not isdir(meta_dir):
            # we might have nothing in linked (and no conda-meta directory)
            result = set()

        result = set(fn[:-5] for fn in os.listdir(meta_dir) if fn.endswith(".json"))
        self.sig_finished.emit("linked", result, "")
        return result

    def split_canonical_name(self, cname):
        """
        Split a canonical package name into (name, version, build) strings.
        """
        result = tuple(cname.rsplit("-", 2))
        self.sig_finished.emit("split_canonical_name", result, "")
        return result

    def set_root_prefix(self, prefix=None):
        """
        Set the prefix to the root environment (default is /opt/anaconda).
        """
        if prefix:
            CondaProcess.ROOT_PREFIX = prefix
        else:
            # Find conda instance, and then use info() to get 'root_prefix'
            if CondaProcess.ROOT_PREFIX is None:
                info = self.info(abspath=False)
                CondaProcess.ROOT_PREFIX = info["root_prefix"]

    def get_conda_version(self):
        """
        Return the version of conda being used (invoked) as a string.
        """
        if self._is_not_running():
            self._function_called = "get_conda_version"
            self._call_conda(["--version"])

    def get_envs(self):
        """
        Return all of the (named) environment (this does not include the root
        environment), as a list of absolute path to their prefixes.
        """
        if self._is_not_running():
            info = self.info()
            result = info["envs"]
            self.sig_finished.emit("get_envs", result, "")
            return result

    def get_prefix_envname(self, name):
        """
        Given the name of an environment return its full prefix path, or None
        if it cannot be found.
        """
        if self._is_not_running():
            if name == "root":
                pref = CondaProcess.ROOT_PREFIX
            for prefix in self.get_envs():
                if basename(prefix) == name:
                    pref = prefix
                    break
            self.sig_finished.emit("get_prefix_envname", pref, "")
            return pref

    def info(self, abspath=True):
        """
        Return a dictionary with configuration information.

        No guarantee is made about which keys exist.  Therefore this function
        should only be used for testing and debugging.
        """
        if self._is_not_running():
            qprocess = QProcess()
            cmd_list = self._abspath(abspath)
            cmd_list.extend(["info", "--json"])
            qprocess.start(cmd_list[0], cmd_list[1:])
            qprocess.waitForFinished()
            output = qprocess.readAllStandardOutput()
            output = handle_qbytearray(output, CondaProcess.ENCODING)
            info = json.loads(output)

            self.sig_finished.emit("info", str(info), "")
            return info

    def package_info(self, package, abspath=True):
        """
        Return a dictionary with package information.

        Structure is {
            'package_name': [{
                'depends': list,
                'version': str,
                'name': str,
                'license': str,
                'fn': ...,
                ...
            }]
        }
        """
        if self._is_not_running():
            self._function_called = "package_info"
            self._call_and_parse(["info", package, "--json"], abspath=abspath)

    def search(self, regex=None, spec=None, **kwargs):
        """
        Search for packages.
        """
        cmd_list = ["search", "--json"]

        if regex and spec:
            raise TypeError("conda search: only one of regex or spec allowed")

        if regex:
            cmd_list.append(regex)

        if spec:
            cmd_list.extend(["--spec", spec])

        if "platform" in kwargs:
            platform = kwargs.pop("platform")
            platforms = ("win-32", "win-64", "osx-64", "linux-32", "linux-64")
            if platform not in platforms:
                raise TypeError("conda search: platform must be one of " + ", ".join(platforms))
            cmd_list.extend(["--platform", platform])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs, ("canonical", "unknown", "use_index_cache", "outdated", "override_channels")
            )
        )

        if self._is_not_running():
            self._function_called = "search"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def share(self, prefix):
        """
        Create a "share package" of the environment located in `prefix`,
        and return a dictionary with (at least) the following keys:
          - 'path': the full path to the created package
          - 'warnings': a list of warnings

        This file is created in a temp directory, and it is the callers
        responsibility to remove this directory (after the file has been
        handled in some way).
        """
        if self._is_not_running:
            self._function_called = "share"
            self._call_and_parse(["share", "--json", "--prefix", prefix])

    def create(self, name=None, path=None, pkgs=None):
        """
        Create an environment either by name or path with a specified set of
        packages
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError("must specify a list of one or more packages to " "install into new environment")

        cmd_list = ["create", "--yes", "--quiet"]
        if name:
            ref = name
            search = [os.path.join(d, name) for d in self.info()["envs_dirs"]]
            cmd_list = ["create", "--yes", "--quiet", "--name", name]
        elif path:
            ref = path
            search = [path]
            cmd_list = ["create", "--yes", "--quiet", "--prefix", path]
        else:
            raise TypeError("must specify either an environment name or a path" " for new environment")

        if any(os.path.exists(path) for path in search):
            raise CondaEnvExistsError("Conda environment [%s] already exists" % ref)

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = "create"
            self._call_conda(cmd_list)

    def install(self, name=None, path=None, pkgs=None, dep=True):
        """
        Install packages into an environment either by name or path with a
        specified set of packages
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError("must specify a list of one or more packages to " "install into existing environment")

        cmd_list = ["install", "--yes", "--json", "--force-pscheck"]
        if name:
            cmd_list.extend(["--name", name])
        elif path:
            cmd_list.extend(["--prefix", path])
        else:  # just install into the current environment, whatever that is
            pass

        cmd_list.extend(pkgs)

        if not dep:
            cmd_list.extend(["--no-deps"])

        if self._is_not_running:
            self._function_called = "install"
            self._call_conda(cmd_list)

    def update(self, *pkgs, **kwargs):
        """
        Update package(s) (in an environment) by name.
        """
        cmd_list = ["update", "--json", "--quiet", "--yes"]

        if not pkgs and not kwargs.get("all"):
            raise TypeError(
                "Must specify at least one package to update, \
                            or all=True."
            )

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                (
                    "dry_run",
                    "no_deps",
                    "override_channels",
                    "no_pin",
                    "force",
                    "all",
                    "use_index_cache",
                    "use_local",
                    "alt_hint",
                ),
            )
        )

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = "update"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def remove(self, *pkgs, **kwargs):
        """
        Remove a package (from an environment) by name.

        Returns {
            success: bool, (this is always true),
            (other information)
        }
        """
        cmd_list = ["remove", "--json", "--quiet", "--yes", "--force-pscheck"]

        if not pkgs and not kwargs.get("all"):
            raise TypeError(
                "Must specify at least one package to remove, \
                            or all=True."
            )

        if kwargs.get("name") and kwargs.get("path"):
            raise TypeError("conda remove: At most one of name, path allowed")

        if kwargs.get("name"):
            cmd_list.extend(["--name", kwargs.pop("name")])

        if kwargs.get("path"):
            cmd_list.extend(["--prefix", kwargs.pop("path")])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs, ("dry_run", "features", "override_channels", "no_pin", "force", "all")
            )
        )

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = "remove"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def remove_environment(self, name=None, path=None, **kwargs):
        """
        Remove an environment entirely.

        See ``remove``.
        """
        if self._is_not_running:
            self._function_called = "remove_environment"
            self.remove(name=name, path=path, all=True, **kwargs)

    def clone_environment(self, clone, name=None, path=None, **kwargs):
        """
        Clone the environment ``clone`` into ``name`` or ``path``.
        """
        cmd_list = ["create", "--json", "--quiet"]

        if (name and path) or not (name or path):
            raise TypeError(
                "conda clone_environment: exactly one of name or \
                            path required"
            )

        if name:
            cmd_list.extend(["--name", name])

        if path:
            cmd_list.extend(["--prefix", path])

        cmd_list.extend(["--clone", clone])

        cmd_list.extend(
            self._setup_install_commands_from_kwargs(
                kwargs,
                (
                    "dry_run",
                    "unknown",
                    "use_index_cache",
                    "use_local",
                    "no_pin",
                    "force",
                    "all",
                    "channel",
                    "override_channels",
                    "no_default_packages",
                ),
            )
        )

        if self._is_not_running():
            self._function_called = "clone_environment"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def clone(self, path, prefix):
        """
        Clone a "share package" (located at `path`) by creating a new
        environment at `prefix`, and return a dictionary with (at least) the
        following keys:
          - 'warnings': a list of warnings

        The directory `path` is located in, should be some temp directory or
        some other directory OUTSIDE /opt/anaconda.  After calling this
        function, the original file (at `path`) may be removed (by the caller
        of this function).
        The return object is a list of warnings.
        """
        if self._process.state() == QProcess.NotRunning:
            self._function_called = "clone"
            self._call_and_parse(["clone", "--json", "--prefix", prefix, path])

    def _setup_config_from_kwargs(kwargs):
        cmd_list = ["--json", "--force"]

        if "file" in kwargs:
            cmd_list.extend(["--file", kwargs["file"]])

        if "system" in kwargs:
            cmd_list.append("--system")

        return cmd_list

    def config_path(self, **kwargs):
        """
        Get the path to the config file.
        """
        cmd_list = ["config", "--get"]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = "config_path"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def config_get(self, *keys, **kwargs):
        """
        Get the values of configuration keys.

        Returns a dictionary of values. Note, the key may not be in the
        dictionary if the key wasn't set in the configuration file.
        """
        cmd_list = ["config", "--get"]
        cmd_list.extend(keys)
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = "config_get"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def config_set(self, key, value, **kwargs):
        """
        Set a key to a (bool) value.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ["config", "--set", key, str(value)]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = "config_set"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def config_add(self, key, value, **kwargs):
        """
        Add a value to a key.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ["config", "--add", key, value]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = "config_add"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def config_remove(self, key, value, **kwargs):
        """
        Remove a value from a key.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ["config", "--remove", key, value]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = "config_remove"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def config_delete(self, key, **kwargs):
        """
        Remove a key entirely.

        Returns a list of warnings Conda may have emitted.
        """
        cmd_list = ["config", "--remove-key", key]
        cmd_list.extend(self._setup_config_from_kwargs(kwargs))

        if self._is_not_running:
            self._function_called = "config_delete"
            self._call_and_parse(cmd_list, abspath=kwargs.get("abspath", True))

    def run(self, command, abspath=True):
        """
        Launch the specified app by name or full package name.

        Returns a dictionary containing the key "fn", whose value is the full
        package (ending in ``.tar.bz2``) of the app.
        """
        cmd_list = ["run", "--json", command]

        if self._is_not_running:
            self._function_called = "run"
            self._call_and_parse(cmd_list, abspath=abspath)

    # --- Additional methods not in conda-api
    # ------------------------------------------------------------------------
    def pip(self, name):
        """Get list of pip installed packages."""
        cmd_list = ["list", "-n", name]

        if self._is_not_running:
            self._function_called = "pip"
            self._call_conda(cmd_list)

    def dependencies(self, name=None, path=None, pkgs=None, dep=True):
        """
        Install packages into an environment either by name or path with a
        specified set of packages.
        """
        if not pkgs or not isinstance(pkgs, (list, tuple)):
            raise TypeError("must specify a list of one or more packages to " "install into existing environment")

        cmd_list = ["install", "--dry-run", "--json"]
        cmd_list = ["install", "--dry-run", "--json", "--force-pscheck"]

        if not dep:
            cmd_list.extend(["--no-deps"])

        if name:
            cmd_list.extend(["--name", name])
        elif path:
            cmd_list.extend(["--prefix", path])
        else:
            pass

        cmd_list.extend(pkgs)

        if self._is_not_running:
            self._function_called = "install_dry"
            self._call_and_parse(cmd_list)