internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: dask/dask

Issue:
apply_gufunc input validation fails to raise exception on extra args
**What happened**:
There is a missing raise in [apply_gufunc](https://github.com/dask/dask/blob/1a6a85258bccb14fe37bb54c211beeeffd46cd55/dask/array/gufunc.py#L343)
**What you expected to happen**:
ValueError to be raised.
This is later somehow fixed in a way I don't understand in transpose_args

**Minimal Complete Verifiable Example**:

```python
    def add(x, y):
        return x + y

    a = da.from_array(np.array([1, 2, 3]), chunks=2, name="a")
    b = da.from_array(np.array([1, 2, 3]), chunks=2, name="b") 
    # this should raise an error
    z = apply_gufunc(add, "(),()->()", a, b, 0, output_dtypes=a.dtype)
```

**Anything else we need to know?**:
This was found using [lgtm](https://lgtm.com/projects/g/dask/dask/alerts/?mode=tree&ruleFocus=1505923886371)
PR and added test on the way.

**Environment**:

- Dask version: 2021.05.1
- Python version:  3.8.10
- Operating System: linux
- Install method (conda, pip, source): conda



Discussion:

Resolving patch:
diff --git a/dask/array/gufunc.py b/dask/array/gufunc.py
--- a/dask/array/gufunc.py
+++ b/dask/array/gufunc.py
@@ -340,7 +340,7 @@ def apply_gufunc(func, signature, *args, **kwargs):
     args = [asarray(a) for a in args]
 
     if len(input_coredimss) != len(args):
-        ValueError(
+        raise ValueError(
             "According to `signature`, `func` requires %d arguments, but %s given"
             % (len(input_coredimss), len(args))
         )


Test patch:
diff --git a/dask/array/tests/test_gufunc.py b/dask/array/tests/test_gufunc.py
old mode 100755
new mode 100644
--- a/dask/array/tests/test_gufunc.py
+++ b/dask/array/tests/test_gufunc.py
@@ -56,6 +56,16 @@ def foo(x):
         apply_gufunc(foo, "(i)->()", a, axes=[0, 0])
 
 
+def test_apply_gufunc_axes_args_validation():
+    def add(x, y):
+        return x + y
+
+    a = da.from_array(np.array([1, 2, 3]), chunks=2, name="a")
+    b = da.from_array(np.array([1, 2, 3]), chunks=2, name="b")
+    with pytest.raises(ValueError):
+        apply_gufunc(add, "(),()->()", a, b, 0, output_dtypes=a.dtype)
+
+
 def test__validate_normalize_axes_01():
     with pytest.raises(ValueError):
         _validate_normalize_axes([(1, 0)], None, False, [("i", "j")], ("j",))
Repository: dask/dask

Issue:
Add Series.__getitem__ to support slice / scalar 
currently `NotImplementedError`. Based on #1109, it should return `Series` always even if indexer is scalar.



Discussion:

Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -3273,10 +3273,7 @@ def __getitem__(self, key):
             dsk = partitionwise_graph(operator.getitem, name, self, key)
             graph = HighLevelGraph.from_collections(name, dsk, dependencies=[self, key])
             return Series(graph, name, self._meta, self.divisions)
-        raise NotImplementedError(
-            "Series getitem is only supported for other series objects "
-            "with matching partition structure"
-        )
+        return self.loc[key]
 
     @derived_from(pd.DataFrame)
     def _get_numeric_data(self, how="any", subset=None):


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -3669,6 +3669,13 @@ def test_getitem_with_bool_dataframe_as_key():
     assert_eq(df[df > 3], ddf[ddf > 3])
 
 
+def test_getitem_with_non_series():
+    s = pd.Series(list(range(10)), index=list("abcdefghij"))
+    ds = dd.from_pandas(s, npartitions=3)
+
+    assert_eq(s[["a", "b"]], ds[["a", "b"]])
+
+
 def test_ipython_completion():
     df = pd.DataFrame({"a": [1], "b": [2]})
     ddf = dd.from_pandas(df, npartitions=1)
Repository: dask/dask

Issue:
`DataFrame.head` shouldn't warn when there's one partition
dask.data

```python
In [12]: import dask.dataframe as dd, pandas as pd

In [13]: df = dd.from_pandas(pd.DataFrame({"A": [1, 2]}), npartitions=1)

In [14]: df.head()
/home/taugspurger/miniconda3/envs/stac-table/lib/python3.9/site-packages/dask/dataframe/core.py:6778: UserWarning: Insufficient elements for `head`. 5 elements requested, only 2 elements available. Try passing larger `npartitions` to `head`.
  warnings.warn(msg.format(n, len(r)))
Out[14]:
   A
0  1
1  2
```

When there's just a single partition (or, more generally, if all the partitions are used), then there's no reason to warn.

I think the fix is change https://github.com/dask/dask/blob/7cfee4a394df591280c31e87b961873bec750c43/dask/dataframe/core.py#L1081-L1084 or perhaps https://github.com/dask/dask/blob/7cfee4a394df591280c31e87b961873bec750c43/dask/dataframe/core.py#L1071 to use `safe=False` when `npartitions == self.npartitions` (or when `npartitions=-1)`.


Discussion:
I would love to take this up

Resolving patch:
diff --git a/dask/dataframe/core.py b/dask/dataframe/core.py
--- a/dask/dataframe/core.py
+++ b/dask/dataframe/core.py
@@ -1068,7 +1068,11 @@ def head(self, n=5, npartitions=1, compute=True):
         compute : bool, optional
             Whether to compute the result, default is True.
         """
-        return self._head(n=n, npartitions=npartitions, compute=compute, safe=True)
+        if npartitions <= -1:
+            npartitions = self.npartitions
+        # No need to warn if we're already looking at all partitions
+        safe = npartitions != self.npartitions
+        return self._head(n=n, npartitions=npartitions, compute=compute, safe=safe)
 
     def _head(self, n, npartitions, compute, safe):
         if npartitions <= -1:


Test patch:
diff --git a/dask/dataframe/tests/test_dataframe.py b/dask/dataframe/tests/test_dataframe.py
--- a/dask/dataframe/tests/test_dataframe.py
+++ b/dask/dataframe/tests/test_dataframe.py
@@ -138,15 +138,28 @@ def test_head_npartitions_warn():
     with pytest.warns(UserWarning, match=match):
         d.head(5)
 
-    with pytest.warns(None):
+    match = "Insufficient elements"
+    with pytest.warns(UserWarning, match=match):
         d.head(100)
 
-    with pytest.warns(None):
+    with pytest.warns(UserWarning, match=match):
         d.head(7)
 
-    with pytest.warns(None):
+    with pytest.warns(UserWarning, match=match):
         d.head(7, npartitions=2)
 
+    # No warn if all partitions are inspected
+    for n in [3, -1]:
+        with pytest.warns(None) as rec:
+            d.head(10, npartitions=n)
+    assert not rec
+
+    # With default args, this means that a 1 partition dataframe won't warn
+    d2 = dd.from_pandas(pd.DataFrame({"x": [1, 2, 3]}), npartitions=1)
+    with pytest.warns(None) as rec:
+        d2.head()
+    assert not rec
+
 
 def test_index_head():
     assert_eq(d.index.head(2), full.index[:2])
Repository: dask/dask

Issue:
⚠️ Upstream CI failed ⚠️
[Workflow Run URL](https://github.com/dask/dask/actions/runs/1304259797)


Discussion:
Ok there are real new errors having to do with resample `closed` being deprecated. I will open a PR.
Side note: I've manually added the `upstream` label to this issue. I think that should prevent new issue reports from being opened (but let's wait to see if this is actually the case)

Resolving patch:
diff --git a/dask/dataframe/_compat.py b/dask/dataframe/_compat.py
--- a/dask/dataframe/_compat.py
+++ b/dask/dataframe/_compat.py
@@ -12,6 +12,7 @@
 PANDAS_GT_130 = PANDAS_VERSION >= parse_version("1.3.0")
 PANDAS_GT_131 = PANDAS_VERSION >= parse_version("1.3.1")
 PANDAS_GT_133 = PANDAS_VERSION >= parse_version("1.3.3")
+PANDAS_GT_140 = PANDAS_VERSION.release == (1, 4, 0)  # include pre-release
 
 import pandas.testing as tm
 
diff --git a/dask/dataframe/tseries/resample.py b/dask/dataframe/tseries/resample.py
--- a/dask/dataframe/tseries/resample.py
+++ b/dask/dataframe/tseries/resample.py
@@ -6,6 +6,7 @@
 from ...highlevelgraph import HighLevelGraph
 from ...utils import derived_from
 from .. import methods
+from .._compat import PANDAS_GT_140
 from ..core import DataFrame, Series
 
 
@@ -32,11 +33,20 @@ def _resample_series(
         *how_args, **how_kwargs
     )
 
+    if PANDAS_GT_140:
+        if reindex_closed is None:
+            inclusive = "both"
+        else:
+            inclusive = reindex_closed
+        closed_kwargs = {"inclusive": inclusive}
+    else:
+        closed_kwargs = {"closed": reindex_closed}
+
     new_index = pd.date_range(
         start.tz_localize(None),
         end.tz_localize(None),
         freq=rule,
-        closed=reindex_closed,
+        **closed_kwargs,
         name=out.index.name,
     ).tz_localize(start.tz, nonexistent="shift_forward")
 


Test patch:
Repository: dask/dask

Issue:
Support Path objects in from_zarr
Related to #8006 but the opposite side of the coin: while one can save `to_zarr`, it is still not possible to give a `pathlib.Path` object to `dask.array.from_zarr`, although this is possible with`zarr.open`.


Discussion:
Yes, this seems to be because fsspec does not support `Path`s and `from_zarr` uses fsspec directly where `to_zarr` uses `zarr.open`. On the zarr side, someone passing fsspec mappers around shouldn't be using `Path`s, so it's probably best not to do this `Path` -> `str` conversion in zarr (which is where the fix for #8006 came from), but here as the fsspec mapper is an internal implementation detail it's probably fair to add another conditional like `if isinstance(url, os.PathLike): url = os.fspath(url)`.

Resolving patch:
diff --git a/dask/array/core.py b/dask/array/core.py
--- a/dask/array/core.py
+++ b/dask/array/core.py
@@ -3277,7 +3277,9 @@ def from_zarr(
     storage_options = storage_options or {}
     if isinstance(url, zarr.Array):
         z = url
-    elif isinstance(url, str):
+    elif isinstance(url, (str, os.PathLike)):
+        if isinstance(url, os.PathLike):
+            url = os.fspath(url)
         mapper = get_mapper(url, **storage_options)
         z = zarr.Array(mapper, read_only=True, path=component, **kwargs)
     else:


Test patch:
diff --git a/dask/array/tests/test_array_core.py b/dask/array/tests/test_array_core.py
--- a/dask/array/tests/test_array_core.py
+++ b/dask/array/tests/test_array_core.py
@@ -1,5 +1,6 @@
 import contextlib
 import copy
+import pathlib
 import xml.etree.ElementTree
 from unittest import mock
 
@@ -4275,6 +4276,17 @@ def test_zarr_roundtrip():
         assert a2.chunks == a.chunks
 
 
+def test_zarr_roundtrip_with_path_like():
+    pytest.importorskip("zarr")
+    with tmpdir() as d:
+        path = pathlib.Path(d)
+        a = da.zeros((3, 3), chunks=(1, 1))
+        a.to_zarr(path)
+        a2 = da.from_zarr(path)
+        assert_eq(a, a2)
+        assert a2.chunks == a.chunks
+
+
 @pytest.mark.parametrize("compute", [False, True])
 def test_zarr_return_stored(compute):
     pytest.importorskip("zarr")
Repository: dask/dask

Issue:
dask dataframe read_parquet split_row_groups docs outadated?
The current [documentation](https://docs.dask.org/en/latest/generated/dask.dataframe.read_parquet.html#dask-dataframe-read-parquet) regarding `split_row_groups` in dask dataframe `read_parquet` says " Only the “pyarrow” engine supports this argument." however when running these tests [`dask/dataframe/io/tests/test_parquet.py::test_split_row_groups`](https://github.com/dask/dask/blob/7446308083e2f284229a11e9b3e330aa9e73cf96/dask/dataframe/io/tests/test_parquet.py#L2558-L2596) you’ll see that `fastparquet` , `pyarrow-dataset`, and `pyarrow-legacy` engines will be used (if installed) and the tests pass for all the cases. 

Does this mean that the docs are outdated?

@dask/io 



Discussion:
This looks right to me @ncclementi. @martindurant can you comment on fastparquet's support for `split_row_groups`?
split_row_groups is handled by dask and works as expected, I believe - following @rjzamora 's work.

Resolving patch:
diff --git a/dask/dataframe/io/parquet/core.py b/dask/dataframe/io/parquet/core.py
--- a/dask/dataframe/io/parquet/core.py
+++ b/dask/dataframe/io/parquet/core.py
@@ -197,7 +197,6 @@ def read_parquet(
         parquet-file row-group. If False, each partition will correspond to a
         complete file.  If a positive integer value is given, each dataframe
         partition will correspond to that number of parquet row-groups (or fewer).
-        Only the "pyarrow" engine supports this argument.
     chunksize : int or str, default None
         The desired size of each output ``DataFrame`` partition in terms of total
         (uncompressed) parquet storage space. If specified, adjacent row-groups


Test patch:
Repository: dask/dask

Issue:
Update DataFrameGroupBy.apply warning
This was brought up in a [SO post](https://stackoverflow.com/questions/70265639/best-way-to-perform-arbitrary-operations-on-groups-with-dask-dataframes), and in addition to answering the question, Sultan pointed out these notes are out of date, since shuffling is in fact used: https://github.com/dask/dask/blob/124ae53f9627ec51659398877ba9d2cc6652f28b/dask/dataframe/groupby.py#L36-L41



Discussion:
I think those docs are confusingly worded but correct. Reductions (i.e. `mean`, `sum`, ...) do not shuffle and `apply` does. 
I thought the thing that is out of date is the `Warning` note here: https://docs.dask.org/en/latest/generated/dask.dataframe.groupby.DataFrameGroupBy.apply.html#dask.dataframe.groupby.DataFrameGroupBy.apply
> I think those docs are confusingly worded but correct. Reductions (i.e. `mean`, `sum`, ...) do not shuffle and `apply` does.

Ah that makes sense, thanks for clarifying!
> I thought the thing that is out of date is the `Warning` note here: https://docs.dask.org/en/latest/generated/dask.dataframe.groupby.DataFrameGroupBy.apply.html#dask.dataframe.groupby.DataFrameGroupBy.apply

Ah! Yeah that one is wrong. We do a shuffle (if needed) and then each group is contained in one partition.
Great, sorry for the confusion! I'll edit the title :)

Resolving patch:
diff --git a/dask/dataframe/groupby.py b/dask/dataframe/groupby.py
--- a/dask/dataframe/groupby.py
+++ b/dask/dataframe/groupby.py
@@ -1643,9 +1643,10 @@ def apply(self, func, *args, **kwargs):
 
            Pandas' groupby-apply can be used to to apply arbitrary functions,
            including aggregations that result in one row per group. Dask's
-           groupby-apply will apply ``func`` once to each partition-group pair,
-           so when ``func`` is a reduction you'll end up with one row per
-           partition-group pair. To apply a custom aggregation with Dask,
+           groupby-apply will apply ``func`` once on each group, doing a shuffle
+           if needed, such that each group is contained in one partition.
+           When ``func`` is a reduction, e.g., you'll end up with one row
+           per group. To apply a custom aggregation with Dask,
            use :class:`dask.dataframe.groupby.Aggregation`.
 
         Parameters
@@ -1728,9 +1729,10 @@ def transform(self, func, *args, **kwargs):
 
            Pandas' groupby-transform can be used to to apply arbitrary functions,
            including aggregations that result in one row per group. Dask's
-           groupby-transform will apply ``func`` once to each partition-group pair,
-           so when ``func`` is a reduction you'll end up with one row per
-           partition-group pair. To apply a custom aggregation with Dask,
+           groupby-transform will apply ``func`` once on each group, doing a shuffle
+           if needed, such that each group is contained in one partition.
+           When ``func`` is a reduction, e.g., you'll end up with one row
+           per group. To apply a custom aggregation with Dask,
            use :class:`dask.dataframe.groupby.Aggregation`.
 
         Parameters


Test patch:
Repository: dask/dask

Issue:
Set minimum pyyaml version
- Currently, in `setup.py`, there is not a minimum version set for **pyyaml**
  - https://github.com/dask/dask/blob/3c46e89aea2af010e69049cd638094fea2ddd576/setup.py#L37
- It would be ideal if dask set the minimum require version


Discussion:
Thanks for raising an issue @gsheni. Agreed setting a minimum version for `pyyaml` would be a nice improvement. Do you have a suggestion on what a good minimum version would be? Looking at `pyyaml` releases, 5.3.1 seems like a reasonable minimum as it's almost 2 years old.

The relevant places where we would need to add `pyyaml >= <minimum-version>` are:

- https://github.com/dask/dask/blob/c2b962fec1ba45440fe928869dc64cfe9cc36506/setup.py#L37
- https://github.com/dask/dask/blob/7d1a02cac553d60280d23163824b471b06200114/continuous_integration/recipe/meta.yaml#L29
- All the `environment-mindeps-*` conda environment files in the `continuous_integration` directory

@gsheni is this something you're interested in working on? (no obligation though)

cc @jsignell @jakirkham for visibility 

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@
     "fsspec >= 0.6.0",
     "packaging >= 20.0",
     "partd >= 0.3.10",
-    "pyyaml",
+    "pyyaml >= 5.3.1",
     "toolz >= 0.8.2",
 ]
 


Test patch:
Repository: dask/dask

Issue:
Document `dask.DataFrame.from_delayed` compatibility with `Future`s
`dask.DataFrame.from_delayed` works when either an iterable of `Delayed` objects or an iterable of `Future`s are passed in as input. However, the docstring for `from_delayed`

https://github.com/dask/dask/blob/358a5e367eedc8f2963651071a72f43e0ac4f887/dask/dataframe/io/io.py#L573-L576

only mentions compatibly with `Delayed` objects. We should update the `from_delayed` docstring to also mentioned `Future`s can be used 


Discussion:

Resolving patch:
diff --git a/dask/dataframe/io/io.py b/dask/dataframe/io/io.py
--- a/dask/dataframe/io/io.py
+++ b/dask/dataframe/io/io.py
@@ -570,10 +570,11 @@ def from_delayed(
 
     Parameters
     ----------
-    dfs : list of Delayed
+    dfs : list of Delayed or Future
         An iterable of ``dask.delayed.Delayed`` objects, such as come from
-        ``dask.delayed`` These comprise the individual partitions of the
-        resulting dataframe.
+        ``dask.delayed`` or an iterable of ``distributed.Future`` objects,
+        such as come from ``client.submit`` interface. These comprise the individual
+        partitions of the resulting dataframe.
     $META
     divisions : tuple, str, optional
         Partition boundaries along the index.


Test patch:
Repository: dask/dask

Issue:
Show length of layer in HLG JupyterLab repr
When looking at the new interactive HLG repr in a Jupyter notebook, one key piece of information I often want to know is how many tasks are in a given layer (and in the total graph). This is not currently displayed in the repr, but easily could be.

![image](https://user-images.githubusercontent.com/3309802/146460850-9f8a0074-ad27-41d8-a220-6d8bdbfc3123.png)

Adding an `tasks (unoptimized)` count to both each layer, and the overall graph, would be nice.


Discussion:
Just as a note, this information is available on the repr for the array itself `arr.data`
The total number of tasks across all layers is available, but knowing the number of tasks _per_ layer isn't AFAICT
Oh yeah, just meant that the logic is probably similar if people want a first place to look.

Resolving patch:
diff --git a/dask/highlevelgraph.py b/dask/highlevelgraph.py
--- a/dask/highlevelgraph.py
+++ b/dask/highlevelgraph.py
@@ -511,6 +511,7 @@ def layer_info_dict(self):
         info = {
             "layer_type": type(self).__name__,
             "is_materialized": self.is_materialized(),
+            "number of outputs": f"{len(self.get_output_keys())}",
         }
         if self.annotations is not None:
             for key, val in self.annotations.items():
@@ -1146,6 +1147,7 @@ def _repr_html_(self):
             type=type(self).__name__,
             layers=self.layers,
             toposort=self._toposort_layers(),
+            n_outputs=len(self.get_all_external_keys()),
         )
 
 


Test patch:
Repository: dask/dask

Issue:
ProgressBar `out` parameter issues
The `out` parameter in `diagnostics.ProgressBar` is not documented. It appears it should be a file-like object, which means bytes should be written to it by default. The current implementation raises `TypeError: a bytes-like object is required, not 'str'`.

Example:
```python
from tempfile import TemporaryFile

import dask.array as da
from dask.diagnostics import ProgressBar


with TemporaryFile() as temp_file:
    with ProgressBar(out=temp_file):
        da.random.random((10, 10)).compute()
```

Tested using:

- Dask version: 2021.12.0
- Python version: 3.9.7
- Operating System: Mac OS X 12.1
- Install method (conda, pip, source): conda


Discussion:
The lack of documentation (and the name `_file`) certainly makes this hard to decipher, but going back to when this change was first introduced(https://github.com/dask/dask/pull/3185), it looks like the intention was just to allow the user to specify `stdout` or `stderr`. So I think that your original issue is not supported. Would you like to open a pull request to improve the documentation?
I'm a newbie to Dask, but I guess this is a cool first issue. Could you assign it to me, please?

Thanks!
Go ahead @ppsbs!

Resolving patch:
diff --git a/dask/diagnostics/progress.py b/dask/diagnostics/progress.py
--- a/dask/diagnostics/progress.py
+++ b/dask/diagnostics/progress.py
@@ -37,6 +37,10 @@ class ProgressBar(Callback):
         Width of the bar
     dt : float, optional
         Update resolution in seconds, default is 0.1 seconds
+    out : file object, optional
+        File object to which the progress bar will be written
+        It can be ``sys.stdout``, ``sys.stderr`` or any other file object able to write ``str`` objects
+        Default is ``sys.stdout``
 
     Examples
     --------


Test patch:
Repository: dask/dask

Issue:
`@derived_from` decorator not copying docstring
Earlier today I noticed that some methods which use our `derived_from` utility to copy docstrings over from some other method (e.g. `dask.dataframe.DataFrame.ffill` copies it's docstring from the corresponding pandas `ffill` method) 

https://github.com/dask/dask/blob/85bc7b5bb888c8a96eee3db07b260ca11d7cc130/dask/dataframe/core.py#L1443-L1445

isn't working 

```python
In [1]: import dask.dataframe as dd

In [2]: dd.DataFrame.ffill?
Signature: dd.DataFrame.ffill(self, axis=None, limit=None)
File:      ~/projects/dask/dask/dask/dataframe/core.py
Type:      function
```

while other methods, e.g. `dd.DataFrame.cummax`, are working as expected (i.e. the docstring is correctly copied over from pandas to Dask). 


Discussion:
Hi tried to solve it seems like `pandas.DataFrame.ffill.__doc__` is empty unlike cummax but somehow `help(pandas.DataFrame.ffill) `seems to work 

Resolving patch:
diff --git a/dask/utils.py b/dask/utils.py
--- a/dask/utils.py
+++ b/dask/utils.py
@@ -750,6 +750,14 @@ def _derived_from(cls, method, ua_args=None, extra="", skipblocks=0):
     if doc is None:
         doc = ""
 
+    # pandas DataFrame/Series sometimes override methods without setting __doc__
+    if not doc and cls.__name__ in {"DataFrame", "Series"}:
+        for obj in cls.mro():
+            obj_method = getattr(obj, method.__name__, None)
+            if obj_method is not None and obj_method.__doc__:
+                doc = obj_method.__doc__
+                break
+
     # Insert disclaimer that this is a copied docstring
     if doc:
         doc = ignore_warning(


Test patch:
diff --git a/dask/tests/test_utils.py b/dask/tests/test_utils.py
--- a/dask/tests/test_utils.py
+++ b/dask/tests/test_utils.py
@@ -558,6 +558,8 @@ def test_derived_from_dask_dataframe():
     assert "not supported" in axis_arg.lower()
     assert "dask" in axis_arg.lower()
 
+    assert "Object with missing values filled" in dd.DataFrame.ffill.__doc__
+
 
 def test_parse_bytes():
     assert parse_bytes("100") == 100