python-holidays icon indicating copy to clipboard operation
python-holidays copied to clipboard

Automate New Country Setup Process

Open ankushhKapoor opened this issue 8 months ago • 6 comments

Currently, adding a new country requires manually creating a country file, updating the README, and modifying __init__.py in countries. To streamline this process, we could introduce a make command that:

  • Accepts inputs like country name and country codes
  • Generates a new country file and a corresponding test file with the required structure
  • Updates the README to increment the country count
  • Adds the necessary statement in __init__.py of countries

Automating this setup would standardize the process, reduce manual effort, and minimize errors.

ankushhKapoor avatar Apr 01 '25 18:04 ankushhKapoor

I don't think it will save much time, but if something can be done automatically, why not.

KJhellico avatar Apr 01 '25 19:04 KJhellico

I don't think it will save much time, but if something can be done automatically, why not.

Time? Nah. Effort and laziness? Absolutely! Haha.

Jokes aside, I get your point. The project already has some solid automations for localization, code testing, etc., so it kind of gives off the impression that there should be a command for adding new countries too. Manually doing it is definitely a good learning experience for first-timers, but if someone contributes frequently and adds multiple countries, it starts feeling like a repetitive task. So why not make life a little easier?

ankushhKapoor avatar Apr 01 '25 19:04 ankushhKapoor

List of files to edit/create - I think I got all of them(?):

I think what's need to be input is probably:

  • COUNTRY_NAME_NORMAL: i.e. "United States" -> to be automatically made into COUNTRY_NAME_SNAKECASE ("united_states") and COUNTRY_NAME_PASCALCASE ("UnitedStates") later.
  • COUNTRY_TWO_LETTER i.e. "US"
  • COUNTRY_THREE_LETTER i.e. "USA"
  • OBSERVANCE whether to use HolidayBase or ObservedHolidayBase
  • START_YEAR i.e. 1991

README.md: add information stub, increment country count by 1 (Subdivisions, Supported Language, Supported Categories can probably be manually added later IMO)

<td>[COUNTRY_NAME_NORMAL]</td>
<td>[COUNTRY_TWO_LETTER]</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>

holidays\countries\__init__.py: Add Stub

from .[COUNTRY_NAME_SNAKECASE] import [COUNTRY_NAME_PASCALCASE], [COUNTRY_TWO_LETTER], [COUNTRY_THREE_LETTER]

holidays\countries\[COUNTRY_NAME_SNAKECASE].py: the example below is if OBSERVANCE is True:

[Header from `docs\file_header.txt`]

from holidays.groups import InternationalHolidays
from holidays.observed_holiday_base import ObservedHolidayBase

class [COUNTRY_NAME_PASCALCASE](ObservedHolidayBase, InternationalHolidays):
   """[COUNTRY_NAME_NORMAL] holidays.

   References:
       * <TODO REPLACE LINK TO SOURCE>
   """

   country = "[COUNTRY_TWO_LETTER]"
   start_year = [START_YEAR]

   def __init__(self, *args, **kwargs):
       InternationalHolidays.__init__(self)
       super().__init__(*args, **kwargs)

   def _populate_public_holidays(self):
       # Put your holidays entry here and delete this line


class [COUNTRY_TWO_LETTER]([COUNTRY_NAME_PASCALCASE]):
    pass


class [COUNTRY_THREE_LETTER]([COUNTRY_NAME_PASCALCASE]):
    pass

holidays\registry.py: Add Stub

    "[COUNTRY_NAME_SNAKECASE]": ("[COUNTRY_NAME_PASCALCASE]", "[COUNTRY_TWO_LETTER]", "[COUNTRY_THREE_LETTER]"),

tests\countries\test_[COUNTRY_NAME_SNAKECASE].py: the example below is if OBSERVANCE is True:

[Header from `docs\file_header.txt`]

from unittest import TestCase

from holidays.countries import [COUNTRY_NAME_PASCALCASE], [COUNTRY_TWO_LETTER], [COUNTRY_THREE_LETTER]
from tests.common import CommonCountryTests


class Test[COUNTRY_NAME_PASCALCASE](CommonCountryTests, TestCase):
    @classmethod
    def setUpClass(cls):
        years = range([START_YEAR], 2050)
        super().setUpClass([COUNTRY_NAME_PASCALCASE], years=years, years_non_observed=years)

    def test_country_aliases(self):
        self.assertAliases([COUNTRY_NAME_PASCALCASE], [COUNTRY_TWO_LETTER], [COUNTRY_THREE_LETTER])

    def test_no_holidays(self):
        self.assertNoHolidays([COUNTRY_NAME_PASCALCASE](years=[START_YEAR-1]))

PPsyrius avatar Apr 02 '25 04:04 PPsyrius

List of files to edit/create - I think I got all of them(?):

Thanks for the detailed breakdown! This covers everything—really appreciate it!

You basically did most of the work already, haha. Now it's just up to the script to handle the heavy lifting!

I'll get started on this and share updates soon. Let me know if you have any extra suggestions!

ankushhKapoor avatar Apr 02 '25 05:04 ankushhKapoor

Any progress on this @ankushhKapoor ?

arkid15r avatar May 20 '25 18:05 arkid15r

Any progress on this @ankushhKapoor ?

Hi @arkid15r , I’ve made some initial progress on this with the help of the code shared by @PPsyrius — I have a draft version locally. I wasn’t able to continue as I was focusing on the Nepal PR, and I’ll be back in my town in about 8–10 days and will work on finalizing this and open a PR then.

Since I have college holidays, I should be able to work on this alongside the l10n enhancement. Do you recommend opening both PRs together at the same time or separately? I’m happy to follow whichever approach works best for the project.

ankushhKapoor avatar May 20 '25 19:05 ankushhKapoor

I believe we can close this issue as we have not that much unimplemented yet entities

arkid15r avatar Jun 28 '25 18:06 arkid15r