|
21 | 21 | import zipfile
|
22 | 22 |
|
23 | 23 |
|
| 24 | +# Putting this a package's __init__.py causes it to set __path__ so that it is |
| 25 | +# possible to import modules and subpackages from other directories on sys.path. |
| 26 | +INITPY_CONTENTS = ''' |
| 27 | +try: |
| 28 | + import pkg_resources |
| 29 | + pkg_resources.declare_namespace(__name__) |
| 30 | +except ImportError: |
| 31 | + import pkgutil |
| 32 | + __path__ = pkgutil.extend_path(__path__, __name__) |
| 33 | +''' |
| 34 | + |
| 35 | + |
24 | 36 | class Wheel(object):
|
25 | 37 |
|
26 | 38 | def __init__(self, path):
|
@@ -103,8 +115,37 @@ def extras(self):
|
103 | 115 |
|
104 | 116 | def expand(self, directory):
|
105 | 117 | with zipfile.ZipFile(self.path(), 'r') as whl:
|
| 118 | + names = set(whl.namelist()) |
106 | 119 | whl.extractall(directory)
|
107 | 120 |
|
| 121 | + # Workaround for https://github.com/bazelbuild/rules_python/issues/14 |
| 122 | + for initpy in self.get_init_paths(names): |
| 123 | + with open(os.path.join(directory, initpy), 'w') as f: |
| 124 | + f.write(INITPY_CONTENTS) |
| 125 | + |
| 126 | + def get_init_paths(self, names): |
| 127 | + # Overwrite __init__.py in these directories. |
| 128 | + # (required as googleapis-common-protos has an empty __init__.py, which |
| 129 | + # blocks google.api.core from google-cloud-core) |
| 130 | + NAMESPACES = ["google/api"] |
| 131 | + |
| 132 | + # Find package directories without __init__.py, or where the __init__.py |
| 133 | + # must be overwritten to create a working namespace. This is based on |
| 134 | + # Bazel's PythonUtils.getInitPyFiles(). |
| 135 | + init_paths = set() |
| 136 | + for n in names: |
| 137 | + if os.path.splitext(n)[1] not in ['.so', '.py', '.pyc']: |
| 138 | + continue |
| 139 | + while os.path.sep in n: |
| 140 | + n = os.path.dirname(n) |
| 141 | + initpy = os.path.join(n, '__init__.py') |
| 142 | + initpyc = os.path.join(n, '__init__.pyc') |
| 143 | + if (initpy in names or initpyc in names) and n not in NAMESPACES: |
| 144 | + continue |
| 145 | + init_paths.add(initpy) |
| 146 | + |
| 147 | + return init_paths |
| 148 | + |
108 | 149 | # _parse_metadata parses METADATA files according to https://www.python.org/dev/peps/pep-0314/
|
109 | 150 | def _parse_metadata(self, content):
|
110 | 151 | # TODO: handle fields other than just name
|
|
0 commit comments