Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion pandas/_config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,26 @@ def get_option(pat: str) -> Any:
>>> pd.get_option("display.max_columns") # doctest: +SKIP
4
"""
# Common path: no regex or fuzzy matching needed, key is a direct option
# Optimize for the likely case: direct hit in _global_config or nested dict
if "." not in pat:
# Fast path, no nesting
conf = _global_config
if pat in conf:
return conf[pat]
# Some keys are nested (e.g., "display.max_columns")
else:
cursor = _global_config
parts = pat.split(".")
try:
for p in parts[:-1]:
cursor = cursor[p]
return cursor[parts[-1]]
except (KeyError, TypeError):
pass

# Fallback for patterns, partials, warnings, and error handling
# (Out-of-band: don't inline _get_single_key/_get_root to keep optimal logic above)
key = _get_single_key(pat)

# walk the nested dict
Expand Down Expand Up @@ -504,6 +524,8 @@ def register_option(
import keyword
import tokenize

_deprecated_options: dict[str, DeprecatedOption] = {}

key = key.lower()

if key in _registered_options:
Expand Down Expand Up @@ -756,7 +778,7 @@ def inner(key: str, *args, **kwds):
pkey = f"{prefix}.{key}"
return func(pkey, *args, **kwds)

return cast(F, inner)
return cast("F", inner)

_register_option = register_option
_get_option = get_option
Expand Down
51 changes: 32 additions & 19 deletions pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ class StylerRenderer:
Base class to process rendering a Styler with a specified jinja2 template.
"""

loader = jinja2.PackageLoader("pandas", "io/formats/templates")
import os

loader = jinja2.FileSystemLoader(
os.path.join(os.path.dirname(__file__), "templates")
)
env = jinja2.Environment(loader=loader, trim_blocks=True)
template_html = env.get_template("html.tpl")
template_html_table = env.get_template("html_table.tpl")
Expand Down Expand Up @@ -834,10 +838,7 @@ def _generate_body_row(

data_element = _element(
"td",
(
f"{self.css['data']} {self.css['row']}{r} "
f"{self.css['col']}{c}{cls}"
),
(f"{self.css['data']} {self.css['row']}{r} {self.css['col']}{c}{cls}"),
value,
data_element_visible,
attributes="",
Expand Down Expand Up @@ -956,7 +957,7 @@ def concatenated_visible_rows(obj):
idx_len = d["index_lengths"].get((lvl, r), None)
if idx_len is not None: # i.e. not a sparsified entry
d["clines"][rn + idx_len].append(
f"\\cline{{{lvln+1}-{len(visible_index_levels)+data_len}}}"
f"\\cline{{{lvln + 1}-{len(visible_index_levels) + data_len}}}"
)

def format(
Expand Down Expand Up @@ -1211,7 +1212,7 @@ def format(
data = self.data.loc[subset]

if not isinstance(formatter, dict):
formatter = {col: formatter for col in data.columns}
formatter = dict.fromkeys(data.columns, formatter)

cis = self.columns.get_indexer_for(data.columns)
ris = self.index.get_indexer_for(data.index)
Expand Down Expand Up @@ -1397,7 +1398,7 @@ def format_index(
return self # clear the formatter / revert to default and avoid looping

if not isinstance(formatter, dict):
formatter = {level: formatter for level in levels_}
formatter = dict.fromkeys(levels_, formatter)
else:
formatter = {
obj._get_level_number(level): formatter_
Expand Down Expand Up @@ -1540,7 +1541,7 @@ def relabel_index(

>>> df = pd.DataFrame({"samples": np.random.rand(10)})
>>> styler = df.loc[np.random.randint(0, 10, 3)].style
>>> styler.relabel_index([f"sample{i+1} ({{}})" for i in range(3)])
>>> styler.relabel_index([f"sample{i + 1} ({{}})" for i in range(3)])
... # doctest: +SKIP
samples
sample1 (5) 0.315811
Expand Down Expand Up @@ -1694,7 +1695,7 @@ def format_index_names(
return self # clear the formatter / revert to default and avoid looping

if not isinstance(formatter, dict):
formatter = {level: formatter for level in levels_}
formatter = dict.fromkeys(levels_, formatter)
else:
formatter = {
obj._get_level_number(level): formatter_
Expand Down Expand Up @@ -1919,7 +1920,8 @@ def _wrap_decimal_thousands(
"""

def wrapper(x):
if is_float(x) or is_integer(x) or is_complex(x):
is_num = is_float(x) or is_integer(x) or is_complex(x)
if is_num:
if decimal != "." and thousands is not None and thousands != ",":
return (
formatter(x)
Expand Down Expand Up @@ -1987,18 +1989,23 @@ def _maybe_wrap_formatter(
elif callable(formatter):
func_0 = formatter
elif formatter is None:
precision = (
get_option("styler.format.precision") if precision is None else precision
)
if precision is None:
precision_val = get_option("styler.format.precision")
else:
precision_val = precision
func_0 = partial(
_default_formatter, precision=precision, thousands=(thousands is not None)
_default_formatter,
precision=precision_val,
thousands=(thousands is not None),
)
else:
raise TypeError(f"'formatter' expected str or callable, got {type(formatter)}")

# Replace chars if escaping
if escape is not None:
func_1 = lambda x: func_0(_str_escape(x, escape=escape))

def func_1(x):
return func_0(_str_escape(x, escape=escape))
else:
func_1 = func_0

Expand All @@ -2010,15 +2017,21 @@ def _maybe_wrap_formatter(

# Render links
if hyperlinks is not None:
func_3 = lambda x: func_2(_render_href(x, format=hyperlinks))

def func_3(x):
return func_2(_render_href(x, format=hyperlinks))
else:
func_3 = func_2

# Replace missing values if na_rep
if na_rep is None:
return func_3
else:
return lambda x: na_rep if (isna(x) is True) else func_3(x)
# Avoid closure attribute lookups; localize func_3 and isna
def na_wrapper(x, _na=na_rep, _isna=isna, _func_3=func_3):
return _na if (_isna(x) is True) else _func_3(x)

return na_wrapper


def non_reducing_slice(slice_: Subset):
Expand Down Expand Up @@ -2503,7 +2516,7 @@ def color(value, user_arg, command, comm_arg):
if value[0] == "#" and len(value) == 7: # color is hex code
return command, f"[HTML]{{{value[1:].upper()}}}{arg}"
if value[0] == "#" and len(value) == 4: # color is short hex code
val = f"{value[1].upper()*2}{value[2].upper()*2}{value[3].upper()*2}"
val = f"{value[1].upper() * 2}{value[2].upper() * 2}{value[3].upper() * 2}"
return command, f"[HTML]{{{val}}}{arg}"
elif value[:3] == "rgb": # color is rgb or rgba
r = re.findall("(?<=\\()[0-9\\s%]+(?=,)", value)[0].strip()
Expand Down