Repository: hylang/hy

Issue:
setup.py install doesn't run without dependencies
Right now, if you run `setup.py install` without installing the dependencies first, then the install will fail. This is because all the Hy files are compiled *before* installation, but in order to compile them the dependencies need to be present.

Unfortunately, fixing this isn't particularly pretty. As it stands, setup.py doesn't install the dependencies *at all* thanks to this [ever-open setuptools bug](https://github.com/pypa/setuptools/issues/456). Right now, the setuptools install command does this:

- Gathers the distribution information.
- Builds a binary egg.
- Asks easy_install to install said egg, which will also install dependencies.

In order to install the dependencies prior to compiling the Hy bytecode, one of the following would need to be done (in combination for working around the issue mentioned above):

- Figure out some way to reliably iterate through the dependencies and install them first. This would probably involve copying a bit of small code from easy_install.py.
- Get easy_install to install only the dependencies first. I like this approach, but I'm trying to figure out how to do it without calling egg_info which would involve going through the manifest *twice* on install.

Of course, we could always just say the install command is broken. This isn't as insane as it seems, because right now the only reason you'd be forced to use the install command is for distro packaging, in which case the dependencies will already be installed. In all other cases, `pip install .` is superior in every way.


Discussion:

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,11 @@
 # This file is part of Hy, which is free software licensed under the Expat
 # license. See the LICENSE.
 
-import sys, os
+import glob
+import importlib
+import inspect
+import os
+import sys
 
 from setuptools import find_packages, setup
 from setuptools.command.install import install
@@ -20,16 +24,42 @@
 nice together. """
 
 class Install(install):
+    def __compile_hy_bytecode(self):
+        for path in sorted(glob.iglob('hy/**.hy', recursive=True)):
+            importlib.util.cache_from_source(path, optimize=self.optimize)
+
     def run(self):
-        # Import each Hy module to ensure it's compiled.
-        import os, importlib
-        for dirpath, _, filenames in sorted(os.walk("hy")):
-            for filename in sorted(filenames):
-                if filename.endswith(".hy"):
-                    importlib.import_module(
-                        dirpath.replace("/", ".").replace("\\", ".") +
-                        "." + filename[:-len(".hy")])
-        install.run(self)
+        # Don't bother messing around with deps if they wouldn't be installed anyway.
+        # Code is based on setuptools's install.py.
+        if not (self.old_and_unmanageable or self.single_version_externally_managed
+                or not self._called_from_setup(inspect.currentframe())):
+            easy_install = self.distribution.get_command_class('easy_install')
+
+            cmd = easy_install(
+                self.distribution, args="x", root=self.root, record=self.record,
+            )
+            cmd.ensure_finalized()
+            cmd.always_copy_from = '.'
+            cmd.package_index.scan(glob.glob('*.egg'))
+
+            cmd.args = self.distribution.install_requires
+
+            # Avoid deprecation warnings on new setuptools versions.
+            if 'show_deprecation' in inspect.signature(cmd.run).parameters:
+                cmd.run(show_deprecation=False)
+            else:
+                cmd.run()
+
+            # Make sure any new packages get picked up.
+            import site
+            importlib.reload(site)
+            importlib.invalidate_caches()
+
+        self.__compile_hy_bytecode()
+
+        # The deps won't be reinstalled because of:
+        # https://github.com/pypa/setuptools/issues/456
+        return install.run(self)
 
 install_requires = [
     'rply>=0.7.7',


Test patch: