13
13
14
14
import jinja2
15
15
import jinja2 .sandbox
16
+ import requests
16
17
from conda_forge_feedstock_ops .container_utils import (
17
18
get_default_log_level_args ,
18
19
run_container_operation ,
@@ -140,6 +141,72 @@ def _render_jinja2(tmpl, context):
140
141
)
141
142
142
143
144
+ def _try_pypi_api (url_tmpl : str , context : MutableMapping , hash_type : str ):
145
+ if "name" not in context :
146
+ return None , None
147
+
148
+ if "version" not in context :
149
+ return None , None
150
+
151
+ if not any (pypi_slug in url_tmpl for pypi_slug in ["/pypi.org" , "/pypi.io" , "/files.pythonhosted.org" ]):
152
+ return None , None
153
+
154
+ r = requests .get (
155
+ f"https://pypi.org/simple/{ context ['name' ]} /" ,
156
+ headers = {"Accept" : "application/vnd.pypi.simple.v1+json" }
157
+ )
158
+ r .raise_for_status ()
159
+
160
+ data = r .json ()
161
+ logger .debug ("PyPI API data:\n %s" , pprint .pformat (data ))
162
+
163
+ valid_src_exts = {".tar.gz" , ".tar.bz2" , ".tar.xz" , ".zip" , ".tgz" }
164
+ finfo = None
165
+ ext = None
166
+ for _finfo in data ["files" ]:
167
+ for valid_ext in valid_src_exts :
168
+ if _finfo ["filename" ].endswith (context ["version" ] + valid_ext ):
169
+ ext = valid_ext
170
+ finfo = _finfo
171
+ break
172
+
173
+ if finfo is None or ext is None :
174
+ logger .debug ("src dist for version %s not found in PyPI API" , context ["version" ])
175
+ return None , None
176
+
177
+ bn , _ = os .path .split (url_tmpl )
178
+ pypi_name = finfo ["filename" ].split (context ["version" ] + ext )[0 ]
179
+ logger .debug ("PyPI API file name: %s" , pypi_name )
180
+ name_tmpl = None
181
+ for tmpl in [
182
+ "{{ name }}" ,
183
+ "{{ name.lower() }}" ,
184
+ "{{ name.replace('-', '_') }}" ,
185
+ "{{ name.replace('_', '-') }}" ,
186
+ "{{ name.replace('-', '_').lower() }}" ,
187
+ "{{ name.replace('_', '-').lower() }}" ,
188
+ ]:
189
+ if pypi_name == _render_jinja2 (tmpl , context ) + "-" :
190
+ name_tmpl = tmpl
191
+ break
192
+
193
+ if name_tmpl is not None :
194
+ new_url_tmpl = os .path .join (bn , name_tmpl + "-" + "{{ version }}" + ext )
195
+ else :
196
+ new_url_tmpl = os .path .join (bn , finfo ["filename" ].replace (context ["version" ], "{{ version }}" ))
197
+
198
+ logger .debug ("new url template from PyPI API: %s" , new_url_tmpl )
199
+ url = _render_jinja2 (new_url_tmpl , context )
200
+ new_hash = _try_url_and_hash_it (url , hash_type )
201
+ if new_hash is not None :
202
+ return new_url_tmpl , new_hash
203
+
204
+ new_url_tmpl = finfo ["url" ]
205
+ new_hash = _try_url_and_hash_it (url , hash_type )
206
+ if new_hash is not None :
207
+ return new_url_tmpl , new_hash
208
+
209
+
143
210
def _get_new_url_tmpl_and_hash (url_tmpl : str , context : MutableMapping , hash_type : str ):
144
211
logger .info (
145
212
"hashing URL template: %s" ,
@@ -165,6 +232,13 @@ def _get_new_url_tmpl_and_hash(url_tmpl: str, context: MutableMapping, hash_type
165
232
new_url_tmpl = None
166
233
new_hash = None
167
234
235
+ try :
236
+ new_url_tmpl , new_hash = _try_pypi_api (url_tmpl , context , hash_type )
237
+ if new_hash is not None and new_url_tmpl is not None :
238
+ return new_url_tmpl , new_hash
239
+ except Exception as e :
240
+ logger .debug ("PyPI API url+hash update failed: %s" , repr (e ), exc_info = e )
241
+
168
242
for new_url_tmpl in gen_transformed_urls (url_tmpl ):
169
243
try :
170
244
url = _render_jinja2 (new_url_tmpl , context )
0 commit comments