From cssutils¶
cssutils is a Python CSS Object Model library. It parses a stylesheet or an inline
declaration into a CSSStyleSheet of CSSStyleRule objects, each with a selectorText and a
CSSStyleDeclaration you can read, edit, and re-serialize. It is the tool a BeautifulSoup- or lxml-based scraper
reaches for when it needs to read the rules inside a <style> block rather than just match selectors.
turbohtml.cssom covers that same CSSOM surface – StyleSheet,
RuleList, StyleRule, and StyleDeclaration
are the turbohtml-native spelling of CSSStyleSheet / CSSRuleList / CSSStyleRule / CSSStyleDeclaration –
and adds the piece cssutils leaves out: computed_style() runs the CSS cascade against a parsed
document to answer getComputedStyle. cssutils reads the rules; turbohtml reads the rules and tells you which one
wins for an element.
turbohtml vs cssutils¶
Dimension |
turbohtml |
cssutils |
|---|---|---|
Scope |
Read-only CSSOM plus the cascade ( |
Read/write CSSOM: parse, edit, and re-serialize stylesheets and declarations |
Cascade |
|
None – cssutils models the rules but does not cascade them |
Engine |
Parse, selector match, and cascade in a C extension, sharing the native selector engine |
Pure-Python parser and object model |
Mutation |
Result shapes are read-only |
Declarations and rules are mutable and serialize back to CSS |
Typing |
Fully typed, |
No bundled type stubs |
Dependencies |
Self-contained C extension, no runtime deps |
Pure Python, depends on |
Feature overlap¶
The CSSOM read surface ports directly:
Parse a stylesheet:
cssutils.parseString(css)->turbohtml.cssom.StyleSheet.Iterate rules:
sheet.cssRules(aCSSRuleList) ->rules(aRuleList).A rule’s selector:
rule.selectorText->selector_text.A rule’s declarations:
rule.style(aCSSStyleDeclaration) ->style(aStyleDeclaration).Read a property:
decl.getPropertyValue("color")ordecl["color"]->get()ordecl["color"].Read its priority:
decl.getPropertyPriority("color") == "important"->important().
What turbohtml adds¶
The cascade.
computed_style()collects every<style>sheet plus the element’s inlinestyle, matches the native selector engine, orders by importance, the style attribute, specificity, and source order, then applies inheritance and initial values – thegetComputedStylethat cssutils has no equivalent for. In the JavaScript world this is what jsdom and cssstyle provide; turbohtml draws the same computed-value boundary (see The cascade and computed style).Shorthand expansion in the cascade. A computed style exposes longhands only:
marginbecomes the fourmargin-*values, and each is resolved independently.One engine for selectors and the cascade. The rules match with the same C selector engine as
select(), so:is(),:not(),:has(), attribute operators, and the combinators all behave identically.No dependencies and a typed API, versus cssutils’
cssselectandmore-itertoolsrequirements.
What cssutils has that turbohtml does not¶
Mutation and re-serialization. cssutils declarations and rules are read/write and serialize back to CSS text; turbohtml’s result shapes are read-only. To rewrite CSS, edit the source text or use cssutils.
At-rules. cssutils models
@media,@import,@font-face, and the rest; turbohtml skips at-rules and cascades only top-level style rules.Property-value validation and normalization. cssutils validates values and can normalize them; turbohtml returns the specified value as written.
Performance¶
Not directly benchmarked – cssutils has no cascade to compare computed_style() against, and the
CSSOM parse is a small part of either library. turbohtml’s cascade runs in the C core; its computed-style benchmark
resolves the computed style of every element on a styled page.
How to migrate¶
Reading a stylesheet’s rules ports one call at a time:
import cssutils
sheet = cssutils.parseString("a { color: blue } .box { padding: 4px 8px }")
for rule in sheet.cssRules:
if rule.type == rule.STYLE_RULE:
print(rule.selectorText, rule.style.getPropertyValue("color"))
becomes
from turbohtml.cssom import StyleSheet
sheet = StyleSheet("a { color: blue } .box { padding: 4px 8px }")
for rule in sheet.rules:
print(rule.selector_text, rule.style.get("color"))
a blue
.box None
To go beyond the rules and ask which value actually applies to an element – the step cssutils cannot take – parse the
document and call computed_style():
import turbohtml
from turbohtml.cssom import computed_style
doc = turbohtml.parse(
"<html><head><style>a { color: blue } #x { color: red !important }</style></head>"
"<body><a id=x style='color: green'>link</a></body></html>"
)
print(computed_style(doc.select_one("#x"))["color"])
red