Repository: pandas-dev/pandas
Pull request #61658 (2025-06-14 12:50:04)

DOC/ENH: Holiday days_of_week value error
Changed `Holiday` constructor argument `days_of_week` to raise a `ValueError` on input of the incorrect type as discussed in #61600
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.


sharkipelago: I also edited the docstring a little bit as I was a bit confused what days_of_week was supposed to do from the docstring alone. 

Happy to delete the docstring edits if they don't make sense though.

Patch:
diff --git a/doc/source/development/community.rst b/doc/source/development/community.rst
index ab8294b8f1..1c698d130e 100644
--- a/doc/source/development/community.rst
+++ b/doc/source/development/community.rst
@@ -77,7 +77,7 @@ Any community member can open issues to:
 - Ask questions, e.g. "I noticed the behavior of a certain function
   changed between versions. Is this expected?".
 
-    Ideally, your questions should be related to how pandas works rather
+  - Ideally, your questions should be related to how pandas works rather
     than how you use pandas. `StackOverflow <https://stackoverflow.com/>`_ is
     better suited for answering usage questions, and we ask that all usage
     questions are first asked on StackOverflow. Thank you for respecting our
diff --git a/doc/source/development/contributing.rst b/doc/source/development/contributing.rst
index 4d99f282aa..66178a88e3 100644
--- a/doc/source/development/contributing.rst
+++ b/doc/source/development/contributing.rst
@@ -234,14 +234,14 @@ and merged into project to appear the in the next release. To submit a pull requ
 #. Write a descriptive title that includes prefixes. pandas uses a convention for title
    prefixes. Here are some common ones along with general guidelines for when to use them:
 
-    * ENH: Enhancement, new functionality
-    * BUG: Bug fix
-    * DOC: Additions/updates to documentation
-    * TST: Additions/updates to tests
-    * BLD: Updates to the build process/scripts
-    * PERF: Performance improvement
-    * TYP: Type annotations
-    * CLN: Code cleanup
+   * ENH: Enhancement, new functionality
+   * BUG: Bug fix
+   * DOC: Additions/updates to documentation
+   * TST: Additions/updates to tests
+   * BLD: Updates to the build process/scripts
+   * PERF: Performance improvement
+   * TYP: Type annotations
+   * CLN: Code cleanup
 
 #. Write a description of your changes in the ``Preview Discussion`` tab
 #. Click ``Send Pull Request``.
diff --git a/doc/source/development/contributing_codebase.rst b/doc/source/development/contributing_codebase.rst
index b8d568428c..73bc756de9 100644
--- a/doc/source/development/contributing_codebase.rst
+++ b/doc/source/development/contributing_codebase.rst
@@ -444,11 +444,11 @@ be located.
                result = ser.loc[[3, 4]]
                tm.assert_series_equal(result, expected)
 
-    In cases like this, the test location should be based on the *underlying*
-    method being tested.  Or in the case of a test for a bugfix, the location
-    of the actual bug.  So in this example, we know that ``Series.__getitem__``
-    calls ``Series.loc.__getitem__``, so this is *really* a test for
-    ``loc.__getitem__``.  So this test belongs in ``tests.indexing.test_loc``.
+      In cases like this, the test location should be based on the *underlying*
+      method being tested.  Or in the case of a test for a bugfix, the location
+      of the actual bug.  So in this example, we know that ``Series.__getitem__``
+      calls ``Series.loc.__getitem__``, so this is *really* a test for
+      ``loc.__getitem__``.  So this test belongs in ``tests.indexing.test_loc``.
 
 6. Is your test for a DataFrame or Series method?
 
diff --git a/doc/source/development/contributing_gitpod.rst b/doc/source/development/contributing_gitpod.rst
index b70981b4d3..447b7b20a8 100644
--- a/doc/source/development/contributing_gitpod.rst
+++ b/doc/source/development/contributing_gitpod.rst
@@ -158,8 +158,8 @@ Option 1: using Liveserve
    file and click on **Open with Live Serve**. Alternatively, you can open the
    file in the editor and click on the **Go live** button on the status bar.
 
-    .. image:: ./gitpod-imgs/vscode-statusbar.png
-        :alt: Gitpod workspace VSCode start live serve screenshot
+   .. image:: ./gitpod-imgs/vscode-statusbar.png
+      :alt: Gitpod workspace VSCode start live serve screenshot
 
 #. A simple browser will open to the right-hand side of the editor. We recommend
    closing it and click on the **Open in browser** button in the pop-up.
@@ -182,13 +182,13 @@ uses the rst extension with docutils.
    :kbd:`Ctrl-Shift-P` in Linux and Windows. Start typing "restructured"
    and choose either "Open preview" or "Open preview to the Side".
 
-    .. image:: ./gitpod-imgs/vscode-rst.png
-        :alt: Gitpod workspace VSCode open rst screenshot
+   .. image:: ./gitpod-imgs/vscode-rst.png
+      :alt: Gitpod workspace VSCode open rst screenshot
 
 #. As you work on the document, you will see a live rendering of it on the editor.
 
-    .. image:: ./gitpod-imgs/rst-rendering.png
-        :alt: Gitpod workspace VSCode rst rendering screenshot
+   .. image:: ./gitpod-imgs/rst-rendering.png
+      :alt: Gitpod workspace VSCode rst rendering screenshot
 
 If you want to see the final output with the ``html`` theme you will need to
 rebuild the docs with ``make html`` and use Live Serve as described in option 1.
diff --git a/doc/source/development/debugging_extensions.rst b/doc/source/development/debugging_extensions.rst
index 3b955e7bbb..c8127e0cc2 100644
--- a/doc/source/development/debugging_extensions.rst
+++ b/doc/source/development/debugging_extensions.rst
@@ -10,9 +10,9 @@ pandas uses Cython and C/C++ `extension modules <https://docs.python.org/3/exten
 
 For Python developers with limited or no C/C++ experience this can seem a daunting task. Core developer Will Ayd has written a 3 part blog series to help guide you from the standard Python debugger into these other tools:
 
-  1. `Fundamental Python Debugging Part 1 - Python <https://willayd.com/fundamental-python-debugging-part-1-python.html>`_
-  2. `Fundamental Python Debugging Part 2 - Python Extensions <https://willayd.com/fundamental-python-debugging-part-2-python-extensions.html>`_
-  3. `Fundamental Python Debugging Part 3 - Cython Extensions <https://willayd.com/fundamental-python-debugging-part-3-cython-extensions.html>`_
+1. `Fundamental Python Debugging Part 1 - Python <https://willayd.com/fundamental-python-debugging-part-1-python.html>`_
+2. `Fundamental Python Debugging Part 2 - Python Extensions <https://willayd.com/fundamental-python-debugging-part-2-python-extensions.html>`_
+3. `Fundamental Python Debugging Part 3 - Cython Extensions <https://willayd.com/fundamental-python-debugging-part-3-cython-extensions.html>`_
 
 Debugging locally
 -----------------
diff --git a/doc/source/development/developer.rst b/doc/source/development/developer.rst
index c5c4b7c449..21a840fbe9 100644
--- a/doc/source/development/developer.rst
+++ b/doc/source/development/developer.rst
@@ -116,19 +116,19 @@ The ``metadata`` field is ``None`` except for:
   omitted it is assumed to be nanoseconds.
 * ``categorical``: ``{'num_categories': K, 'ordered': is_ordered, 'type': $TYPE}``
 
-    * Here ``'type'`` is optional, and can be a nested pandas type specification
-      here (but not categorical)
+  * Here ``'type'`` is optional, and can be a nested pandas type specification
+    here (but not categorical)
 
 * ``unicode``: ``{'encoding': encoding}``
 
-    * The encoding is optional, and if not present is UTF-8
+  * The encoding is optional, and if not present is UTF-8
 
 * ``object``: ``{'encoding': encoding}``. Objects can be serialized and stored
   in ``BYTE_ARRAY`` Parquet columns. The encoding can be one of:
 
-    * ``'pickle'``
-    * ``'bson'``
-    * ``'json'``
+  * ``'pickle'``
+  * ``'bson'``
+  * ``'json'``
 
 * ``timedelta``: ``{'unit': 'ns'}``. The ``'unit'`` is optional, and if omitted
   it is assumed to be nanoseconds. This metadata is optional altogether
diff --git a/doc/source/development/maintaining.rst b/doc/source/development/maintaining.rst
index c572559dcc..c37925f7e2 100644
--- a/doc/source/development/maintaining.rst
+++ b/doc/source/development/maintaining.rst
@@ -218,11 +218,11 @@ pandas supports point releases (e.g. ``1.4.3``) that aim to:
 
 1. Fix bugs in new features introduced in the first minor version release.
 
-  * e.g. If a new feature was added in ``1.4`` and contains a bug, a fix can be applied in ``1.4.3``
+   * e.g. If a new feature was added in ``1.4`` and contains a bug, a fix can be applied in ``1.4.3``
 
 2. Fix bugs that used to work in a few minor releases prior. There should be agreement between core team members that a backport is appropriate.
 
-  * e.g. If a feature worked in ``1.2`` and stopped working since ``1.3``, a fix can be applied in ``1.4.3``.
+   * e.g. If a feature worked in ``1.2`` and stopped working since ``1.3``, a fix can be applied in ``1.4.3``.
 
 Since pandas minor releases are based on GitHub branches (e.g. point release of ``1.4`` are based off the ``1.4.x`` branch),
 "backporting" means merging a pull request fix to the ``main`` branch and correct minor branch associated with the next point release.
@@ -289,8 +289,8 @@ The required steps for adding a maintainer are:
 1. Contact the contributor and ask their interest to join.
 2. Add the contributor to the appropriate `GitHub Team <https://github.com/orgs/pandas-dev/teams>`_ if accepted the invitation.
 
-  * ``pandas-core`` is for core team members
-  * ``pandas-triage`` is for pandas triage members
+   * ``pandas-core`` is for core team members
+   * ``pandas-triage`` is for pandas triage members
 
 If adding to ``pandas-core``, there are two additional steps:
 
@@ -467,10 +467,10 @@ Post-Release
    patch releases. The exact instructions are (replace the example version numbers by
    the appropriate ones for the version you are releasing):
 
-    - Log in to the server and use the correct user.
-    - ``cd /var/www/html/pandas-docs/``
-    - ``ln -sfn version/2.1 stable`` (for a major or minor release)
-    - ``ln -sfn version/2.0.3 version/2.0`` (for a patch release)
+   - Log in to the server and use the correct user.
+   - ``cd /var/www/html/pandas-docs/``
+   - ``ln -sfn version/2.1 stable`` (for a major or minor release)
+   - ``ln -sfn version/2.0.3 version/2.0`` (for a patch release)
 
 2. If releasing a major or minor release, open a PR in our source code to update
    ``web/pandas/versions.json``, to have the desired versions in the documentation
@@ -487,8 +487,8 @@ Post-Release
 6. Announce the new release in the official channels (use previous announcements
    for reference):
 
-    - The pandas-dev and pydata mailing lists
-    - X, Mastodon, Telegram and LinkedIn
+   - The pandas-dev and pydata mailing lists
+   - X, Mastodon, Telegram and LinkedIn
 
 7. Update this release instructions to fix anything incorrect and to update about any
    change since the last release.
diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst
index 6de0319873..8d3ac0e396 100644
--- a/doc/source/whatsnew/v3.0.0.rst
+++ b/doc/source/whatsnew/v3.0.0.rst
@@ -65,6 +65,7 @@ Other enhancements
 - :class:`ArrowDtype` now supports ``pyarrow.JsonType`` (:issue:`60958`)
 - :class:`DataFrameGroupBy` and :class:`SeriesGroupBy` methods ``sum``, ``mean``, ``median``, ``prod``, ``min``, ``max``, ``std``, ``var`` and ``sem`` now accept ``skipna`` parameter (:issue:`15675`)
 - :class:`Easter` has gained a new constructor argument ``method`` which specifies the method used to calculate Easter — for example, Orthodox Easter (:issue:`61665`)
+- :class:`Holiday` constructor argument ``days_of_week`` will raise a ``ValueError`` when type is something other than ``None`` or ``tuple`` (:issue:`61658`)
 - :class:`Holiday` has gained the constructor argument and field ``exclude_dates`` to exclude specific datetimes from a custom holiday calendar (:issue:`54382`)
 - :class:`Rolling` and :class:`Expanding` now support ``nunique`` (:issue:`26958`)
 - :class:`Rolling` and :class:`Expanding` now support aggregations ``first`` and ``last`` (:issue:`33155`)
@@ -887,6 +888,7 @@ Other
 - Bug in :func:`eval` with ``engine="numexpr"`` returning unexpected result for float division. (:issue:`59736`)
 - Bug in :func:`to_numeric` raising ``TypeError`` when ``arg`` is a :class:`Timedelta` or :class:`Timestamp` scalar. (:issue:`59944`)
 - Bug in :func:`unique` on :class:`Index` not always returning :class:`Index` (:issue:`57043`)
+- Bug in :meth:`DataFrame.apply` raising ``RecursionError`` when passing ``func=list[int]``. (:issue:`61565`)
 - Bug in :meth:`DataFrame.apply` where passing ``engine="numba"`` ignored ``args`` passed to the applied function (:issue:`58712`)
 - Bug in :meth:`DataFrame.eval` and :meth:`DataFrame.query` which caused an exception when using NumPy attributes via ``@`` notation, e.g., ``df.eval("@np.floor(a)")``. (:issue:`58041`)
 - Bug in :meth:`DataFrame.eval` and :meth:`DataFrame.query` which did not allow to use ``tan`` function. (:issue:`55091`)
diff --git a/pandas/_libs/lib.pyx b/pandas/_libs/lib.pyx
index cded299f77..3b7d659c21 100644
--- a/pandas/_libs/lib.pyx
+++ b/pandas/_libs/lib.pyx
@@ -2,6 +2,7 @@ from collections import abc
 from decimal import Decimal
 from enum import Enum
 from sys import getsizeof
+from types import GenericAlias
 from typing import (
     Literal,
     _GenericAlias,
@@ -1298,7 +1299,7 @@ cdef bint c_is_list_like(object obj, bint allow_sets) except -1:
         getattr(obj, "__iter__", None) is not None and not isinstance(obj, type)
         # we do not count strings/unicode/bytes as list-like
         # exclude Generic types that have __iter__
-        and not isinstance(obj, (str, bytes, _GenericAlias))
+        and not isinstance(obj, (str, bytes, _GenericAlias, GenericAlias))
         # exclude zero-dimensional duck-arrays, effectively scalars
         and not (hasattr(obj, "ndim") and obj.ndim == 0)
         # exclude sets if allow_sets is False
diff --git a/pandas/tseries/holiday.py b/pandas/tseries/holiday.py
index 8ad2541d2e..e61fd6594e 100644
--- a/pandas/tseries/holiday.py
+++ b/pandas/tseries/holiday.py
@@ -192,8 +192,9 @@ class Holiday:
         end_date : datetime-like, default None
             Last date the holiday is observed
         days_of_week : tuple of int or dateutil.relativedelta weekday strs, default None
-            Provide a tuple of days e.g  (0,1,2,3,) for Monday Through Thursday
+            Provide a tuple of days e.g  (0,1,2,3,) for Monday through Thursday
             Monday=0,..,Sunday=6
+            Only instances of the holiday included in days_of_week will be computed
         exclude_dates : DatetimeIndex or default None
             Specific dates to exclude e.g. skipping a specific year's holiday
 
@@ -258,7 +259,8 @@ class Holiday:
         )
         self.end_date = Timestamp(end_date) if end_date is not None else end_date
         self.observance = observance
-        assert days_of_week is None or type(days_of_week) == tuple
+        if not (days_of_week is None or isinstance(days_of_week, tuple)):
+            raise ValueError("days_of_week must be None or tuple.")
         self.days_of_week = days_of_week
         if not (exclude_dates is None or isinstance(exclude_dates, DatetimeIndex)):
             raise ValueError("exclude_dates must be None or of type DatetimeIndex.")


Test patch:
diff --git a/pandas/tests/dtypes/test_inference.py b/pandas/tests/dtypes/test_inference.py
index db98751324..7fd0395009 100644
--- a/pandas/tests/dtypes/test_inference.py
+++ b/pandas/tests/dtypes/test_inference.py
@@ -250,6 +250,15 @@ def test_is_list_like_generic():
     assert inference.is_list_like(tst)
 
 
+def test_is_list_like_native_container_types():
+    # GH 61565
+    # is_list_like was yielding false positives for native container types
+    assert not inference.is_list_like(list[int])
+    assert not inference.is_list_like(list[str])
+    assert not inference.is_list_like(tuple[int])
+    assert not inference.is_list_like(tuple[str])
+
+
 def test_is_sequence():
     is_seq = inference.is_sequence
     assert is_seq((1, 2))
diff --git a/pandas/tests/tseries/holiday/test_holiday.py b/pandas/tests/tseries/holiday/test_holiday.py
index 37e029f31b..054fc07e41 100644
--- a/pandas/tests/tseries/holiday/test_holiday.py
+++ b/pandas/tests/tseries/holiday/test_holiday.py
@@ -454,3 +454,10 @@ def test_exclude_date_value_error():
             Timestamp("2026-06-10"),
         ]
         Holiday("National Ice Tea Day", month=6, day=10, exclude_dates=exclude)
+
+
+def test_days_of_week_value_error():
+    msg = "days_of_week must be None or tuple."
+
+    with pytest.raises(ValueError, match=msg):
+        Holiday("World Blood Donor Day", month=6, day=14, days_of_week=[0, 1])