Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified Makefile
100644 → 100755
Empty file.
72 changes: 30 additions & 42 deletions holidays/countries/peru.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,81 +17,69 @@


class Peru(HolidayBase, ChristianHolidays, InternationalHolidays):
"""Peru holidays.

"""
References:
* <https://web.archive.org/web/20250414165243/https://www.gob.pe/feriados/>
* <https://es.wikipedia.org/wiki/Anexo:Días_feriados_en_el_Perú>
* [Ley N° 31788](https://web.archive.org/web/20250716164223/https://img.lpderecho.pe/wp-content/uploads/2023/06/Ley-31788-LPDerecho.pdf)
* [Ley N° 31822](https://web.archive.org/web/20250716164455/https://img.lpderecho.pe/wp-content/uploads/2023/07/Ley-31822-LPDerecho.pdf)
Copy link
Copy Markdown
Collaborator

@PPsyrius PPsyrius Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we suddenly replacing all relevant references with a single Wikipedia one and removing a supported language?

Not to mention the issue number referenced here is likely a placeholder one

- https://en.wikipedia.org/wiki/Public_holidays_in_Peru
"""

country = "PE"
default_language = "es"
supported_languages = ("en_US", "es", "uk")
# Supported languages
supported_languages = ("en_US", "es")
Comment on lines +27 to +28
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove the stale Ukrainian Peru locale file or keep uk in supported languages.

Line 28 drops uk, but holidays/locale/uk/LC_MESSAGES/PE.po remains as dead data. Please clean it up in this PR to avoid maintenance confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holidays/countries/peru.py` around lines 27 - 28, Update consistency between
the peru.py supported_languages tuple and the Ukrainian locale data: either
restore "uk" in the supported_languages tuple in peru.py (the
supported_languages variable) if the Ukrainian translation file should remain,
or delete the stale Ukrainian Peru PO file (the Ukrainian PE.po locale file) if
you intend to drop Ukrainian support; make the change so supported_languages and
the presence of the Ukrainian PE.po are aligned.


def __init__(self, *args, **kwargs) -> None:
def __init__(self, *args, **kwargs):
ChristianHolidays.__init__(self)
InternationalHolidays.__init__(self)
super().__init__(*args, **kwargs)

def _populate_public_holidays(self):
# New Year's Day.
# New Year's Day
self._add_new_years_day(tr("Año Nuevo"))

# Maundy Thursday.
# Maundy Thursday
self._add_holy_thursday(tr("Jueves Santo"))

# Good Friday.
# Good Friday
self._add_good_friday(tr("Viernes Santo"))

# Easter Sunday.
self._add_easter_sunday(tr("Domingo de Resurrección"))

# Labor Day.
self._add_labor_day(tr("Día del Trabajo"))
# Labor Day
self._add_labor_day(tr("Día del Trabajador"))
Comment on lines +39 to +46

# Added via Ley N° 31788 on June 15th, 2023.
if self._year >= 2024:
# Battle of Arica and Flag Day.
self._add_holiday_jun_7(tr("Batalla de Arica y Día de la Bandera"))
# Battle of Arica and Flag Day
self._add_holiday_jun_7(tr("Día de la Batalla de Arica y Día de la Bandera"))

# Saint Peter and Saint Paul's Day.
self._add_saints_peter_and_paul_day(tr("San Pedro y San Pablo"))
# Saint Peter and Saint Paul
self._add_saints_peter_and_paul_day(tr("Día de San Pedro y San Pablo"))
Comment on lines +45 to +52

# Added via Ley N° 31822 on July 8th, 2023.
if self._year >= 2023:
# Peruvian Air Force Day.
self._add_holiday_jul_23(tr("Día de la Fuerza Aérea del Perú"))
# Peruvian Air Force Day
self._add_holiday_jul_23(tr("Día de la Fuerza Aérea del Perú"))
Comment on lines +48 to +55

# Independence Day.
# Independence Day
self._add_holiday_jul_28(tr("Día de la Independencia"))

# Great Military Parade Day.
self._add_holiday_jul_29(tr("Día de la Gran Parada Militar"))
# Independence Day Holiday
self._add_holiday_jul_29(tr("Fiestas Patrias"))

if self._year >= 2022:
# Battle of Junín.
self._add_holiday_aug_6(tr("Batalla de Junín"))
# Battle of Junín
self._add_holiday_aug_6(tr("Batalla de Junín"))

# Rose of Lima Day.
# Santa Rosa de Lima
self._add_holiday_aug_30(tr("Santa Rosa de Lima"))

# Battle of Angamos.
# Battle of Angamos
self._add_holiday_oct_8(tr("Combate de Angamos"))

# All Saints' Day.
self._add_all_saints_day(tr("Todos Los Santos"))
# All Saints' Day
self._add_all_saints_day(tr("Día de Todos los Santos"))

# Immaculate Conception.
# Immaculate Conception
self._add_immaculate_conception_day(tr("Inmaculada Concepción"))

if self._year >= 2022:
# Battle of Ayacucho.
self._add_holiday_dec_9(tr("Batalla de Ayacucho"))
# Battle of Ayacucho
self._add_holiday_dec_9(tr("Batalla de Ayacucho"))

# Christmas Day.
self._add_christmas_day(tr("Navidad del Señor"))
# Christmas Day
self._add_christmas_day(tr("Navidad"))

Comment on lines +46 to 83
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Peru tr() keys are present in PE locale catalogs.
python - <<'PY'
import re
from pathlib import Path

py = Path("holidays/countries/peru.py").read_text(encoding="utf-8")
keys = re.findall(r'tr\("([^"]+)"\)', py)

for locale in ("es", "en_US"):
    po_path = Path(f"holidays/locale/{locale}/LC_MESSAGES/PE.po")
    po = po_path.read_text(encoding="utf-8")
    missing = [k for k in keys if f'msgid "{k}"' not in po]
    print(f"{locale}: missing {len(missing)} keys")
    for k in missing:
        print(f"  - {k}")
PY

Repository: vacanza/holidays

Length of output: 434


🏁 Script executed:

git diff HEAD~1 holidays/countries/peru.py | head -100

Repository: vacanza/holidays

Length of output: 246


🏁 Script executed:

# Also check what tr() strings were previously in peru.py
git show HEAD~1:holidays/countries/peru.py | grep -A 2 'self._add_labor_day\|self._add_holiday_jun_7\|self._add_saints_peter_and_paul_day\|self._add_holiday_jul_23\|self._add_holiday_jul_28\|self._add_holiday_jul_29\|self._add_holiday_aug_6\|self._add_holiday_aug_30\|self._add_holiday_oct_8\|self._add_all_saints_day\|self._add_immaculate_conception_day\|self._add_holiday_dec_9\|self._add_christmas_day' 2>/dev/null || echo "Cannot access HEAD~1"

Repository: vacanza/holidays

Length of output: 134


🏁 Script executed:

# Alternative: check which strings DO exist in the catalogs to understand the current state
python3 << 'PYEOF'
import re
from pathlib import Path

# Get all msgids from PE.po files
for locale in ("es", "en_US"):
    po_path = Path(f"holidays/locale/{locale}/LC_MESSAGES/PE.po")
    if po_path.exists():
        content = po_path.read_text(encoding="utf-8")
        msgids = re.findall(r'msgid "([^"]+)"', content)
        print(f"\n{locale} locale has {len(msgids)} entries. Sample msgids:")
        # Filter for Spanish-language entries (those that look like holiday names)
        spanish_like = [m for m in msgids if any(c in m for c in 'áéíóúñäöüÁÉÍÓÚÑ') or 'Día' in m or 'de ' in m]
        for msg in spanish_like[:15]:
            print(f"  - {msg}")
PYEOF

Repository: vacanza/holidays

Length of output: 826


🏁 Script executed:

ls -la holidays/locale/uk/LC_MESSAGES/PE.po 2>/dev/null && echo "File exists" || echo "File does not exist"

Repository: vacanza/holidays

Length of output: 157


🏁 Script executed:

# Also verify the current supported_languages in peru.py
grep -n "supported_languages" holidays/countries/peru.py

Repository: vacanza/holidays

Length of output: 104


Localization mismatch: code tr() keys don't align with PE catalogs, and uk locale needs cleanup.

The tr() strings at lines 46, 49, 52, 61, 73, 76, 82, and others don't match existing msgid entries in holidays/locale/es/LC_MESSAGES/PE.po and holidays/locale/en_US/LC_MESSAGES/PE.po. This includes renamed entries ("Día del Trabajo" → "Día del Trabajador", "Navidad del Señor" → "Navidad") and new additions ("Día de San Pedro y San Pablo", "Fiestas Patrias", "Día de Todos los Santos") that break translations.

Update the source code strings to match catalog msgids, or regenerate both PE.po files by running make l10n after finalizing the tr() keys.

Also, remove holidays/locale/uk/LC_MESSAGES/PE.po since Ukrainian support was removed from supported_languages at line 28.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@holidays/countries/peru.py` around lines 46 - 83, The tr() strings in the
Peru holiday registration calls (e.g., _add_labor_day,
_add_saints_peter_and_paul_day, _add_holiday_jul_28, _add_holiday_jul_29,
_add_all_saints_day, _add_christmas_day, etc.) do not match the existing msgid
keys in the PE.po catalogs; update those tr("...") source strings to exactly
match the msgid entries in holidays/locale/*/LC_MESSAGES/PE.po (or finalize the
desired source strings and then run make l10n to regenerate both PE.po files) so
translations remain consistent, and remove the orphaned Ukrainian catalog file
(holidays/locale/uk/LC_MESSAGES/PE.po) since ukrainian was removed from
supported_languages to avoid stale locale files.


class PE(Peru):
Expand Down
137 changes: 27 additions & 110 deletions tests/countries/test_peru.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,120 +10,37 @@
# Website: https://github.com/vacanza/holidays
# License: MIT (see LICENSE file)

from unittest import TestCase
import unittest

from holidays.countries.peru import Peru
from holidays.countries.peru import Peru, PE, PER
from tests.common import CommonCountryTests


class TestPeru(CommonCountryTests, TestCase):
class TestPeru(CommonCountryTests, unittest.TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass(Peru, years=range(1950, 2050))

def test_2019(self):
self.assertHolidayDatesInYear(
2019,
"2019-01-01",
"2019-04-18",
"2019-04-19",
"2019-04-21",
"2019-05-01",
"2019-06-29",
"2019-07-28",
"2019-07-29",
"2019-08-30",
"2019-10-08",
"2019-11-01",
"2019-12-08",
"2019-12-25",
)

def test_2022(self):
self.assertHolidayDatesInYear(
2022,
"2022-01-01",
"2022-04-14",
"2022-04-15",
"2022-04-17",
"2022-05-01",
"2022-06-29",
"2022-07-28",
"2022-07-29",
"2022-08-06",
"2022-08-30",
"2022-10-08",
"2022-11-01",
"2022-12-08",
"2022-12-09",
"2022-12-25",
)

def test_battle_of_arica_and_flag_day(self):
name = "Batalla de Arica y Día de la Bandera"
self.assertHolidayName(name, (f"{year}-06-07" for year in range(2024, 2050)))
self.assertNoHolidayName(name, range(1950, 2024))

def test_peruvian_air_force_day(self):
name = "Día de la Fuerza Aérea del Perú"
self.assertHolidayName(name, (f"{year}-07-23" for year in range(2023, 2050)))
self.assertNoHolidayName(name, range(1950, 2023))

def test_l10n_default(self):
self.assertLocalizedHolidays(
("2022-01-01", "Año Nuevo"),
("2022-04-14", "Jueves Santo"),
("2022-04-15", "Viernes Santo"),
("2022-04-17", "Domingo de Resurrección"),
("2022-05-01", "Día del Trabajo"),
("2022-06-29", "San Pedro y San Pablo"),
("2022-07-28", "Día de la Independencia"),
("2022-07-29", "Día de la Gran Parada Militar"),
("2022-08-06", "Batalla de Junín"),
("2022-08-30", "Santa Rosa de Lima"),
("2022-10-08", "Combate de Angamos"),
("2022-11-01", "Todos Los Santos"),
("2022-12-08", "Inmaculada Concepción"),
("2022-12-09", "Batalla de Ayacucho"),
("2022-12-25", "Navidad del Señor"),
)

def test_l10n_en_us(self):
self.assertLocalizedHolidays(
"en_US",
("2022-01-01", "New Year's Day"),
("2022-04-14", "Maundy Thursday"),
("2022-04-15", "Good Friday"),
("2022-04-17", "Easter Sunday"),
("2022-05-01", "Labor Day"),
("2022-06-29", "Saint Peter and Saint Paul's Day"),
("2022-07-28", "Independence Day"),
("2022-07-29", "Great Military Parade Day"),
("2022-08-06", "Battle of Junín Day"),
("2022-08-30", "Rose of Lima Day"),
("2022-10-08", "Battle of Angamos Day"),
("2022-11-01", "All Saints' Day"),
("2022-12-08", "Immaculate Conception"),
("2022-12-09", "Battle of Ayacucho Day"),
("2022-12-25", "Christmas Day"),
)

def test_l10n_uk(self):
self.assertLocalizedHolidays(
"uk",
("2022-01-01", "Новий рік"),
("2022-04-14", "Великий четвер"),
("2022-04-15", "Страсна пʼятниця"),
("2022-04-17", "Великдень"),
("2022-05-01", "День праці"),
("2022-06-29", "День Святих Петра і Павла"),
("2022-07-28", "День незалежності"),
("2022-07-29", "День Великого військового параду"),
("2022-08-06", "День битви під Хуніном"),
("2022-08-30", "День Святої Рози Лімської"),
("2022-10-08", "День битви під Ангамосом"),
("2022-11-01", "День усіх святих"),
("2022-12-08", "Непорочне зачаття Діви Марії"),
("2022-12-09", "День битви при Аякучо"),
("2022-12-25", "Різдво Христове"),
super().setUpClass(Peru)

def test_country_aliases(self):
self.assertAliases(Peru, PE, PER)

Comment on lines +15 to +26
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Alias checks are duplicated; rely on centralized alias validation.

The explicit alias imports and test_country_aliases are redundant now that alias checks are handled centrally by tests/common.py::check_aliases. Keep this file focused on holiday behavior tests.

♻️ Proposed simplification
-import unittest
-
-from holidays.countries.peru import Peru, PE, PER
+import unittest
+
+from holidays.countries.peru import Peru
 from tests.common import CommonCountryTests
@@
-    def test_country_aliases(self):
-        self.assertAliases(Peru, PE, PER)
-
     def test_2026(self):

Based on learnings: In PR #2881 onwards, alias testing is centralized via check_aliases in tests/common.py, so individual country tests no longer need alias imports or test_country_aliases.

🧰 Tools
🪛 Ruff (0.15.5)

[warning] 21-21: Missing return type annotation for classmethod setUpClass

Add return type annotation: None

(ANN206)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/countries/test_peru.py` around lines 15 - 26, The test file duplicates
centralized alias validation: remove the explicit alias imports (PE, PER) and
the redundant test method test_country_aliases from the TestPeru class so the
suite uses the centralized check_aliases in tests/common.py; update TestPeru to
only inherit CommonCountryTests and focus on holiday behavior (keep Peru
reference used in setUpClass), and ensure no other references to PE or PER
remain in this file.

def test_2026(self):
self.assertHolidays(
Peru(years=2026),
("2026-01-01", "Año Nuevo"),
("2026-04-02", "Jueves Santo"),
("2026-04-03", "Viernes Santo"),
("2026-05-01", "Día del Trabajador"),
("2026-06-07", "Día de la Batalla de Arica y Día de la Bandera"),
("2026-06-29", "Día de San Pedro y San Pablo"),
("2026-07-23", "Día de la Fuerza Aérea del Perú"),
("2026-07-28", "Día de la Independencia"),
("2026-07-29", "Fiestas Patrias"),
("2026-08-06", "Batalla de Junín"),
("2026-08-30", "Santa Rosa de Lima"),
("2026-10-08", "Combate de Angamos"),
("2026-11-01", "Día de Todos los Santos"),
("2026-12-08", "Inmaculada Concepción"),
("2026-12-09", "Batalla de Ayacucho"),
("2026-12-25", "Navidad"),
)
Comment on lines +27 to 46
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add en_US assertions to catch localization key regressions.

This test only validates Spanish/default output for 2026. Since holiday source strings were renamed in holidays/countries/peru.py, add a small en_US check so translation-key drift is caught by tests.

🧪 Proposed test addition
     def test_2026(self):
         self.assertHolidays(
             Peru(years=2026),
@@
             ("2026-12-25", "Navidad"),
         )
+
+    def test_2026_en_us(self):
+        holidays = Peru(years=2026, language="en_US")
+        self.assertEqual(holidays["2026-05-01"], "Labor Day")
+        self.assertEqual(holidays["2026-11-01"], "All Saints' Day")
+        self.assertEqual(holidays["2026-12-25"], "Christmas Day")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_2026(self):
self.assertHolidays(
Peru(years=2026),
("2026-01-01", "Año Nuevo"),
("2026-04-02", "Jueves Santo"),
("2026-04-03", "Viernes Santo"),
("2026-05-01", "Día del Trabajador"),
("2026-06-07", "Día de la Batalla de Arica y Día de la Bandera"),
("2026-06-29", "Día de San Pedro y San Pablo"),
("2026-07-23", "Día de la Fuerza Aérea del Perú"),
("2026-07-28", "Día de la Independencia"),
("2026-07-29", "Fiestas Patrias"),
("2026-08-06", "Batalla de Junín"),
("2026-08-30", "Santa Rosa de Lima"),
("2026-10-08", "Combate de Angamos"),
("2026-11-01", "Día de Todos los Santos"),
("2026-12-08", "Inmaculada Concepción"),
("2026-12-09", "Batalla de Ayacucho"),
("2026-12-25", "Navidad"),
)
def test_2026(self):
self.assertHolidays(
Peru(years=2026),
("2026-01-01", "Año Nuevo"),
("2026-04-02", "Jueves Santo"),
("2026-04-03", "Viernes Santo"),
("2026-05-01", "Día del Trabajador"),
("2026-06-07", "Día de la Batalla de Arica y Día de la Bandera"),
("2026-06-29", "Día de San Pedro y San Pablo"),
("2026-07-23", "Día de la Fuerza Aérea del Perú"),
("2026-07-28", "Día de la Independencia"),
("2026-07-29", "Fiestas Patrias"),
("2026-08-06", "Batalla de Junín"),
("2026-08-30", "Santa Rosa de Lima"),
("2026-10-08", "Combate de Angamos"),
("2026-11-01", "Día de Todos los Santos"),
("2026-12-08", "Inmaculada Concepción"),
("2026-12-09", "Batalla de Ayacucho"),
("2026-12-25", "Navidad"),
)
def test_2026_en_us(self):
holidays = Peru(years=2026, language="en_US")
self.assertEqual(holidays["2026-05-01"], "Labor Day")
self.assertEqual(holidays["2026-11-01"], "All Saints' Day")
self.assertEqual(holidays["2026-12-25"], "Christmas Day")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/countries/test_peru.py` around lines 27 - 46, The test test_2026 in
tests/countries/test_peru.py only asserts Spanish/default holiday names for
Peru(years=2026), so add parallel assertions that exercise the en_US
localization to detect translation-key regressions: call Peru(years=2026,
locale="en_US") (or otherwise set locale to "en_US") and assert the expected
English strings for the same dates using the existing assertHolidays helper;
reference the test method name test_2026 and the Peru class from
holidays/countries/peru.py when adding these en_US checks.

Loading