Skip to content

Commit 5f78b4a

Browse files
committed
Write/overwrite __init__.py for namespaces
This appears to fix bazelbuild#14, but I haven't tested extensively to see if it breaks anything else.
1 parent 63cdc8f commit 5f78b4a

File tree

1 file changed

+40
-0
lines changed

1 file changed

+40
-0
lines changed

rules_python/whl.py

+40
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@
1919
import re
2020
import zipfile
2121

22+
# Putting this a package's __init__.py causes it to set __path__ so that it is
23+
# possible to import modules and subpackages from other directories on sys.path.
24+
INITPY_CONTENTS = '''
25+
try:
26+
import pkg_resources
27+
pkg_resources.declare_namespace(__name__)
28+
except ImportError:
29+
import pkgutil
30+
__path__ = pkgutil.extend_path(__path__, __name__)
31+
'''
32+
2233

2334
class Wheel(object):
2435

@@ -85,6 +96,35 @@ def dependencies(self):
8596
def expand(self, directory):
8697
with zipfile.ZipFile(self.path(), 'r') as whl:
8798
whl.extractall(directory)
99+
names = set(whl.namelist())
100+
101+
# Workaround for https://github.com/bazelbuild/rules_python/issues/14
102+
for initpy in self.get_init_paths(names):
103+
with open(os.path.join(directory, initpy), 'w') as f:
104+
f.write(INITPY_CONTENTS)
105+
106+
def get_init_paths(self, names):
107+
# Overwrite __init__.py in these directories.
108+
# (required as googleapis-common-protos has an empty __init__.py, which
109+
# blocks google.api.core from google-cloud-core)
110+
NAMESPACES = ["google/api"]
111+
112+
# Find package directories without __init__.py, or where the __init__.py
113+
# must be overwritten to create a working namespace. This is based on
114+
# Bazel's PythonUtils.getInitPyFiles().
115+
init_paths = set()
116+
for n in names:
117+
if os.path.splitext(n)[1] not in ['.so', '.py', '.pyc']:
118+
continue
119+
while os.path.sep in n:
120+
n = os.path.dirname(n)
121+
initpy = os.path.join(n, '__init__.py')
122+
initpyc = os.path.join(n, '__init__.pyc')
123+
if (initpy in names or initpyc in names) and n not in NAMESPACES:
124+
continue
125+
init_paths.add(initpy)
126+
127+
return init_paths
88128

89129

90130
parser = argparse.ArgumentParser(

0 commit comments

Comments
 (0)