# Buggy code
def _dotplot(
    dot_size,
    dot_color,
    dot_ax,
    cmap: str = "Reds",
    color_on: Optional[str] = "dot",
    y_label: Optional[str] = None,
    dot_max: Optional[float] = None,
    dot_min: Optional[float] = None,
    standard_scale: Literal["var", "group"] = None,
    smallest_dot: Optional[float] = 0.0,
    largest_dot: Optional[float] = 200,
    size_exponent: Optional[float] = 2,
    edge_color: Optional[ColorLike] = None,
    edge_lw: Optional[float] = None,
    grid: Optional[bool] = False,
    **kwds,
):
    """\
        Makes a *dot plot* given two data frames, one containing
        the doc size and other containing the dot color. The indices and
        columns of the data frame are used to label the output image

        The dots are plotted
        using matplotlib.pyplot.scatter. Thus, additional arguments can be passed.
        Parameters
        ----------
        dot_size: Data frame containing the dot_size.
        dot_color: Data frame containing the dot_color, should have the same,
                shape, columns and indices as dot_size.
        dot_ax: matplotlib axis
        y_lebel:
        cmap
            String denoting matplotlib color map.
        color_on
            Options are 'dot' or 'square'. Be default the colomap is applied to
            the color of the dot. Optionally, the colormap can be applied to an
            square behind the dot, in which case the dot is transparent and only
            the edge is shown.
        y_label: String. Label for y axis
        dot_max
            If none, the maximum dot size is set to the maximum fraction value found
            (e.g. 0.6). If given, the value should be a number between 0 and 1.
            All fractions larger than dot_max are clipped to this value.
        dot_min
            If none, the minimum dot size is set to 0. If given,
            the value should be a number between 0 and 1.
            All fractions smaller than dot_min are clipped to this value.
        standard_scale
            Whether or not to standardize that dimension between 0 and 1,
            meaning for each variable or group,
            subtract the minimum and divide each by its maximum.
        smallest_dot
            If none, the smallest dot has size 0.
            All expression levels with `dot_min` are plotted with this size.
        edge_color
            Dot edge color. When `color_on='dot'` the default is no edge. When
            `color_on='square'`, edge color is white
        edge_lw
            Dot edge line width. When `color_on='dot'` the default is no edge. When
            `color_on='square'`, line width = 1.5
        grid
            Adds a grid to the plot
        **kwds
            Are passed to :func:`matplotlib.pyplot.scatter`.

        Returns
        -------
        matplotlib.colors.Normalize, dot_min, dot_max

        """
    assert dot_size.shape == dot_color.shape, (
        "please check that dot_size and dot_color dataframes have the same shape"
    )

    assert list(dot_size.index) == list(dot_color.index), (
        "please check that dot_size and dot_color dataframes have the same index"
    )

    assert list(dot_size.columns) == list(dot_color.columns), (
        "please check that the dot_size and dot_color dataframes have the same columns"
    )

    if standard_scale == "group":
        dot_color = dot_color.sub(dot_color.min(1), axis=0)
        dot_color = dot_color.div(dot_color.max(1), axis=0).fillna(0)
    elif standard_scale == "var":
        dot_color -= dot_color.min(0)
        dot_color = (dot_color / dot_color.max(0)).fillna(0)
    elif standard_scale is None:
        pass

    # make scatter plot in which
    # x = var_names
    # y = groupby category
    # size = fraction
    # color = mean expression

    y, x = np.indices(dot_color.shape)
    y = y.flatten() + 0.5
    x = x.flatten() + 0.5
    frac = dot_size.values.flatten()
    mean_flat = dot_color.values.flatten()
    cmap = pl.get_cmap(kwds.get("cmap", cmap))
    if "cmap" in kwds:
        del kwds["cmap"]
    if dot_max is None:
        dot_max = np.ceil(max(frac) * 10) / 10
    else:
        if dot_max < 0 or dot_max > 1:
            raise ValueError("`dot_max` value has to be between 0 and 1")
    if dot_min is None:
        dot_min = 0
    else:
        if dot_min < 0 or dot_min > 1:
            raise ValueError("`dot_min` value has to be between 0 and 1")

    if dot_min != 0 or dot_max != 1:
        # clip frac between dot_min and  dot_max
        frac = np.clip(frac, dot_min, dot_max)
        old_range = dot_max - dot_min
        # re-scale frac between 0 and 1
        frac = (frac - dot_min) / old_range

    size = frac**size_exponent
    # rescale size to match smallest_dot and largest_dot
    size = size * (largest_dot - smallest_dot) + smallest_dot

    import matplotlib.colors

    normalize = matplotlib.colors.Normalize(
        vmin=kwds.get("vmin"), vmax=kwds.get("vmax")
    )

    if color_on == "square":
        if edge_color is None:
            from seaborn.utils import relative_luminance

            # use either black or white for the edge color
            # depending on the luminance of the background
            # square color
            edge_color = []
            for color_value in cmap(normalize(mean_flat)):
                lum = relative_luminance(color_value)
                edge_color.append(".15" if lum > 0.408 else "w")

        edge_lw = 1.5 if edge_lw is None else edge_lw

        # first make a heatmap similar to `sc.pl.matrixplot`
        # (squares with the asigned colormap). Circles will be plotted
        # on top
        dot_ax.pcolor(dot_color.values, cmap=cmap, norm=normalize)
        for axis in ["top", "bottom", "left", "right"]:
            dot_ax.spines[axis].set_linewidth(1.5)
        kwds = fix_kwds(
            kwds,
            s=size,
            cmap=cmap,
            norm=None,
            linewidth=edge_lw,
            facecolor="none",
            edgecolor=edge_color,
        )
        dot_ax.scatter(x, y, **kwds)
    else:
        edge_color = "none" if edge_color is None else edge_color
        edge_lw = 0.5 if edge_lw is None else edge_lw

        color = cmap(normalize(mean_flat))
        kwds = fix_kwds(
            kwds,
            s=size,
            cmap=cmap,
            color=color,
            norm=None,
            linewidth=edge_lw,
            edgecolor=edge_color,
        )

        dot_ax.scatter(x, y, **kwds)

    y_ticks = np.arange(dot_color.shape[0]) + 0.5
    dot_ax.set_yticks(y_ticks)
    dot_ax.set_yticklabels(
        [dot_color.index[idx] for idx, _ in enumerate(y_ticks)], minor=False
    )

    x_ticks = np.arange(dot_color.shape[1]) + 0.5
    dot_ax.set_xticks(x_ticks)
    dot_ax.set_xticklabels(
        [dot_color.columns[idx] for idx, _ in enumerate(x_ticks)],
        rotation=90,
        ha="center",
        minor=False,
    )
    dot_ax.tick_params(axis="both", labelsize="small")
    dot_ax.grid(False)
    dot_ax.set_ylabel(y_label)

    # to be consistent with the heatmap plot, is better to
    # invert the order of the y-axis, such that the first group is on
    # top
    dot_ax.set_ylim(dot_color.shape[0], 0)
    dot_ax.set_xlim(0, dot_color.shape[1])

    if color_on == "dot":
        # add more distance to the x and y lims with the color is on the
        # dots
        dot_ax.set_ylim(dot_color.shape[0] + 0.5, -0.5)

        dot_ax.set_xlim(-0.3, dot_color.shape[1] + 0.3)

    if grid:
        dot_ax.grid(True, color="gray", linewidth=0.1)
        dot_ax.set_axisbelow(True)

    return normalize, dot_min, dot_max


# Traceback (sphinx.errors.SphinxWarning)
/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/setuptools_scm/git.py:68: UserWarning: "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/checkouts/1204" is shallow and may cause errors
warnings.warn('"{}" is shallow and may cause errors'.format(wd.path))
/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/checkouts/1204/docs/conf.py:112: RemovedInSphinx40Warning: The app.add_stylesheet() is deprecated. Please use app.add_css_file() instead.
app.add_stylesheet('css/custom.css')

Traceback (most recent call last):
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/cmd/build.py", line 280, in build_main
app.build(args.force_all, filenames)
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/application.py", line 348, in build
self.builder.build_update()
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 299, in build_update
len(to_build))
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 311, in build
updated_docnames = set(self.read())
File "/home/docs/.pyenv/versions/3.6.8/lib/python3.6/contextlib.py", line 88, in __exit__
next(self.gen)
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/util/logging.py", line 213, in pending_warnings
memhandler.flushTo(logger)
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/util/logging.py", line 178, in flushTo
logger.handle(record)
File "/home/docs/.pyenv/versions/3.6.8/lib/python3.6/logging/__init__.py", line 1454, in handle
self.callHandlers(record)
File "/home/docs/.pyenv/versions/3.6.8/lib/python3.6/logging/__init__.py", line 1516, in callHandlers
hdlr.handle(record)
File "/home/docs/.pyenv/versions/3.6.8/lib/python3.6/logging/__init__.py", line 861, in handle
rv = self.filter(record)
File "/home/docs/.pyenv/versions/3.6.8/lib/python3.6/logging/__init__.py", line 720, in filter
result = f.filter(record)
File "/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/envs/1204/lib/python3.6/site-packages/sphinx/util/logging.py", line 415, in filter
raise SphinxWarning(location + ":" + str(message))
sphinx.errors.SphinxWarning: /home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/checkouts/1204/scanpy/plotting/_dotplot.py:docstring of scanpy.pl.dotplot:122:Inline strong start-string without end-string.

Warning, treated as error:
/home/docs/checkouts/readthedocs.org/user_builds/icb-scanpy/checkouts/1204/scanpy/plotting/_dotplot.py:docstring of scanpy.pl.dotplot:122:Inline strong start-string without end-string.

# Fixed code
def _dotplot(
    dot_size,
    dot_color,
    dot_ax,
    cmap: str = "Reds",
    color_on: Optional[str] = "dot",
    y_label: Optional[str] = None,
    dot_max: Optional[float] = None,
    dot_min: Optional[float] = None,
    standard_scale: Literal["var", "group"] = None,
    smallest_dot: Optional[float] = 0.0,
    largest_dot: Optional[float] = 200,
    size_exponent: Optional[float] = 2,
    edge_color: Optional[ColorLike] = None,
    edge_lw: Optional[float] = None,
    grid: Optional[bool] = False,
    **kwds,
):
    """\
        Makes a *dot plot* given two data frames, one containing
        the doc size and other containing the dot color. The indices and
        columns of the data frame are used to label the output image

        The dots are plotted using :func:`matplotlib.pyplot.scatter`. Thus, additional
        arguments can be passed.

        Parameters
        ----------
        dot_size: Data frame containing the dot_size.
        dot_color: Data frame containing the dot_color, should have the same,
                shape, columns and indices as dot_size.
        dot_ax: matplotlib axis
        y_lebel:
        cmap
            String denoting matplotlib color map.
        color_on
            Options are 'dot' or 'square'. Be default the colomap is applied to
            the color of the dot. Optionally, the colormap can be applied to an
            square behind the dot, in which case the dot is transparent and only
            the edge is shown.
        y_label: String. Label for y axis
        dot_max
            If none, the maximum dot size is set to the maximum fraction value found
            (e.g. 0.6). If given, the value should be a number between 0 and 1.
            All fractions larger than dot_max are clipped to this value.
        dot_min
            If none, the minimum dot size is set to 0. If given,
            the value should be a number between 0 and 1.
            All fractions smaller than dot_min are clipped to this value.
        standard_scale
            Whether or not to standardize that dimension between 0 and 1,
            meaning for each variable or group,
            subtract the minimum and divide each by its maximum.
        smallest_dot
            If none, the smallest dot has size 0.
            All expression levels with `dot_min` are plotted with this size.
        edge_color
            Dot edge color. When `color_on='dot'` the default is no edge. When
            `color_on='square'`, edge color is white
        edge_lw
            Dot edge line width. When `color_on='dot'` the default is no edge. When
            `color_on='square'`, line width = 1.5
        grid
            Adds a grid to the plot
        kwds
            Are passed to :func:`matplotlib.pyplot.scatter`.

        Returns
        -------
        matplotlib.colors.Normalize, dot_min, dot_max

        """
    assert dot_size.shape == dot_color.shape, (
        "please check that dot_size and dot_color dataframes have the same shape"
    )

    assert list(dot_size.index) == list(dot_color.index), (
        "please check that dot_size and dot_color dataframes have the same index"
    )

    assert list(dot_size.columns) == list(dot_color.columns), (
        "please check that the dot_size and dot_color dataframes have the same columns"
    )

    if standard_scale == "group":
        dot_color = dot_color.sub(dot_color.min(1), axis=0)
        dot_color = dot_color.div(dot_color.max(1), axis=0).fillna(0)
    elif standard_scale == "var":
        dot_color -= dot_color.min(0)
        dot_color = (dot_color / dot_color.max(0)).fillna(0)
    elif standard_scale is None:
        pass

    # make scatter plot in which
    # x = var_names
    # y = groupby category
    # size = fraction
    # color = mean expression

    y, x = np.indices(dot_color.shape)
    y = y.flatten() + 0.5
    x = x.flatten() + 0.5
    frac = dot_size.values.flatten()
    mean_flat = dot_color.values.flatten()
    cmap = pl.get_cmap(kwds.get("cmap", cmap))
    if "cmap" in kwds:
        del kwds["cmap"]
    if dot_max is None:
        dot_max = np.ceil(max(frac) * 10) / 10
    else:
        if dot_max < 0 or dot_max > 1:
            raise ValueError("`dot_max` value has to be between 0 and 1")
    if dot_min is None:
        dot_min = 0
    else:
        if dot_min < 0 or dot_min > 1:
            raise ValueError("`dot_min` value has to be between 0 and 1")

    if dot_min != 0 or dot_max != 1:
        # clip frac between dot_min and  dot_max
        frac = np.clip(frac, dot_min, dot_max)
        old_range = dot_max - dot_min
        # re-scale frac between 0 and 1
        frac = (frac - dot_min) / old_range

    size = frac**size_exponent
    # rescale size to match smallest_dot and largest_dot
    size = size * (largest_dot - smallest_dot) + smallest_dot

    import matplotlib.colors

    normalize = matplotlib.colors.Normalize(
        vmin=kwds.get("vmin"), vmax=kwds.get("vmax")
    )

    if color_on == "square":
        if edge_color is None:
            from seaborn.utils import relative_luminance

            # use either black or white for the edge color
            # depending on the luminance of the background
            # square color
            edge_color = []
            for color_value in cmap(normalize(mean_flat)):
                lum = relative_luminance(color_value)
                edge_color.append(".15" if lum > 0.408 else "w")

        edge_lw = 1.5 if edge_lw is None else edge_lw

        # first make a heatmap similar to `sc.pl.matrixplot`
        # (squares with the asigned colormap). Circles will be plotted
        # on top
        dot_ax.pcolor(dot_color.values, cmap=cmap, norm=normalize)
        for axis in ["top", "bottom", "left", "right"]:
            dot_ax.spines[axis].set_linewidth(1.5)
        kwds = fix_kwds(
            kwds,
            s=size,
            cmap=cmap,
            norm=None,
            linewidth=edge_lw,
            facecolor="none",
            edgecolor=edge_color,
        )
        dot_ax.scatter(x, y, **kwds)
    else:
        edge_color = "none" if edge_color is None else edge_color
        edge_lw = 0.5 if edge_lw is None else edge_lw

        color = cmap(normalize(mean_flat))
        kwds = fix_kwds(
            kwds,
            s=size,
            cmap=cmap,
            color=color,
            norm=None,
            linewidth=edge_lw,
            edgecolor=edge_color,
        )

        dot_ax.scatter(x, y, **kwds)

    y_ticks = np.arange(dot_color.shape[0]) + 0.5
    dot_ax.set_yticks(y_ticks)
    dot_ax.set_yticklabels(
        [dot_color.index[idx] for idx, _ in enumerate(y_ticks)], minor=False
    )

    x_ticks = np.arange(dot_color.shape[1]) + 0.5
    dot_ax.set_xticks(x_ticks)
    dot_ax.set_xticklabels(
        [dot_color.columns[idx] for idx, _ in enumerate(x_ticks)],
        rotation=90,
        ha="center",
        minor=False,
    )
    dot_ax.tick_params(axis="both", labelsize="small")
    dot_ax.grid(False)
    dot_ax.set_ylabel(y_label)

    # to be consistent with the heatmap plot, is better to
    # invert the order of the y-axis, such that the first group is on
    # top
    dot_ax.set_ylim(dot_color.shape[0], 0)
    dot_ax.set_xlim(0, dot_color.shape[1])

    if color_on == "dot":
        # add more distance to the x and y lims with the color is on the
        # dots
        dot_ax.set_ylim(dot_color.shape[0] + 0.5, -0.5)

        dot_ax.set_xlim(-0.3, dot_color.shape[1] + 0.3)

    if grid:
        dot_ax.grid(True, color="gray", linewidth=0.1)
        dot_ax.set_axisbelow(True)

    return normalize, dot_min, dot_max