BUG: Placeholders not being filled on docstrings
State: closed

- [x] closes #39115 
- [x] tests added / passed
- [x] Ensure all linting tests pass, see [here](https://pandas.pydata.org/pandas-docs/dev/development/contributing.html#code-standards) for how to run them


Labels:
name: Docs

Comments:
It's my first time actually working with the code on this repo.

I've contributed previously, but only with minor changes to documentation, and I've always done that from the GitHub built-in editor.

I believe I've done everything correctly, but I'm completely unfamiliar with the codebase here.

I've built the documentation locally and looked around to see if there was anything wrong, and it didn't seem like it.

I didn't expect I'd be able to even find what was causing the bug I reported, let alone fix it, and I can't help but feel like I'm missing something important.

Anyways, the fix I've put together is really simple, but I fear it might not be exactly how the maintainers would like it to be fixed, so if needed I can try to come up with something else to mitigate this issue.

What I mean by "not how the maintainers would like it fixed" is that, for this fix to work, an empty `@doc()` decorator is used on the first `mad` method, as to populate it with some docstring generation goodies. This may or may not be a pattern that fits with the rest of the codebase, so although it works, it could be considered an inappropriate solution for breaking consistency or something along those lines.

This was the simplest way I found for fixing this, and in my opinion it has a nice pattern to it, but I do think there are other ways of fixing this.
> the docs are showing warnings which is a bit odd; make sure you have up to date code & precommit hooks

I did run all tests I managed to find. I've run `pre-commit` (and it's also properly added as a hook), I've run `./ci/code_checks.sh`, `validate_docstrings.py` on the ones I modified, and everything seemed to have worked fine.

I'll run some more tests, and see if I can find out what those warnings are about.

Edit: I forgot to mention, I've just forked this repo yesterday, and I did `git pull upstream` after cloning my fork to my local machine too (but it was up-to-date already). And the branch I've created was from `master`.
> > the docs are showing warnings which is a bit odd; make sure you have up to date code & precommit hooks
> 
> I did run all tests I managed to find. I've run `pre-commit` (and it's also properly added as a hook), I've run `./ci/code_checks.sh`, `validate_docstrings.py` on the ones I modified, and everything seemed to have worked fine.
> 
> I'll run some more tests, and see if I can find out what those warnings are about.

try to actually build the docs (these are not tests)
I did build them, it took ages but worked fine.
I've also shared a screenshot of the specific doc I was aiming to fix:

https://github.com/pandas-dev/pandas/issues/39115#issuecomment-759154264
![Screenshot](https://i.imgur.com/hBorCaQ.png)
I've merged the current `master` to my branch, and am running `pre-commit run --all-files` now, will run all tests I can find afterwards.
`pytest pandas` output:

<details>

```
======================================================================================================== FAILURES ========================================================================================================
___________________________________________________________________________________ TestCategoricalWarnings.test_tab_complete_warning ____________________________________________________________________________________

self = <pandas.tests.arrays.categorical.test_warnings.TestCategoricalWarnings object at 0x7fc94a445a00>, ip = <IPython.core.interactiveshell.InteractiveShell object at 0x7fc94a2f3a90>

    @async_mark()
    async def test_tab_complete_warning(self, ip):
        # https://github.com/pandas-dev/pandas/issues/16409
        pytest.importorskip("IPython", minversion="6.0.0")
        from IPython.core.completer import provisionalcompleter
    
        code = "import pandas as pd; c = Categorical([])"
        await ip.run_code(code)
    
        # GH 31324 newer jedi version raises Deprecation warning
        import jedi
    
        if jedi.__version__ < "0.16.0":
            warning = tm.assert_produces_warning(None)
        else:
            warning = tm.assert_produces_warning(
                DeprecationWarning, check_stacklevel=False
            )
        with warning:
            with provisionalcompleter("ignore"):
>               list(ip.Completer.completions("c.", 1))

pandas/tests/arrays/categorical/test_warnings.py:29: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
env/lib/python3.8/site-packages/IPython/core/completer.py:1818: in completions
    for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
env/lib/python3.8/site-packages/IPython/core/completer.py:1861: in _completions
    matched_text, matches, matches_origin, jedi_matches = self._complete(
env/lib/python3.8/site-packages/IPython/core/completer.py:2029: in _complete
    completions = self._jedi_matches(
env/lib/python3.8/site-packages/IPython/core/completer.py:1373: in _jedi_matches
    interpreter = jedi.Interpreter(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Interpreter' object has no attribute '_orig_path'") raised in repr()] Interpreter object at 0x7fc94a408e80>, code = 'c'
namespaces = [{'In': [''], 'Out': {}, '_': '', '__': '', ...}, {'In': [''], 'Out': {}, '_': '', '__': '', ...}], kwds = {'column': 1, 'line': 1}
environment = <jedi.api.environment.InterpreterEnvironment object at 0x7fc94a4084c0>

    def __init__(self, code, namespaces, **kwds):
        try:
            namespaces = [dict(n) for n in namespaces]
        except Exception:
            raise TypeError("namespaces must be a non-empty list of dicts.")
    
        environment = kwds.get('environment', None)
        if environment is None:
            environment = InterpreterEnvironment()
        else:
            if not isinstance(environment, InterpreterEnvironment):
                raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
    
>       super().__init__(code, environment=environment,
                         project=Project(Path.cwd()), **kwds)
E       TypeError: __init__() got an unexpected keyword argument 'column'

env/lib/python3.8/site-packages/jedi/api/__init__.py:725: TypeError
-------------------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<string> in <module>

NameError: name 'Categorical' is not defined
_________________________________________________________________________________ TestDataFrameMisc.test_tab_complete_warning[DataFrame] _________________________________________________________________________________

self = <pandas.tests.frame.test_api.TestDataFrameMisc object at 0x7fc92fc1c0a0>, ip = <IPython.core.interactiveshell.InteractiveShell object at 0x7fc92fcb0160>, frame_or_series = <class 'pandas.core.frame.DataFrame'>

    @async_mark()
    @td.check_file_leaks
    async def test_tab_complete_warning(self, ip, frame_or_series):
        # GH 16409
        pytest.importorskip("IPython", minversion="6.0.0")
        from IPython.core.completer import provisionalcompleter
    
        if frame_or_series is DataFrame:
            code = "from pandas import DataFrame; obj = DataFrame()"
        else:
            code = "from pandas import Series; obj = Series(dtype=object)"
    
        await ip.run_code(code)
    
        # TODO: remove it when Ipython updates
        # GH 33567, jedi version raises Deprecation warning in Ipython
        import jedi
    
        if jedi.__version__ < "0.17.0":
            warning = tm.assert_produces_warning(None)
        else:
            warning = tm.assert_produces_warning(
                DeprecationWarning, check_stacklevel=False
            )
        with warning:
            with provisionalcompleter("ignore"):
>               list(ip.Completer.completions("obj.", 1))

pandas/tests/frame/test_api.py:289: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
env/lib/python3.8/site-packages/IPython/core/completer.py:1818: in completions
    for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
env/lib/python3.8/site-packages/IPython/core/completer.py:1861: in _completions
    matched_text, matches, matches_origin, jedi_matches = self._complete(
env/lib/python3.8/site-packages/IPython/core/completer.py:2029: in _complete
    completions = self._jedi_matches(
env/lib/python3.8/site-packages/IPython/core/completer.py:1373: in _jedi_matches
    interpreter = jedi.Interpreter(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Interpreter' object has no attribute '_orig_path'") raised in repr()] Interpreter object at 0x7fc92fd77910>, code = 'o'
namespaces = [{'DataFrame': <class 'pandas.core.frame.DataFrame'>, 'In': [''], 'Out': {}, '_': '', ...}, {'DataFrame': <class 'pandas.core.frame.DataFrame'>, 'In': [''], 'Out': {}, '_': '', ...}]
kwds = {'column': 1, 'line': 1}, environment = <jedi.api.environment.InterpreterEnvironment object at 0x7fc92fd777f0>

    def __init__(self, code, namespaces, **kwds):
        try:
            namespaces = [dict(n) for n in namespaces]
        except Exception:
            raise TypeError("namespaces must be a non-empty list of dicts.")
    
        environment = kwds.get('environment', None)
        if environment is None:
            environment = InterpreterEnvironment()
        else:
            if not isinstance(environment, InterpreterEnvironment):
                raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
    
>       super().__init__(code, environment=environment,
                         project=Project(Path.cwd()), **kwds)
E       TypeError: __init__() got an unexpected keyword argument 'column'

env/lib/python3.8/site-packages/jedi/api/__init__.py:725: TypeError
__________________________________________________________________________________ TestDataFrameMisc.test_tab_complete_warning[Series] ___________________________________________________________________________________

self = <pandas.tests.frame.test_api.TestDataFrameMisc object at 0x7fc92fc9f8e0>, ip = <IPython.core.interactiveshell.InteractiveShell object at 0x7fc92fc9f490>, frame_or_series = <class 'pandas.core.series.Series'>

    @async_mark()
    @td.check_file_leaks
    async def test_tab_complete_warning(self, ip, frame_or_series):
        # GH 16409
        pytest.importorskip("IPython", minversion="6.0.0")
        from IPython.core.completer import provisionalcompleter
    
        if frame_or_series is DataFrame:
            code = "from pandas import DataFrame; obj = DataFrame()"
        else:
            code = "from pandas import Series; obj = Series(dtype=object)"
    
        await ip.run_code(code)
    
        # TODO: remove it when Ipython updates
        # GH 33567, jedi version raises Deprecation warning in Ipython
        import jedi
    
        if jedi.__version__ < "0.17.0":
            warning = tm.assert_produces_warning(None)
        else:
            warning = tm.assert_produces_warning(
                DeprecationWarning, check_stacklevel=False
            )
        with warning:
            with provisionalcompleter("ignore"):
>               list(ip.Completer.completions("obj.", 1))

pandas/tests/frame/test_api.py:289: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
env/lib/python3.8/site-packages/IPython/core/completer.py:1818: in completions
    for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
env/lib/python3.8/site-packages/IPython/core/completer.py:1861: in _completions
    matched_text, matches, matches_origin, jedi_matches = self._complete(
env/lib/python3.8/site-packages/IPython/core/completer.py:2029: in _complete
    completions = self._jedi_matches(
env/lib/python3.8/site-packages/IPython/core/completer.py:1373: in _jedi_matches
    interpreter = jedi.Interpreter(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Interpreter' object has no attribute '_orig_path'") raised in repr()] Interpreter object at 0x7fc92fbbd1c0>, code = 'o'
namespaces = [{'In': [''], 'Out': {}, 'Series': <class 'pandas.core.series.Series'>, '_': '', ...}, {'In': [''], 'Out': {}, 'Series': <class 'pandas.core.series.Series'>, '_': '', ...}], kwds = {'column': 1, 'line': 1}
environment = <jedi.api.environment.InterpreterEnvironment object at 0x7fc92fbbd0d0>

    def __init__(self, code, namespaces, **kwds):
        try:
            namespaces = [dict(n) for n in namespaces]
        except Exception:
            raise TypeError("namespaces must be a non-empty list of dicts.")
    
        environment = kwds.get('environment', None)
        if environment is None:
            environment = InterpreterEnvironment()
        else:
            if not isinstance(environment, InterpreterEnvironment):
                raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
    
>       super().__init__(code, environment=environment,
                         project=Project(Path.cwd()), **kwds)
E       TypeError: __init__() got an unexpected keyword argument 'column'

env/lib/python3.8/site-packages/jedi/api/__init__.py:725: TypeError
__________________________________________________________________________________________ TestIndex.test_tab_complete_warning ___________________________________________________________________________________________

self = <pandas.tests.indexes.test_base.TestIndex object at 0x7fc917a6c940>, ip = <IPython.core.interactiveshell.InteractiveShell object at 0x7fc917a6cf10>

    @async_mark()
    async def test_tab_complete_warning(self, ip):
        # https://github.com/pandas-dev/pandas/issues/16409
        pytest.importorskip("IPython", minversion="6.0.0")
        from IPython.core.completer import provisionalcompleter
    
        code = "import pandas as pd; idx = Index([1, 2])"
        await ip.run_code(code)
    
        # GH 31324 newer jedi version raises Deprecation warning
        import jedi
    
        if jedi.__version__ < "0.16.0":
            warning = tm.assert_produces_warning(None)
        else:
            warning = tm.assert_produces_warning(
                DeprecationWarning, check_stacklevel=False
            )
        with warning:
            with provisionalcompleter("ignore"):
>               list(ip.Completer.completions("idx.", 4))

pandas/tests/indexes/test_base.py:1963: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
env/lib/python3.8/site-packages/IPython/core/completer.py:1818: in completions
    for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
env/lib/python3.8/site-packages/IPython/core/completer.py:1861: in _completions
    matched_text, matches, matches_origin, jedi_matches = self._complete(
env/lib/python3.8/site-packages/IPython/core/completer.py:2029: in _complete
    completions = self._jedi_matches(
env/lib/python3.8/site-packages/IPython/core/completer.py:1373: in _jedi_matches
    interpreter = jedi.Interpreter(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Interpreter' object has no attribute '_orig_path'") raised in repr()] Interpreter object at 0x7fc926764fa0>, code = 'idx.'
namespaces = [{'In': [''], 'Out': {}, '_': '', '__': '', ...}, {'In': [''], 'Out': {}, '_': '', '__': '', ...}], kwds = {'column': 4, 'line': 1}
environment = <jedi.api.environment.InterpreterEnvironment object at 0x7fc926764040>

    def __init__(self, code, namespaces, **kwds):
        try:
            namespaces = [dict(n) for n in namespaces]
        except Exception:
            raise TypeError("namespaces must be a non-empty list of dicts.")
    
        environment = kwds.get('environment', None)
        if environment is None:
            environment = InterpreterEnvironment()
        else:
            if not isinstance(environment, InterpreterEnvironment):
                raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
    
>       super().__init__(code, environment=environment,
                         project=Project(Path.cwd()), **kwds)
E       TypeError: __init__() got an unexpected keyword argument 'column'

env/lib/python3.8/site-packages/jedi/api/__init__.py:725: TypeError
-------------------------------------------------------------------------------------------------- Captured stdout call --------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<string> in <module>

NameError: name 'Index' is not defined
___________________________________________________________________________________ TestReadHtml.test_encode[chinese_utf-16.html-bs4] ____________________________________________________________________________________

self = <pandas.tests.io.test_html.TestReadHtml object at 0x7fc8e5ca18b0>, html_encoding_file = '/home/micael/projects/pandas-MicaelJarniac/pandas/tests/io/data/html_encoding/chinese_utf-16.html'

    def test_encode(self, html_encoding_file):
        base_path = os.path.basename(html_encoding_file)
        root = os.path.splitext(base_path)[0]
        _, encoding = root.split("_")
    
        try:
            with open(html_encoding_file, "rb") as fobj:
>               from_string = self.read_html(
                    fobj.read(), encoding=encoding, index_col=0
                ).pop()

pandas/tests/io/test_html.py:1161: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pandas/util/_decorators.py:299: in wrapper
    return func(*args, **kwargs)
pandas/io/html.py:1081: in read_html
    return _parse(
pandas/io/html.py:909: in _parse
    raise retained
pandas/io/html.py:889: in _parse
    tables = p.parse_tables()
pandas/io/html.py:209: in parse_tables
    tables = self._parse_tables(self._build_doc(), self.match, self.attrs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.io.html._BeautifulSoupHtml5LibFrameParser object at 0x7fc8e5caaaf0>

    def _build_doc(self):
        from bs4 import BeautifulSoup
    
        bdoc = self._setup_build_doc()
        if isinstance(bdoc, bytes) and self.encoding is not None:
>           udoc = bdoc.decode(self.encoding)
E           UnicodeDecodeError: 'utf-16-le' codec can't decode byte 0x00 in position 850: truncated data

pandas/io/html.py:590: UnicodeDecodeError
___________________________________________________________________________________ TestReadHtml.test_encode[chinese_utf-32.html-bs4] ____________________________________________________________________________________

self = <pandas.tests.io.test_html.TestReadHtml object at 0x7fc8e5ad2ca0>, html_encoding_file = '/home/micael/projects/pandas-MicaelJarniac/pandas/tests/io/data/html_encoding/chinese_utf-32.html'

    def test_encode(self, html_encoding_file):
        base_path = os.path.basename(html_encoding_file)
        root = os.path.splitext(base_path)[0]
        _, encoding = root.split("_")
    
        try:
            with open(html_encoding_file, "rb") as fobj:
>               from_string = self.read_html(
                    fobj.read(), encoding=encoding, index_col=0
                ).pop()

pandas/tests/io/test_html.py:1161: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pandas/util/_decorators.py:299: in wrapper
    return func(*args, **kwargs)
pandas/io/html.py:1081: in read_html
    return _parse(
pandas/io/html.py:909: in _parse
    raise retained
pandas/io/html.py:889: in _parse
    tables = p.parse_tables()
pandas/io/html.py:209: in parse_tables
    tables = self._parse_tables(self._build_doc(), self.match, self.attrs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.io.html._BeautifulSoupHtml5LibFrameParser object at 0x7fc8e5af58b0>

    def _build_doc(self):
        from bs4 import BeautifulSoup
    
        bdoc = self._setup_build_doc()
        if isinstance(bdoc, bytes) and self.encoding is not None:
>           udoc = bdoc.decode(self.encoding)
E           UnicodeDecodeError: 'utf-32-le' codec can't decode bytes in position 192-195: code point not in range(0x110000)

pandas/io/html.py:590: UnicodeDecodeError
___________________________________________________________________________________ TestReadHtml.test_encode[chinese_utf-32.html-lxml] ___________________________________________________________________________________

self = <pandas.tests.io.test_html.TestReadHtml object at 0x7fc8e4945370>, html_encoding_file = '/home/micael/projects/pandas-MicaelJarniac/pandas/tests/io/data/html_encoding/chinese_utf-32.html'

    def test_encode(self, html_encoding_file):
        base_path = os.path.basename(html_encoding_file)
        root = os.path.splitext(base_path)[0]
        _, encoding = root.split("_")
    
        try:
            with open(html_encoding_file, "rb") as fobj:
>               from_string = self.read_html(
                    fobj.read(), encoding=encoding, index_col=0
                ).pop()

pandas/tests/io/test_html.py:1161: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
pandas/util/_decorators.py:299: in wrapper
    return func(*args, **kwargs)
pandas/io/html.py:1081: in read_html
    return _parse(
pandas/io/html.py:909: in _parse
    raise retained
pandas/io/html.py:889: in _parse
    tables = p.parse_tables()
pandas/io/html.py:209: in parse_tables
    tables = self._parse_tables(self._build_doc(), self.match, self.attrs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.io.html._LxmlFrameParser object at 0x7fc8e4945be0>, doc = <Element table at 0x7fc8e5bd89f0>, match = re.compile('.+'), kwargs = None

    def _parse_tables(self, doc, match, kwargs):
        pattern = match.pattern
    
        # 1. check all descendants for the given pattern and only search tables
        # 2. go up the tree until we find a table
        xpath_expr = f"//table//*[re:test(text(), {repr(pattern)})]/ancestor::table"
    
        # if any table attributes were given build an xpath expression to
        # search for them
        if kwargs:
            xpath_expr += _build_xpath_expr(kwargs)
    
        tables = doc.xpath(xpath_expr, namespaces=_re_namespace)
    
        tables = self._handle_hidden_tables(tables, "attrib")
        if self.displayed_only:
            for table in tables:
                # lxml utilizes XPATH 1.0 which does not have regex
                # support. As a result, we find all elements with a style
                # attribute and iterate them to check for display:none
                for elem in table.xpath(".//*[@style]"):
                    if "display:none" in elem.attrib.get("style", "").replace(" ", ""):
                        elem.getparent().remove(elem)
    
        if not tables:
>           raise ValueError(f"No tables found matching regex {repr(pattern)}")
E           ValueError: No tables found matching regex '.+'

pandas/io/html.py:680: ValueError
_____________________________________________________________________________________________ TestS3.test_write_s3_csv_fails _____________________________________________________________________________________________

self = <pandas.tests.io.parser.test_network.TestS3 object at 0x7fc928538640>
tips_df =      total_bill   tip     sex smoker   day    time  size
0         16.99  1.01  Female     No   Sun  Dinner     2
1   ....75    Male     No   Sat  Dinner     2
243       18.78  3.00  Female     No  Thur  Dinner     2

[244 rows x 7 columns]
s3so = {'client_kwargs': {'endpoint_url': 'http://127.0.0.1:5555/'}}

    def test_write_s3_csv_fails(self, tips_df, s3so):
        # GH 32486
        # Attempting to write to an invalid S3 path should raise
        import botocore
    
        # GH 34087
        # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html
        # Catch a ClientError since AWS Service Errors are defined dynamically
        error = (FileNotFoundError, botocore.exceptions.ClientError)
    
        with pytest.raises(error, match="The specified bucket does not exist"):
>           tips_df.to_csv(
                "s3://an_s3_bucket_data_doesnt_exit/not_real.csv", storage_options=s3so
            )
E           Failed: DID NOT RAISE (<class 'FileNotFoundError'>, <class 'botocore.exceptions.ClientError'>)

pandas/tests/io/parser/test_network.py:223: Failed
___________________________________________________________________________________________ TestS3.test_write_s3_parquet_fails ___________________________________________________________________________________________

self = <pandas.tests.io.parser.test_network.TestS3 object at 0x7fc914bcec70>
tips_df =      total_bill   tip     sex smoker   day    time  size
0         16.99  1.01  Female     No   Sun  Dinner     2
1   ....75    Male     No   Sat  Dinner     2
243       18.78  3.00  Female     No  Thur  Dinner     2

[244 rows x 7 columns]
s3so = {'client_kwargs': {'endpoint_url': 'http://127.0.0.1:5555/'}}

    @td.skip_if_no("pyarrow")
    def test_write_s3_parquet_fails(self, tips_df, s3so):
        # GH 27679
        # Attempting to write to an invalid S3 path should raise
        import botocore
    
        # GH 34087
        # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html
        # Catch a ClientError since AWS Service Errors are defined dynamically
        error = (FileNotFoundError, botocore.exceptions.ClientError)
    
        with pytest.raises(error, match="The specified bucket does not exist"):
>           tips_df.to_parquet(
                "s3://an_s3_bucket_data_doesnt_exit/not_real.parquet",
                storage_options=s3so,
            )
E           Failed: DID NOT RAISE (<class 'FileNotFoundError'>, <class 'botocore.exceptions.ClientError'>)

pandas/tests/io/parser/test_network.py:239: Failed
________________________________________________________________________________________ TestDataFrameColor.test_invalid_colormap ________________________________________________________________________________________

self = <pandas.tests.plotting.frame.test_frame_color.TestDataFrameColor object at 0x7fc8aba3faf0>

    def test_invalid_colormap(self):
        df = DataFrame(np.random.randn(3, 2), columns=["A", "B"])
        msg = (
            "'invalid_colormap' is not a valid value for name; supported values are "
            "'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', "
            "'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', "
            "'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', "
            "'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', "
            "'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', "
            "'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', "
            "'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', "
            "'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', "
            "'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', "
            "'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', "
            "'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', "
            "'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', "
            "'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', "
            "'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', "
            "'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', "
            "'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', "
            "'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', "
            "'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', "
            "'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', "
            "'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', "
            "'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', "
            "'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', "
            "'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', "
            "'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', "
            "'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', "
            "'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', "
            "'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', "
            "'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'"
        )
        with pytest.raises(ValueError, match=msg):
>           df.plot(colormap="invalid_colormap")

pandas/tests/plotting/frame/test_frame_color.py:685: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.plotting._core.PlotAccessor object at 0x7fc8abaa4c10>, args = (), kwargs = {'ax': None, 'colormap': 'invalid_colormap', 'figsize': None, 'fontsize': None, ...}
plot_backend = <module 'pandas.plotting._matplotlib' from '/home/micael/projects/pandas-MicaelJarniac/pandas/plotting/_matplotlib/__init__.py'>, x = None, y = None, kind = 'line'
data =           A         B
0  0.509784  1.251911
1  2.500527 -0.462616
2  0.737699  0.756503, data_cols = Index(['A', 'B'], dtype='object')

    def __call__(self, *args, **kwargs):
        plot_backend = _get_plot_backend(kwargs.pop("backend", None))
    
        x, y, kind, kwargs = self._get_call_args(
            plot_backend.__name__, self._parent, args, kwargs
        )
    
        kind = self._kind_aliases.get(kind, kind)
    
        # when using another backend, get out of the way
        if plot_backend.__name__ != "pandas.plotting._matplotlib":
            return plot_backend.plot(self._parent, x=x, y=y, kind=kind, **kwargs)
    
        if kind not in self._all_kinds:
            raise ValueError(f"{kind} is not a valid plot kind")
    
        # The original data structured can be transformed before passed to the
        # backend. For example, for DataFrame is common to set the index as the
        # `x` parameter, and return a Series with the parameter `y` as values.
        data = self._parent.copy()
    
        if isinstance(data, ABCSeries):
            kwargs["reuse_plot"] = True
    
        if kind in self._dataframe_kinds:
            if isinstance(data, ABCDataFrame):
                return plot_backend.plot(data, x=x, y=y, kind=kind, **kwargs)
            else:
                raise ValueError(f"plot kind {kind} can only be used for data frames")
        elif kind in self._series_kinds:
            if isinstance(data, ABCDataFrame):
                if y is None and kwargs.get("subplots") is False:
                    raise ValueError(
                        f"{kind} requires either y column or 'subplots=True'"
                    )
                elif y is not None:
                    if is_integer(y) and not data.columns.holds_integer():
                        y = data.columns[y]
                    # converted to series actually. copy to not modify
                    data = data[y].copy()
                    data.index.name = y
        elif isinstance(data, ABCDataFrame):
            data_cols = data.columns
            if x is not None:
                if is_integer(x) and not data.columns.holds_integer():
                    x = data_cols[x]
                elif not isinstance(data[x], ABCSeries):
                    raise ValueError("x must be a label or position")
                data = data.set_index(x)
            if y is not None:
                # check if we have y as int or list of ints
                int_ylist = is_list_like(y) and all(is_integer(c) for c in y)
                int_y_arg = is_integer(y) or int_ylist
                if int_y_arg and not data.columns.holds_integer():
                    y = data_cols[y]
    
                label_kw = kwargs["label"] if "label" in kwargs else False
                for kw in ["xerr", "yerr"]:
                    if kw in kwargs and (
                        isinstance(kwargs[kw], str) or is_integer(kwargs[kw])
                    ):
                        try:
                            kwargs[kw] = data[kwargs[kw]]
                        except (IndexError, KeyError, TypeError):
                            pass
    
                # don't overwrite
                data = data[y].copy()
    
                if isinstance(data, ABCSeries):
                    label_name = label_kw or y
                    data.name = label_name
                else:
                    match = is_list_like(label_kw) and len(label_kw) == len(y)
                    if label_kw and not match:
                        raise ValueError(
                            "label should be list-like and same length as y"
                        )
                    label_name = label_kw or data.columns
                    data.columns = label_name
    
>       return plot_backend.plot(data, kind=kind, **kwargs)

pandas/plotting/_core.py:959: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

data =           A         B
0  0.509784  1.251911
1  2.500527 -0.462616
2  0.737699  0.756503, kind = 'line', kwargs = {'ax': None, 'colormap': 'invalid_colormap', 'figsize': None, 'fontsize': None, ...}
plt = <module 'matplotlib.pyplot' from '/home/micael/projects/pandas-MicaelJarniac/env/lib/python3.8/site-packages/matplotlib/pyplot.py'>, plot_obj = <pandas.plotting._matplotlib.core.LinePlot object at 0x7fc8abaaa0a0>

    def plot(data, kind, **kwargs):
        # Importing pyplot at the top of the file (before the converters are
        # registered) causes problems in matplotlib 2 (converters seem to not
        # work)
        import matplotlib.pyplot as plt
    
        if kwargs.pop("reuse_plot", False):
            ax = kwargs.get("ax")
            if ax is None and len(plt.get_fignums()) > 0:
                with plt.rc_context():
                    ax = plt.gca()
                kwargs["ax"] = getattr(ax, "left_ax", ax)
        plot_obj = PLOT_CLASSES[kind](data, **kwargs)
>       plot_obj.generate()

pandas/plotting/_matplotlib/__init__.py:61: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.plotting._matplotlib.core.LinePlot object at 0x7fc8abaaa0a0>

    def generate(self):
        self._args_adjust()
        self._compute_plot_data()
        self._setup_subplots()
>       self._make_plot()

pandas/plotting/_matplotlib/core.py:279: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.plotting._matplotlib.core.LinePlot object at 0x7fc8abaaa0a0>

    def _make_plot(self):
        if self._is_ts_plot():
            data = maybe_convert_index(self._get_ax(0), self.data)
    
            x = data.index  # dummy, not used
            plotf = self._ts_plot
            it = self._iter_data(data=data, keep_index=True)
        else:
            x = self._get_xticks(convert_period=True)
            # pandas\plotting\_matplotlib\core.py:1100: error: Incompatible
            # types in assignment (expression has type "Callable[[Any, Any,
            # Any, Any, Any, Any, KwArg(Any)], Any]", variable has type
            # "Callable[[Any, Any, Any, Any, KwArg(Any)], Any]")  [assignment]
            plotf = self._plot  # type: ignore[assignment]
            it = self._iter_data()
    
        stacking_id = self._get_stacking_id()
        is_errorbar = com.any_not_none(*self.errors.values())
    
>       colors = self._get_colors()

pandas/plotting/_matplotlib/core.py:1136: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <pandas.plotting._matplotlib.core.LinePlot object at 0x7fc8abaaa0a0>, num_colors = 2, color_kwds = 'color'

    def _get_colors(self, num_colors=None, color_kwds="color"):
        if num_colors is None:
            num_colors = self.nseries
    
>       return get_standard_colors(
            num_colors=num_colors,
            colormap=self.colormap,
            color=self.kwds.get(color_kwds),
        )

pandas/plotting/_matplotlib/core.py:783: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

num_colors = 2, colormap = 'invalid_colormap', color_type = 'default', color = None

    def get_standard_colors(
        num_colors: int,
        colormap: Optional["Colormap"] = None,
        color_type: str = "default",
        color: Optional[Union[Dict[str, Color], Color, Collection[Color]]] = None,
    ):
        """
        Get standard colors based on `colormap`, `color_type` or `color` inputs.
    
        Parameters
        ----------
        num_colors : int
            Minimum number of colors to be returned.
            Ignored if `color` is a dictionary.
        colormap : :py:class:`matplotlib.colors.Colormap`, optional
            Matplotlib colormap.
            When provided, the resulting colors will be derived from the colormap.
        color_type : {"default", "random"}, optional
            Type of colors to derive. Used if provided `color` and `colormap` are None.
            Ignored if either `color` or `colormap` are not None.
        color : dict or str or sequence, optional
            Color(s) to be used for deriving sequence of colors.
            Can be either be a dictionary, or a single color (single color string,
            or sequence of floats representing a single color),
            or a sequence of colors.
    
        Returns
        -------
        dict or list
            Standard colors. Can either be a mapping if `color` was a dictionary,
            or a list of colors with a length of `num_colors` or more.
    
        Warns
        -----
        UserWarning
            If both `colormap` and `color` are provided.
            Parameter `color` will override.
        """
        if isinstance(color, dict):
            return color
    
>       colors = _derive_colors(
            color=color,
            colormap=colormap,
            color_type=color_type,
            num_colors=num_colors,
        )

pandas/plotting/_matplotlib/style.py:71: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _derive_colors(
        *,
        color: Optional[Union[Color, Collection[Color]]],
        colormap: Optional[Union[str, "Colormap"]],
        color_type: str,
        num_colors: int,
    ) -> List[Color]:
        """
        Derive colors from either `colormap`, `color_type` or `color` inputs.
    
        Get a list of colors either from `colormap`, or from `color`,
        or from `color_type` (if both `colormap` and `color` are None).
    
        Parameters
        ----------
        color : str or sequence, optional
            Color(s) to be used for deriving sequence of colors.
            Can be either be a single color (single color string, or sequence of floats
            representing a single color), or a sequence of colors.
        colormap : :py:class:`matplotlib.colors.Colormap`, optional
            Matplotlib colormap.
            When provided, the resulting colors will be derived from the colormap.
        color_type : {"default", "random"}, optional
            Type of colors to derive. Used if provided `color` and `colormap` are None.
            Ignored if either `color` or `colormap`` are not None.
        num_colors : int
            Number of colors to be extracted.
    
        Returns
        -------
        list
            List of colors extracted.
    
        Warns
        -----
        UserWarning
            If both `colormap` and `color` are provided.
            Parameter `color` will override.
        """
        if color is None and colormap is not None:
>           return _get_colors_from_colormap(colormap, num_colors=num_colors)

pandas/plotting/_matplotlib/style.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

colormap = 'invalid_colormap', num_colors = 2

    def _get_colors_from_colormap(
        colormap: Union[str, "Colormap"],
        num_colors: int,
    ) -> List[Color]:
        """Get colors from colormap."""
>       colormap = _get_cmap_instance(colormap)

pandas/plotting/_matplotlib/style.py:147: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

colormap = 'invalid_colormap'

    def _get_cmap_instance(colormap: Union[str, "Colormap"]) -> "Colormap":
        """Get instance of matplotlib colormap."""
        if isinstance(colormap, str):
            cmap = colormap
>           colormap = cm.get_cmap(colormap)

pandas/plotting/_matplotlib/style.py:155: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

name = 'invalid_colormap', lut = None

    def get_cmap(name=None, lut=None):
        """
        Get a colormap instance, defaulting to rc values if *name* is None.
    
        Colormaps added with :func:`register_cmap` take precedence over
        built-in colormaps.
    
        Notes
        -----
        Currently, this returns the global colormap object, which is deprecated.
        In Matplotlib 3.5, you will no longer be able to modify the global
        colormaps in-place.
    
        Parameters
        ----------
        name : `matplotlib.colors.Colormap` or str or None, default: None
            If a `.Colormap` instance, it will be returned. Otherwise, the name of
            a colormap known to Matplotlib, which will be resampled by *lut*. The
            default, None, means :rc:`image.cmap`.
        lut : int or None, default: None
            If *name* is not already a Colormap instance and *lut* is not None, the
            colormap will be resampled to have *lut* entries in the lookup table.
        """
        if name is None:
            name = mpl.rcParams['image.cmap']
        if isinstance(name, colors.Colormap):
            return name
>       cbook._check_in_list(sorted(_cmap_registry), name=name)

env/lib/python3.8/site-packages/matplotlib/cm.py:204: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

_values = ['Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', ...], kwargs = {'name': 'invalid_colormap'}, values = ['Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', ...], k = 'name'
v = 'invalid_colormap'

    def _check_in_list(_values, **kwargs):
        """
        For each *key, value* pair in *kwargs*, check that *value* is in *_values*;
        if not, raise an appropriate ValueError.
    
        Examples
        --------
        >>> cbook._check_in_list(["foo", "bar"], arg=arg, other_arg=other_arg)
        """
        values = _values
        for k, v in kwargs.items():
            if v not in values:
>               raise ValueError(
                    "{!r} is not a valid value for {}; supported values are {}"
                    .format(v, k, ', '.join(map(repr, values))))
E               ValueError: 'invalid_colormap' is not a valid value for name; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'crest', 'crest_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'flare', 'flare_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'icefire', 'icefire_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'mako', 'mako_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'rocket', 'rocket_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'vlag', 'vlag_r', 'winter', 'winter_r'

env/lib/python3.8/site-packages/matplotlib/cbook/__init__.py:2266: ValueError

During handling of the above exception, another exception occurred:

self = <pandas.tests.plotting.frame.test_frame_color.TestDataFrameColor object at 0x7fc8aba3faf0>

    def test_invalid_colormap(self):
        df = DataFrame(np.random.randn(3, 2), columns=["A", "B"])
        msg = (
            "'invalid_colormap' is not a valid value for name; supported values are "
            "'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', "
            "'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', "
            "'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', "
            "'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', "
            "'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', "
            "'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', "
            "'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', "
            "'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', "
            "'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', "
            "'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', "
            "'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', "
            "'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', "
            "'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', "
            "'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', "
            "'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', "
            "'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', "
            "'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', "
            "'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', "
            "'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', "
            "'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', "
            "'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', "
            "'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', "
            "'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', "
            "'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', "
            "'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', "
            "'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', "
            "'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', "
            "'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'"
        )
        with pytest.raises(ValueError, match=msg):
>           df.plot(colormap="invalid_colormap")
E           AssertionError: Regex pattern "'invalid_colormap' is not a valid value for name; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'winter', 'winter_r'" does not match "'invalid_colormap' is not a valid value for name; supported values are 'Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'crest', 'crest_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'flare', 'flare_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'icefire', 'icefire_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'mako', 'mako_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'rocket', 'rocket_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'turbo', 'turbo_r', 'twilight', 'twilight_r', 'twilight_shifted', 'twilight_shifted_r', 'viridis', 'viridis_r', 'vlag', 'vlag_r', 'winter', 'winter_r'".

pandas/tests/plotting/frame/test_frame_color.py:685: AssertionError
___________________________________________________________________________________________ test_tab_complete_ipython6_warning ___________________________________________________________________________________________

ip = <IPython.core.interactiveshell.InteractiveShell object at 0x7fc8aefd2520>

    @async_mark()
    @td.check_file_leaks
    async def test_tab_complete_ipython6_warning(ip):
        from IPython.core.completer import provisionalcompleter
    
        code = dedent(
            """\
        import pandas._testing as tm
        s = tm.makeTimeSeries()
        rs = s.resample("D")
        """
        )
        await ip.run_code(code)
    
        # TODO: remove it when Ipython updates
        # GH 33567, jedi version raises Deprecation warning in Ipython
        import jedi
    
        if jedi.__version__ < "0.17.0":
            warning = tm.assert_produces_warning(None)
        else:
            warning = tm.assert_produces_warning(DeprecationWarning, check_stacklevel=False)
        with warning:
            with provisionalcompleter("ignore"):
>               list(ip.Completer.completions("rs.", 1))

pandas/tests/resample/test_resampler_grouper.py:44: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
env/lib/python3.8/site-packages/IPython/core/completer.py:1818: in completions
    for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
env/lib/python3.8/site-packages/IPython/core/completer.py:1861: in _completions
    matched_text, matches, matches_origin, jedi_matches = self._complete(
env/lib/python3.8/site-packages/IPython/core/completer.py:2029: in _complete
    completions = self._jedi_matches(
env/lib/python3.8/site-packages/IPython/core/completer.py:1373: in _jedi_matches
    interpreter = jedi.Interpreter(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("'Interpreter' object has no attribute '_orig_path'") raised in repr()] Interpreter object at 0x7fc8aeea7400>, code = 'r'
namespaces = [{'In': [''], 'Out': {}, '_': '', '__': '', ...}, {'In': [''], 'Out': {}, '_': '', '__': '', ...}], kwds = {'column': 1, 'line': 1}
environment = <jedi.api.environment.InterpreterEnvironment object at 0x7fc8aeea7ca0>

    def __init__(self, code, namespaces, **kwds):
        try:
            namespaces = [dict(n) for n in namespaces]
        except Exception:
            raise TypeError("namespaces must be a non-empty list of dicts.")
    
        environment = kwds.get('environment', None)
        if environment is None:
            environment = InterpreterEnvironment()
        else:
            if not isinstance(environment, InterpreterEnvironment):
                raise TypeError("The environment needs to be an InterpreterEnvironment subclass.")
    
>       super().__init__(code, environment=environment,
                         project=Project(Path.cwd()), **kwds)
E       TypeError: __init__() got an unexpected keyword argument 'column'

env/lib/python3.8/site-packages/jedi/api/__init__.py:725: TypeError
==================================================================================================== warnings summary ====================================================================================================
pandas/tests/io/parser/test_index_col.py::test_index_col_large_csv[c_high]
pandas/tests/io/parser/test_index_col.py::test_index_col_large_csv[c_low]
pandas/tests/series/methods/test_isin.py::test_isin_large_series_mixed_dtypes_and_nan
  /home/micael/projects/pandas-MicaelJarniac/env/lib/python3.8/site-packages/numpy/lib/arraysetops.py:580: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
    mask |= (ar1 == a)

pandas/tests/scalar/timedelta/test_arithmetic.py::TestTimedeltaMultiplicationDivision::test_td_div_nan[nan1]
  /home/micael/projects/pandas-MicaelJarniac/pandas/tests/scalar/timedelta/test_arithmetic.py:445: RuntimeWarning: invalid value encountered in double_scalars
    result = td // nan

-- Docs: https://docs.pytest.org/en/stable/warnings.html
================================================================================================ short test summary info =================================================================================================
FAILED pandas/tests/arrays/categorical/test_warnings.py::TestCategoricalWarnings::test_tab_complete_warning - TypeError: __init__() got an unexpected keyword argument 'column'
FAILED pandas/tests/frame/test_api.py::TestDataFrameMisc::test_tab_complete_warning[DataFrame] - TypeError: __init__() got an unexpected keyword argument 'column'
FAILED pandas/tests/frame/test_api.py::TestDataFrameMisc::test_tab_complete_warning[Series] - TypeError: __init__() got an unexpected keyword argument 'column'
FAILED pandas/tests/indexes/test_base.py::TestIndex::test_tab_complete_warning - TypeError: __init__() got an unexpected keyword argument 'column'
FAILED pandas/tests/io/test_html.py::TestReadHtml::test_encode[chinese_utf-16.html-bs4] - UnicodeDecodeError: 'utf-16-le' codec can't decode byte 0x00 in position 850: truncated data
FAILED pandas/tests/io/test_html.py::TestReadHtml::test_encode[chinese_utf-32.html-bs4] - UnicodeDecodeError: 'utf-32-le' codec can't decode bytes in position 192-195: code point not in range(0x110000)
FAILED pandas/tests/io/test_html.py::TestReadHtml::test_encode[chinese_utf-32.html-lxml] - ValueError: No tables found matching regex '.+'
FAILED pandas/tests/io/parser/test_network.py::TestS3::test_write_s3_csv_fails - Failed: DID NOT RAISE (<class 'FileNotFoundError'>, <class 'botocore.exceptions.ClientError'>)
FAILED pandas/tests/io/parser/test_network.py::TestS3::test_write_s3_parquet_fails - Failed: DID NOT RAISE (<class 'FileNotFoundError'>, <class 'botocore.exceptions.ClientError'>)
FAILED pandas/tests/plotting/frame/test_frame_color.py::TestDataFrameColor::test_invalid_colormap - AssertionError: Regex pattern "'invalid_colormap' is not a valid value for name; supported values are 'Accent', 'Ac...
FAILED pandas/tests/resample/test_resampler_grouper.py::test_tab_complete_ipython6_warning - TypeError: __init__() got an unexpected keyword argument 'column'
=========================================================== 11 failed, 151742 passed, 3756 skipped, 1061 xfailed, 5 xpassed, 4 warnings in 2724.45s (0:45:24) ============================================================
```
</details>

I'll run the tests on the `master` branch and compare the outputs, to see which ones are unique to my branch.
I found one instance where the `@doc()` decorator was being used with an empty `dict`, and the documentation that resulted from it actually included the documentation for `str`, which isn't what was supposed to happen.

I've now modified the fix slightly as to prevent this.

Before:
```python
        decorated.__doc__ = "".join(
            [
                component.format(**params)
                if isinstance(component, str) and params != {}
                else dedent(component.__doc__ or "")
                for component in docstring_components
            ]
```

After:
```python
        decorated.__doc__ = "".join(
            [
                (component.format(**params) if params != {} else component)
                if isinstance(component, str)
                else dedent(component.__doc__ or "")
                for component in docstring_components
            ]
```

So now, even if an empty `dict` is sent as an argument for `@doc()`, it'll still check if `component` is a string, and will only skip the `.format()` part.

What was happening before is that, since `@doc()` was being used with an empty `dict`, it was adding `component.__doc__` to the docstring, but on this specific case, `component` was itself a string, so instead of adding the contents of `component`, it added `str.__doc__`.

The reason there's this check before using `.format()` is that, if `params` is an empty `dict`, and `component` happens to include any formatting placeholders (`{hi}`), that would raise a `KeyError`.

On this specific docstring, `component` is a string but didn't have any formatting placeholder, so calling `.format()` on it with an empty `params` `dict` wasn't an issue, and the result was simply that `component` was added as-is to the docstring, since `.format()` had nothing to do.

But with the docstring I've been working to fix, `component` does have formatting placeholders, so without this check it wouldn't work.

I've converted this PR to a draft because I still have to re-run all tests and rebuild the docs since I changed the code again.
Okay, I've run all tests again, everything seems normal.

Here's the result of `pytest` on the new branch:
```
10 failed, 148317 passed, 6247 skipped, 1050 xfailed, 3 xpassed, 1 warning in 1291.63s (0:21:31) 
```

And on the `master` branch:
```
10 failed, 148317 passed, 6247 skipped, 1050 xfailed, 3 xpassed, 1 warning in 1063.60s (0:17:43)
```

So yeah, there are 10 tests failing, but those tests were failing on `master` too, without my changes, so I don't believe they're related.

The docs built correctly, and from my inspections everything was in order.

I've also run all `pre-commit` hooks (and on `--all-files` too, just to be safe), and everything passed.

And I also compared the output of `python scripts/validate_docstrings.py` on both the new branch and `master`, and the only difference is that on the new branch there are 3 fewer documentation errors, all 3 related to the ones I fixed:
```diff
- /home/micael/projects/pandas-MicaelJarniac/pandas/core/generic.py:#L#:SS03:pandas.DataFrame.mad:Summary does not end with a period
- None:None:SS03:pandas.core.groupby.DataFrameGroupBy.mad:Summary does not end with a period
- /home/micael/projects/pandas-MicaelJarniac/pandas/core/generic.py:#L#:SS03:pandas.Series.mad:Summary does not end with a period
```
I've merged `master` to this branch again. I believe that'll sort out some CI tests not passing here.

On `pytest`, the results are still the same between `master` and my branch, but this time they both have 2 fewer fails.

New branch:
`8 failed, 148313 passed, 6249 skipped, 1052 xfailed, 3 xpassed, 1 warning in 1023.97s (0:17:03)`

`master`:
`8 failed, 148313 passed, 6249 skipped, 1052 xfailed, 3 xpassed, 1 warning in 1191.31s (0:19:51)`
I was thinking about adding tests to catch if `{klass}`, `{desc}`, `{axis_descr}`, `{name1}`, `{name2}`, `{see_also}`, `{examples}` and similar ends up in final documentation. This should prevent similar issues in the future.

I'm not sure if there's any instance where those are actually supposed to show up like that, but if there are, I don't think they'd be many, so I guess it'd be possible to whitelist (allowlist? passlist?) those specific instances, while having the checks on everything else.
Thanks @MicaelJarniac for the PR.

I think the solution should perhaps be more like...

```diff
diff --git a/pandas/util/_decorators.py b/pandas/util/_decorators.py
index d002e8a4eb..91a55d1bf8 100644
--- a/pandas/util/_decorators.py
+++ b/pandas/util/_decorators.py
@@ -372,14 +372,17 @@ def doc(*docstrings: Union[str, Callable], **params) -> Callable[[F], F]:
                 docstring_components.append(docstring)
 
         # formatting templates and concatenating docstring
-        decorated.__doc__ = "".join(
+        docstring = "".join(
             [
-                component.format(**params)
+                component
                 if isinstance(component, str)
                 else dedent(component.__doc__ or "")
                 for component in docstring_components
             ]
         )
+        if params:
+            docstring = docstring.format(**params)
+        decorated.__doc__ = docstring
 
         # error: "F" has no attribute "_docstring_components"
         decorated._docstring_components = (  # type: ignore[attr-defined]
```

Then we don't need to apply the additional doc decorator while also using the doc decorator to inherit docstrings, e.g. 

```
@doc(ExtensionArray.searchsorted)
    def searchsorted(self, value, side="left", sorter=None):
```

when no parameters are specified and the inherited docstring contains curly braces, such as 

```
        Parameters
        ----------
        value : array_like
            Values to insert into `self`.
        side : {'left', 'right'}, optional
```

to prevent `KeyError: "'left', 'right'"` when no templating is needed
@MicaelJarniac a bit of history.

we have @Appender and @Substition decorators, 177 and 96 usages repspectively.

the @doc decorator was intended to be a replacement for these, currently 189 usages.

The @doc decorator does seem to be causing issues with partial templating and where curly braces appear in the docstring.

I would suspect that any changes to the @doc decorator could cause issues elsewhere and I would be reticent to backport any changes here to fix the broken docs.

An alternative could be to use the legacy @Appender and @Substition to fix the DataFrame.mad and Series.mad docstrings for now.

IIRC it is also possible for use 

```
       @doc(
            NDFrame.mad.__doc__,
            desc="Return the mean absolute deviation of the values "
            "over the requested axis.",
```

instead of 

```
       @doc(
            NDFrame.mad,
            desc="Return the mean absolute deviation of the values "
            "over the requested axis.",
```
to get around the bug. so that would be easier.
I'll look into those, that does sound like the safest approach for the short term.

And as for the long term, I'll try to think of a way for using the `@doc(...)` decorator without breaking stuff all over the place. One possibility that comes to mind is using a custom RegEx instead of the built-in `.format(...)` to fill in the placeholders, as that would give us flexibility as to what should and what shouldn't raise a `KeyError` when filling templates.

For example, something like:
```python
for placeholder, value in params.items():
    docstring = re.sub("{" + placeholder + "}", value, docstring)
```

Something like that would mean that only placeholders specifically provided with `**params` would be replaced, and everything else that just so happens to have curly-braces around it would be left alone, no `KeyError` needed.
@MicaelJarniac we have a release planned for Monday, so updated this to just fix the Series.mad and DataFrame.mad docstrings for now

```
Help on function mad in module pandas.core.generic:

mad(self, axis=None, skipna=None, level=None)
    Return the mean absolute deviation of the values over the requested axis.
    
    Parameters
    ----------
    axis : {index (0), columns (1)}
        Axis for the function to be applied on.
    skipna : bool, default None
        Exclude NA/null values when computing the result.
    level : int or level name, default None
        If the axis is a MultiIndex (hierarchical), count along a
        particular level, collapsing into a Series.
    
    Returns
    -------
    Series or DataFrame (if level specified)
```
and Series.mad

```

Help on function mad in module pandas.core.generic:

mad(self, axis=None, skipna=None, level=None)
    Return the mean absolute deviation of the values over the requested axis.
    
    Parameters
    ----------
    axis : {index (0)}
        Axis for the function to be applied on.
    skipna : bool, default None
        Exclude NA/null values when computing the result.
    level : int or level name, default None
        If the axis is a MultiIndex (hierarchical), count along a
        particular level, collapsing into a scalar.
    
    Returns
    -------
    scalar or Series (if level specified)
```
looks ok to me. @simonjayhawkins merge if good; we need a longer term soln right?
> we need a longer term soln right?

all the other methods in `_add_numeric_operations` have the docstring stored as a string variable and are not causing issues. I quite like that the doctstring for `mad` is in the actual implementation and am therefore happier to add the ignore for mypy for now to keep this issue visible. 
Thanks @MicaelJarniac feel free to open another PR with the changes to the @doc decorator that you originally had.