|  | @@ -41,12 +41,12 @@ 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
 | 
	
		
			
				|  |  | +from wheel import bdist_wheel
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import support
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -59,6 +59,8 @@ BINARIES_REPOSITORY = os.environ.get(
 | 
	
		
			
				|  |  |  USE_GRPC_CUSTOM_BDIST = bool(int(os.environ.get(
 | 
	
		
			
				|  |  |      'GRPC_PYTHON_USE_CUSTOM_BDIST', '1')))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +GRPC_CUSTOM_BDIST_EXT = '.whl'
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  CONF_PY_ADDENDUM = """
 | 
	
		
			
				|  |  |  extensions.append('sphinx.ext.napoleon')
 | 
	
		
			
				|  |  |  napoleon_google_docstring = True
 | 
	
	
		
			
				|  | @@ -74,46 +76,52 @@ class CommandError(Exception):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  # 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_grpc_custom_bdist_egg(decorated_basename, target_egg_basename):
 | 
	
		
			
				|  |  | -  """Returns a string path to a .egg file for Linux to install.
 | 
	
		
			
				|  |  | +def _get_grpc_custom_bdist(decorated_basename, target_bdist_basename):
 | 
	
		
			
				|  |  | +  """Returns a string path to a bdist file for Linux to install.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  If we can retrieve a pre-compiled egg from online, uses it. Else, emits a
 | 
	
		
			
				|  |  | +  If we can retrieve a pre-compiled bdist from online, uses it. Else, emits a
 | 
	
		
			
				|  |  |    warning and builds from source.
 | 
	
		
			
				|  |  |    """
 | 
	
		
			
				|  |  | +  # TODO(atash): somehow the name that's returned from `wheel` is different
 | 
	
		
			
				|  |  | +  # between different versions of 'wheel' (but from a compatibility standpoint,
 | 
	
		
			
				|  |  | +  # the names are compatible); we should have some way of determining name
 | 
	
		
			
				|  |  | +  # compatibility in the same way `wheel` does to avoid having to rename all of
 | 
	
		
			
				|  |  | +  # the custom wheels that we build/upload to GCS.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    # Break import style to ensure that setup.py has had a chance to install the
 | 
	
		
			
				|  |  | -  # relevant package eggs.
 | 
	
		
			
				|  |  | +  # relevant package.
 | 
	
		
			
				|  |  |    from six.moves.urllib import request
 | 
	
		
			
				|  |  | -  decorated_path = decorated_basename + '.egg'
 | 
	
		
			
				|  |  | +  decorated_path = decorated_basename + GRPC_CUSTOM_BDIST_EXT
 | 
	
		
			
				|  |  |    try:
 | 
	
		
			
				|  |  |      url = BINARIES_REPOSITORY + '/{target}'.format(target=decorated_path)
 | 
	
		
			
				|  |  | -    egg_data = request.urlopen(url).read()
 | 
	
		
			
				|  |  | +    bdist_data = request.urlopen(url).read()
 | 
	
		
			
				|  |  |    except IOError as error:
 | 
	
		
			
				|  |  |      raise CommandError(
 | 
	
		
			
				|  |  | -        '{}\n\nCould not find the bdist egg {}: {}'
 | 
	
		
			
				|  |  | +        '{}\n\nCould not find the bdist {}: {}'
 | 
	
		
			
				|  |  |              .format(traceback.format_exc(), decorated_path, error.message))
 | 
	
		
			
				|  |  | -  # Our chosen local egg path.
 | 
	
		
			
				|  |  | -  egg_path = target_egg_basename + '.egg'
 | 
	
		
			
				|  |  | +  # Our chosen local bdist path.
 | 
	
		
			
				|  |  | +  bdist_path = target_bdist_basename + GRPC_CUSTOM_BDIST_EXT
 | 
	
		
			
				|  |  |    try:
 | 
	
		
			
				|  |  | -    with open(egg_path, 'w') as egg_file:
 | 
	
		
			
				|  |  | -      egg_file.write(egg_data)
 | 
	
		
			
				|  |  | +    with open(bdist_path, 'w') as bdist_file:
 | 
	
		
			
				|  |  | +      bdist_file.write(bdist_data)
 | 
	
		
			
				|  |  |    except IOError as error:
 | 
	
		
			
				|  |  |      raise CommandError(
 | 
	
		
			
				|  |  | -        '{}\n\nCould not write grpcio egg: {}'
 | 
	
		
			
				|  |  | +        '{}\n\nCould not write grpcio bdist: {}'
 | 
	
		
			
				|  |  |              .format(traceback.format_exc(), error.message))
 | 
	
		
			
				|  |  | -  return egg_path
 | 
	
		
			
				|  |  | +  return bdist_path
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class EggNameMixin(object):
 | 
	
		
			
				|  |  | -  """Mixin for setuptools.Command classes to enable acquiring the egg name."""
 | 
	
		
			
				|  |  | +class WheelNameMixin(object):
 | 
	
		
			
				|  |  | +  """Mixin for setuptools.Command classes to enable acquiring the bdist name."""
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  def egg_name(self, with_custom):
 | 
	
		
			
				|  |  | +  def wheel_name(self, with_custom):
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      Args:
 | 
	
		
			
				|  |  | -      with_custom: Boolean describing whether or not to decorate the egg name
 | 
	
		
			
				|  |  | +      with_custom: Boolean describing whether or not to decorate the bdist 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]
 | 
	
		
			
				|  |  | +    wheel_command = self.get_finalized_command('bdist_wheel')
 | 
	
		
			
				|  |  | +    base = wheel_command.get_archive_basename()
 | 
	
		
			
				|  |  |      if with_custom:
 | 
	
		
			
				|  |  |        flavor = 'ucs2' if sys.maxunicode == 65535 else 'ucs4'
 | 
	
		
			
				|  |  |        return '{base}-{flavor}'.format(base=base, flavor=flavor)
 | 
	
	
		
			
				|  | @@ -121,7 +129,7 @@ class EggNameMixin(object):
 | 
	
		
			
				|  |  |        return base
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -class Install(install.install, EggNameMixin):
 | 
	
		
			
				|  |  | +class Install(install.install, WheelNameMixin):
 | 
	
		
			
				|  |  |    """Custom Install command for gRPC Python.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    This is for bdist shims and whatever else we might need a custom install
 | 
	
	
		
			
				|  | @@ -147,15 +155,15 @@ class Install(install.install, EggNameMixin):
 | 
	
		
			
				|  |  |      if self.use_grpc_custom_bdist:
 | 
	
		
			
				|  |  |        try:
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -          egg_path = _get_grpc_custom_bdist_egg(self.egg_name(True),
 | 
	
		
			
				|  |  | -                                                self.egg_name(False))
 | 
	
		
			
				|  |  | +          bdist_path = _get_grpc_custom_bdist(self.wheel_name(True),
 | 
	
		
			
				|  |  | +                                            self.wheel_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)
 | 
	
		
			
				|  |  | +          self._run_bdist_retrieval_install(bdist_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
 | 
	
	
		
			
				|  | @@ -171,29 +179,19 @@ class Install(install.install, EggNameMixin):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    # 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_bdist_retrieval_install(self, bdist_path):
 | 
	
		
			
				|  |  | +    import pip
 | 
	
		
			
				|  |  | +    pip.main(['install', bdist_path])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class BdistWheelCustomName(bdist_wheel.bdist_wheel, WheelNameMixin):
 | 
	
		
			
				|  |  | +  """Thin wrapper around the bdist 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)
 | 
	
		
			
				|  |  | +    bdist_wheel.bdist_wheel.run(self)
 | 
	
		
			
				|  |  | +    output = self.distribution.dist_files[-1][2]
 | 
	
		
			
				|  |  | +    target = os.path.join(self.dist_dir, '{}.whl'.format(self.wheel_name(True)))
 | 
	
		
			
				|  |  | +    shutil.move(output, target)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class SphinxDocumentation(setuptools.Command):
 |