19
19
import re
20
20
import zipfile
21
21
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
+
22
33
23
34
class Wheel (object ):
24
35
@@ -62,7 +73,7 @@ def metadata(self):
62
73
with whl .open (os .path .join (self ._dist_info (), 'metadata.json' )) as f :
63
74
return json .loads (f .read ().decode ("utf-8" ))
64
75
except KeyError :
65
- pass
76
+ pass
66
77
# fall back to METADATA file (https://www.python.org/dev/peps/pep-0427/)
67
78
with whl .open (os .path .join (self ._dist_info (), 'METADATA' )) as f :
68
79
return self ._parse_metadata (f .read ().decode ("utf-8" ))
@@ -102,29 +113,66 @@ def extras(self):
102
113
103
114
def expand (self , directory ):
104
115
with zipfile .ZipFile (self .path (), 'r' ) as whl :
116
+ names = set (whl .namelist ())
105
117
whl .extractall (directory )
106
118
119
+ # Workaround for https://github.com/bazelbuild/rules_python/issues/14
120
+ for initpy in self .get_init_paths (names ):
121
+ with open (os .path .join (directory , initpy ), 'w' ) as f :
122
+ f .write (INITPY_CONTENTS )
123
+
124
+ def get_init_paths (self , names ):
125
+ # Overwrite __init__.py in these directories.
126
+ # (required as googleapis-common-protos has an empty __init__.py, which
127
+ # blocks google.api.core from google-cloud-core)
128
+ NAMESPACES = ["google/api" ]
129
+
130
+ # Find package directories without __init__.py, or where the __init__.py
131
+ # must be overwritten to create a working namespace. This is based on
132
+ # Bazel's PythonUtils.getInitPyFiles().
133
+ init_paths = set ()
134
+ for n in names :
135
+ if os .path .splitext (n )[1 ] not in ['.so' , '.py' , '.pyc' ]:
136
+ continue
137
+ while os .path .sep in n :
138
+ n = os .path .dirname (n )
139
+ initpy = os .path .join (n , '__init__.py' )
140
+ initpyc = os .path .join (n , '__init__.pyc' )
141
+ if (initpy in names or initpyc in names ) and n not in NAMESPACES :
142
+ continue
143
+ init_paths .add (initpy )
144
+
145
+ return init_paths
146
+
107
147
# _parse_metadata parses METADATA files according to https://www.python.org/dev/peps/pep-0314/
108
148
def _parse_metadata (self , content ):
109
149
# TODO: handle fields other than just name
110
150
name_pattern = re .compile ('Name: (.*)' )
111
- return { 'name' : name_pattern .search (content ).group (1 ) }
151
+ return {'name' : name_pattern .search (content ).group (1 )}
112
152
113
153
114
154
parser = argparse .ArgumentParser (
115
155
description = 'Unpack a WHL file as a py_library.' )
116
156
117
- parser .add_argument ('--whl' , action = 'store' ,
118
- help = ('The .whl file we are expanding.' ))
157
+ parser .add_argument (
158
+ '--whl' , action = 'store' , help = ('The .whl file we are expanding.' ))
159
+
160
+ parser .add_argument (
161
+ '--requirements' ,
162
+ action = 'store' ,
163
+ help = 'The pip_import from which to draw dependencies.' )
119
164
120
- parser .add_argument ('--requirements' , action = 'store' ,
121
- help = 'The pip_import from which to draw dependencies.' )
165
+ parser .add_argument (
166
+ '--directory' ,
167
+ action = 'store' ,
168
+ default = '.' ,
169
+ help = 'The directory into which to expand things.' )
122
170
123
- parser .add_argument ('--directory' , action = 'store' , default = '.' ,
124
- help = 'The directory into which to expand things.' )
171
+ parser .add_argument (
172
+ '--extras' ,
173
+ action = 'append' ,
174
+ help = 'The set of extras for which to generate library targets.' )
125
175
126
- parser .add_argument ('--extras' , action = 'append' ,
127
- help = 'The set of extras for which to generate library targets.' )
128
176
129
177
def main ():
130
178
args = parser .parse_args ()
@@ -149,24 +197,22 @@ def main():
149
197
deps = [{dependencies}],
150
198
)
151
199
{extras}""" .format (
152
- requirements = args .requirements ,
153
- dependencies = ',' .join ([
154
- 'requirement("%s")' % d
155
- for d in whl .dependencies ()
156
- ]),
157
- extras = '\n \n ' .join ([
158
- """py_library(
200
+ requirements = args .requirements ,
201
+ dependencies = ',' .join (
202
+ ['requirement("%s")' % d for d in whl .dependencies ()]),
203
+ extras = '\n \n ' .join ([
204
+ """py_library(
159
205
name = "{extra}",
160
206
deps = [
161
207
":pkg",{deps}
162
208
],
163
209
)""" .format (extra = extra ,
164
- deps = ',' .join ([
165
- 'requirement("%s")' % dep
166
- for dep in whl .dependencies (extra )
167
- ]))
168
- for extra in args . extras or []
169
- ])))
210
+ deps = ',' .join (
211
+ [ 'requirement("%s")' % dep
212
+ for dep in whl .dependencies (extra )]) )
213
+ for extra in args . extras or []
214
+ ])))
215
+
170
216
171
217
if __name__ == '__main__' :
172
218
main ()
0 commit comments