Skip to content

Commit

Permalink
Merge pull request #1510 from dstufft/use-zip-get-pip
Browse files Browse the repository at this point in the history
Use a ZipFile as the get-pip.py script
  • Loading branch information
dstufft committed Jan 28, 2014
2 parents ed11675 + 645180e commit 8575e0c
Show file tree
Hide file tree
Showing 5 changed files with 20,487 additions and 21,678 deletions.
175 changes: 154 additions & 21 deletions contrib/build-installer
@@ -1,31 +1,164 @@
#!/usr/bin/env python

import base64
import os
import sys
from packager import generate_script
import zipfile

WRAPPER_SCRIPT = b"""
#!/usr/bin/env python
#
# Hi There!
# You may be wondering what this giant blob of binary data here is, you might
# even be worried that we're up to something nefarious (good for you for being
# paranoid!). This is a base4 encoding of a zip file, this zip file contains
# an entire copy of pip.
#
# Pip is a thing that installs packages, pip itself is a package that someone
# might want to install, especially if they're looking to run this get-pip.py
# script. Pip has a lot of code to deal with the security of installing
# packages, various edge cases on various platforms, and other such sort of
# "tribal knowledge" that has been encoded in it's code base. Because of this
# we basically include an entire copy of pip inside this blob. We do this
# because the alternatives are attempt to implement a "minipip" that probably
# doesn't do things correctly and has weird edge cases, or compress pip itself
# down into a single file.
#
# If you're wondering how this is created, the secret is
# "contrib/build-installer" from the pip repository.
ZIPFILE = b\"\"\"
{zipfile}
\"\"\"
import base64
import os.path
import pkgutil
import shutil
import sys
import tempfile
def bootstrap(tmpdir=None):
# Import pip so we can use it to install pip and maybe setuptools too
import pip
here = os.path.dirname(os.path.abspath(__file__))
file_name = os.path.join(here, 'get-pip.py')
# We always want to install pip
packages = ["pip"]
# Check if the user has requested us not to install setuptools
if "--no-setuptools" in sys.argv or os.environ.get("PIP_NO_SETUPTOOLS"):
args = [x for x in sys.argv[1:] if x != "--no-setuptools"]
else:
args = sys.argv[1:]
# We want to see if setuptools is available before attempting to
# install it
try:
import setuptools
except ImportError:
packages += ["setuptools"]
delete_tmpdir = False
try:
# Create a temporary directory to act as a working directory if we were
# not given one.
if tmpdir is None:
tmpdir = tempfile.mkdtemp()
delete_tmpdir = True
# We need to extract the SSL certificates from requests so that they
# can be passed to --cert
cert_path = os.path.join(tmpdir, "cacert.pem")
with open(cert_path, "wb") as cert:
cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem"))
# Use an environment variable here so that users can still pass
# --cert via sys.argv
os.environ.setdefault("PIP_CERT", cert_path)
# Execute the included pip and use it to install the latest pip and
# setuptools from PyPI
sys.exit(pip.main(["install", "--upgrade"] + packages + args))
finally:
# Remove our temporary directory
if delete_tmpdir and tmpdir:
shutil.rmtree(tmpdir, ignore_errors=True)
entry = """
import pip
pip.bootstrap()
"""
def main():
sys.stdout.write("Creating pip bootstrapper...")
script = generate_script(entry, ['pip'])
f = open(file_name, 'w')
tmpdir = None
try:
f.write(script)
# Create a temporary working directory
tmpdir = tempfile.mkdtemp()
# Unpack the zipfile into the temporary directory
pip_zip = os.path.join(tmpdir, "pip.zip")
with open(pip_zip, "wb") as fp:
fp.write(base64.decodestring(ZIPFILE))
# Add the zipfile to sys.path so that we can import it
sys.path = [pip_zip] + sys.path
# Run the bootstrap
bootstrap(tmpdir=tmpdir)
finally:
f.close()
sys.stdout.write('done.\n')
if hasattr(os, 'chmod'):
oldmode = os.stat(file_name).st_mode & 07777
newmode = (oldmode | 0555) & 07777
os.chmod(file_name, newmode)
sys.stdout.write('Made resulting file %s executable.\n\n' % file_name)

if __name__ == '__main__':
# Clean up our temporary working directory
if tmpdir:
shutil.rmtree(tmpdir, ignore_errors=True)
if __name__ == "__main__":
main()
""".lstrip()


def getmodname(rootpath, path):
parts = path.split(os.sep)[len(rootpath.split(os.sep)):]
return '/'.join(parts)


def main():
sys.stdout.write("Creating pip bootstrapper...")

here = os.path.dirname(os.path.abspath(__file__))
toplevel = os.path.dirname(here)
get_pip = os.path.join(here, "get-pip.py")

# Get all of the files we want to add to the zip file
all_files = []
for root, dirs, files in os.walk(os.path.join(toplevel, "pip")):
for pyfile in files:
if os.path.splitext(pyfile)[1] in {".py", ".pem", ".cfg", ".exe"}:
all_files.append(
getmodname(toplevel, os.path.join(root, pyfile))
)

with zipfile.ZipFile(get_pip, "w", compression=zipfile.ZIP_DEFLATED) as z:
# Write the pip files to the zip archive
for filename in all_files:
z.write(os.path.join(toplevel, filename), filename)

# Get the binary data that compromises our zip file
with open(get_pip, "rb") as fp:
data = fp.read()

# Write out the wrapper script that will take the place of the zip script
# The reason we need to do this instead of just directly executing the
# zip script is that while Python will happily execute a zip script if
# passed it on the file system, it will not however allow this to work if
# passed it via stdin. This means that this wrapper script is required to
# make ``curl https://...../get-pip.py | python`` continue to work.
with open(get_pip, "wb") as fp:
fp.write(WRAPPER_SCRIPT.format(zipfile=base64.encodestring(data)))

# Ensure the permissions on the newly created file
if hasattr(os, "chmod"):
oldmode = os.stat(get_pip).st_mode & 0o7777
newmode = (oldmode | 0o555) & 0o7777
os.chmod(get_pip, newmode)

sys.stdout.write("done.\n")


if __name__ == "__main__":
main()
38 changes: 0 additions & 38 deletions contrib/build-standalone

This file was deleted.

0 comments on commit 8575e0c

Please sign in to comment.