Commits

Aleš Erjavec committed efdd815

Use 'easy_install' to install add-ons.

  • Participants
  • Parent commits d38d2b1

Comments (0)

Files changed (2)

File Orange/OrangeCanvas/orngDlgs.py

 
         self.busy(True)
         self.repaint()
+
+        addons = Orange.utils.addons.open_addons(flag="r")
+
         add, remove, upgrade = self.to_install(), self.to_remove(), self.to_upgrade
 
         def errormessage(title, message, details=None, exc_info=None):
 
             return box.exec_()
 
+        def subprocesswait(process):
+            output = []
+            while process.poll() is None:
+                try:
+                    line = process.stdout.readline()
+                except IOError as ex:
+                    if ex.errno != 4:
+                        raise
+                else:
+                    output.append(line)
+                    qApp.processEvents(QEventLoop.ExcludeUserInputEvents)
+                    print line,
+
+            if process.returncode:
+                output = "".join(output)
+                output += process.stdout.read()
+
+                errormessage("Error",
+                             "'easy_install' exited with error code %i" %
+                             process.returncode,
+                             details=output)
+            return process.returncode
+
+        def easy_install(req):
+            try:
+                process = Orange.utils.addons.easy_install_process([req])
+            except (OSError, IOError):
+                # TODO: Should show some usefull message (executable not
+                # found, permission error, ...
+                raise
+            else:
+                subprocesswait(process)
+
         for name in upgrade:
-            try:
-                self.busy("Upgrading %s ..." % name)
-                self.repaint()
-                Orange.utils.addons.upgrade(name, self.pcb)
-            except subprocess.CalledProcessError, ex:
-                errormessage("Error",
-                             "setup.py script exited with error code %i" \
-                             % ex.returncode,
-                             details=ex.output)
-            except Exception, e:
-                errormessage("Error",
-                             "Problem upgrading add-on %s: %s" % (name, e),
-                             exc_info=True)
+            req = "{0}=={1}".format(
+                name, addons[name.lower()].available_version)
+
+            self.busy("Upgrading %s ..." % name)
+            self.progress.setRange(0, 0)
+            self.repaint()
+
+            easy_install(req)
 
         for name in remove:
+            self.busy("Uninstalling %s ..." % name)
+            self.repaint()
             try:
-                self.busy("Uninstalling %s ..." % name)
-                self.repaint()
                 Orange.utils.addons.uninstall(name, self.pcb)
             except Exception, e:
                 errormessage("Error",
                              exc_info=True)
 
         for name in add:
-            try:
-                self.busy("Installing %s ..." % name)
-                self.repaint()
-                Orange.utils.addons.install(name, self.pcb)
-            except subprocess.CalledProcessError, ex:
-                errormessage("Error",
-                             "setup.py script exited with error code %i" \
-                             % ex.returncode,
-                             details=ex.output)
+            req = "{0}=={1}".format(
+                name, addons[name.lower()].available_version)
 
-            except Exception, e:
-                errormessage("Error",
-                             "Problem installing add-on %s: %s" % (name, e),
-                             exc_info=True)
+            self.busy("Installing %s ..." % name)
+            self.progress.setRange(0, 0)
+            self.repaint()
 
-        if len(upgrade) > 0:
-            QMessageBox.warning(self, "Restart Orange", "After upgrading add-ons, it is very important to restart Orange to make sure the changes have been applied.")
-        elif len(remove) > 0:  # Don't bother with this if there has already been one (more important) warning.
-            QMessageBox.warning(self, "Restart Orange", "After removal of add-ons, it is suggested that you restart Orange for the changes to become effective.")
+            easy_install(req)
+
+        if len(add) + len(upgrade) + len(remove) > 0:
+            QMessageBox.information(
+                self, "Restart Orange",
+                "Please restart Orange for changes to take effect.")
 
         QDialog.accept(self)
 

File Orange/utils/addons.py

 import urllib2
 import site
 import itertools
+import pipes
 
 from collections import namedtuple, defaultdict
 from contextlib import closing
     for func in addon_refresh_callback:
         func()
 
+
+def easy_install_process(args, bufsize=-1):
+    from setuptools.command import easy_install
+    # Check if easy_install supports '--user' switch
+    if "user" in [opt[0] for opt in easy_install.easy_install.user_options]:
+        has_user_site = True
+    else:
+        has_user_site = False
+
+    if has_user_site and site.USER_SITE in sys.path:
+        args = ["--user"] + args
+
+    # properly quote arguments if necessary
+    args = map(pipes.quote, args)
+
+    script = """
+import sys
+from setuptools.command.easy_install import main
+sys.exit(main({args!r}))
+"""
+    script = script.format(args=args)
+
+    return python_process(["-c", script], bufsize=bufsize)
+
+
+def python_process(args, script_name=None, cwd=None, env=None, **kwargs):
+    """
+    Run a `sys.executable` in a subprocess with `args`.
+    """
+    executable = sys.executable
+    if os.name == "nt" and os.path.basename(executable) == "pythonw.exe":
+        dirname, _ = os.path.split(executable)
+        executable = os.path.join(dirname, "python.exe")
+        # by default a new console window would show up when executing the
+        # script
+        startupinfo = subprocess.STARTUPINFO()
+        if hasattr(subprocess, "STARTF_USESHOWWINDOW"):
+            startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
+        else:
+            # This flag was missing in inital releases of 2.7
+            startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW
+
+        kwargs["startupinfo"] = startupinfo
+
+    if script_name is not None:
+        script = script_name
+    else:
+        script = executable
+
+    process = subprocess.Popen(
+        [script] + args,
+        executable=executable,
+        cwd=cwd,
+        env=env,
+        stderr=subprocess.STDOUT,
+        stdout=subprocess.PIPE,
+        **kwargs
+    )
+
+    return process
+
+
 def uninstall(name, progress_callback=None):
     try:
         import pip.req