Skip to content
Merged
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
63 changes: 48 additions & 15 deletions holidays/holiday_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1172,26 +1172,40 @@ def pop(self, key: DateLike, default: Union[str, Any] = None) -> Union[str, Any]

return dict.pop(self, self.__keytransform__(key), default)

def pop_named(self, name: str) -> list[date]:
def pop_named(self, holiday_name: str, lookup: str = "icontains") -> list[date]:
"""Remove all holidays matching the given name.

This method removes all dates associated with a holiday name, so they are
no longer considered holidays. The match is case-insensitive, and partial
matches are also removed.
no longer considered holidays. The search by default is case-insensitive and
includes partial matches.

Args:
name:
holiday_name:
The holiday's name to try to match.

lookup:
The holiday name lookup type:

* contains - case sensitive contains match;
* exact - case sensitive exact match;
* startswith - case sensitive starts with match;
* icontains - case insensitive contains match;
* iexact - case insensitive exact match;
* istartswith - case insensitive starts with match;

Returns:
A list of dates removed.

Raises:
KeyError: if date is not a holiday.
"""
use_exact_name = HOLIDAY_NAME_DELIMITER in name
if not (dts := self.get_named(name, split_multiple_names=not use_exact_name)):
raise KeyError(name)
use_exact_name = HOLIDAY_NAME_DELIMITER in holiday_name
if not (
dts := self.get_named(
holiday_name, lookup=lookup, split_multiple_names=not use_exact_name
)
):
raise KeyError(holiday_name)

popped = []
for dt in dts:
Expand All @@ -1200,16 +1214,35 @@ def pop_named(self, name: str) -> list[date]:
popped.append(dt)

# Keep the rest of holidays falling on the same date.
if not use_exact_name:
name_lower = name.lower()
if use_exact_name:
continue
if lookup == "icontains":
holiday_name_lower = holiday_name.lower()
holiday_names = [
holiday_name
for holiday_name in holiday_names
if name_lower not in holiday_name.lower()
name for name in holiday_names if holiday_name_lower not in name.lower()
]

if holiday_names:
self[dt] = HOLIDAY_NAME_DELIMITER.join(holiday_names)
elif lookup == "iexact":
holiday_name_lower = holiday_name.lower()
holiday_names = [
name for name in holiday_names if holiday_name_lower != name.lower()
]
elif lookup == "istartswith":
holiday_name_lower = holiday_name.lower()
holiday_names = [
name
for name in holiday_names
if holiday_name_lower != name[: len(holiday_name)].lower()
]
elif lookup == "contains":
holiday_names = [name for name in holiday_names if holiday_name not in name]
elif lookup == "exact":
holiday_names = [name for name in holiday_names if holiday_name != name]
else: # startswith
holiday_names = [
name for name in holiday_names if holiday_name != name[: len(holiday_name)]
]
if holiday_names:
self[dt] = HOLIDAY_NAME_DELIMITER.join(holiday_names)

return popped

Expand Down
76 changes: 76 additions & 0 deletions tests/test_holiday_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,82 @@ def test_partial(self):
self.assertNotIn(dt, self.hb)
self.assertRaises(KeyError, lambda: self.hb.pop_named("New Year"))

def test_contains(self):
self.assertIn("2022-01-01", self.hb)
removed_dates = self.hb.pop_named("Day", lookup="contains")
self.assertEqual(len(removed_dates), 6)
self.assertNotIn("2022-01-01", self.hb)
self.assertNotIn("2022-06-19", self.hb)
self.assertNotIn("2022-06-20", self.hb)
self.assertNotIn("2022-07-04", self.hb)
self.assertNotIn("2022-12-25", self.hb)
self.assertNotIn("2022-12-26", self.hb)

def test_icontains(self):
self.assertIn("2022-01-01", self.hb)
removed_dates = self.hb.pop_named("day", lookup="icontains")
self.assertEqual(len(removed_dates), 6)
self.assertNotIn("2022-01-01", self.hb)
self.assertNotIn("2022-06-19", self.hb)
self.assertNotIn("2022-06-20", self.hb)
self.assertNotIn("2022-07-04", self.hb)
self.assertNotIn("2022-12-25", self.hb)
self.assertNotIn("2022-12-26", self.hb)

def test_exact(self):
self.assertIn("2022-01-01", self.hb)
removed_dates = self.hb.pop_named("Independence Day", lookup="exact")
self.assertEqual(len(removed_dates), 1)
self.assertNotIn("2022-07-04", self.hb)
self.assertIn("2022-06-19", self.hb)
self.assertIn("2022-06-20", self.hb)

hb = CountryStub1(years=2025)
hb["2024-02-02"] = "Big Groundhog Day; Groundhog Day"
removed_dates = hb.pop_named("Groundhog Day", lookup="exact")
self.assertEqual(len(removed_dates), 1)
self.assertEqual(hb["2024-02-02"], "Big Groundhog Day")

def test_iexact(self):
self.assertIn("2022-01-01", self.hb)
removed_dates = self.hb.pop_named("independence day", lookup="iexact")
self.assertEqual(len(removed_dates), 1)
self.assertNotIn("2022-07-04", self.hb)
self.assertIn("2022-06-19", self.hb)
self.assertIn("2022-06-20", self.hb)

hb = CountryStub1(years=2025)
hb["2024-02-02"] = "Big Groundhog Day; Groundhog Day"
removed_dates = hb.pop_named("Groundhog day", lookup="iexact")
self.assertEqual(len(removed_dates), 1)
self.assertEqual(hb["2024-02-02"], "Big Groundhog Day")

def test_startswith(self):
self.assertIn("2022-01-01", self.hb)
removed_dates = self.hb.pop_named("Independence", lookup="startswith")
self.assertEqual(len(removed_dates), 1)
self.assertNotIn("2022-07-04", self.hb)
self.assertIn("2022-06-19", self.hb)
self.assertIn("2022-06-20", self.hb)

removed_dates = self.hb.pop_named("Christmas", lookup="startswith")
self.assertEqual(len(removed_dates), 2)
self.assertNotIn("2022-12-25", self.hb)
self.assertNotIn("2022-12-26", self.hb)

def test_istartswith(self):
self.assertIn("2022-01-01", self.hb)
removed_dates = self.hb.pop_named("independence", lookup="istartswith")
self.assertEqual(len(removed_dates), 1)
self.assertNotIn("2022-07-04", self.hb)
self.assertIn("2022-06-19", self.hb)
self.assertIn("2022-06-20", self.hb)

removed_dates = self.hb.pop_named("christmas", lookup="istartswith")
self.assertEqual(len(removed_dates), 2)
self.assertNotIn("2022-12-25", self.hb)
self.assertNotIn("2022-12-26", self.hb)


class TestRepr(unittest.TestCase):
def test_base(self):
Expand Down