From 6fdb61e531096378428b59ced87e487d37b1fb9b Mon Sep 17 00:00:00 2001 From: Christoph Berg Date: Sun, 15 Dec 2019 22:21:46 +0100 Subject: [PATCH] Don't use timestamps as parameter defaults Constructs like def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()): are actually wrong because they initialize the parameter default at module load time, not at function call time. If the program is running over some time, the result will be wrong. As a side-effect, this fix makes the docs (and the whole project) build reproducibly because previously the build time was embedded in the sphinx docs: lookup_prefix(prefix, timestamp=datetime.datetime(2019, 11, 27, 3, 4, 36, 93157, tzinfo=)) --- pyhamtools/callinfo.py | 53 +++++++++++++++++++++++++++++++---------- pyhamtools/locator.py | 4 +++- pyhamtools/lookuplib.py | 22 ++++++++++++----- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/pyhamtools/callinfo.py b/pyhamtools/callinfo.py index d115047..02d9912 100644 --- a/pyhamtools/callinfo.py +++ b/pyhamtools/callinfo.py @@ -10,7 +10,6 @@ from pyhamtools.consts import LookupConventions as const from pyhamtools.callsign_exceptions import callsign_exceptions UTC = pytz.UTC -timestamp_now = datetime.utcnow().replace(tzinfo=UTC) if sys.version_info < (2, 7, ): class NullHandler(logging.Handler): @@ -78,9 +77,11 @@ class Callinfo(object): else: raise ValueError - def _iterate_prefix(self, callsign, timestamp=timestamp_now): + def _iterate_prefix(self, callsign, timestamp=None): """truncate call until it corresponds to a Prefix in the database""" prefix = callsign + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) if re.search('(VK|AX|VI)9[A-Z]{3}', callsign): #special rule for VK9 calls if timestamp > datetime(2006,1,1, tzinfo=UTC): @@ -109,7 +110,7 @@ class Callinfo(object): check = callsign[-4:].upper() return "/B" in check or "/BCN" in check - def _dismantle_callsign(self, callsign, timestamp=timestamp_now): + def _dismantle_callsign(self, callsign, timestamp=None): """ try to identify the callsign's identity by analyzing it in the following order: Args: @@ -122,6 +123,8 @@ class Callinfo(object): """ entire_callsign = callsign.upper() + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) if re.search('[/A-Z0-9\-]{3,15}', entire_callsign): # make sure the call has at least 3 characters @@ -221,7 +224,9 @@ class Callinfo(object): self._logger.debug("Could not decode " + callsign) raise KeyError("Callsign could not be decoded") - def _lookup_callsign(self, callsign, timestamp=timestamp_now): + def _lookup_callsign(self, callsign, timestamp=None): + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) # Check if operation is invalid invalid = False @@ -264,7 +269,7 @@ class Callinfo(object): # Dismantel the callsign and check if the prefix is known return self._dismantle_callsign(callsign, timestamp) - def get_all(self, callsign, timestamp=timestamp_now): + def get_all(self, callsign, timestamp=None): """ Lookup a callsign and return all data available from the underlying database Args: @@ -302,6 +307,9 @@ class Callinfo(object): would be missing with Clublog (API or XML) :py:class:`LookupLib`. """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + callsign_data = self._lookup_callsign(callsign, timestamp) try: @@ -312,7 +320,7 @@ class Callinfo(object): return callsign_data - def is_valid_callsign(self, callsign, timestamp=timestamp_now): + def is_valid_callsign(self, callsign, timestamp=None): """ Checks if a callsign is valid Args: @@ -332,13 +340,16 @@ class Callinfo(object): True """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + try: if self.get_all(callsign, timestamp): return True except KeyError: return False - def get_lat_long(self, callsign, timestamp=timestamp_now): + def get_lat_long(self, callsign, timestamp=None): """ Returns Latitude and Longitude for a callsign Args: @@ -369,13 +380,16 @@ class Callinfo(object): dedicated entry in the database exists. Best results will be retrieved with QRZ.com Lookup. """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + callsign_data = self.get_all(callsign, timestamp=timestamp) return { const.LATITUDE: callsign_data[const.LATITUDE], const.LONGITUDE: callsign_data[const.LONGITUDE] } - def get_cqz(self, callsign, timestamp=timestamp_now): + def get_cqz(self, callsign, timestamp=None): """ Returns CQ Zone of a callsign Args: @@ -389,9 +403,12 @@ class Callinfo(object): KeyError: no CQ Zone found for callsign """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + return self.get_all(callsign, timestamp)[const.CQZ] - def get_ituz(self, callsign, timestamp=timestamp_now): + def get_ituz(self, callsign, timestamp=None): """ Returns ITU Zone of a callsign Args: @@ -408,9 +425,12 @@ class Callinfo(object): Currently, only Country-files.com lookup database contains ITU Zones """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + return self.get_all(callsign, timestamp)[const.ITUZ] - def get_country_name(self, callsign, timestamp=timestamp_now): + def get_country_name(self, callsign, timestamp=None): """ Returns the country name where the callsign is located Args: @@ -432,9 +452,12 @@ class Callinfo(object): - Clublog: "FEDERAL REPUBLIC OF GERMANY" """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + return self.get_all(callsign, timestamp)[const.COUNTRY] - def get_adif_id(self, callsign, timestamp=timestamp_now): + def get_adif_id(self, callsign, timestamp=None): """ Returns ADIF id of a callsign's country Args: @@ -448,9 +471,12 @@ class Callinfo(object): KeyError: No Country found for callsign """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + return self.get_all(callsign, timestamp)[const.ADIF] - def get_continent(self, callsign, timestamp=timestamp_now): + def get_continent(self, callsign, timestamp=None): """ Returns the continent Identifier of a callsign Args: @@ -474,4 +500,7 @@ class Callinfo(object): - OC: Oceania - AN: Antarctica """ + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + return self.get_all(callsign, timestamp)[const.CONTINENT] diff --git a/pyhamtools/locator.py b/pyhamtools/locator.py index 5190b40..0660892 100644 --- a/pyhamtools/locator.py +++ b/pyhamtools/locator.py @@ -265,7 +265,7 @@ def calculate_heading_longpath(locator1, locator2): return lp -def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()): +def calculate_sunrise_sunset(locator, calc_date=None): """calculates the next sunset and sunrise for a Maidenhead locator at a give date & time Args: @@ -303,6 +303,8 @@ def calculate_sunrise_sunset(locator, calc_date=datetime.utcnow()): latitude, longitude = locator_to_latlong(locator) + if calc_date is None: + calc_date = datetime.utcnow() if type(calc_date) != datetime: raise ValueError diff --git a/pyhamtools/lookuplib.py b/pyhamtools/lookuplib.py index 392b3f2..76aa260 100644 --- a/pyhamtools/lookuplib.py +++ b/pyhamtools/lookuplib.py @@ -22,7 +22,6 @@ from .consts import LookupConventions as const from .exceptions import APIKeyMissingError UTC = pytz.UTC -timestamp_now = datetime.utcnow().replace(tzinfo=UTC) if sys.version_info < (2, 7,): class NullHandler(logging.Handler): @@ -319,7 +318,7 @@ class LookupLib(object): return new_dict - def lookup_callsign(self, callsign=None, timestamp=timestamp_now): + def lookup_callsign(self, callsign=None, timestamp=None): """ Returns lookup data if an exception exists for a callsign @@ -364,6 +363,8 @@ class LookupLib(object): """ callsign = callsign.strip().upper() + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) if self._lookuptype == "clublogapi": callsign_data = self._lookup_clublogAPI(callsign=callsign, timestamp=timestamp, apikey=self._apikey) @@ -482,7 +483,7 @@ class LookupLib(object): raise KeyError - def lookup_prefix(self, prefix, timestamp=timestamp_now): + def lookup_prefix(self, prefix, timestamp=None): """ Returns lookup data of a Prefix @@ -524,6 +525,8 @@ class LookupLib(object): """ prefix = prefix.strip().upper() + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) if self._lookuptype == "clublogxml" or self._lookuptype == "countryfile": @@ -537,7 +540,7 @@ class LookupLib(object): # no matching case raise KeyError - def is_invalid_operation(self, callsign, timestamp=datetime.utcnow().replace(tzinfo=UTC)): + def is_invalid_operation(self, callsign, timestamp=None): """ Returns True if an operations is known as invalid @@ -577,6 +580,8 @@ class LookupLib(object): """ callsign = callsign.strip().upper() + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) if self._lookuptype == "clublogxml": @@ -624,7 +629,7 @@ class LookupLib(object): raise KeyError - def lookup_zone_exception(self, callsign, timestamp=datetime.utcnow().replace(tzinfo=UTC)): + def lookup_zone_exception(self, callsign, timestamp=None): """ Returns a CQ Zone if an exception exists for the given callsign @@ -659,6 +664,8 @@ class LookupLib(object): """ callsign = callsign.strip().upper() + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) if self._lookuptype == "clublogxml": @@ -672,7 +679,7 @@ class LookupLib(object): #no matching case raise KeyError - def _lookup_clublogAPI(self, callsign=None, timestamp=timestamp_now, url="https://secure.clublog.org/dxcc", apikey=None): + def _lookup_clublogAPI(self, callsign=None, timestamp=None, url="https://secure.clublog.org/dxcc", apikey=None): """ Set up the Lookup object for Clublog Online API """ @@ -686,6 +693,9 @@ class LookupLib(object): "call" : callsign } + if timestamp is None: + timestamp = datetime.utcnow().replace(tzinfo=UTC) + if sys.version_info.major == 3: encodeurl = url + "?" + urllib.parse.urlencode(params) else: