Repository: pandas-dev/pandas
Pull request #57014 (2024-01-22 19:44:42)

Fix docstring
- [x] xref #27976 (Replace xxxx with the GitHub issue number)
- [ ] [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
- [ ] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [ ] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [ ] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.



Patch:
diff --git a/ci/code_checks.sh b/ci/code_checks.sh
index e7509760bf..ee98960453 100755
--- a/ci/code_checks.sh
+++ b/ci/code_checks.sh
@@ -113,7 +113,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.Series.dt.ceil\
         pandas.Series.dt.month_name\
         pandas.Series.dt.day_name\
-        pandas.Series.str.count\
         pandas.Series.str.wrap\
         pandas.Series.cat.rename_categories\
         pandas.Series.cat.reorder_categories\
@@ -126,7 +125,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.Series.plot.line\
         pandas.Series.plot.pie\
         pandas.DataFrame.clip\
-        pandas.DataFrame.interpolate\
         pandas.DataFrame.plot\
         pandas.DataFrame.plot.bar\
         pandas.DataFrame.plot.barh\
@@ -185,7 +183,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.Interval\
         pandas.Grouper\
         pandas.core.groupby.SeriesGroupBy.apply\
-        pandas.core.groupby.DataFrameGroupBy.apply\
         pandas.core.groupby.SeriesGroupBy.transform\
         pandas.core.groupby.DataFrameGroupBy.transform\
         pandas.core.groupby.DataFrameGroupBy.nth\
@@ -201,7 +198,6 @@ if [[ -z "$CHECK" || "$CHECK" == "docstrings" ]]; then
         pandas.HDFStore.append\
         pandas.core.window.rolling.Rolling.quantile\
         pandas.core.window.expanding.Expanding.quantile\
-        pandas.core.resample.Resampler.interpolate\
         pandas.api.extensions.ExtensionArray.argsort # There should be no backslash in the final line, please keep this comment in the last ignored function
     RET=$(($RET + $?)) ; echo $MSG "DONE"
 
diff --git a/doc/source/whatsnew/v2.2.1.rst b/doc/source/whatsnew/v2.2.1.rst
index 22a6c0cd10..75445c978d 100644
--- a/doc/source/whatsnew/v2.2.1.rst
+++ b/doc/source/whatsnew/v2.2.1.rst
@@ -13,7 +13,7 @@ including other versions of pandas.
 
 Fixed regressions
 ~~~~~~~~~~~~~~~~~
--
+- Fixed regression in :func:`merge_ordered` raising ``TypeError`` for ``fill_method="ffill"`` and ``how="left"`` (:issue:`57010`)
 
 .. ---------------------------------------------------------------------------
 .. _whatsnew_221.bug_fixes:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index 2fe1f4a3bc..0ebdcf1947 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -8296,7 +8296,7 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
 
             .. deprecated:: 2.1.0
 
-        ``**kwargs`` : optional
+        **kwargs : optional
             Keyword arguments to pass on to the interpolating function.
 
         Returns
diff --git a/pandas/core/groupby/groupby.py b/pandas/core/groupby/groupby.py
index d86062e35a..b2afaffc26 100644
--- a/pandas/core/groupby/groupby.py
+++ b/pandas/core/groupby/groupby.py
@@ -192,6 +192,10 @@ _apply_docs = {
         A callable that takes a {input} as its first argument, and
         returns a dataframe, a series or a scalar. In addition the
         callable may take positional and keyword arguments.
+
+    *args : tuple
+        Optional positional arguments to pass to ``func``.
+
     include_groups : bool, default True
         When True, will attempt to apply ``func`` to the groupings in
         the case that they are columns of the DataFrame. If this raises a
@@ -205,8 +209,8 @@ _apply_docs = {
            Setting include_groups to True is deprecated. Only the value
            False will be allowed in a future version of pandas.
 
-    args, kwargs : tuple and dict
-        Optional positional and keyword arguments to pass to ``func``.
+    **kwargs : dict
+        Optional keyword arguments to pass to ``func``.
 
     Returns
     -------
diff --git a/pandas/core/resample.py b/pandas/core/resample.py
index 2bc1d3f722..082196abc1 100644
--- a/pandas/core/resample.py
+++ b/pandas/core/resample.py
@@ -1028,7 +1028,7 @@ class Resampler(BaseGroupBy, PandasObject):
 
             .. deprecated:: 2.1.0
 
-        ``**kwargs`` : optional
+        **kwargs : optional
             Keyword arguments to pass on to the interpolating function.
 
         Returns
diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py
index 51e2751aef..22304bbdd1 100644
--- a/pandas/core/reshape/merge.py
+++ b/pandas/core/reshape/merge.py
@@ -1930,10 +1930,9 @@ class _OrderedMerge(_MergeOperation):
 
         if self.fill_method == "ffill":
             if left_indexer is None:
-                raise TypeError("left_indexer cannot be None")
-            left_indexer = cast("npt.NDArray[np.intp]", left_indexer)
-            right_indexer = cast("npt.NDArray[np.intp]", right_indexer)
-            left_join_indexer = libjoin.ffill_indexer(left_indexer)
+                left_join_indexer = None
+            else:
+                left_join_indexer = libjoin.ffill_indexer(left_indexer)
             if right_indexer is None:
                 right_join_indexer = None
             else:
diff --git a/pandas/core/strings/accessor.py b/pandas/core/strings/accessor.py
index 6c271ef102..f0ad5b662d 100644
--- a/pandas/core/strings/accessor.py
+++ b/pandas/core/strings/accessor.py
@@ -2351,8 +2351,6 @@ class StringMethods(NoNewAttributesMixin):
         flags : int, default 0, meaning no flags
             Flags for the `re` module. For a complete list, `see here
             <https://docs.python.org/3/howto/regex.html#compilation-flags>`_.
-        **kwargs
-            For compatibility with other string methods. Not used.
 
         Returns
         -------


Test patch:
diff --git a/ci/run_tests.sh b/ci/run_tests.sh
index 48ef21686a..39ab0890a3 100755
--- a/ci/run_tests.sh
+++ b/ci/run_tests.sh
@@ -10,7 +10,7 @@ echo PYTHONHASHSEED=$PYTHONHASHSEED
 
 COVERAGE="-s --cov=pandas --cov-report=xml --cov-append --cov-config=pyproject.toml"
 
-PYTEST_CMD="MESONPY_EDITABLE_VERBOSE=1 PYTHONDEVMODE=1 PYTHONWARNDEFAULTENCODING=1 pytest -r fEs -n $PYTEST_WORKERS --dist=loadfile $TEST_ARGS $COVERAGE $PYTEST_TARGET"
+PYTEST_CMD="MESONPY_EDITABLE_VERBOSE=1 PYTHONDEVMODE=1 PYTHONWARNDEFAULTENCODING=1 pytest -r fE -n $PYTEST_WORKERS --dist=loadfile $TEST_ARGS $COVERAGE $PYTEST_TARGET"
 
 if [[ "$PATTERN" ]]; then
   PYTEST_CMD="$PYTEST_CMD -m \"$PATTERN\""
diff --git a/pandas/tests/reshape/merge/test_merge_ordered.py b/pandas/tests/reshape/merge/test_merge_ordered.py
index db71a732b4..7deecf6bbf 100644
--- a/pandas/tests/reshape/merge/test_merge_ordered.py
+++ b/pandas/tests/reshape/merge/test_merge_ordered.py
@@ -216,3 +216,26 @@ class TestMergeOrdered:
             ValueError, match=re.escape("fill_method must be 'ffill' or None")
         ):
             merge_ordered(left, right, on="key", fill_method=invalid_method)
+
+    def test_ffill_left_merge(self):
+        # GH 57010
+        df1 = DataFrame(
+            {
+                "key": ["a", "c", "e", "a", "c", "e"],
+                "lvalue": [1, 2, 3, 1, 2, 3],
+                "group": ["a", "a", "a", "b", "b", "b"],
+            }
+        )
+        df2 = DataFrame({"key": ["b", "c", "d"], "rvalue": [1, 2, 3]})
+        result = merge_ordered(
+            df1, df2, fill_method="ffill", left_by="group", how="left"
+        )
+        expected = DataFrame(
+            {
+                "key": ["a", "c", "e", "a", "c", "e"],
+                "lvalue": [1, 2, 3, 1, 2, 3],
+                "group": ["a", "a", "a", "b", "b", "b"],
+                "rvalue": [np.nan, 2.0, 2.0, np.nan, 2.0, 2.0],
+            }
+        )
+        tm.assert_frame_equal(result, expected)