libmozdata 0.2.12__tar.gz → 0.2.14__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {libmozdata-0.2.12/libmozdata.egg-info → libmozdata-0.2.14}/PKG-INFO +1 -1
  2. libmozdata-0.2.14/VERSION +1 -0
  3. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/FileStats.py +3 -2
  4. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/bugzilla.py +6 -3
  5. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/config.py +0 -2
  6. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/connection.py +1 -1
  7. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/patchanalysis.py +7 -3
  8. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/phabricator.py +29 -8
  9. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/utils.py +13 -11
  10. {libmozdata-0.2.12 → libmozdata-0.2.14/libmozdata.egg-info}/PKG-INFO +1 -1
  11. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata.egg-info/SOURCES.txt +0 -1
  12. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata.egg-info/requires.txt +2 -2
  13. {libmozdata-0.2.12 → libmozdata-0.2.14}/requirements.txt +2 -2
  14. libmozdata-0.2.12/VERSION +0 -1
  15. libmozdata-0.2.12/libmozdata/release_owners.py +0 -86
  16. {libmozdata-0.2.12 → libmozdata-0.2.14}/MANIFEST.in +0 -0
  17. {libmozdata-0.2.12 → libmozdata-0.2.14}/README.md +0 -0
  18. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/BZInfo.py +0 -0
  19. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/FXRevision.py +0 -0
  20. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/HGFileInfo.py +0 -0
  21. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/__init__.py +0 -0
  22. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/buildhub.py +0 -0
  23. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/clouseau.py +0 -0
  24. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/fx_trains.py +0 -0
  25. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/handler.py +0 -0
  26. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/hgmozilla.py +0 -0
  27. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/lando.py +0 -0
  28. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/modules.json +0 -0
  29. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/modules.py +0 -0
  30. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/redash.py +0 -0
  31. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/socorro.py +0 -0
  32. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/vcs_map.py +0 -0
  33. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/versions.py +0 -0
  34. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata/wiki_parser.py +0 -0
  35. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata.egg-info/dependency_links.txt +0 -0
  36. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata.egg-info/not-zip-safe +0 -0
  37. {libmozdata-0.2.12 → libmozdata-0.2.14}/libmozdata.egg-info/top_level.txt +0 -0
  38. {libmozdata-0.2.12 → libmozdata-0.2.14}/setup.cfg +0 -0
  39. {libmozdata-0.2.12 → libmozdata-0.2.14}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: libmozdata
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: Library to access and aggregate several Mozilla data sources.
5
5
  Home-page: https://github.com/mozilla/libmozdata
6
6
  Author: Mozilla Release Management
@@ -0,0 +1 @@
1
+ 0.2.14
@@ -4,7 +4,7 @@
4
4
 
5
5
  import argparse
6
6
  import numbers
7
- from datetime import datetime, timedelta
7
+ from datetime import datetime, timedelta, timezone
8
8
  from pprint import pprint
9
9
 
10
10
  from . import config, modules, utils
@@ -36,7 +36,8 @@ class FileStats(object):
36
36
  )
37
37
  self.utc_ts_from = (
38
38
  utils.get_timestamp(
39
- datetime.utcfromtimestamp(utc_ts) + timedelta(-self.max_days)
39
+ datetime.fromtimestamp(utc_ts, tz=timezone.utc)
40
+ + timedelta(-self.max_days)
40
41
  )
41
42
  if isinstance(utc_ts, numbers.Number) and utc_ts > 0
42
43
  else None
@@ -561,7 +561,8 @@ class Bugzilla(BugzillaBase):
561
561
  res: result
562
562
  """
563
563
  if res.status_code == 200:
564
- for bug in res.json()["bugs"]:
564
+ data = res.json()
565
+ for bug in data["bugs"]:
565
566
  self.bughandler.handle(bug)
566
567
  elif self.RAISE_ERROR:
567
568
  res.raise_for_status()
@@ -616,7 +617,8 @@ class Bugzilla(BugzillaBase):
616
617
  timeout=self.TIMEOUT,
617
618
  )
618
619
  if r.ok:
619
- count = r.json()["bug_count"]
620
+ data = r.json()
621
+ count = data["bug_count"]
620
622
  del params["count_only"]
621
623
  params["limit"] = Bugzilla.BUGZILLA_CHUNK_SIZE
622
624
  params["order"] = "bug_id"
@@ -652,7 +654,8 @@ class Bugzilla(BugzillaBase):
652
654
 
653
655
  def cb(res, *args, **kwargs):
654
656
  if res.status_code == 200:
655
- for bug in res.json()["bugs"]:
657
+ data = res.json()
658
+ for bug in data["bugs"]:
656
659
  _list.add(bug["id"])
657
660
  elif self.RAISE_ERROR:
658
661
  res.raise_for_status()
@@ -78,7 +78,6 @@ def set_config(conf):
78
78
 
79
79
 
80
80
  def get(section, option, default=None, type=str, required=False):
81
- global __config
82
81
  value = __config.get(section, option, default=default, type=type)
83
82
  if required:
84
83
  assert value is not None, f"Option {option} in section {section} is not set"
@@ -86,5 +85,4 @@ def get(section, option, default=None, type=str, required=False):
86
85
 
87
86
 
88
87
  def set_default_value(section, option, value):
89
- global __config
90
88
  __config.set_default(section, option, value)
@@ -98,7 +98,7 @@ class Connection(object):
98
98
  self.RAISE_ERROR = kwargs["raise_error"]
99
99
 
100
100
  if not self.USER_AGENT:
101
- config.get("User-Agent", "name", required=True)
101
+ self.USER_AGENT = config.get("User-Agent", "name", required=True)
102
102
 
103
103
  self.exec_queries()
104
104
 
@@ -4,7 +4,7 @@ import numbers
4
4
  import re
5
5
  import warnings
6
6
  import weakref
7
- from datetime import datetime, timedelta
7
+ from datetime import datetime, timedelta, timezone
8
8
 
9
9
  import whatthepatch
10
10
 
@@ -665,7 +665,9 @@ def bug_analysis(bug, uplift_channel=None, author_cache={}, reviewer_cache={}):
665
665
  hgmozilla.RawRevision.get_revision(obj["channel"], rev),
666
666
  author_names,
667
667
  reviewers,
668
- utils.as_utc(datetime.utcfromtimestamp(obj["creation_date"])),
668
+ utils.as_utc(
669
+ datetime.fromtimestamp(obj["creation_date"], tz=timezone.utc)
670
+ ),
669
671
  )
670
672
  )
671
673
  else:
@@ -976,7 +978,9 @@ def get_patch_info(
976
978
  bug_pattern = re.compile(r"[\t ]*[Bb][Uu][Gg][\t ]*([0-9]+)")
977
979
 
978
980
  def handler_revision(json, data):
979
- data["date"] = utils.as_utc(datetime.utcfromtimestamp(json["pushdate"][0]))
981
+ data["date"] = utils.as_utc(
982
+ datetime.fromtimestamp(json["pushdate"][0], tz=timezone.utc)
983
+ )
980
984
  data["backedout"] = json.get("backedoutby", "") != ""
981
985
  m = bug_pattern.search(json["desc"])
982
986
  if not m or m.group(1) != data["bugid"]:
@@ -7,6 +7,8 @@ import collections
7
7
  import enum
8
8
  import json
9
9
  import logging
10
+ import math
11
+ import time
10
12
  from functools import cached_property
11
13
  from urllib.parse import urlencode, urlparse
12
14
 
@@ -208,12 +210,18 @@ class PhabricatorAPI(object):
208
210
  Phabricator Rest API client
209
211
  """
210
212
 
211
- def __init__(self, api_key, url=MOZILLA_PHABRICATOR_PROD):
213
+ def __init__(self, api_key, url=MOZILLA_PHABRICATOR_PROD, max_retries=5):
212
214
  self.USER_AGENT = config.get("User-Agent", "name", required=True)
213
215
  self.api_key = api_key
214
216
  self.url = url
215
217
  assert self.url.endswith("/api/"), "Phabricator API must end with /api/"
216
218
 
219
+ # Number of API calls retries on 50x before raising an error
220
+ self.max_retries = max_retries
221
+ assert (
222
+ isinstance(self.max_retries, int) and self.max_retries > 0
223
+ ), "Invalid max_retries parameter"
224
+
217
225
  @cached_property
218
226
  def user(self):
219
227
  """
@@ -236,7 +244,7 @@ class PhabricatorAPI(object):
236
244
  diff_id=None,
237
245
  revision_phid=None,
238
246
  output_cursor=False,
239
- **params
247
+ **params,
240
248
  ):
241
249
  """
242
250
  Find details of differential diffs from a Differential diff or revision
@@ -661,12 +669,25 @@ class PhabricatorAPI(object):
661
669
  # Add api token to payload
662
670
  payload["__conduit__"] = {"token": self.api_key}
663
671
 
664
- # Run POST request on api
665
- response = requests.post(
666
- self.url + path,
667
- headers=self.get_header(),
668
- data=urlencode({"params": json.dumps(payload), "output": "json"}),
669
- )
672
+ # Run POST request on api, retrying on 50x errors
673
+ for nb_try in range(1, self.max_retries + 1):
674
+ response = requests.post(
675
+ self.url + path,
676
+ headers=self.get_header(),
677
+ data=urlencode({"params": json.dumps(payload), "output": "json"}),
678
+ )
679
+ if response.status_code >= 500:
680
+ # Retry after a while on 50x using exponential backoff
681
+ backoff = math.exp(nb_try)
682
+ logger.info(
683
+ f"Phabricator failed with status code {response.status_code}, retrying in {backoff:.1f} seconds..."
684
+ )
685
+ time.sleep(backoff)
686
+ else:
687
+ # Stop retrying on the first successful try or other HTTP errors
688
+ break
689
+
690
+ # Raise immediate 40x errors or out-of-retries 50x
670
691
  response.raise_for_status()
671
692
 
672
693
  # Check response
@@ -7,7 +7,7 @@ import math
7
7
  import operator
8
8
  import os.path
9
9
  import random
10
- from datetime import date, datetime, timedelta
10
+ from datetime import date, datetime, timedelta, timezone
11
11
  from itertools import count
12
12
 
13
13
  import dateutil.parser
@@ -44,7 +44,7 @@ def get_timestamp(dt):
44
44
  int: the corresponding timestamp
45
45
  """
46
46
  if isinstance(dt, six.string_types):
47
- dt = datetime.utcnow() if dt == "now" else get_date_ymd(dt)
47
+ dt = datetime.now(timezone.utc) if dt == "now" else get_date_ymd(dt)
48
48
  return int(calendar.timegm(dt.timetuple()))
49
49
 
50
50
 
@@ -57,7 +57,7 @@ def get_date_from_timestamp(ts):
57
57
  Returns:
58
58
  datetime.datetime: the corresponding datetime in UTC tz
59
59
  """
60
- return datetime.utcfromtimestamp(ts).replace(tzinfo=pytz.utc)
60
+ return datetime.fromtimestamp(ts, tz=pytz.utc)
61
61
 
62
62
 
63
63
  def get_date_ymd(dt):
@@ -75,13 +75,13 @@ def get_date_ymd(dt):
75
75
  return as_utc(dt)
76
76
 
77
77
  if dt == "today":
78
- today = datetime.utcnow()
78
+ today = datetime.now(timezone.utc)
79
79
  return pytz.utc.localize(datetime(today.year, today.month, today.day))
80
80
  elif dt == "tomorrow":
81
- tomorrow = datetime.utcnow() + timedelta(1)
81
+ tomorrow = datetime.now(timezone.utc) + timedelta(1)
82
82
  return pytz.utc.localize(datetime(tomorrow.year, tomorrow.month, tomorrow.day))
83
83
  elif dt == "yesterday":
84
- yesterday = datetime.utcnow() - timedelta(1)
84
+ yesterday = datetime.now(timezone.utc) - timedelta(1)
85
85
  return pytz.utc.localize(
86
86
  datetime(yesterday.year, yesterday.month, yesterday.day)
87
87
  )
@@ -131,7 +131,7 @@ def get_now_timestamp():
131
131
  Returns:
132
132
  int: timestamp for now
133
133
  """
134
- return get_timestamp(datetime.utcnow())
134
+ return get_timestamp(datetime.now(timezone.utc))
135
135
 
136
136
 
137
137
  def is64(cpu_name):
@@ -327,10 +327,12 @@ def get_params_for_url(params):
327
327
  "?"
328
328
  + "&".join(
329
329
  [
330
- quote(name) + "=" + quote(str(value))
331
- if not isinstance(value, list)
332
- else "&".join(
333
- [quote(name) + "=" + quote(str(intValue)) for intValue in value]
330
+ (
331
+ quote(name) + "=" + quote(str(value))
332
+ if not isinstance(value, list)
333
+ else "&".join(
334
+ [quote(name) + "=" + quote(str(intValue)) for intValue in value]
335
+ )
334
336
  )
335
337
  for name, value in sorted(params.items(), key=lambda p: p[0])
336
338
  if value is not None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: libmozdata
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: Library to access and aggregate several Mozilla data sources.
5
5
  Home-page: https://github.com/mozilla/libmozdata
6
6
  Author: Mozilla Release Management
@@ -23,7 +23,6 @@ libmozdata/modules.py
23
23
  libmozdata/patchanalysis.py
24
24
  libmozdata/phabricator.py
25
25
  libmozdata/redash.py
26
- libmozdata/release_owners.py
27
26
  libmozdata/socorro.py
28
27
  libmozdata/utils.py
29
28
  libmozdata/vcs_map.py
@@ -1,9 +1,9 @@
1
- icalendar>=3.10
1
+ icalendar>=7.2.0
2
2
  python-dateutil>=2.5.2
3
3
  python-hglib>=2.6.1
4
4
  pytz>=2022
5
5
  requests-futures>=0.9.8
6
6
  requests[security]>=2.7.0
7
7
  setuptools>=28.6.1
8
- six>=1.10.0
8
+ six>=1.17.0
9
9
  whatthepatch>=0.0.4
@@ -1,9 +1,9 @@
1
- icalendar>=3.10
1
+ icalendar>=7.2.0
2
2
  python-dateutil>=2.5.2
3
3
  python-hglib>=2.6.1
4
4
  pytz>=2022
5
5
  requests-futures>=0.9.8
6
6
  requests[security]>=2.7.0
7
7
  setuptools>=28.6.1
8
- six>=1.10.0
8
+ six>=1.17.0
9
9
  whatthepatch>=0.0.4
libmozdata-0.2.12/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.2.12
@@ -1,86 +0,0 @@
1
- # This Source Code Form is subject to the terms of the Mozilla Public
2
- # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
- # You can obtain one at http://mozilla.org/MPL/2.0/.
4
-
5
- import requests
6
- from requests.exceptions import HTTPError
7
-
8
- from . import config, utils
9
- from .wiki_parser import InvalidWiki, WikiParser
10
-
11
- OWNERS_URL = "https://wiki.mozilla.org/Release_Management/Release_owners"
12
- _OWNERS = None
13
-
14
-
15
- def _get_sub_versions(s):
16
- s = s.strip()
17
- s = s.split(".")
18
- return [int(v.split(" ", 1)[0]) for v in s]
19
-
20
-
21
- def get_versions(s):
22
- fx = "Firefox "
23
- if not s.startswith(fx):
24
- raise InvalidWiki('Invalid version format, expect: "Firefox ..."')
25
- N = len(fx)
26
- version = s[N:]
27
- versions = version.split(";")
28
- return [_get_sub_versions(v) for v in versions]
29
-
30
-
31
- def _get_list_people(s):
32
- return [x.strip() for x in s.split(",")]
33
-
34
-
35
- def get_owners():
36
- global _OWNERS
37
- if _OWNERS is not None:
38
- return _OWNERS
39
-
40
- resp = requests.get(
41
- OWNERS_URL,
42
- headers={"User-Agent": config.get("User-Agent", "name", required=True)},
43
- )
44
- try:
45
- resp.raise_for_status()
46
- except HTTPError as e:
47
- raise InvalidWiki("Failed to load wiki data") from e
48
-
49
- html = resp.text.encode("ascii", errors="ignore")
50
-
51
- parser = WikiParser(tables=[0])
52
- try:
53
- parser.feed(html)
54
- except StopIteration:
55
- table = parser.get_tables()[0]
56
- if [
57
- "Firefox Version",
58
- "Owner",
59
- "Secondary",
60
- "Engineering REO",
61
- "Release Duty",
62
- "Corresponding ESR",
63
- "Release Date",
64
- ] != table[0]:
65
- raise InvalidWiki("Column headers are wrong")
66
-
67
- _OWNERS = []
68
- for row in table[1:]:
69
- try:
70
- # sometimes the date is 2019-XX-XX (when the date is not known)
71
- release_date = utils.get_date_ymd(row[6])
72
- except (AssertionError, ValueError):
73
- continue
74
-
75
- _OWNERS.append(
76
- {
77
- "version": get_versions(row[0])[0][0],
78
- "owner": row[1],
79
- "secondary": row[2],
80
- "engineering reo": row[3],
81
- "release duty": _get_list_people(row[4]),
82
- "corresponding esr": row[5],
83
- "release date": release_date,
84
- }
85
- )
86
- return _OWNERS
File without changes
File without changes
File without changes
File without changes