Repository: pypa/pip

Issue:
Compile pyc files during Wheel Install
Installing from sdist compiles pyc files whereas installing from Wheel doesn't.



Discussion:

Resolving patch:
diff --git a/pip/commands/install.py b/pip/commands/install.py
--- a/pip/commands/install.py
+++ b/pip/commands/install.py
@@ -136,6 +136,21 @@ def __init__(self, *args, **kw):
             default=None,
             help="Install everything relative to this alternate root directory.")
 
+        cmd_opts.add_option(
+            "--compile",
+            action="store_true",
+            dest="compile",
+            default=True,
+            help="Compile py files to pyc",
+        )
+
+        cmd_opts.add_option(
+            "--no-compile",
+            action="store_false",
+            dest="compile",
+            help="Do not compile py files to pyc",
+        )
+
         cmd_opts.add_option(cmdoptions.use_wheel.make())
         cmd_opts.add_option(cmdoptions.no_use_wheel.make())
 
@@ -227,6 +242,7 @@ def run(self, options, args):
             use_user_site=options.use_user_site,
             target_dir=temp_target_dir,
             session=session,
+            pycompile=options.compile,
         )
         for name in args:
             requirement_set.add_requirement(
diff --git a/pip/req.py b/pip/req.py
--- a/pip/req.py
+++ b/pip/req.py
@@ -39,7 +39,7 @@ class InstallRequirement(object):
 
     def __init__(self, req, comes_from, source_dir=None, editable=False,
                  url=None, as_egg=False, update=True, prereleases=None,
-                 editable_options=None, from_bundle=False):
+                 editable_options=None, from_bundle=False, pycompile=True):
         self.extras = ()
         if isinstance(req, string_types):
             req = pkg_resources.Requirement.parse(req)
@@ -74,6 +74,8 @@ def __init__(self, req, comes_from, source_dir=None, editable=False,
         self.target_dir = None
         self.from_bundle = from_bundle
 
+        self.pycompile = pycompile
+
         # True if pre-releases are acceptable
         if prereleases:
             self.prereleases = True
@@ -638,6 +640,11 @@ def install(self, install_options, global_options=(), root=None):
             if root is not None:
                 install_args += ['--root', root]
 
+            if self.pycompile:
+                install_args += ["--compile"]
+            else:
+                install_args += ["--no-compile"]
+
             if running_under_virtualenv():
                 ## FIXME: I'm not sure if this is a reasonable location; probably not
                 ## but we can't put it in the default location, as that is a virtualenv symlink that isn't writable
@@ -843,6 +850,7 @@ def move_wheel_files(self, wheeldir, root=None):
             user=self.use_user_site,
             home=self.target_dir,
             root=root,
+            pycompile=self.pycompile,
         )
 
     @property
@@ -884,7 +892,7 @@ class RequirementSet(object):
     def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
                  upgrade=False, ignore_installed=False, as_egg=False, target_dir=None,
                  ignore_dependencies=False, force_reinstall=False, use_user_site=False,
-                 session=None):
+                 session=None, pycompile=True):
         self.build_dir = build_dir
         self.src_dir = src_dir
         self.download_dir = download_dir
@@ -904,6 +912,7 @@ def __init__(self, build_dir, src_dir, download_dir, download_cache=None,
         self.use_user_site = use_user_site
         self.target_dir = target_dir #set from --target option
         self.session = session or PipSession()
+        self.pycompile = pycompile
 
     def __str__(self):
         reqs = [req for req in self.requirements.values()
@@ -916,6 +925,7 @@ def add_requirement(self, install_req):
         install_req.as_egg = self.as_egg
         install_req.use_user_site = self.use_user_site
         install_req.target_dir = self.target_dir
+        install_req.pycompile = self.pycompile
         if not name:
             #url or path requirement w/o an egg fragment
             self.unnamed_requirements.append(install_req)
diff --git a/pip/wheel.py b/pip/wheel.py
--- a/pip/wheel.py
+++ b/pip/wheel.py
@@ -3,6 +3,7 @@
 """
 from __future__ import with_statement
 
+import compileall
 import csv
 import functools
 import hashlib
@@ -134,7 +135,8 @@ def get_entrypoints(filename):
     return console, gui
 
 
-def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None):
+def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None,
+        pycompile=True):
     """Install a wheel"""
 
     scheme = distutils_scheme(name, user=user, home=home, root=root)
@@ -156,6 +158,10 @@ def move_wheel_files(name, req, wheeldir, user=False, home=None, root=None):
     changed = set()
     generated = []
 
+    # Compile all of the pyc files that we're going to be installing
+    if pycompile:
+        compileall.compile_dir(source, force=True, quiet=True)
+
     def normpath(src, p):
         return make_path_relative(src, p).replace(os.path.sep, '/')
 


Test patch:
diff --git a/tests/functional/test_install.py b/tests/functional/test_install.py
--- a/tests/functional/test_install.py
+++ b/tests/functional/test_install.py
@@ -1,6 +1,7 @@
 import os
 import sys
 import textwrap
+import glob
 
 from os.path import abspath, join, curdir, pardir
 
@@ -456,3 +457,43 @@ def test_url_req_case_mismatch(script, data):
     assert egg_folder in result.files_created, str(result)
     egg_folder = script.site_packages / 'Upper-2.0-py%s.egg-info' % pyversion
     assert egg_folder not in result.files_created, str(result)
+
+
+def test_compiles_pyc(script):
+    """
+    Test installing with --compile on
+    """
+    del script.environ["PYTHONDONTWRITEBYTECODE"]
+    script.pip("install", "--compile", "--no-use-wheel", "INITools==0.2")
+
+    # There are many locations for the __init__.pyc file so attempt to find
+    #   any of them
+    exists = [
+        os.path.exists(script.site_packages_path/"initools/__init__.pyc"),
+    ]
+
+    exists += glob.glob(
+        script.site_packages_path/"initools/__pycache__/__init__*.pyc"
+    )
+
+    assert any(exists)
+
+
+def test_no_compiles_pyc(script, data):
+    """
+    Test installing from wheel with --compile on
+    """
+    del script.environ["PYTHONDONTWRITEBYTECODE"]
+    script.pip("install", "--no-compile", "--no-use-wheel", "INITools==0.2")
+
+    # There are many locations for the __init__.pyc file so attempt to find
+    #   any of them
+    exists = [
+        os.path.exists(script.site_packages_path/"initools/__init__.pyc"),
+    ]
+
+    exists += glob.glob(
+        script.site_packages_path/"initools/__pycache__/__init__*.pyc"
+    )
+
+    assert not any(exists)
diff --git a/tests/functional/test_install_wheel.py b/tests/functional/test_install_wheel.py
--- a/tests/functional/test_install_wheel.py
+++ b/tests/functional/test_install_wheel.py
@@ -1,5 +1,7 @@
 import os
 import pytest
+import glob
+
 from tests.lib.path import Path
 
 
@@ -186,3 +188,44 @@ def test_install_from_wheel_gui_entrypoint(script, data):
         wrapper_file = script.bin / 't1'
     assert wrapper_file in result.files_created
 
+
+def test_wheel_compiles_pyc(script, data):
+    """
+    Test installing from wheel with --compile on
+    """
+    script.pip(
+        "install", "--compile", "simple.dist==0.1", "--no-index",
+        "--find-links=" + data.find_links
+    )
+    # There are many locations for the __init__.pyc file so attempt to find
+    #   any of them
+    exists = [
+        os.path.exists(script.site_packages_path/"simpledist/__init__.pyc"),
+    ]
+
+    exists += glob.glob(
+        script.site_packages_path/"simpledist/__pycache__/__init__*.pyc"
+    )
+
+    assert any(exists)
+
+
+def test_wheel_no_compiles_pyc(script, data):
+    """
+    Test installing from wheel with --compile on
+    """
+    script.pip(
+        "install", "--no-compile", "simple.dist==0.1", "--no-index",
+        "--find-links=" + data.find_links
+    )
+    # There are many locations for the __init__.pyc file so attempt to find
+    #   any of them
+    exists = [
+        os.path.exists(script.site_packages_path/"simpledist/__init__.pyc"),
+    ]
+
+    exists += glob.glob(
+        script.site_packages_path/"simpledist/__pycache__/__init__*.pyc"
+    )
+
+    assert not any(exists)