Skip to content

Commit

Permalink
Enable binary linux distributions for Python
Browse files Browse the repository at this point in the history
  • Loading branch information
soltanmm-google committed Jan 27, 2016
1 parent 7c43f49 commit 154b0ee
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def cython_extensions(package_names, module_names, include_dirs, libraries,
}

INSTALL_REQUIRES = (
'six>=1.10',
'enum34>=1.0.4',
'futures>=2.2.0',
# TODO(atash): eventually split the grpcio package into a metapackage
Expand All @@ -131,13 +132,15 @@ def cython_extensions(package_names, module_names, include_dirs, libraries,
) + INSTALL_REQUIRES

COMMAND_CLASS = {
'install': commands.Install,
'doc': commands.SphinxDocumentation,
'build_proto_modules': commands.BuildProtoModules,
'build_project_metadata': commands.BuildProjectMetadata,
'build_py': commands.BuildPy,
'build_ext': commands.BuildExt,
'gather': commands.Gather,
'run_interop': commands.RunInterop,
'bdist_egg_grpc_custom': commands.BdistEggCustomName,
}

# Ensure that package data is copied over before any commands have been run:
Expand Down Expand Up @@ -187,7 +190,7 @@ def cython_extensions(package_names, module_names, include_dirs, libraries,

setuptools.setup(
name='grpcio',
version='0.12.0b5',
version='0.12.0b6',
license=LICENSE,
ext_modules=CYTHON_EXTENSION_MODULES,
packages=list(PACKAGES),
Expand Down
130 changes: 130 additions & 0 deletions src/python/grpcio/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,22 @@
"""Provides distutils command classes for the GRPC Python setup process."""

import distutils
import glob
import os
import os.path
import platform
import re
import shutil
import subprocess
import sys
import traceback

import setuptools
from setuptools.command import bdist_egg
from setuptools.command import build_ext
from setuptools.command import build_py
from setuptools.command import easy_install
from setuptools.command import install
from setuptools.command import test

import support
Expand All @@ -58,6 +65,129 @@ class CommandError(Exception):
"""Simple exception class for GRPC custom commands."""


# TODO(atash): Remove this once PyPI has better Linux bdist support. See
# https://bitbucket.org/pypa/pypi/issues/120/binary-wheels-for-linux-are-not-supported
def _get_linux_bdist_egg(decorated_basename, target_egg_basename):
"""Returns a string path to a .egg file for Linux to install.
If we can retrieve a pre-compiled egg from online, uses it. Else, emits a
warning and builds from source.
"""
# Break import style to ensure that setup.py has had a chance to install the
# relevant package eggs.
from six.moves.urllib import request
decorated_path = decorated_basename + '.egg'
try:
url = (
'https://storage.googleapis.com/grpc-precompiled-binaries/'
'python/{target}'
.format(target=decorated_path))
egg_data = request.urlopen(url).read()
except IOError as error:
raise CommandError(
'{}\n\nCould not find the bdist egg {}: {}'
.format(traceback.format_exc(), decorated_path, error.message))
# Our chosen local egg path.
egg_path = target_egg_basename + '.egg'
try:
with open(egg_path, 'w') as egg_file:
egg_file.write(egg_data)
except IOError as error:
raise CommandError(
'{}\n\nCould not write grpcio egg: {}'
.format(traceback.format_exc(), error.message))
return egg_path


class EggNameMixin(object):

def egg_name(self, with_custom):
"""
Args:
with_custom: Boolean describing whether or not to decorate the egg name
with custom gRPC-specific target information.
"""
egg_command = self.get_finalized_command('bdist_egg')
base = os.path.splitext(os.path.basename(egg_command.egg_output))[0]
if with_custom:
flavor = 'ucs2' if sys.maxunicode == 65535 else 'ucs4'
return '{base}-{flavor}'.format(base=base, flavor=flavor)
else:
return base


class Install(install.install, EggNameMixin):
"""Custom Install command for gRPC Python.
This is for bdist shims and whatever else we might need a custom install
command for.
"""

user_options = install.install.user_options + [
# TODO(atash): remove this once manylinux gets on PyPI. See
# https://bitbucket.org/pypa/pypi/issues/120/binary-wheels-for-linux-are-not-supported
('use-linux-bdist', None,
'Whether to retrieve a binary for Linux instead of building from '
'source.'),
]

def initialize_options(self):
install.install.initialize_options(self)
self.use_linux_bdist = False

def finalize_options(self):
install.install.finalize_options(self)

def run(self):
if self.use_linux_bdist:
try:
egg_path = _get_linux_bdist_egg(self.egg_name(True),
self.egg_name(False))
except CommandError as error:
sys.stderr.write(
'\nWARNING: Failed to acquire grpcio prebuilt binary:\n'
'{}.\n\n'.format(error.message))
raise
try:
self._run_bdist_retrieval_install(egg_path)
except Exception as error:
# if anything else happens (and given how there's no way to really know
# what's happening in setuptools here, I mean *anything*), warn the user
# and fall back to building from source.
sys.stderr.write(
'{}\nWARNING: Failed to install grpcio prebuilt binary.\n\n'
.format(traceback.format_exc()))
install.install.run(self)
else:
install.install.run(self)

# TODO(atash): Remove this once PyPI has better Linux bdist support. See
# https://bitbucket.org/pypa/pypi/issues/120/binary-wheels-for-linux-are-not-supported
def _run_bdist_retrieval_install(self, bdist_egg):
easy_install = self.distribution.get_command_class('easy_install')
easy_install_command = easy_install(
self.distribution, args='x', root=self.root, record=self.record,
)
easy_install_command.ensure_finalized()
easy_install_command.always_copy_from = '.'
easy_install_command.package_index.scan(glob.glob('*.egg'))
arguments = [bdist_egg]
if setuptools.bootstrap_install_from:
args.insert(0, setuptools.bootstrap_install_from)
easy_install_command.args = arguments
easy_install_command.run()
setuptools.bootstrap_install_from = None


class BdistEggCustomName(bdist_egg.bdist_egg, EggNameMixin):
"""Thin wrapper around the bdist_egg command to build with our custom name."""

def run(self):
bdist_egg.bdist_egg.run(self)
target = os.path.join(self.dist_dir, '{}.egg'.format(self.egg_name(True)))
shutil.move(self.get_outputs()[0], target)


class SphinxDocumentation(setuptools.Command):
"""Command to generate documentation via sphinx."""

Expand Down

0 comments on commit 154b0ee

Please sign in to comment.