Skip to content
This repository was archived by the owner on Oct 21, 2022. It is now read-only.

Commit dd44369

Browse files
authored
Cache the github head version result based on commit number (#201)
1 parent 5367684 commit dd44369

5 files changed

Lines changed: 80 additions & 72 deletions

File tree

badge_server/main.py

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939

4040
def _get_result_from_cache(
4141
package_name: str,
42-
badge_type: badge_utils.BadgeType) -> dict:
42+
badge_type: badge_utils.BadgeType,
43+
commit_number: str = None) -> dict:
4344
"""Get check result from cache."""
4445
# Return unknown if package not in whitelist
4546
if not utils._is_package_in_whitelist([package_name]):
@@ -49,7 +50,9 @@ def _get_result_from_cache(
4950
details={})
5051
# Get the result from cache, return None if not in cache
5152
else:
52-
result = cache.get('{}_{}'.format(package_name, badge_type.value))
53+
package_key = '{}_{}'.format(
54+
package_name, commit_number) if commit_number else package_name
55+
result = cache.get('{}_{}'.format(package_key, badge_type.value))
5356

5457
if result is None:
5558
result = badge_utils._build_default_result(
@@ -109,7 +112,7 @@ def _get_pair_status_for_packages(pkg_sets):
109112
return version_and_res
110113

111114

112-
def _get_all_results_from_cache(package_name):
115+
def _get_all_results_from_cache(package_name, commit_number=None):
113116
"""Get all the check results from cache.
114117
115118
Rules:
@@ -118,13 +121,16 @@ def _get_all_results_from_cache(package_name):
118121
"""
119122
self_compat_res = _get_result_from_cache(
120123
package_name=package_name,
121-
badge_type=badge_utils.BadgeType.SELF_COMP_BADGE)
124+
badge_type=badge_utils.BadgeType.SELF_COMP_BADGE,
125+
commit_number=commit_number)
122126
google_compat_res = _get_result_from_cache(
123127
package_name=package_name,
124-
badge_type=badge_utils.BadgeType.GOOGLE_COMP_BADGE)
128+
badge_type=badge_utils.BadgeType.GOOGLE_COMP_BADGE,
129+
commit_number=commit_number)
125130
dependency_res = _get_result_from_cache(
126131
package_name=package_name,
127-
badge_type=badge_utils.BadgeType.DEP_BADGE)
132+
badge_type=badge_utils.BadgeType.DEP_BADGE,
133+
commit_number=commit_number)
128134

129135
if self_compat_res['py3']['status'] == 'SUCCESS' and \
130136
google_compat_res['py3']['status'] == 'SUCCESS' and \
@@ -177,6 +183,8 @@ def one_badge_image():
177183
badge_name = badge_utils.GITHUB_HEAD_NAME
178184
is_github = True
179185

186+
commit_number = badge_utils._calculate_commit_number(package_name)
187+
180188
force_run_check = flask.request.args.get('force_run_check')
181189
# Remove the last '/' from the url root
182190
url_prefix = flask.request.url_root[:-1]
@@ -186,19 +194,23 @@ def one_badge_image():
186194
requests.get(url_prefix + flask.url_for(
187195
'self_compatibility_badge_image',
188196
package=package_name,
189-
force_run_check=force_run_check))
197+
force_run_check=force_run_check,
198+
commit_number=commit_number))
190199
# Google compatibility badge
191200
requests.get(url_prefix + flask.url_for(
192201
'google_compatibility_badge_image',
193202
package=package_name,
194-
force_run_check=force_run_check))
203+
force_run_check=force_run_check,
204+
commit_number=commit_number))
195205
# Self dependency badge
196206
requests.get(url_prefix + flask.url_for(
197207
'self_dependency_badge_image',
198208
package=package_name,
199-
force_run_check=force_run_check))
209+
force_run_check=force_run_check,
210+
commit_number=commit_number))
200211

201-
status, timestamp, _, _, _ = _get_all_results_from_cache(package_name)
212+
status, timestamp, _, _, _ = _get_all_results_from_cache(
213+
package_name, commit_number=commit_number)
202214
color = badge_utils.STATUS_COLOR_MAPPING[status]
203215

204216
details_link = url_prefix + flask.url_for('one_badge_target',
@@ -224,24 +236,30 @@ def one_badge_image():
224236
@app.route('/one_badge_target')
225237
def one_badge_target():
226238
package_name = flask.request.args.get('package')
239+
commit_number = badge_utils._calculate_commit_number(package_name)
240+
227241
status, _, self_compat_res, google_compat_res, dependency_res = \
228-
_get_all_results_from_cache(package_name)
242+
_get_all_results_from_cache(package_name, commit_number)
229243

230244
return flask.render_template(
231245
'one-badge.html',
232246
package_name=package_name,
233247
self_compat_res=self_compat_res,
234248
google_compat_res=google_compat_res,
235-
dependency_res=dependency_res)
249+
dependency_res=dependency_res,
250+
commit_number=commit_number)
236251

237252

238253
@app.route('/self_compatibility_badge_image')
239254
def self_compatibility_badge_image():
240255
"""Badge showing whether a package is compatible with itself."""
241256
package_name = flask.request.args.get('package')
242257
force_run_check = flask.request.args.get('force_run_check')
258+
commit_number = flask.request.args.get('commit_number')
243259

244260
badge_name = flask.request.args.get('badge_name')
261+
package_key = '{}_{}'.format(
262+
package_name, commit_number) if commit_number else package_name
245263

246264
if badge_name is None:
247265
badge_name = 'self compatibility'
@@ -285,16 +303,15 @@ def run_check():
285303
badge_utils.TIMESTAMP_FORMAT)
286304

287305
# Write the result to Cloud Datastore
288-
cache.set(
289-
'{}_self_comp_badge'.format(package_name), version_and_res)
306+
cache.set('{}_self_comp_badge'.format(package_key), version_and_res)
290307

291308
if not utils._is_package_in_whitelist([package_name]):
292309
self_comp_res = badge_utils._build_default_result(
293310
badge_type=badge_utils.BadgeType.SELF_COMP_BADGE,
294311
status='UNKNOWN',
295312
details=badge_utils.PACKAGE_NOT_SUPPORTED)
296313
else:
297-
self_comp_res = cache.get('{}_self_comp_badge'.format(package_name))
314+
self_comp_res = cache.get('{}_self_comp_badge'.format(package_key))
298315

299316
if self_comp_res is None:
300317
details = version_and_res
@@ -347,7 +364,11 @@ def self_dependency_badge_image():
347364

348365
package_name = flask.request.args.get('package')
349366
force_run_check = flask.request.args.get('force_run_check')
367+
commit_number = flask.request.args.get('commit_number')
368+
350369
badge_name = flask.request.args.get('badge_name')
370+
package_key = '{}_{}'.format(
371+
package_name, commit_number) if commit_number else package_name
351372

352373
if badge_name is None:
353374
badge_name = 'dependency status'
@@ -382,17 +403,15 @@ def run_check():
382403
badge_utils.TIMESTAMP_FORMAT)
383404

384405
# Write the result to Cloud Datastore
385-
cache.set(
386-
'{}_dependency_badge'.format(package_name), res)
406+
cache.set('{}_dependency_badge'.format(package_key), res)
387407

388408
if not utils._is_package_in_whitelist([package_name]):
389409
dependency_res = badge_utils._build_default_result(
390410
badge_type=badge_utils.BadgeType.DEP_BADGE,
391411
status='UNKNOWN',
392412
details={})
393413
else:
394-
dependency_res = cache.get(
395-
'{}_dependency_badge'.format(package_name))
414+
dependency_res = cache.get('{}_dependency_badge'.format(package_key))
396415

397416
if dependency_res is None:
398417
details = badge_utils.DEFAULT_DEPENDENCY_RESULT
@@ -433,7 +452,11 @@ def google_compatibility_badge_image():
433452
to one of the failure types, details can be found at the target link."""
434453
package_name = flask.request.args.get('package')
435454
force_run_check = flask.request.args.get('force_run_check')
455+
commit_number = flask.request.args.get('commit_number')
456+
436457
badge_name = flask.request.args.get('badge_name')
458+
package_key = '{}_{}'.format(
459+
package_name, commit_number) if commit_number else package_name
437460

438461
if badge_name is None:
439462
badge_name = 'google compatibility'
@@ -492,11 +515,9 @@ def run_check():
492515
result = version_and_res
493516

494517
# Write the result to Cloud Datastore
495-
cache.set(
496-
'{}_google_comp_badge'.format(package_name), result)
518+
cache.set('{}_google_comp_badge'.format(package_key), result)
497519

498-
google_comp_res = cache.get(
499-
'{}_google_comp_badge'.format(package_name))
520+
google_comp_res = cache.get('{}_google_comp_badge'.format(package_key))
500521

501522
if not utils._is_package_in_whitelist([package_name]):
502523
google_comp_res = badge_utils._build_default_result(

badge_server/templates/one-badge.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
<div class="col-md-12">
3131
<h1 class="title-4">Package name: {{ package_name }}
3232
</h1>
33+
{% if commit_number | length > 0 %}
34+
<h1 class="title-4">Commit number: {{ commit_number }}
35+
</h1>
36+
{% endif %}
3337
<hr class="line-seprate">
3438
</div>
3539
</div>

badge_server/utils.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
"""Common utils methods for badge server."""
1616

1717
import enum
18+
import json
19+
import logging
1820
import os
21+
from urllib.parse import urlparse
22+
from urllib.request import urlopen
23+
1924
from typing import Optional
2025

2126
import pybadges
@@ -39,6 +44,7 @@
3944

4045
URL_PREFIX = 'https://img.shields.io/badge/'
4146
GITHUB_HEAD_NAME = 'github head'
47+
GITHUB_API = 'https://api.github.com/repos'
4248
SVG_CONTENT_TYPE = 'image/svg+xml'
4349
EMPTY_DETAILS = 'NO DETAILS'
4450
PACKAGE_NOT_SUPPORTED = "The package is not supported by checker server."
@@ -141,3 +147,26 @@ def _get_badge(res: dict, badge_name: str) -> str:
141147
left_text=badge_name,
142148
right_text=status,
143149
right_color=color)
150+
151+
152+
def _calculate_commit_number(package: str) -> Optional[str]:
153+
"""Calculate the github head version commit number."""
154+
url_parsed = urlparse(package)
155+
if url_parsed.scheme and url_parsed.netloc == 'github.com':
156+
try:
157+
owner, repo, *_ = url_parsed.path[1:].split('/')
158+
repo = repo.split('.git')[0]
159+
except ValueError:
160+
return None
161+
else:
162+
url = '{0}/{1}/{2}/commits'.format(GITHUB_API, owner, repo)
163+
try:
164+
with urlopen(url) as f:
165+
commits = json.loads(f.read())
166+
return commits[0]['sha']
167+
except Exception as e:
168+
logging.warning(
169+
'Unable to generate caching key for "%s": %s', package, e)
170+
return None
171+
172+
return None

compatibility_lib/compatibility_lib/get_compatibility_data.py

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,11 @@ def _result_dict_to_compatibility_result(results, python_version):
5353
return res_list
5454

5555

56-
def _generate_pairs_for_github_head():
57-
"""Generate pairs for each github head package with the PyPI packages.
58-
59-
e.g. [(github_pkg, pkg1), (github_pkg, pkg2),...]
60-
"""
61-
pkg_pairs = []
62-
63-
for gh_pkg in configs.WHITELIST_URLS.keys():
64-
gh_pairs = [(gh_pkg, package) for package in configs.PKG_LIST]
65-
pkg_pairs.extend(gh_pairs)
66-
67-
return pkg_pairs
68-
69-
7056
def write_to_status_table():
71-
"""Get the compatibility status for PyPI and github head versions."""
57+
"""Get the compatibility status for PyPI versions."""
7258
# Write self compatibility status to BigQuery
7359
self_res_list = []
74-
packages = configs.PKG_LIST + list(configs.WHITELIST_URLS.keys())
60+
packages = configs.PKG_LIST
7561
for py_version in [PY2, PY3]:
7662
results = checker.get_self_compatibility(
7763
python_version=py_version,
@@ -88,14 +74,6 @@ def write_to_status_table():
8874
res_list = _result_dict_to_compatibility_result(results, py_version)
8975
store.save_compatibility_statuses(res_list)
9076

91-
# For github head versions
92-
pkg_sets = _generate_pairs_for_github_head()
93-
results = checker.get_pairwise_compatibility(
94-
python_version=py_version,
95-
pkg_sets=pkg_sets)
96-
res_list = _result_dict_to_compatibility_result(results, py_version)
97-
store.save_compatibility_statuses(res_list)
98-
9977

10078
if __name__ == '__main__':
10179
write_to_status_table()

compatibility_lib/compatibility_lib/test_get_compatibility_data.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -72,30 +72,6 @@ def mock_init():
7272
'compatibility_lib.get_compatibility_data.store',
7373
self.fake_store)
7474

75-
def test__generate_pairs_for_github_head(self):
76-
pkg_list = ['package1', 'package2']
77-
gh_pkgs = {
78-
'gh_pkg1_url': 'gh_pkg1',
79-
'gh_pkg2_url':'gh_pkg2'
80-
}
81-
patch_pkg_list = mock.patch(
82-
'compatibility_lib.configs.PKG_LIST', pkg_list)
83-
patch_gh_list = mock.patch(
84-
'compatibility_lib.configs.WHITELIST_URLS', gh_pkgs)
85-
86-
with self.patch_constructor, self.patch_checker, self.patch_store,\
87-
patch_pkg_list, patch_gh_list:
88-
from compatibility_lib import get_compatibility_data
89-
90-
pairs = get_compatibility_data._generate_pairs_for_github_head()
91-
92-
expected = [('gh_pkg1_url', 'package1'),
93-
('gh_pkg1_url', 'package2'),
94-
('gh_pkg2_url', 'package1'),
95-
('gh_pkg2_url', 'package2')]
96-
97-
self.assertEqual(set(pairs), set(expected))
98-
9975
def test__result_dict_to_compatibility_result(self):
10076
with self.patch_constructor, self.patch_checker, self.patch_store:
10177
from compatibility_lib import compatibility_store
@@ -123,7 +99,7 @@ def test_write_to_status_table(self):
12399
saved_results = self.fake_store._packages_to_compatibility_result.get(
124100
frozenset({self.packages[0]}))
125101
self.assertIsNotNone(saved_results)
126-
self.assertEqual(len(saved_results), 6)
102+
self.assertEqual(len(saved_results), 4)
127103
saved_item = saved_results[0]
128104
self.assertEqual(saved_item.packages, self.packages)
129105
self.assertEqual(saved_item.dependency_info, self.dependency_info)

0 commit comments

Comments
 (0)