# holidays # -------- # A fast, efficient Python library for generating country, province and state # specific sets of holidays on the fly. It aims to make determining whether a # specific date is a holiday as fast and flexible as possible. # # Authors: Vacanza Team and individual contributors (see CONTRIBUTORS file) # dr-prodigy (c) 2017-2023 # ryanss (c) 2014-2017 # Website: https://github.com/vacanza/holidays # License: MIT (see LICENSE file) import importlib from collections.abc import Iterable from threading import RLock from typing import Any, Optional, Union from holidays.holiday_base import HolidayBase RegistryDict = dict[str, tuple[str, ...]] COUNTRIES: RegistryDict = { "afghanistan": ("Afghanistan", "AF", "AFG"), "aland_islands": ("AlandIslands", "AX", "ALA", "HolidaysAX"), "albania": ("Albania", "AL", "ALB"), "algeria": ("Algeria", "DZ", "DZA"), "american_samoa": ("AmericanSamoa", "AS", "ASM", "HolidaysAS"), "andorra": ("Andorra", "AD", "AND"), "angola": ("Angola", "AO", "AGO"), "anguilla": ("Anguilla", "AI", "AIA"), "antigua_and_barbuda": ("AntiguaAndBarbuda", "AG", "ATG"), "argentina": ("Argentina", "AR", "ARG"), "armenia": ("Armenia", "AM", "ARM"), "aruba": ("Aruba", "AW", "ABW"), "australia": ("Australia", "AU", "AUS"), "austria": ("Austria", "AT", "AUT"), "azerbaijan": ("Azerbaijan", "AZ", "AZE"), "bahamas": ("Bahamas", "BS", "BHS"), "bahrain": ("Bahrain", "BH", "BAH"), "bangladesh": ("Bangladesh", "BD", "BGD"), "barbados": ("Barbados", "BB", "BRB"), "belarus": ("Belarus", "BY", "BLR"), "belgium": ("Belgium", "BE", "BEL"), "belize": ("Belize", "BZ", "BLZ"), "benin": ("Benin", "BJ", "BEN"), "bermuda": ("Bermuda", "BM", "BMU"), "bolivia": ("Bolivia", "BO", "BOL"), "bonaire_sint_eustatius_and_saba": ("BonaireSintEustatiusAndSaba", "BQ", "BES"), "bosnia_and_herzegovina": ("BosniaAndHerzegovina", "BA", "BIH"), "botswana": ("Botswana", "BW", "BWA"), "brazil": ("Brazil", "BR", "BRA"), "british_virgin_islands": ("BritishVirginIslands", "VG", "VGB"), "brunei": ("Brunei", "BN", "BRN"), "bulgaria": ("Bulgaria", "BG", "BLG"), "burkina_faso": ("BurkinaFaso", "BF", "BFA"), "burundi": ("Burundi", "BI", "BDI"), "cabo_verde": ("CaboVerde", "CV", "CPV"), "cambodia": ("Cambodia", "KH", "KHM"), "cameroon": ("Cameroon", "CM", "CMR"), "canada": ("Canada", "CA", "CAN"), "cayman_islands": ("CaymanIslands", "KY", "CYM"), "central_african_republic": ("CentralAfricanRepublic", "CF", "CAF"), "chad": ("Chad", "TD", "TCD"), "chile": ("Chile", "CL", "CHL"), "china": ("China", "CN", "CHN"), "christmas_island": ("ChristmasIsland", "CX", "CXR"), "cocos_islands": ("CocosIslands", "CC", "CCK"), "colombia": ("Colombia", "CO", "COL"), "congo": ("Congo", "CG", "COG"), "cook_islands": ("CookIslands", "CK", "COK"), "costa_rica": ("CostaRica", "CR", "CRI"), "croatia": ("Croatia", "HR", "HRV"), "cuba": ("Cuba", "CU", "CUB"), "curacao": ("Curacao", "CW", "CUW"), "cyprus": ("Cyprus", "CY", "CYP"), "czechia": ("Czechia", "CZ", "CZE"), "denmark": ("Denmark", "DK", "DNK"), "djibouti": ("Djibouti", "DJ", "DJI"), "dominica": ("Dominica", "DM", "DMA"), "dominican_republic": ("DominicanRepublic", "DO", "DOM"), "dr_congo": ("DRCongo", "CD", "COD"), "ecuador": ("Ecuador", "EC", "ECU"), "egypt": ("Egypt", "EG", "EGY"), "jordan": ("Jordan", "JO", "JOR"), "el_salvador": ("ElSalvador", "SV", "SLV"), "equatorial_guinea": ("EquatorialGuinea", "GQ", "GNQ"), "estonia": ("Estonia", "EE", "EST"), "eswatini": ("Eswatini", "SZ", "SZW", "Swaziland"), "ethiopia": ("Ethiopia", "ET", "ETH"), "falkland_islands": ("FalklandIslands", "FK", "FLK"), "faroe_islands": ("FaroeIslands", "FO", "FRO"), "fiji": ("Fiji", "FJ", "FJI"), "finland": ("Finland", "FI", "FIN"), "france": ("France", "FR", "FRA"), "french_guiana": ("FrenchGuiana", "GF", "GUF", "HolidaysGF"), "french_polynesia": ("FrenchPolynesia", "PF", "PYF", "HolidaysPF"), "french_southern_territories": ("FrenchSouthernTerritories", "TF", "ATF", "HolidaysTF"), "gabon": ("Gabon", "GA", "GAB"), "georgia": ("Georgia", "GE", "GEO"), "germany": ("Germany", "DE", "DEU"), "ghana": ("Ghana", "GH", "GHA"), "gibraltar": ("Gibraltar", "GI", "GIB"), "greece": ("Greece", "GR", "GRC"), "greenland": ("Greenland", "GL", "GRL"), "grenada": ("Grenada", "GD", "GRD"), "guadeloupe": ("Guadeloupe", "GP", "GLP", "HolidaysGP"), "guam": ("Guam", "GU", "GUM", "HolidaysGU"), "guatemala": ("Guatemala", "GT", "GUA"), "guernsey": ("Guernsey", "GG", "GGY"), "guinea": ("Guinea", "GN", "GIN"), "guyana": ("Guyana", "GY", "GUY"), "haiti": ("Haiti", "HT", "HTI"), "honduras": ("Honduras", "HN", "HND"), "hongkong": ("HongKong", "HK", "HKG"), "hungary": ("Hungary", "HU", "HUN"), "iceland": ("Iceland", "IS", "ISL"), "india": ("India", "IN", "IND"), "indonesia": ("Indonesia", "ID", "IDN"), "iran": ("Iran", "IR", "IRN"), "ireland": ("Ireland", "IE", "IRL"), "isle_of_man": ("IsleOfMan", "IM", "IMN"), "israel": ("Israel", "IL", "ISR"), "italy": ("Italy", "IT", "ITA"), "ivory_coast": ("IvoryCoast", "CI", "CIV"), "jamaica": ("Jamaica", "JM", "JAM"), "japan": ("Japan", "JP", "JPN"), "jersey": ("Jersey", "JE", "JEY"), "kazakhstan": ("Kazakhstan", "KZ", "KAZ"), "kenya": ("Kenya", "KE", "KEN"), "kuwait": ("Kuwait", "KW", "KWT"), "kyrgyzstan": ("Kyrgyzstan", "KG", "KGZ"), "laos": ("Laos", "LA", "LAO"), "latvia": ("Latvia", "LV", "LVA"), "lebanon": ("Lebanon", "LB", "LBN"), "lesotho": ("Lesotho", "LS", "LSO"), "libya": ("Libya", "LY", "LBY"), "liechtenstein": ("Liechtenstein", "LI", "LIE"), "lithuania": ("Lithuania", "LT", "LTU"), "luxembourg": ("Luxembourg", "LU", "LUX"), "macau": ("Macau", "MO", "MAC"), "madagascar": ("Madagascar", "MG", "MDG"), "malawi": ("Malawi", "MW", "MWI"), "malaysia": ("Malaysia", "MY", "MYS"), "maldives": ("Maldives", "MV", "MDV"), "mali": ("Mali", "ML", "MLI"), "malta": ("Malta", "MT", "MLT"), "marshall_islands": ("MarshallIslands", "MH", "MHL", "HolidaysMH"), "martinique": ("Martinique", "MQ", "MTQ", "HolidaysMQ"), "mauritania": ("Mauritania", "MR", "MRT"), "mauritius": ("Mauritius", "MU", "MUS"), "mayotte": ("Mayotte", "YT", "MYT", "HolidaysYT"), "mexico": ("Mexico", "MX", "MEX"), "micronesia": ("Micronesia", "FM", "FSM"), "moldova": ("Moldova", "MD", "MDA"), "monaco": ("Monaco", "MC", "MCO"), "mongolia": ("Mongolia", "MN", "MNG"), "montenegro": ("Montenegro", "ME", "MNE"), "montserrat": ("Montserrat", "MS", "MSR"), "morocco": ("Morocco", "MA", "MOR"), "mozambique": ("Mozambique", "MZ", "MOZ"), "namibia": ("Namibia", "NA", "NAM"), "nauru": ("Nauru", "NR", "NRU"), "nepal": ("Nepal", "NP", "NPL"), "netherlands": ("Netherlands", "NL", "NLD"), "new_caledonia": ("NewCaledonia", "NC", "NCL", "HolidaysNC"), "new_zealand": ("NewZealand", "NZ", "NZL"), "nicaragua": ("Nicaragua", "NI", "NIC"), "niger": ("Niger", "NE", "NER"), "nigeria": ("Nigeria", "NG", "NGA"), "niue": ("Niue", "NU", "NIU"), "norfolk_island": ("NorfolkIsland", "NF", "NFK"), "north_macedonia": ("NorthMacedonia", "MK", "MKD"), "northern_mariana_islands": ("NorthernMarianaIslands", "MP", "MNP", "HolidaysMP"), "norway": ("Norway", "NO", "NOR"), "oman": ("Oman", "OM", "OMN"), "pakistan": ("Pakistan", "PK", "PAK"), "palau": ("Palau", "PW", "PLW"), "palestine": ("Palestine", "PS", "PSE"), "panama": ("Panama", "PA", "PAN"), "papua_new_guinea": ("PapuaNewGuinea", "PG", "PNG"), "paraguay": ("Paraguay", "PY", "PRY"), "peru": ("Peru", "PE", "PER"), "philippines": ("Philippines", "PH", "PHL"), "poland": ("Poland", "PL", "POL"), "portugal": ("Portugal", "PT", "PRT"), "puerto_rico": ("PuertoRico", "PR", "PRI", "HolidaysPR"), "qatar": ("Qatar", "QA", "QAT"), "reunion": ("Reunion", "RE", "REU", "HolidaysRE"), "romania": ("Romania", "RO", "ROU"), "russia": ("Russia", "RU", "RUS"), "saint_barthelemy": ("SaintBarthelemy", "BL", "BLM", "HolidaysBL"), "saint_kitts_and_nevis": ("SaintKittsAndNevis", "KN", "KNA"), "saint_lucia": ("SaintLucia", "LC", "LCA"), "saint_martin": ("SaintMartin", "MF", "MAF", "HolidaysMF"), "saint_pierre_and_miquelon": ("SaintPierreAndMiquelon", "PM", "SPM", "HolidaysPM"), "saint_vincent_and_the_grenadines": ("SaintVincentAndTheGrenadines", "VC", "VCT"), "samoa": ("Samoa", "WS", "WSM"), "san_marino": ("SanMarino", "SM", "SMR"), "sao_tome_and_principe": ("SaoTomeAndPrincipe", "ST", "STP"), "saudi_arabia": ("SaudiArabia", "SA", "SAU"), "senegal": ("Senegal", "SN", "SEN"), "serbia": ("Serbia", "RS", "SRB"), "seychelles": ("Seychelles", "SC", "SYC"), "sierra_leone": ("SierraLeone", "SL", "SLE"), "singapore": ("Singapore", "SG", "SGP"), "sint_maarten": ("SintMaarten", "SX", "SXM"), "slovakia": ("Slovakia", "SK", "SVK"), "slovenia": ("Slovenia", "SI", "SVN"), "solomon_islands": ("SolomonIslands", "SB", "SLB"), "south_africa": ("SouthAfrica", "ZA", "ZAF"), "south_korea": ("SouthKorea", "KR", "KOR", "Korea"), "spain": ("Spain", "ES", "ESP"), "sri_lanka": ("SriLanka", "LK", "LKA"), "suriname": ("Suriname", "SR", "SUR"), "svalbard_and_jan_mayen": ("SvalbardAndJanMayen", "SJ", "SJM", "HolidaysSJ"), "sweden": ("Sweden", "SE", "SWE"), "switzerland": ("Switzerland", "CH", "CHE"), "taiwan": ("Taiwan", "TW", "TWN"), "tanzania": ("Tanzania", "TZ", "TZA"), "thailand": ("Thailand", "TH", "THA"), "timor_leste": ("TimorLeste", "TL", "TLS"), "togo": ("Togo", "TG", "TGO"), "tokelau": ("Tokelau", "TK", "TKL"), "tonga": ("Tonga", "TO", "TON"), "trinidad_and_tobago": ("TrinidadAndTobago", "TT", "TTO"), "tunisia": ("Tunisia", "TN", "TUN"), "turkey": ("Turkey", "TR", "TUR"), "turks_and_caicos_islands": ("TurksAndCaicosIslands", "TC", "TCA"), "tuvalu": ("Tuvalu", "TV", "TUV"), "ukraine": ("Ukraine", "UA", "UKR"), "united_arab_emirates": ("UnitedArabEmirates", "AE", "ARE"), "united_kingdom": ("UnitedKingdom", "GB", "GBR", "UK"), "united_states_minor_outlying_islands": ( "UnitedStatesMinorOutlyingIslands", "UM", "UMI", "HolidaysUM", ), "united_states_virgin_islands": ("UnitedStatesVirginIslands", "VI", "VIR", "HolidaysVI"), "united_states": ("UnitedStates", "US", "USA"), "uruguay": ("Uruguay", "UY", "URY"), "uzbekistan": ("Uzbekistan", "UZ", "UZB"), "vanuatu": ("Vanuatu", "VU", "VTU"), "vatican_city": ("VaticanCity", "VA", "VAT"), "venezuela": ("Venezuela", "VE", "VEN"), "vietnam": ("Vietnam", "VN", "VNM"), "wallis_and_futuna": ("WallisAndFutuna", "WF", "WLF", "HolidaysWF"), "yemen": ("Yemen", "YE", "YEM"), "zambia": ("Zambia", "ZM", "ZMB"), "zimbabwe": ("Zimbabwe", "ZW", "ZWE"), } FINANCIAL: RegistryDict = { "european_central_bank": ("EuropeanCentralBank", "XECB", "ECB", "TAR"), "ice_futures_europe": ("ICEFuturesEurope", "IFEU"), "ny_stock_exchange": ("NewYorkStockExchange", "XNYS", "NYSE"), "brasil_bolsa_balcao": ("BrasilBolsaBalcao", "BVMF", "B3"), } # A re-entrant lock. Once a thread has acquired a re-entrant lock, # the same thread may acquire it again without blocking. # https://docs.python.org/3/library/threading.html#rlock-objects IMPORT_LOCK = RLock() class EntityLoader: """Country and financial holidays entities lazy loader.""" __slots__ = ("entity", "entity_name", "module_name") def __init__(self, path: str, *args, **kwargs) -> None: """Set up a lazy loader.""" if args: raise TypeError( "This is a holidays entity loader class. " "For entity inheritance purposes please import a class you " "want to derive from directly: e.g., " "`from holidays.countries import Entity` or " "`from holidays.financial import Entity`." ) entity_path = path.split(".") self.entity = None self.entity_name = entity_path[-1] self.module_name = ".".join(entity_path[0:-1]) super().__init__(*args, **kwargs) def __call__(self, *args, **kwargs) -> HolidayBase: """Create a new instance of a lazy-loaded entity.""" cls = self.get_entity() return cls(*args, **kwargs) # type: ignore[misc, operator] def __getattr__(self, name: str) -> Optional[Any]: """Return attribute of a lazy-loaded entity.""" cls = self.get_entity() return getattr(cls, name) def __str__(self) -> str: """Return lazy loader object string representation.""" return ( f"A lazy loader for {self.get_entity()}. For inheritance please " f"use the '{self.module_name}.{self.entity_name}' class directly." ) def get_entity(self) -> Optional[HolidayBase]: """Return lazy-loaded entity.""" if self.entity is None: # Avoid deadlock due to importlib.import_module not being thread-safe by caching all # the first imports in a dedicated thread. with IMPORT_LOCK: self.entity = getattr(importlib.import_module(self.module_name), self.entity_name) return self.entity @staticmethod def _get_entity_codes( container: RegistryDict, entity_length: Union[int, Iterable[int]], include_aliases: bool = True, ) -> Iterable[str]: entity_length = {entity_length} if isinstance(entity_length, int) else set(entity_length) for entities in container.values(): for entity in entities: if len(entity) in entity_length: yield entity # Assuming that the alpha-2 code goes first. if not include_aliases: break @staticmethod def get_country_codes(include_aliases: bool = True) -> Iterable[str]: """Get supported country codes. :param include_aliases: Whether to include entity aliases (e.g. UK for GB). """ return EntityLoader._get_entity_codes(COUNTRIES, 2, include_aliases) @staticmethod def get_financial_codes(include_aliases: bool = True) -> Iterable[str]: """Get supported financial codes. :param include_aliases: Whether to include entity aliases(e.g. TAR for ECB, XNYS for NYSE). """ return EntityLoader._get_entity_codes(FINANCIAL, (3, 4), include_aliases) @staticmethod def load(prefix: str, scope: dict) -> None: """Load country or financial entities.""" entity_mapping = COUNTRIES if prefix == "countries" else FINANCIAL for module, entities in entity_mapping.items(): scope.update( { entity: EntityLoader(f"holidays.{prefix}.{module}.{entity}") for entity in entities } )