From 4e76eb6ea38a3730345b5104a06b981cd9f53d8f Mon Sep 17 00:00:00 2001 From: Rodrigo Queiro Date: Fri, 3 Nov 2017 10:41:33 +0100 Subject: [PATCH] Write/overwrite __init__.py for namespaces This appears to fix #14, but I haven't tested extensively to see if it breaks anything else. --- packaging/whl.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packaging/whl.py b/packaging/whl.py index c140a13a2f..8cdf22b8a2 100644 --- a/packaging/whl.py +++ b/packaging/whl.py @@ -20,6 +20,17 @@ import re import zipfile +# Putting this a package's __init__.py causes it to set __path__ so that it is +# possible to import modules and subpackages from other directories on sys.path. +INITPY_CONTENTS = ''' +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__) +''' + class Wheel(object): @@ -109,6 +120,35 @@ def extras(self): def expand(self, directory): with zipfile.ZipFile(self.path(), 'r') as whl: whl.extractall(directory) + names = set(whl.namelist()) + + # Workaround for https://github.com/bazelbuild/rules_python/issues/14 + for initpy in self.get_init_paths(names): + with open(os.path.join(directory, initpy), 'w') as f: + f.write(INITPY_CONTENTS) + + def get_init_paths(self, names): + # Overwrite __init__.py in these directories. + # (required as googleapis-common-protos has an empty __init__.py, which + # blocks google.api.core from google-cloud-core) + NAMESPACES = ["google/api"] + + # Find package directories without __init__.py, or where the __init__.py + # must be overwritten to create a working namespace. This is based on + # Bazel's PythonUtils.getInitPyFiles(). + init_paths = set() + for n in names: + if os.path.splitext(n)[1] not in ['.so', '.py', '.pyc']: + continue + while os.path.sep in n: + n = os.path.dirname(n) + initpy = os.path.join(n, '__init__.py') + initpyc = os.path.join(n, '__init__.pyc') + if (initpy in names or initpyc in names) and n not in NAMESPACES: + continue + init_paths.add(initpy) + + return init_paths # _parse_metadata parses METADATA files according to https://www.python.org/dev/peps/pep-0314/ def _parse_metadata(self, content):