debian-workalendar/workalendar/registry.py

126 lines
4.0 KiB
Python

from importlib import import_module
from .core import Calendar
from .exceptions import ISORegistryError
class IsoRegistry:
"""
Registry for all calendars retrievable
by ISO 3166-2 codes associated with countries
where they are used as official calendars.
Two letter codes are favored for any subdivisions.
"""
STANDARD_MODULES = (
# Europe Countries
'europe',
# United States of America
'usa',
# American continent outside of USA
'america',
# African continent
'africa',
# Asia
'asia',
# Oceania
'oceania',
)
def __init__(self, load_standard_modules=True):
self.region_registry = dict()
if load_standard_modules:
for module_name in self.STANDARD_MODULES:
module = 'workalendar.{}'.format(module_name)
all_classes = getattr(import_module(module), '__all__')
self.load_module_from_items(module, all_classes)
def register(self, iso_code, cls):
"""
Store the ``cls`` in the region_registry.
"""
if not issubclass(cls, Calendar):
raise ISORegistryError(
"Class `{}` is not a Calendar class".format(cls)
)
self.region_registry[iso_code] = cls
def load_module_from_items(self, module_name, items):
"""
Load all registered classes in the registry
"""
for item in items:
cls = getattr(import_module(module_name), item)
iso_stuff = getattr(cls, '__iso_code', None)
if iso_stuff:
iso_code, class_name = iso_stuff
if iso_code and cls.__name__ == class_name:
self.register(iso_code, cls)
def get_calendar_class(self, iso_code):
"""
Retrieve calendar class associated with given ``iso_code``.
If calendar of subdivision is not registered
(for subdivision like ISO codes, e.g. GB-ENG)
returns calendar of containing region
(e.g. United Kingdom for ISO code GB) if it's available.
:rtype: Calendar
"""
return self.region_registry.get(iso_code)
def get_subregions(self, iso_code):
"""
Returns subregion calendar classes for given region iso_code.
>>> registry = IsoRegistry()
>>> # assuming calendars registered are: DE, DE-HH, DE-BE
>>> registry.get_subregions('DE')
{'DE-HH': <class 'workalendar.europe.germany.Hamburg'>,
'DE-BE': <class 'workalendar.europe.germany.Berlin'>}
:rtype dict
:return dict where keys are ISO codes strings
and values are calendar classes
"""
items = dict()
for key, value in self.region_registry.items():
if key.startswith("{}-".format(iso_code)):
items[key] = value
return items
def get_calendars(self, region_codes=None, include_subregions=False):
"""
Returns calendar classes for regions
:param region_codes list of ISO codes for selected regions. If empty,
the function will return all items from the
registry.
:param include_subregions boolean if subregions
of selected regions should be included in result
:rtype dict
:return dict where keys are ISO codes strings
and values are calendar classes
"""
if not region_codes:
# Here it contains all subregions
if include_subregions:
return self.region_registry.copy()
items = {k: v for k, v in self.region_registry.items()
if '-' not in k}
return items
items = dict()
for code in region_codes:
try:
items[code] = self.region_registry[code]
except KeyError:
continue
if include_subregions:
items.update(self.get_subregions(code))
return items
registry = IsoRegistry()