Skip to content

Commit

Permalink
Merge pull request #114 from Breakthrough-Energy/jon/nrel-api
Browse files Browse the repository at this point in the history
Factor out code for interacting with nrel api and add retry logic
  • Loading branch information
jenhagg authored Oct 8, 2020
2 parents 0735309 + d91c12f commit ffa32d4
Show file tree
Hide file tree
Showing 10 changed files with 580 additions and 89 deletions.
1 change: 1 addition & 0 deletions prereise/gather/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
"hydrodata",
"get_monthly_net_generation",
"trim_eia_form_923",
"request_util",
]
61 changes: 61 additions & 0 deletions prereise/gather/request_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import functools
import time
from datetime import timedelta
from urllib.error import HTTPError


class RateLimit:
"""Provides a way to call an arbitrary function at most once per interval.
:param int/float interval: the amount of time in seconds to wait between actions
"""

def __init__(self, interval=None):
"""Constructor"""
self.interval = interval
self.last_run_at = None if interval is None else time.time() - interval

def invoke(self, action):
"""Call the action and return its value, waiting if necessary
:param callable action: the thing to do
:return: (*Any*) -- the return value of the action
"""
if self.interval is None:
return action()
elapsed = time.time() - self.last_run_at
if elapsed < self.interval:
time.sleep(self.interval - elapsed)
result = action()
self.last_run_at = time.time()
return result


def retry(_func=None, retry_count=5, interval=None, allowed_exceptions=(HTTPError)):
"""Creates a decorator to handle retry logic.
:param int retry_count: the max number of retries
:param int/float interval: minimum spacing between retries
:param tuple allowed_exceptions: exceptions for which the function will be retried, all others will be surfaced to the caller
:return: (*Any*) -- the return value of the decorated function
"""

def decorator(func):
limiter = RateLimit(interval)

@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(retry_count):
try:
return limiter.invoke(lambda: func(*args, **kwargs))
except allowed_exceptions as e:
print(str(e))
pass
print("Max retries reached!!")

return wrapper

if _func is None:
return decorator
return decorator(_func)
2 changes: 1 addition & 1 deletion prereise/gather/solardata/nsrdb/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__all__ = ["naive", "sam"]
__all__ = ["naive", "sam", "nrel_api"]
Loading

0 comments on commit ffa32d4

Please sign in to comment.