Beispiel #1
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
Beispiel #2
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
class Installer:
    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)
        # use path of parent process
        env.insert("PATH",
                   QProcessEnvironment.systemEnvironment().value("PATH"))
        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)

    def set_output_widget(self, output_widget: QTextEdit):
        if output_widget:
            self._output_widget = output_widget
            self.process.setParent(output_widget)

    def _on_stdout_ready(self):
        if self._output_widget:
            text = self.process.readAllStandardOutput().data().decode()
            self._output_widget.append(text)

    def install(self, pkg_list: Sequence[str]):
        cmd = ['-m', 'pip', 'install', '--upgrade']
        if running_as_bundled_app() and sys.platform.startswith('linux'):
            cmd += [
                '--no-warn-script-location',
                '--prefix',
                user_plugin_dir(),
            ]
        self.process.setArguments(cmd + list(pkg_list))
        if self._output_widget:
            self._output_widget.clear()
        self.process.start()

    def uninstall(self, pkg_list: Sequence[str]):
        args = ['-m', 'pip', 'uninstall', '-y']
        self.process.setArguments(args + list(pkg_list))
        if self._output_widget:
            self._output_widget.clear()
        self.process.start()

        for pkg in pkg_list:
            plugin_manager.unregister(pkg)
    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)
Beispiel #5
0
class QtPipDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setup_ui()
        self.setAttribute(Qt.WA_DeleteOnClose)

        # create install process
        self.process = QProcess(self)
        self.process.setProgram(sys.executable)
        self.process.setProcessChannelMode(QProcess.MergedChannels)
        # 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)

        # connections
        self.install_button.clicked.connect(self._install)
        self.uninstall_button.clicked.connect(self._uninstall)
        self.process.readyReadStandardOutput.connect(self._on_stdout_ready)

        from ..plugins import plugin_manager

        self.process.finished.connect(plugin_manager.discover)
        self.process.finished.connect(plugin_manager.prune)

    def setup_ui(self):
        layout = QVBoxLayout()
        self.setLayout(layout)
        title = QLabel("Install/Uninstall Packages:")
        title.setObjectName("h2")
        self.line_edit = QLineEdit()
        self.install_button = QPushButton("install", self)
        self.uninstall_button = QPushButton("uninstall", self)
        self.text_area = QTextEdit(self, readOnly=True)
        hlay = QHBoxLayout()
        hlay.addWidget(self.line_edit)
        hlay.addWidget(self.install_button)
        hlay.addWidget(self.uninstall_button)
        layout.addWidget(title)
        layout.addLayout(hlay)
        layout.addWidget(self.text_area)
        self.setFixedSize(700, 400)
        self.setMaximumHeight(800)
        self.setMaximumWidth(1280)

    def _install(self):
        cmd = ['-m', 'pip', 'install']
        if running_as_bundled_app() and sys.platform.startswith('linux'):
            cmd += [
                '--no-warn-script-location',
                '--prefix',
                user_plugin_dir(),
            ]
        self.process.setArguments(cmd + self.line_edit.text().split())
        self.text_area.clear()
        self.process.start()

    def _uninstall(self):
        args = ['-m', 'pip', 'uninstall', '-y']
        self.process.setArguments(args + self.line_edit.text().split())
        self.text_area.clear()
        self.process.start()

    def _on_stdout_ready(self):
        text = self.process.readAllStandardOutput().data().decode()
        self.text_area.append(text)