text: import copy
  import re
  import textwrap
  
  import numpy as np
  import pytest
  
  import pandas.util._test_decorators as td
  
  import pandas as pd
  from pandas import DataFrame
  import pandas._testing as tm
  
  jinja2 = pytest.importorskip("jinja2")
  from pandas.io.formats.style import (  # isort:skip
      Styler,
      _get_level_lengths,
      _maybe_convert_css_to_tuples,
  )
  
  
  class TestStyler:
      def setup_method(self, method):
          np.random.seed(24)
          self.s = DataFrame({"A": np.random.permutation(range(6))})
          self.df = DataFrame({"A": [0, 1], "B": np.random.randn(2)})
          self.f = lambda x: x
          self.g = lambda x: x
  
          def h(x, foo="bar"):
              return pd.Series(f"color: {foo}", index=x.index, name=x.name)
  
          self.h = h
          self.styler = Styler(self.df)
          self.attrs = DataFrame({"A": ["color: red", "color: blue"]})
          self.dataframes = [
              self.df,
              DataFrame(
                  {"f": [1.0, 2.0], "o": ["a", "b"], "c": pd.Categorical(["a", "b"])}
              ),
          ]
  
      def test_init_non_pandas(self):
          msg = "``data`` must be a Series or DataFrame"
          with pytest.raises(TypeError, match=msg):
              Styler([1, 2, 3])
  
      def test_init_series(self):
          result = Styler(pd.Series([1, 2]))
          assert result.data.ndim == 2
  
      def test_repr_html_ok(self):
          self.styler._repr_html_()
  
      def test_repr_html_mathjax(self):
          # gh-19824
          assert "tex2jax_ignore" not in self.styler._repr_html_()
  
          with pd.option_context("display.html.use_mathjax", False):
              assert "tex2jax_ignore" in self.styler._repr_html_()
  
      def test_update_ctx(self):
          self.styler._update_ctx(self.attrs)
          expected = {(0, 0): ["color: red"], (1, 0): ["color: blue"]}
          assert self.styler.ctx == expected
  
      def test_update_ctx_flatten_multi(self):
          attrs = DataFrame({"A": ["color: red; foo: bar", "color: blue; foo: baz"]})
          self.styler._update_ctx(attrs)
          expected = {
              (0, 0): ["color: red", " foo: bar"],
              (1, 0): ["color: blue", " foo: baz"],
          }
          assert self.styler.ctx == expected
  
      def test_update_ctx_flatten_multi_traliing_semi(self):
          attrs = DataFrame({"A": ["color: red; foo: bar;", "color: blue; foo: baz;"]})
          self.styler._update_ctx(attrs)
          expected = {
              (0, 0): ["color: red", " foo: bar"],
              (1, 0): ["color: blue", " foo: baz"],
          }
          assert self.styler.ctx == expected
  
      def test_copy(self):
          s2 = copy.copy(self.styler)
          assert self.styler is not s2
          assert self.styler.ctx is s2.ctx  # shallow
          assert self.styler._todo is s2._todo
  
          self.styler._update_ctx(self.attrs)
          self.styler.highlight_max()
          assert self.styler.ctx == s2.ctx
          assert self.styler._todo == s2._todo
  
      def test_deepcopy(self):
          s2 = copy.deepcopy(self.styler)
          assert self.styler is not s2
          assert self.styler.ctx is not s2.ctx
          assert self.styler._todo is not s2._todo
  
          self.styler._update_ctx(self.attrs)
          self.styler.highlight_max()
          assert self.styler.ctx != s2.ctx
          assert s2._todo == []
          assert self.styler._todo != s2._todo
  
      def test_clear(self):
          # updated in GH 39396
          tt = DataFrame({"A": [None, "tt"]})
          css = DataFrame({"A": [None, "cls-a"]})
          s = self.df.style.highlight_max().set_tooltips(tt).set_td_classes(css)
          # _todo, tooltips and cell_context items added to..
          assert len(s._todo) > 0
          assert s.tooltips
          assert len(s.cell_context) > 0
  
          s = s._compute()
          # ctx and _todo items affected when a render takes place
          assert len(s.ctx) > 0
          assert len(s._todo) == 0  # _todo is emptied after compute.
  
          s._todo = [1]
          s.clear()
          # ctx, _todo, tooltips and cell_context items all revert to null state.
          assert len(s.ctx) == 0
          assert len(s._todo) == 0
          assert not s.tooltips
          assert len(s.cell_context) == 0
  
      def test_render(self):
          df = DataFrame({"A": [0, 1]})
          style = lambda x: pd.Series(["color: red", "color: blue"], name=x.name)
          s = Styler(df, uuid="AB").apply(style)
          s.render()
          # it worked?
  
      def test_multiple_render(self):
          # GH 39396
          s = Styler(self.df, uuid_len=0).applymap(lambda x: "color: red;", subset=["A"])
          s.render()  # do 2 renders to ensure css styles not duplicated
          assert (
              '<style  type="text/css" >\n#T__row0_col0,#T__row1_col0{\n            '
              "color:  red;\n        }</style>" in s.render()
          )
  
      def test_render_empty_dfs(self):
          empty_df = DataFrame()
          es = Styler(empty_df)
          es.render()
          # An index but no columns
          DataFrame(columns=["a"]).style.render()
          # A column but no index
          DataFrame(index=["a"]).style.render()
          # No IndexError raised?
  
      def test_render_double(self):
          df = DataFrame({"A": [0, 1]})
          style = lambda x: pd.Series(
              ["color: red; border: 1px", "color: blue; border: 2px"], name=x.name
          )
          s = Styler(df, uuid="AB").apply(style)
          s.render()
          # it worked?
  
      def test_set_properties(self):
          df = DataFrame({"A": [0, 1]})
          result = df.style.set_properties(color="white", size="10px")._compute().ctx
          # order is deterministic
          v = ["color: white", "size: 10px"]
          expected = {(0, 0): v, (1, 0): v}
          assert result.keys() == expected.keys()
          for v1, v2 in zip(result.values(), expected.values()):
              assert sorted(v1) == sorted(v2)
  
      def test_set_properties_subset(self):
          df = DataFrame({"A": [0, 1]})
          result = (
              df.style.set_properties(subset=pd.IndexSlice[0, "A"], color="white")
              ._compute()
              .ctx
          )
          expected = {(0, 0): ["color: white"]}
          assert result == expected
  
      def test_empty_index_name_doesnt_display(self):
          # https://github.com/pandas-dev/pandas/pull/12090#issuecomment-180695902
          df = DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
          result = df.style._translate()
  
          expected = [
              [
                  {
                      "class": "blank level0",
                      "type": "th",
                      "value": "",
                      "is_visible": True,
                      "display_value": "",
                  },
                  {
                      "class": "col_heading level0 col0",
                      "display_value": "A",
                      "type": "th",
                      "value": "A",
                      "is_visible": True,
                  },
                  {
                      "class": "col_heading level0 col1",
                      "display_value": "B",
                      "type": "th",
                      "value": "B",
                      "is_visible": True,
                  },
                  {
                      "class": "col_heading level0 col2",
                      "display_value": "C",
                      "type": "th",
                      "value": "C",
                      "is_visible": True,
                  },
              ]
          ]
  
          assert result["head"] == expected
  
      def test_index_name(self):
          # https://github.com/pandas-dev/pandas/issues/11655
          df = DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
          result = df.set_index("A").style._translate()
  
          expected = [
              [
                  {
                      "class": "blank level0",
                      "type": "th",
                      "value": "",
                      "display_value": "",
                      "is_visible": True,
                  },
                  {
                      "class": "col_heading level0 col0",
                      "type": "th",
                      "value": "B",
                      "display_value": "B",
                      "is_visible": True,
                  },
                  {
                      "class": "col_heading level0 col1",
                      "type": "th",
                      "value": "C",
                      "display_value": "C",
                      "is_visible": True,
                  },
              ],
              [
                  {"class": "index_name level0", "type": "th", "value": "A"},
                  {"class": "blank", "type": "th", "value": ""},
                  {"class": "blank", "type": "th", "value": ""},
              ],
          ]
  
          assert result["head"] == expected
  
      def test_multiindex_name(self):
          # https://github.com/pandas-dev/pandas/issues/11655
          df = DataFrame({"A": [1, 2], "B": [3, 4], "C": [5, 6]})
          result = df.set_index(["A", "B"]).style._translate()
  
          expected = [
              [
                  {
                      "class": "blank",
                      "type": "th",
                      "value": "",
                      "display_value": "",
                      "is_visible": True,
                  },
                  {
                      "class": "blank level0",
                      "type": "th",
                      "value": "",
                      "display_value": "",
                      "is_visible": True,
                  },
                  {
                      "class": "col_heading level0 col0",
                      "type": "th",
                      "value": "C",
                      "display_value": "C",
                      "is_visible": True,
                  },
              ],
              [
                  {"class": "index_name level0", "type": "th", "value": "A"},
                  {"class": "index_name level1", "type": "th", "value": "B"},
                  {"class": "blank", "type": "th", "value": ""},
              ],
          ]
  
          assert result["head"] == expected
  
      def test_numeric_columns(self):
          # https://github.com/pandas-dev/pandas/issues/12125
          # smoke test for _translate
          df = DataFrame({0: [1, 2, 3]})
          df.style._translate()
  
      def test_apply_axis(self):
          df = DataFrame({"A": [0, 0], "B": [1, 1]})
          f = lambda x: [f"val: {x.max()}" for v in x]
          result = df.style.apply(f, axis=1)
          assert len(result._todo) == 1
          assert len(result.ctx) == 0
          result._compute()
          expected = {
              (0, 0): ["val: 1"],
              (0, 1): ["val: 1"],
              (1, 0): ["val: 1"],
              (1, 1): ["val: 1"],
          }
          assert result.ctx == expected
  
          result = df.style.apply(f, axis=0)
          expected = {
              (0, 0): ["val: 0"],
              (0, 1): ["val: 1"],
              (1, 0): ["val: 0"],
              (1, 1): ["val: 1"],
          }
          result._compute()
          assert result.ctx == expected
          result = df.style.apply(f)  # default
          result._compute()
          assert result.ctx == expected
  
      def test_apply_subset(self):
          axes = [0, 1]
          slices = [
              pd.IndexSlice[:],
              pd.IndexSlice[:, ["A"]],
              pd.IndexSlice[[1], :],
              pd.IndexSlice[[1], ["A"]],
              pd.IndexSlice[:2, ["A", "B"]],
          ]
          for ax in axes:
              for slice_ in slices:
                  result = (
                      self.df.style.apply(self.h, axis=ax, subset=slice_, foo="baz")
                      ._compute()
                      .ctx
                  )
                  expected = {
                      (r, c): ["color: baz"]
                      for r, row in enumerate(self.df.index)
                      for c, col in enumerate(self.df.columns)
                      if row in self.df.loc[slice_].index
                      and col in self.df.loc[slice_].columns
                  }
                  assert result == expected
  
      def test_applymap_subset(self):
          def f(x):
              return "foo: bar"
  
          slices = [
              pd.IndexSlice[:],
              pd.IndexSlice[:, ["A"]],
              pd.IndexSlice[[1], :],
              pd.IndexSlice[[1], ["A"]],
              pd.IndexSlice[:2, ["A", "B"]],
          ]
  
          for slice_ in slices:
              result = self.df.style.applymap(f, subset=slice_)._compute().ctx
              expected = {
                  (r, c): ["foo: bar"]
                  for r, row in enumerate(self.df.index)
                  for c, col in enumerate(self.df.columns)
                  if row in self.df.loc[slice_].index
                  and col in self.df.loc[slice_].columns
              }
              assert result == expected
  
      @pytest.mark.parametrize(
          "slice_",
          [
              pd.IndexSlice[:, pd.IndexSlice["x", "A"]],
              pd.IndexSlice[:, pd.IndexSlice[:, "A"]],
              pd.IndexSlice[:, pd.IndexSlice[:, ["A", "C"]]],  # missing col element
              pd.IndexSlice[pd.IndexSlice["a", 1], :],
              pd.IndexSlice[pd.IndexSlice[:, 1], :],
              pd.IndexSlice[pd.IndexSlice[:, [1, 3]], :],  # missing row element
              pd.IndexSlice[:, ("x", "A")],
              pd.IndexSlice[("a", 1), :],
          ],
      )
      def test_applymap_subset_multiindex(self, slice_):
          # GH 19861
          # edited for GH 33562
          idx = pd.MultiIndex.from_product([["a", "b"], [1, 2]])
          col = pd.MultiIndex.from_product([["x", "y"], ["A", "B"]])
          df = DataFrame(np.random.rand(4, 4), columns=col, index=idx)
          df.style.applymap(lambda x: "color: red;", subset=slice_).render()
  
      def test_applymap_subset_multiindex_code(self):
          # https://github.com/pandas-dev/pandas/issues/25858
          # Checks styler.applymap works with multindex when codes are provided
          codes = np.array([[0, 0, 1, 1], [0, 1, 0, 1]])
          columns = pd.MultiIndex(
              levels=[["a", "b"], ["%", "#"]], codes=codes, names=["", ""]
          )
          df = DataFrame(
              [[1, -1, 1, 1], [-1, 1, 1, 1]], index=["hello", "world"], columns=columns
          )
          pct_subset = pd.IndexSlice[:, pd.IndexSlice[:, "%":"%"]]
  
          def color_negative_red(val):
              color = "red" if val < 0 else "black"
              return f"color: {color}"
  
          df.loc[pct_subset]
          df.style.applymap(color_negative_red, subset=pct_subset)
  
      def test_where_with_one_style(self):
          # GH 17474
          def f(x):
              return x > 0.5
  
          style1 = "foo: bar"
  
          result = self.df.style.where(f, style1)._compute().ctx
          expected = {
              (r, c): [style1]
              for r, row in enumerate(self.df.index)
              for c, col in enumerate(self.df.columns)
              if f(self.df.loc[row, col])
          }
          assert result == expected
  
      def test_where_subset(self):
          # GH 17474
          def f(x):
              return x > 0.5
  
          style1 = "foo: bar"
          style2 = "baz: foo"
  
          slices = [
              pd.IndexSlice[:],
              pd.IndexSlice[:, ["A"]],
              pd.IndexSlice[[1], :],
              pd.IndexSlice[[1], ["A"]],
              pd.IndexSlice[:2, ["A", "B"]],
          ]
  
          for slice_ in slices:
              result = (
                  self.df.style.where(f, style1, style2, subset=slice_)._compute().ctx
              )
              expected = {
                  (r, c): [style1 if f(self.df.loc[row, col]) else style2]
                  for r, row in enumerate(self.df.index)
                  for c, col in enumerate(self.df.columns)
                  if row in self.df.loc[slice_].index
                  and col in self.df.loc[slice_].columns
              }
              assert result == expected
  
      def test_where_subset_compare_with_applymap(self):
          # GH 17474
          def f(x):
              return x > 0.5
  
          style1 = "foo: bar"
          style2 = "baz: foo"
  
          def g(x):
              return style1 if f(x) else style2
  
          slices = [
              pd.IndexSlice[:],
              pd.IndexSlice[:, ["A"]],
              pd.IndexSlice[[1], :],
              pd.IndexSlice[[1], ["A"]],
              pd.IndexSlice[:2, ["A", "B"]],
          ]
  
          for slice_ in slices:
              result = (
                  self.df.style.where(f, style1, style2, subset=slice_)._compute().ctx
              )
              expected = self.df.style.applymap(g, subset=slice_)._compute().ctx
              assert result == expected
  
      def test_empty(self):
          df = DataFrame({"A": [1, 0]})
          s = df.style
          s.ctx = {(0, 0): ["color: red"], (1, 0): [""]}
  
          result = s._translate()["cellstyle"]
          expected = [
              {"props": [("color", " red")], "selectors": ["row0_col0"]},
              {"props": [("", "")], "selectors": ["row1_col0"]},
          ]
          assert result == expected
  
      def test_duplicate(self):
          df = DataFrame({"A": [1, 0]})
          s = df.style
          s.ctx = {(0, 0): ["color: red"], (1, 0): ["color: red"]}
  
          result = s._translate()["cellstyle"]
          expected = [
              {"props": [("color", " red")], "selectors": ["row0_col0", "row1_col0"]}
          ]
          assert result == expected
  
      def test_bar_align_left(self):
          df = DataFrame({"A": [0, 1, 2]})
          result = df.style.bar()._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient("
                  "90deg,#d65f5f 50.0%, transparent 50.0%)",
              ],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient("
                  "90deg,#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
          result = df.style.bar(color="red", width=50)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,red 25.0%, transparent 25.0%)",
              ],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,red 50.0%, transparent 50.0%)",
              ],
          }
          assert result == expected
  
          df["C"] = ["a"] * len(df)
          result = df.style.bar(color="red", width=50)._compute().ctx
          assert result == expected
          df["C"] = df["C"].astype("category")
          result = df.style.bar(color="red", width=50)._compute().ctx
          assert result == expected
  
      def test_bar_align_left_0points(self):
          df = DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
          result = df.style.bar()._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (0, 1): ["width: 10em", " height: 80%"],
              (0, 2): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
              ],
              (1, 2): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
              ],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 100.0%"
                  ", transparent 100.0%)",
              ],
              (2, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 100.0%"
                  ", transparent 100.0%)",
              ],
              (2, 2): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 100.0%"
                  ", transparent 100.0%)",
              ],
          }
          assert result == expected
  
          result = df.style.bar(axis=1)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 50.0%, transparent 50.0%)",
              ],
              (0, 2): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 100.0%"
                  ", transparent 100.0%)",
              ],
              (1, 0): ["width: 10em", " height: 80%"],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 50.0%"
                  ", transparent 50.0%)",
              ],
              (1, 2): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 100.0%"
                  ", transparent 100.0%)",
              ],
              (2, 0): ["width: 10em", " height: 80%"],
              (2, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 50.0%"
                  ", transparent 50.0%)",
              ],
              (2, 2): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,#d65f5f 100.0%"
                  ", transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_pos_and_neg(self):
          df = DataFrame({"A": [-10, 0, 20, 90]})
  
          result = df.style.bar(align="mid", color=["#d65f5f", "#5fba7d"])._compute().ctx
  
          expected = {
              (0, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 10.0%, transparent 10.0%)",
              ],
              (1, 0): ["width: 10em", " height: 80%"],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 10.0%, #5fba7d 10.0%"
                  ", #5fba7d 30.0%, transparent 30.0%)",
              ],
              (3, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 10.0%, "
                  "#5fba7d 10.0%, #5fba7d 100.0%, "
                  "transparent 100.0%)",
              ],
          }
  
          assert result == expected
  
      def test_bar_align_mid_all_pos(self):
          df = DataFrame({"A": [10, 20, 50, 100]})
  
          result = df.style.bar(align="mid", color=["#d65f5f", "#5fba7d"])._compute().ctx
  
          expected = {
              (0, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#5fba7d 10.0%, transparent 10.0%)",
              ],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#5fba7d 20.0%, transparent 20.0%)",
              ],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#5fba7d 50.0%, transparent 50.0%)",
              ],
              (3, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#5fba7d 100.0%, transparent 100.0%)",
              ],
          }
  
          assert result == expected
  
      def test_bar_align_mid_all_neg(self):
          df = DataFrame({"A": [-100, -60, -30, -20]})
  
          result = df.style.bar(align="mid", color=["#d65f5f", "#5fba7d"])._compute().ctx
  
          expected = {
              (0, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 40.0%, "
                  "#d65f5f 40.0%, #d65f5f 100.0%, "
                  "transparent 100.0%)",
              ],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 70.0%, "
                  "#d65f5f 70.0%, #d65f5f 100.0%, "
                  "transparent 100.0%)",
              ],
              (3, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 80.0%, "
                  "#d65f5f 80.0%, #d65f5f 100.0%, "
                  "transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_zero_pos_and_neg(self):
          # See https://github.com/pandas-dev/pandas/pull/14757
          df = DataFrame({"A": [-10, 0, 20, 90]})
  
          result = (
              df.style.bar(align="zero", color=["#d65f5f", "#5fba7d"], width=90)
              ._compute()
              .ctx
          )
          expected = {
              (0, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 40.0%, #d65f5f 40.0%, "
                  "#d65f5f 45.0%, transparent 45.0%)",
              ],
              (1, 0): ["width: 10em", " height: 80%"],
              (2, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 45.0%, #5fba7d 45.0%, "
                  "#5fba7d 55.0%, transparent 55.0%)",
              ],
              (3, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 45.0%, #5fba7d 45.0%, "
                  "#5fba7d 90.0%, transparent 90.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_left_axis_none(self):
          df = DataFrame({"A": [0, 1], "B": [2, 4]})
          result = df.style.bar(axis=None)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 25.0%, transparent 25.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 50.0%, transparent 50.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_zero_axis_none(self):
          df = DataFrame({"A": [0, 1], "B": [-2, 4]})
          result = df.style.bar(align="zero", axis=None)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 50.0%, #d65f5f 50.0%, "
                  "#d65f5f 62.5%, transparent 62.5%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 25.0%, #d65f5f 25.0%, "
                  "#d65f5f 50.0%, transparent 50.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 50.0%, #d65f5f 50.0%, "
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_axis_none(self):
          df = DataFrame({"A": [0, 1], "B": [-2, 4]})
          result = df.style.bar(align="mid", axis=None)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 33.3%, #d65f5f 33.3%, "
                  "#d65f5f 50.0%, transparent 50.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 33.3%, transparent 33.3%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 33.3%, #d65f5f 33.3%, "
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_vmin(self):
          df = DataFrame({"A": [0, 1], "B": [-2, 4]})
          result = df.style.bar(align="mid", axis=None, vmin=-6)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 60.0%, #d65f5f 60.0%, "
                  "#d65f5f 70.0%, transparent 70.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 40.0%, #d65f5f 40.0%, "
                  "#d65f5f 60.0%, transparent 60.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 60.0%, #d65f5f 60.0%, "
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_vmax(self):
          df = DataFrame({"A": [0, 1], "B": [-2, 4]})
          result = df.style.bar(align="mid", axis=None, vmax=8)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 20.0%, #d65f5f 20.0%, "
                  "#d65f5f 30.0%, transparent 30.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 20.0%, transparent 20.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 20.0%, #d65f5f 20.0%, "
                  "#d65f5f 60.0%, transparent 60.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_vmin_vmax_wide(self):
          df = DataFrame({"A": [0, 1], "B": [-2, 4]})
          result = df.style.bar(align="mid", axis=None, vmin=-3, vmax=7)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 30.0%, #d65f5f 30.0%, "
                  "#d65f5f 40.0%, transparent 40.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 10.0%, #d65f5f 10.0%, "
                  "#d65f5f 30.0%, transparent 30.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 30.0%, #d65f5f 30.0%, "
                  "#d65f5f 70.0%, transparent 70.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_vmin_vmax_clipping(self):
          df = DataFrame({"A": [0, 1], "B": [-2, 4]})
          result = df.style.bar(align="mid", axis=None, vmin=-1, vmax=3)._compute().ctx
          expected = {
              (0, 0): ["width: 10em", " height: 80%"],
              (1, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 25.0%, #d65f5f 25.0%, "
                  "#d65f5f 50.0%, transparent 50.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 25.0%, transparent 25.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 25.0%, #d65f5f 25.0%, "
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_mid_nans(self):
          df = DataFrame({"A": [1, None], "B": [-1, 3]})
          result = df.style.bar(align="mid", axis=None)._compute().ctx
          expected = {
              (0, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 25.0%, #d65f5f 25.0%, "
                  "#d65f5f 50.0%, transparent 50.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg,"
                  "#d65f5f 25.0%, transparent 25.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 25.0%, #d65f5f 25.0%, "
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_align_zero_nans(self):
          df = DataFrame({"A": [1, None], "B": [-1, 2]})
          result = df.style.bar(align="zero", axis=None)._compute().ctx
          expected = {
              (0, 0): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 50.0%, #d65f5f 50.0%, "
                  "#d65f5f 75.0%, transparent 75.0%)",
              ],
              (0, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 25.0%, #d65f5f 25.0%, "
                  "#d65f5f 50.0%, transparent 50.0%)",
              ],
              (1, 1): [
                  "width: 10em",
                  " height: 80%",
                  "background: linear-gradient(90deg, "
                  "transparent 50.0%, #d65f5f 50.0%, "
                  "#d65f5f 100.0%, transparent 100.0%)",
              ],
          }
          assert result == expected
  
      def test_bar_bad_align_raises(self):
          df = DataFrame({"A": [-100, -60, -30, -20]})
          msg = "`align` must be one of {'left', 'zero',' mid'}"
          with pytest.raises(ValueError, match=msg):
              df.style.bar(align="poorly", color=["#d65f5f", "#5fba7d"])
  
      def test_format_with_na_rep(self):
          # GH 21527 28358
          df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
  
          ctx = df.style.format(None, na_rep="-")._translate()
          assert ctx["body"][0][1]["display_value"] == "-"
          assert ctx["body"][0][2]["display_value"] == "-"
  
          ctx = df.style.format("{:.2%}", na_rep="-")._translate()
          assert ctx["body"][0][1]["display_value"] == "-"
          assert ctx["body"][0][2]["display_value"] == "-"
          assert ctx["body"][1][1]["display_value"] == "110.00%"
          assert ctx["body"][1][2]["display_value"] == "120.00%"
  
          ctx = df.style.format("{:.2%}", na_rep="-", subset=["B"])._translate()
          assert ctx["body"][0][2]["display_value"] == "-"
          assert ctx["body"][1][2]["display_value"] == "120.00%"
  
      def test_init_with_na_rep(self):
          # GH 21527 28358
          df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
  
          ctx = Styler(df, na_rep="NA")._translate()
          assert ctx["body"][0][1]["display_value"] == "NA"
          assert ctx["body"][0][2]["display_value"] == "NA"
  
      def test_set_na_rep(self):
          # GH 21527 28358
          df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
  
          ctx = df.style.set_na_rep("NA")._translate()
          assert ctx["body"][0][1]["display_value"] == "NA"
          assert ctx["body"][0][2]["display_value"] == "NA"
  
          ctx = (
              df.style.set_na_rep("NA")
              .format(None, na_rep="-", subset=["B"])
              ._translate()
          )
          assert ctx["body"][0][1]["display_value"] == "NA"
          assert ctx["body"][0][2]["display_value"] == "-"
  
      def test_format_non_numeric_na(self):
          # GH 21527 28358
          df = DataFrame(
              {
                  "object": [None, np.nan, "foo"],
                  "datetime": [None, pd.NaT, pd.Timestamp("20120101")],
              }
          )
  
          ctx = df.style.set_na_rep("NA")._translate()
          assert ctx["body"][0][1]["display_value"] == "NA"
          assert ctx["body"][0][2]["display_value"] == "NA"
          assert ctx["body"][1][1]["display_value"] == "NA"
          assert ctx["body"][1][2]["display_value"] == "NA"
  
          ctx = df.style.format(None, na_rep="-")._translate()
          assert ctx["body"][0][1]["display_value"] == "-"
          assert ctx["body"][0][2]["display_value"] == "-"
          assert ctx["body"][1][1]["display_value"] == "-"
          assert ctx["body"][1][2]["display_value"] == "-"
  
      def test_format_with_bad_na_rep(self):
          # GH 21527 28358
          df = DataFrame([[None, None], [1.1, 1.2]], columns=["A", "B"])
          msg = "Expected a string, got -1 instead"
          with pytest.raises(TypeError, match=msg):
              df.style.format(None, na_rep=-1)
  
      def test_highlight_null(self, null_color="red"):
          df = DataFrame({"A": [0, np.nan]})
          result = df.style.highlight_null()._compute().ctx
          expected = {(1, 0): ["background-color: red"]}
          assert result == expected
  
      def test_highlight_null_subset(self):
          # GH 31345
          df = DataFrame({"A": [0, np.nan], "B": [0, np.nan]})
          result = (
              df.style.highlight_null(null_color="red", subset=["A"])
              .highlight_null(null_color="green", subset=["B"])
              ._compute()
              .ctx
          )
          expected = {
              (1, 0): ["background-color: red"],
              (1, 1): ["background-color: green"],
          }
          assert result == expected
  
      def test_nonunique_raises(self):
          df = DataFrame([[1, 2]], columns=["A", "A"])
          msg = "style is not supported for non-unique indices."
          with pytest.raises(ValueError, match=msg):
              df.style
  
          with pytest.raises(ValueError, match=msg):
              Styler(df)
  
      def test_caption(self):
          styler = Styler(self.df, caption="foo")
          result = styler.render()
          assert all(["caption" in result, "foo" in result])
  
          styler = self.df.style
          result = styler.set_caption("baz")
          assert styler is result
          assert styler.caption == "baz"
  
      def test_uuid(self):
          styler = Styler(self.df, uuid="abc123")
          result = styler.render()
          assert "abc123" in result
  
          styler = self.df.style
          result = styler.set_uuid("aaa")
          assert result is styler
          assert result.uuid == "aaa"
  
      def test_unique_id(self):
          # See https://github.com/pandas-dev/pandas/issues/16780
          df = DataFrame({"a": [1, 3, 5, 6], "b": [2, 4, 12, 21]})
          result = df.style.render(uuid="test")
          assert "test" in result
          ids = re.findall('id="(.*?)"', result)
          assert np.unique(ids).size == len(ids)
  
      def test_table_styles(self):
          style = [{"selector": "th", "props": [("foo", "bar")]}]  # default format
          styler = Styler(self.df, table_styles=style)
          result = " ".join(styler.render().split())
          assert "th { foo: bar; }" in result
  
          styler = self.df.style
          result = styler.set_table_styles(style)
          assert styler is result
          assert styler.table_styles == style
  
          # GH 39563
          style = [{"selector": "th", "props": "foo:bar;"}]  # css string format
          styler = self.df.style.set_table_styles(style)
          result = " ".join(styler.render().split())
          assert "th { foo: bar; }" in result
  
      def test_maybe_convert_css_to_tuples(self):
          expected = [("a", "b"), ("c", "d e")]
          assert _maybe_convert_css_to_tuples("a:b;c:d e;") == expected
          assert _maybe_convert_css_to_tuples("a: b ;c:  d e  ") == expected
          expected = []
          assert _maybe_convert_css_to_tuples("") == expected
  
      def test_maybe_convert_css_to_tuples_err(self):
          msg = "Styles supplied as string must follow CSS rule formats"
          with pytest.raises(ValueError, match=msg):
              _maybe_convert_css_to_tuples("err")
  
      def test_table_attributes(self):
          attributes = 'class="foo" data-bar'
          styler = Styler(self.df, table_attributes=attributes)
          result = styler.render()
          assert 'class="foo" data-bar' in result
  
          result = self.df.style.set_table_attributes(attributes).render()
          assert 'class="foo" data-bar' in result
  
      def test_precision(self):
          with pd.option_context("display.precision", 10):
              s = Styler(self.df)
          assert s.precision == 10
          s = Styler(self.df, precision=2)
          assert s.precision == 2
  
          s2 = s.set_precision(4)
          assert s is s2
          assert s.precision == 4
  
      def test_apply_none(self):
          def f(x):
              return DataFrame(
                  np.where(x == x.max(), "color: red", ""),
                  index=x.index,
                  columns=x.columns,
              )
  
          result = DataFrame([[1, 2], [3, 4]]).style.apply(f, axis=None)._compute().ctx
          assert result[(1, 1)] == ["color: red"]
  
      def test_trim(self):
          result = self.df.style.render()  # trim=True
          assert result.count("#") == 0
  
          result = self.df.style.highlight_max().render()
          assert result.count("#") == len(self.df.columns)
  
      def test_highlight_max(self):
          df = DataFrame([[1, 2], [3, 4]], columns=["A", "B"])
          # max(df) = min(-df)
          for max_ in [True, False]:
              if max_:
                  attr = "highlight_max"
              else:
                  df = -df
                  attr = "highlight_min"
              result = getattr(df.style, attr)()._compute().ctx
              assert result[(1, 1)] == ["background-color: yellow"]
  
              result = getattr(df.style, attr)(color="green")._compute().ctx
              assert result[(1, 1)] == ["background-color: green"]
  
              result = getattr(df.style, attr)(subset="A")._compute().ctx
              assert result[(1, 0)] == ["background-color: yellow"]
  
              result = getattr(df.style, attr)(axis=0)._compute().ctx
              expected = {
                  (1, 0): ["background-color: yellow"],
                  (1, 1): ["background-color: yellow"],
              }
              assert result == expected
  
              result = getattr(df.style, attr)(axis=1)._compute().ctx
              expected = {
                  (0, 1): ["background-color: yellow"],
                  (1, 1): ["background-color: yellow"],
              }
              assert result == expected
  
          # separate since we can't negate the strs
          df["C"] = ["a", "b"]
          result = df.style.highlight_max()._compute().ctx
          expected = {(1, 1): ["background-color: yellow"]}
  
          result = df.style.highlight_min()._compute().ctx
          expected = {(0, 0): ["background-color: yellow"]}
  
      def test_export(self):
          f = lambda x: "color: red" if x > 0 else "color: blue"
          g = lambda x, z: f"color: {z}" if x > 0 else f"color: {z}"
          style1 = self.styler
          style1.applymap(f).applymap(g, z="b").highlight_max()
          result = style1.export()
          style2 = self.df.style
          style2.use(result)
          assert style1._todo == style2._todo
          style2.render()
  
      def test_display_format(self):
          df = DataFrame(np.random.random(size=(2, 2)))
          ctx = df.style.format("{:0.1f}")._translate()
  
          assert all(["display_value" in c for c in row] for row in ctx["body"])
          assert all(
              [len(c["display_value"]) <= 3 for c in row[1:]] for row in ctx["body"]
          )
          assert len(ctx["body"][0][1]["display_value"].lstrip("-")) <= 3
  
      def test_display_format_raises(self):
          df = DataFrame(np.random.randn(2, 2))
          msg = "Expected a template string or callable, got 5 instead"
          with pytest.raises(TypeError, match=msg):
              df.style.format(5)
  
          msg = "Expected a template string or callable, got True instead"
          with pytest.raises(TypeError, match=msg):
              df.style.format(True)
  
      def test_display_set_precision(self):
          # Issue #13257
          df = DataFrame(data=[[1.0, 2.0090], [3.2121, 4.566]], columns=["a", "b"])
          s = Styler(df)
  
          ctx = s.set_precision(1)._translate()
  
          assert s.precision == 1
          assert ctx["body"][0][1]["display_value"] == "1.0"
          assert ctx["body"][0][2]["display_value"] == "2.0"
          assert ctx["body"][1][1]["display_value"] == "3.2"
          assert ctx["body"][1][2]["display_value"] == "4.6"
  
          ctx = s.set_precision(2)._translate()
          assert s.precision == 2
          assert ctx["body"][0][1]["display_value"] == "1.00"
          assert ctx["body"][0][2]["display_value"] == "2.01"
          assert ctx["body"][1][1]["display_value"] == "3.21"
          assert ctx["body"][1][2]["display_value"] == "4.57"
  
          ctx = s.set_precision(3)._translate()
          assert s.precision == 3
          assert ctx["body"][0][1]["display_value"] == "1.000"
          assert ctx["body"][0][2]["display_value"] == "2.009"
          assert ctx["body"][1][1]["display_value"] == "3.212"
          assert ctx["body"][1][2]["display_value"] == "4.566"
  
      def test_display_subset(self):
          df = DataFrame([[0.1234, 0.1234], [1.1234, 1.1234]], columns=["a", "b"])
          ctx = df.style.format(
              {"a": "{:0.1f}", "b": "{0:.2%}"}, subset=pd.IndexSlice[0, :]
          )._translate()
          expected = "0.1"
          raw_11 = "1.123400"
          assert ctx["body"][0][1]["display_value"] == expected
          assert ctx["body"][1][1]["display_value"] == raw_11
          assert ctx["body"][0][2]["display_value"] == "12.34%"
  
          ctx = df.style.format("{:0.1f}", subset=pd.IndexSlice[0, :])._translate()
          assert ctx["body"][0][1]["display_value"] == expected
          assert ctx["body"][1][1]["display_value"] == raw_11
  
          ctx = df.style.format("{:0.1f}", subset=pd.IndexSlice["a"])._translate()
          assert ctx["body"][0][1]["display_value"] == expected
          assert ctx["body"][0][2]["display_value"] == "0.123400"
  
          ctx = df.style.format("{:0.1f}", subset=pd.IndexSlice[0, "a"])._translate()
          assert ctx["body"][0][1]["display_value"] == expected
          assert ctx["body"][1][1]["display_value"] == raw_11
  
          ctx = df.style.format(
              "{:0.1f}", subset=pd.IndexSlice[[0, 1], ["a"]]
          )._translate()
          assert ctx["body"][0][1]["display_value"] == expected
          assert ctx["body"][1][1]["display_value"] == "1.1"
          assert ctx["body"][0][2]["display_value"] == "0.123400"
          assert ctx["body"][1][2]["display_value"] == raw_11
  
      def test_display_dict(self):
          df = DataFrame([[0.1234, 0.1234], [1.1234, 1.1234]], columns=["a", "b"])
          ctx = df.style.format({"a": "{:0.1f}", "b": "{0:.2%}"})._translate()
          assert ctx["body"][0][1]["display_value"] == "0.1"
          assert ctx["body"][0][2]["display_value"] == "12.34%"
          df["c"] = ["aaa", "bbb"]
          ctx = df.style.format({"a": "{:0.1f}", "c": str.upper})._translate()
          assert ctx["body"][0][1]["display_value"] == "0.1"
          assert ctx["body"][0][3]["display_value"] == "AAA"
  
      def test_bad_apply_shape(self):
          df = DataFrame([[1, 2], [3, 4]])
          msg = "returned the wrong shape"
          with pytest.raises(ValueError, match=msg):
              df.style._apply(lambda x: "x", subset=pd.IndexSlice[[0, 1], :])
  
          with pytest.raises(ValueError, match=msg):
              df.style._apply(lambda x: [""], subset=pd.IndexSlice[[0, 1], :])
  
          with pytest.raises(ValueError, match=msg):
              df.style._apply(lambda x: ["", "", "", ""])
  
          with pytest.raises(ValueError, match=msg):
              df.style._apply(lambda x: ["", "", ""], subset=1)
  
          msg = "Length mismatch: Expected axis has 3 elements"
          with pytest.raises(ValueError, match=msg):
              df.style._apply(lambda x: ["", "", ""], axis=1)
  
      def test_apply_bad_return(self):
          def f(x):
              return ""
  
          df = DataFrame([[1, 2], [3, 4]])
          msg = "must return a DataFrame when passed to `Styler.apply` with axis=None"
          with pytest.raises(TypeError, match=msg):
              df.style._apply(f, axis=None)
  
      def test_apply_bad_labels(self):
          def f(x):
              return DataFrame(index=[1, 2], columns=["a", "b"])
  
          df = DataFrame([[1, 2], [3, 4]])
          msg = "must have identical index and columns as the input"
          with pytest.raises(ValueError, match=msg):
              df.style._apply(f, axis=None)
  
      def test_get_level_lengths(self):
          index = pd.MultiIndex.from_product([["a", "b"], [0, 1, 2]])
          expected = {
              (0, 0): 3,
              (0, 3): 3,
              (1, 0): 1,
              (1, 1): 1,
              (1, 2): 1,
              (1, 3): 1,
              (1, 4): 1,
              (1, 5): 1,
          }
          result = _get_level_lengths(index)
          tm.assert_dict_equal(result, expected)
  
      def test_get_level_lengths_un_sorted(self):
          index = pd.MultiIndex.from_arrays([[1, 1, 2, 1], ["a", "b", "b", "d"]])
          expected = {
              (0, 0): 2,
              (0, 2): 1,
              (0, 3): 1,
              (1, 0): 1,
              (1, 1): 1,
              (1, 2): 1,
              (1, 3): 1,
          }
          result = _get_level_lengths(index)
          tm.assert_dict_equal(result, expected)
  
      def test_mi_sparse(self):
          df = DataFrame(
              {"A": [1, 2]}, index=pd.MultiIndex.from_arrays([["a", "a"], [0, 1]])
          )
  
          result = df.style._translate()
          body_0 = result["body"][0][0]
          expected_0 = {
              "value": "a",
              "display_value": "a",
              "is_visible": True,
              "type": "th",
              "attributes": ['rowspan="2"'],
              "class": "row_heading level0 row0",
              "id": "level0_row0",
          }
          tm.assert_dict_equal(body_0, expected_0)
  
          body_1 = result["body"][0][1]
          expected_1 = {
              "value": 0,
              "display_value": 0,
              "is_visible": True,
              "type": "th",
              "class": "row_heading level1 row0",
              "id": "level1_row0",
          }
          tm.assert_dict_equal(body_1, expected_1)
  
          body_10 = result["body"][1][0]
          expected_10 = {
              "value": "a",
              "display_value": "a",
              "is_visible": False,
              "type": "th",
              "class": "row_heading level0 row1",
              "id": "level0_row1",
          }
          tm.assert_dict_equal(body_10, expected_10)
  
          head = result["head"][0]
          expected = [
              {
                  "type": "th",
                  "class": "blank",
                  "value": "",
                  "is_visible": True,
                  "display_value": "",
              },
              {
                  "type": "th",
                  "class": "blank level0",
                  "value": "",
                  "is_visible": True,
                  "display_value": "",
              },
              {
                  "type": "th",
                  "class": "col_heading level0 col0",
                  "value": "A",
                  "is_visible": True,
                  "display_value": "A",
              },
          ]
          assert head == expected
  
      def test_mi_sparse_disabled(self):
          with pd.option_context("display.multi_sparse", False):
              df = DataFrame(
                  {"A": [1, 2]}, index=pd.MultiIndex.from_arrays([["a", "a"], [0, 1]])
              )
              result = df.style._translate()
          body = result["body"]
          for row in body:
              assert "attributes" not in row[0]
  
      def test_mi_sparse_index_names(self):
          df = DataFrame(
              {"A": [1, 2]},
              index=pd.MultiIndex.from_arrays(
                  [["a", "a"], [0, 1]], names=["idx_level_0", "idx_level_1"]
              ),
          )
          result = df.style._translate()
          head = result["head"][1]
          expected = [
              {"class": "index_name level0", "value": "idx_level_0", "type": "th"},
              {"class": "index_name level1", "value": "idx_level_1", "type": "th"},
              {"class": "blank", "value": "", "type": "th"},
          ]
  
          assert head == expected
  
      def test_mi_sparse_column_names(self):
          df = DataFrame(
              np.arange(16).reshape(4, 4),
              index=pd.MultiIndex.from_arrays(
                  [["a", "a", "b", "a"], [0, 1, 1, 2]],
                  names=["idx_level_0", "idx_level_1"],
              ),
              columns=pd.MultiIndex.from_arrays(
                  [["C1", "C1", "C2", "C2"], [1, 0, 1, 0]], names=["col_0", "col_1"]
              ),
          )
          result = df.style._translate()
          head = result["head"][1]
          expected = [
              {
                  "class": "blank",
                  "value": "",
                  "display_value": "",
                  "type": "th",
                  "is_visible": True,
              },
              {
                  "class": "index_name level1",
                  "value": "col_1",
                  "display_value": "col_1",
                  "is_visible": True,
                  "type": "th",
              },
              {
                  "class": "col_heading level1 col0",
                  "display_value": 1,
                  "is_visible": True,
                  "type": "th",
                  "value": 1,
              },
              {
                  "class": "col_heading level1 col1",
                  "display_value": 0,
                  "is_visible": True,
                  "type": "th",
                  "value": 0,
              },
              {
                  "class": "col_heading level1 col2",
                  "display_value": 1,
                  "is_visible": True,
                  "type": "th",
                  "value": 1,
              },
              {
                  "class": "col_heading level1 col3",
                  "display_value": 0,
                  "is_visible": True,
                  "type": "th",
                  "value": 0,
              },
          ]
          assert head == expected
  
      def test_hide_single_index(self):
          # GH 14194
          # single unnamed index
          ctx = self.df.style._translate()
          assert ctx["body"][0][0]["is_visible"]
          assert ctx["head"][0][0]["is_visible"]
          ctx2 = self.df.style.hide_index()._translate()
          assert not ctx2["body"][0][0]["is_visible"]
          assert not ctx2["head"][0][0]["is_visible"]
  
          # single named index
          ctx3 = self.df.set_index("A").style._translate()
          assert ctx3["body"][0][0]["is_visible"]
          assert len(ctx3["head"]) == 2  # 2 header levels
          assert ctx3["head"][0][0]["is_visible"]
  
          ctx4 = self.df.set_index("A").style.hide_index()._translate()
          assert not ctx4["body"][0][0]["is_visible"]
          assert len(ctx4["head"]) == 1  # only 1 header levels
          assert not ctx4["head"][0][0]["is_visible"]
  
      def test_hide_multiindex(self):
          # GH 14194
          df = DataFrame(
              {"A": [1, 2]},
              index=pd.MultiIndex.from_arrays(
                  [["a", "a"], [0, 1]], names=["idx_level_0", "idx_level_1"]
              ),
          )
          ctx1 = df.style._translate()
          # tests for 'a' and '0'
          assert ctx1["body"][0][0]["is_visible"]
          assert ctx1["body"][0][1]["is_visible"]
          # check for blank header rows
          assert ctx1["head"][0][0]["is_visible"]
          assert ctx1["head"][0][1]["is_visible"]
  
          ctx2 = df.style.hide_index()._translate()
          # tests for 'a' and '0'
          assert not ctx2["body"][0][0]["is_visible"]
          assert not ctx2["body"][0][1]["is_visible"]
          # check for blank header rows
          assert not ctx2["head"][0][0]["is_visible"]
          assert not ctx2["head"][0][1]["is_visible"]
  
      def test_hide_columns_single_level(self):
          # GH 14194
          # test hiding single column
          ctx = self.df.style._translate()
          assert ctx["head"][0][1]["is_visible"]
          assert ctx["head"][0][1]["display_value"] == "A"
          assert ctx["head"][0][2]["is_visible"]
          assert ctx["head"][0][2]["display_value"] == "B"
          assert ctx["body"][0][1]["is_visible"]  # col A, row 1
          assert ctx["body"][1][2]["is_visible"]  # col B, row 1
  
          ctx = self.df.style.hide_columns("A")._translate()
          assert not ctx["head"][0][1]["is_visible"]
          assert not ctx["body"][0][1]["is_visible"]  # col A, row 1
          assert ctx["body"][1][2]["is_visible"]  # col B, row 1
  
          # test hiding mulitiple columns
          ctx = self.df.style.hide_columns(["A", "B"])._translate()
          assert not ctx["head"][0][1]["is_visible"]
          assert not ctx["head"][0][2]["is_visible"]
          assert not ctx["body"][0][1]["is_visible"]  # col A, row 1
          assert not ctx["body"][1][2]["is_visible"]  # col B, row 1
  
      def test_hide_columns_mult_levels(self):
          # GH 14194
          # setup dataframe with multiple column levels and indices
          i1 = pd.MultiIndex.from_arrays(
              [["a", "a"], [0, 1]], names=["idx_level_0", "idx_level_1"]
          )
          i2 = pd.MultiIndex.from_arrays(
              [["b", "b"], [0, 1]], names=["col_level_0", "col_level_1"]
          )
          df = DataFrame([[1, 2], [3, 4]], index=i1, columns=i2)
          ctx = df.style._translate()
          # column headers
          assert ctx["head"][0][2]["is_visible"]
          assert ctx["head"][1][2]["is_visible"]
          assert ctx["head"][1][3]["display_value"] == 1
          # indices
          assert ctx["body"][0][0]["is_visible"]
          # data
          assert ctx["body"][1][2]["is_visible"]
          assert ctx["body"][1][2]["display_value"] == 3
          assert ctx["body"][1][3]["is_visible"]
          assert ctx["body"][1][3]["display_value"] == 4
  
          # hide top column level, which hides both columns
          ctx = df.style.hide_columns("b")._translate()
          assert not ctx["head"][0][2]["is_visible"]  # b
          assert not ctx["head"][1][2]["is_visible"]  # 0
          assert not ctx["body"][1][2]["is_visible"]  # 3
          assert ctx["body"][0][0]["is_visible"]  # index
  
          # hide first column only
          ctx = df.style.hide_columns([("b", 0)])._translate()
          assert ctx["head"][0][2]["is_visible"]  # b
          assert not ctx["head"][1][2]["is_visible"]  # 0
          assert not ctx["body"][1][2]["is_visible"]  # 3
          assert ctx["body"][1][3]["is_visible"]
          assert ctx["body"][1][3]["display_value"] == 4
  
          # hide second column and index
          ctx = df.style.hide_columns([("b", 1)]).hide_index()._translate()
          assert not ctx["body"][0][0]["is_visible"]  # index
          assert ctx["head"][0][2]["is_visible"]  # b
          assert ctx["head"][1][2]["is_visible"]  # 0
          assert not ctx["head"][1][3]["is_visible"]  # 1
          assert not ctx["body"][1][3]["is_visible"]  # 4
          assert ctx["body"][1][2]["is_visible"]
          assert ctx["body"][1][2]["display_value"] == 3
  
      def test_pipe(self):
          def set_caption_from_template(styler, a, b):
              return styler.set_caption(f"Dataframe with a = {a} and b = {b}")
  
          styler = self.df.style.pipe(set_caption_from_template, "A", b="B")
          assert "Dataframe with a = A and b = B" in styler.render()
  
          # Test with an argument that is a (callable, keyword_name) pair.
          def f(a, b, styler):
              return (a, b, styler)
  
          styler = self.df.style
          result = styler.pipe((f, "styler"), a=1, b=2)
          assert result == (1, 2, styler)
  
      def test_no_cell_ids(self):
          # GH 35588
          # GH 35663
          df = DataFrame(data=[[0]])
          styler = Styler(df, uuid="_", cell_ids=False)
          styler.render()
          s = styler.render()  # render twice to ensure ctx is not updated
          assert s.find('<td  class="data row0 col0" >') != -1
  
      @pytest.mark.parametrize(
          "classes",
          [
              DataFrame(
                  data=[["", "test-class"], [np.nan, None]],
                  columns=["A", "B"],
                  index=["a", "b"],
              ),
              DataFrame(data=[["test-class"]], columns=["B"], index=["a"]),
              DataFrame(data=[["test-class", "unused"]], columns=["B", "C"], index=["a"]),
          ],
      )
      def test_set_data_classes(self, classes):
          # GH 36159
          df = DataFrame(data=[[0, 1], [2, 3]], columns=["A", "B"], index=["a", "b"])
          s = Styler(df, uuid_len=0, cell_ids=False).set_td_classes(classes).render()
          assert '<td  class="data row0 col0" >0</td>' in s
          assert '<td  class="data row0 col1 test-class" >1</td>' in s
          assert '<td  class="data row1 col0" >2</td>' in s
          assert '<td  class="data row1 col1" >3</td>' in s
          # GH 39317
          s = Styler(df, uuid_len=0, cell_ids=True).set_td_classes(classes).render()
          assert '<td id="T__row0_col0" class="data row0 col0" >0</td>' in s
          assert '<td id="T__row0_col1" class="data row0 col1 test-class" >1</td>' in s
          assert '<td id="T__row1_col0" class="data row1 col0" >2</td>' in s
          assert '<td id="T__row1_col1" class="data row1 col1" >3</td>' in s
  
      def test_set_data_classes_reindex(self):
          # GH 39317
          df = DataFrame(
              data=[[0, 1, 2], [3, 4, 5], [6, 7, 8]], columns=[0, 1, 2], index=[0, 1, 2]
          )
          classes = DataFrame(
              data=[["mi", "ma"], ["mu", "mo"]],
              columns=[0, 2],
              index=[0, 2],
          )
          s = Styler(df, uuid_len=0).set_td_classes(classes).render()
          assert '<td id="T__row0_col0" class="data row0 col0 mi" >0</td>' in s
          assert '<td id="T__row0_col2" class="data row0 col2 ma" >2</td>' in s
          assert '<td id="T__row1_col1" class="data row1 col1" >4</td>' in s
          assert '<td id="T__row2_col0" class="data row2 col0 mu" >6</td>' in s
          assert '<td id="T__row2_col2" class="data row2 col2 mo" >8</td>' in s
  
      def test_chaining_table_styles(self):
          # GH 35607
          df = DataFrame(data=[[0, 1], [1, 2]], columns=["A", "B"])
          styler = df.style.set_table_styles(
              [{"selector": "", "props": [("background-color", "yellow")]}]
          ).set_table_styles(
              [{"selector": ".col0", "props": [("background-color", "blue")]}],
              overwrite=False,
          )
          assert len(styler.table_styles) == 2
  
      def test_column_and_row_styling(self):
          # GH 35607
          df = DataFrame(data=[[0, 1], [1, 2]], columns=["A", "B"])
          s = Styler(df, uuid_len=0)
          s = s.set_table_styles({"A": [{"selector": "", "props": [("color", "blue")]}]})
          assert "#T__ .col0 {\n          color: blue;\n    }" in s.render()
          s = s.set_table_styles(
              {0: [{"selector": "", "props": [("color", "blue")]}]}, axis=1
          )
          assert "#T__ .row0 {\n          color: blue;\n    }" in s.render()
  
      def test_colspan_w3(self):
          # GH 36223
          df = DataFrame(data=[[1, 2]], columns=[["l0", "l0"], ["l1a", "l1b"]])
          s = Styler(df, uuid="_", cell_ids=False)
          assert '<th class="col_heading level0 col0" colspan="2">l0</th>' in s.render()
  
      def test_rowspan_w3(self):
          # GH 38533
          df = DataFrame(data=[[1, 2]], index=[["l0", "l0"], ["l1a", "l1b"]])
          s = Styler(df, uuid="_", cell_ids=False)
          assert (
              '<th id="T___level0_row0" class="row_heading '
              'level0 row0" rowspan="2">l0</th>' in s.render()
          )
  
      @pytest.mark.parametrize("len_", [1, 5, 32, 33, 100])
      def test_uuid_len(self, len_):
          # GH 36345
          df = DataFrame(data=[["A"]])
          s = Styler(df, uuid_len=len_, cell_ids=False).render()
          strt = s.find('id="T_')
          end = s[strt + 6 :].find('"')
          if len_ > 32:
              assert end == 32 + 1
          else:
              assert end == len_ + 1
  
      @pytest.mark.parametrize("len_", [-2, "bad", None])
      def test_uuid_len_raises(self, len_):
          # GH 36345
          df = DataFrame(data=[["A"]])
          msg = "``uuid_len`` must be an integer in range \\[0, 32\\]."
          with pytest.raises(TypeError, match=msg):
              Styler(df, uuid_len=len_, cell_ids=False).render()
  
      @pytest.mark.parametrize(
          "ttips",
          [
              DataFrame(
                  data=[["Min", "Max"], [np.nan, ""]],
                  columns=["A", "B"],
                  index=["a", "b"],
              ),
              DataFrame(data=[["Max", "Min"]], columns=["B", "A"], index=["a"]),
              DataFrame(
                  data=[["Min", "Max", None]], columns=["A", "B", "C"], index=["a"]
              ),
          ],
      )
      def test_tooltip_render(self, ttips):
          # GH 21266
          df = DataFrame(data=[[0, 3], [1, 2]], columns=["A", "B"], index=["a", "b"])
          s = Styler(df, uuid_len=0).set_tooltips(ttips).render()
  
          # test tooltip table level class
          assert "#T__ .pd-t {\n          visibility: hidden;\n" in s
  
          # test 'Min' tooltip added
          assert (
              "#T__ #T__row0_col0:hover .pd-t {\n          visibility: visible;\n    }  "
              + '  #T__ #T__row0_col0 .pd-t::after {\n          content: "Min";\n    }'
              in s
          )
          assert (
              '<td id="T__row0_col0" class="data row0 col0" >0<span class="pd-t">'
              + "</span></td>"
              in s
          )
  
          # test 'Max' tooltip added
          assert (
              "#T__ #T__row0_col1:hover .pd-t {\n          visibility: visible;\n    }  "
              + '  #T__ #T__row0_col1 .pd-t::after {\n          content: "Max";\n    }'
              in s
          )
          assert (
              '<td id="T__row0_col1" class="data row0 col1" >3<span class="pd-t">'
              + "</span></td>"
              in s
          )
  
      def test_tooltip_reindex(self):
          # GH 39317
          df = DataFrame(
              data=[[0, 1, 2], [3, 4, 5], [6, 7, 8]], columns=[0, 1, 2], index=[0, 1, 2]
          )
          ttips = DataFrame(
              data=[["Mi", "Ma"], ["Mu", "Mo"]],
              columns=[0, 2],
              index=[0, 2],
          )
          s = Styler(df, uuid_len=0).set_tooltips(DataFrame(ttips)).render()
          assert '#T__ #T__row0_col0 .pd-t::after {\n          content: "Mi";\n    }' in s
          assert '#T__ #T__row0_col2 .pd-t::after {\n          content: "Ma";\n    }' in s
          assert '#T__ #T__row2_col0 .pd-t::after {\n          content: "Mu";\n    }' in s
          assert '#T__ #T__row2_col2 .pd-t::after {\n          content: "Mo";\n    }' in s
  
      def test_tooltip_ignored(self):
          # GH 21266
          df = DataFrame(data=[[0, 1], [2, 3]])
          s = Styler(df).set_tooltips_class("pd-t").render()  # no set_tooltips()
          assert '<style  type="text/css" >\n</style>' in s
          assert '<span class="pd-t"></span>' not in s
  
      def test_tooltip_class(self):
          # GH 21266
          df = DataFrame(data=[[0, 1], [2, 3]])
          s = (
              Styler(df, uuid_len=0)
              .set_tooltips(DataFrame([["tooltip"]]))
              .set_tooltips_class(name="other-class", properties=[("color", "green")])
              .render()
          )
          assert "#T__ .other-class {\n          color: green;\n" in s
          assert (
              '#T__ #T__row0_col0 .other-class::after {\n          content: "tooltip";\n'
              in s
          )
  
          # GH 39563
          s = (
              Styler(df, uuid_len=0)
              .set_tooltips(DataFrame([["tooltip"]]))
              .set_tooltips_class(name="other-class", properties="color:green;color:red;")
              .render()
          )
          assert (
              "#T__ .other-class {\n          color: green;\n          color: red;\n "
              in s
          )
  
  
  @td.skip_if_no_mpl
  class TestStylerMatplotlibDep:
      def test_background_gradient(self):
          df = DataFrame([[1, 2], [2, 4]], columns=["A", "B"])
  
          for c_map in [None, "YlOrRd"]:
              result = df.style.background_gradient(cmap=c_map)._compute().ctx
              assert all("#" in x[0] for x in result.values())
              assert result[(0, 0)] == result[(0, 1)]
              assert result[(1, 0)] == result[(1, 1)]
  
          result = (
              df.style.background_gradient(subset=pd.IndexSlice[1, "A"])._compute().ctx
          )
  
          assert result[(1, 0)] == ["background-color: #fff7fb", "color: #000000"]
  
      @pytest.mark.parametrize(
          "c_map,expected",
          [
              (
                  None,
                  {
                      (0, 0): ["background-color: #440154", "color: #f1f1f1"],
                      (1, 0): ["background-color: #fde725", "color: #000000"],
                  },
              ),
              (
                  "YlOrRd",
                  {
                      (0, 0): ["background-color: #ffffcc", "color: #000000"],
                      (1, 0): ["background-color: #800026", "color: #f1f1f1"],
                  },
              ),
          ],
      )
      def test_text_color_threshold(self, c_map, expected):
          df = DataFrame([1, 2], columns=["A"])
          result = df.style.background_gradient(cmap=c_map)._compute().ctx
          assert result == expected
  
      @pytest.mark.parametrize("text_color_threshold", [1.1, "1", -1, [2, 2]])
      def test_text_color_threshold_raises(self, text_color_threshold):
          df = DataFrame([[1, 2], [2, 4]], columns=["A", "B"])
          msg = "`text_color_threshold` must be a value from 0 to 1."
          with pytest.raises(ValueError, match=msg):
              df.style.background_gradient(
                  text_color_threshold=text_color_threshold
              )._compute()
  
      @td.skip_if_no_mpl
      def test_background_gradient_axis(self):
          df = DataFrame([[1, 2], [2, 4]], columns=["A", "B"])
  
          low = ["background-color: #f7fbff", "color: #000000"]
          high = ["background-color: #08306b", "color: #f1f1f1"]
          mid = ["background-color: #abd0e6", "color: #000000"]
          result = df.style.background_gradient(cmap="Blues", axis=0)._compute().ctx
          assert result[(0, 0)] == low
          assert result[(0, 1)] == low
          assert result[(1, 0)] == high
          assert result[(1, 1)] == high
  
          result = df.style.background_gradient(cmap="Blues", axis=1)._compute().ctx
          assert result[(0, 0)] == low
          assert result[(0, 1)] == high
          assert result[(1, 0)] == low
          assert result[(1, 1)] == high
  
          result = df.style.background_gradient(cmap="Blues", axis=None)._compute().ctx
          assert result[(0, 0)] == low
          assert result[(0, 1)] == mid
          assert result[(1, 0)] == mid
          assert result[(1, 1)] == high
  
      def test_background_gradient_vmin_vmax(self):
          # GH 12145
          df = DataFrame(range(5))
          ctx = df.style.background_gradient(vmin=1, vmax=3)._compute().ctx
          assert ctx[(0, 0)] == ctx[(1, 0)]
          assert ctx[(4, 0)] == ctx[(3, 0)]
  
      def test_background_gradient_int64(self):
          # GH 28869
          df1 = pd.Series(range(3)).to_frame()
          df2 = pd.Series(range(3), dtype="Int64").to_frame()
          ctx1 = df1.style.background_gradient()._compute().ctx
          ctx2 = df2.style.background_gradient()._compute().ctx
          assert ctx2[(0, 0)] == ctx1[(0, 0)]
          assert ctx2[(1, 0)] == ctx1[(1, 0)]
          assert ctx2[(2, 0)] == ctx1[(2, 0)]
  
  
  def test_block_names():
      # catch accidental removal of a block
      expected = {
          "before_style",
          "style",
          "table_styles",
          "before_cellstyle",
          "cellstyle",
          "before_table",
          "table",
          "caption",
          "thead",
          "tbody",
          "after_table",
          "before_head_rows",
          "head_tr",
          "after_head_rows",
          "before_rows",
          "tr",
          "after_rows",
      }
      result = set(Styler.template.blocks)
      assert result == expected
  
  
  def test_from_custom_template(tmpdir):
      p = tmpdir.mkdir("templates").join("myhtml.tpl")
      p.write(
          textwrap.dedent(
              """\
          {% extends "html.tpl" %}
          {% block table %}
          <h1>{{ table_title|default("My Table") }}</h1>
          {{ super() }}
          {% endblock table %}"""
          )
      )
      result = Styler.from_custom_template(str(tmpdir.join("templates")), "myhtml.tpl")
      assert issubclass(result, Styler)
      assert result.env is not Styler.env
      assert result.template is not Styler.template
      styler = result(DataFrame({"A": [1, 2]}))
      assert styler.render()
  