-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
ENH: Use a proper CSS parser for pandas Styler objects #48869
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
b9b8cad
7454dc5
e00f5a7
2859601
ee48a6d
f7d4832
6388662
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,9 +13,12 @@ | |
) | ||
import warnings | ||
|
||
from pandas.compat._optional import import_optional_dependency | ||
from pandas.errors import CSSWarning | ||
from pandas.util._exceptions import find_stack_level | ||
|
||
tinycss2 = import_optional_dependency("tinycss2") | ||
|
||
|
||
def _side_expander(prop_fmt: str) -> Callable: | ||
""" | ||
|
@@ -377,8 +380,20 @@ def _error(): | |
|
||
def atomize(self, declarations: Iterable) -> Generator[tuple[str, str], None, None]: | ||
for prop, value in declarations: | ||
prop = prop.lower() | ||
value = value.lower() | ||
# Need to reparse the value here in case the prop was passed | ||
# through as a dict from the styler rather than through this | ||
# classes parse method | ||
tokens = [] | ||
for token in tinycss2.parse_component_value_list(value): | ||
if token.type == "ident": | ||
# The old css parser normalized all identifier values, do | ||
# so here to keep backwards compatibility | ||
token.value = token.lower_value | ||
Comment on lines
+388
to
+391
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See related discussion here #48660 (comment) regarding case normalization. Seems like its worthy of further investigation of whether this is truly necessary for backwards compatibility There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not 100%, I looked into this back in the 0.24 code. But I believe the reason was to allow users to set a font like |
||
|
||
tokens.append(token) | ||
|
||
value = tinycss2.serialize(tokens).strip() | ||
|
||
if prop in self.CSS_EXPANSIONS: | ||
expand = self.CSS_EXPANSIONS[prop] | ||
yield from expand(self, prop, value) | ||
|
@@ -395,18 +410,29 @@ def parse(self, declarations_str: str) -> Iterator[tuple[str, str]]: | |
---------- | ||
declarations_str : str | ||
""" | ||
for decl in declarations_str.split(";"): | ||
if not decl.strip(): | ||
continue | ||
prop, sep, val = decl.partition(":") | ||
prop = prop.strip().lower() | ||
# TODO: don't lowercase case sensitive parts of values (strings) | ||
val = val.strip().lower() | ||
if sep: | ||
yield prop, val | ||
else: | ||
declarations = tinycss2.parse_declaration_list( | ||
declarations_str, skip_comments=True, skip_whitespace=True | ||
) | ||
|
||
for decl in declarations: | ||
if decl.type == "error": | ||
warnings.warn( | ||
f"Ill-formatted attribute: expected a colon in {repr(decl)}", | ||
decl.message, | ||
CSSWarning, | ||
stacklevel=find_stack_level(inspect.currentframe()), | ||
) | ||
else: | ||
tokens = [] | ||
for token in decl.value: | ||
if token.type == "ident": | ||
# The old css parser normalized all identifier values, | ||
# do so here to keep backwards compatibility | ||
token.value = token.lower_value | ||
|
||
tokens.append(token) | ||
|
||
value = tinycss2.serialize(tokens).strip() | ||
if decl.important: | ||
value = f"{value} !important" | ||
|
||
yield decl.lower_name, value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actively maintained? This looks more or less abandoned
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you looking at tinycss rather than tinycss2? The later has commits from a few weeks ago on the bleeding edge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked https://github.com/Kozea/tinycss2/commits/master and there are like 25 commits in the last 2 years
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What can I say. There's not much more activity needed than that. It's a simple parser that just produces an AST from some CSS, only a few 100 lines of code if you strip out the comments and doc strings. Doesn't do any of the other fancy processing of interpreting the CSS content. That's all we need here to pull out the key-value pairs being passed in.