A CSS Cascading Style Sheets library for Python implementing DOM Level 2 Style specifications
—
Helper functions for CSS processing, URL handling, import resolution, stylesheet manipulation, and other common CSS operations.
Functions for handling URLs and resolving CSS @import dependencies.
def getUrls(sheet):
"""
Generator yielding all URL values in a stylesheet.
Extracts URLs from @import rules and url() values in property declarations,
returning clean URL strings without url() wrapper or quotes.
Parameters:
- sheet (CSSStyleSheet): Stylesheet to extract URLs from
Yields:
str: URL strings found in the stylesheet
"""
def replaceUrls(sheetOrStyle, replacer, ignoreImportRules=False):
"""
Replace all URLs in stylesheet or style declaration.
Calls replacer function for each URL found in @import rules and url() values,
replacing the URL with the function's return value.
Parameters:
- sheetOrStyle (CSSStyleSheet/CSSStyleDeclaration): Target to modify
- replacer (callable): Function that takes URL string and returns replacement
- ignoreImportRules (bool): If True, skip URLs in @import rules
"""
def resolveImports(sheet, target=None):
"""
Recursively resolve @import rules into a flat stylesheet.
Combines all imported stylesheets into a single stylesheet, handling
media queries by wrapping rules in @media blocks when necessary.
Parameters:
- sheet (CSSStyleSheet): Source stylesheet with @import rules
- target (CSSStyleSheet): Target stylesheet (created if None)
Returns:
CSSStyleSheet: Flattened stylesheet with imports resolved
"""Functions for managing global cssutils settings.
def setSerializer(serializer):
"""
Set the global serializer used by all cssutils classes.
All CSS objects will use this serializer for their cssText property
and string representation.
Parameters:
- serializer (CSSSerializer): Serializer instance to use globally
"""Utility functions for CSS value processing and manipulation.
def normalize(text):
"""
Normalize CSS identifier by removing escapes and converting to lowercase.
Parameters:
- text (str): CSS identifier to normalize
Returns:
str: Normalized identifier
"""
def path2url(path):
"""
Convert file system path to file:// URL.
Parameters:
- path (str): File system path
Returns:
str: file:// URL
"""
def string(value):
"""
Create CSS string value with proper quoting.
Parameters:
- value (str): String content
Returns:
str: Properly quoted CSS string
"""
def stringvalue(cssString):
"""
Extract string content from CSS string value.
Removes quotes and handles escape sequences.
Parameters:
- cssString (str): CSS string value with quotes
Returns:
str: Unquoted string content
"""
def uri(value):
"""
Create CSS URI value with url() wrapper.
Parameters:
- value (str): URI content
Returns:
str: CSS URI value with url() wrapper
"""
def urivalue(cssUri):
"""
Extract URI content from CSS url() value.
Removes url() wrapper and quotes.
Parameters:
- cssUri (str): CSS url() value
Returns:
str: Clean URI string
"""Classes and functions for managing CSS parsing and validation errors.
class ErrorHandler:
"""
Error handling and logging for CSS parsing operations.
Constructor:
ErrorHandler(log=None, defaultloglevel=40)
Parameters:
- log: Custom logger instance
- defaultloglevel (int): Default logging level (40 = ERROR)
"""
# Properties
raiseExceptions: bool # Whether to raise exceptions or just log
# Methods
def setLog(log):
"""Set custom logger instance"""
def setLevel(level):
"""Set logging level"""CSS property validation with predefined profiles for different CSS specifications.
class Profiles:
"""
CSS property validation profiles.
Constructor:
Profiles(log=None)
Parameters:
- log: Logger for validation messages
"""
# Methods
def addProfile(profile, replace=False):
"""
Add validation profile.
Parameters:
- profile (dict): Property validation rules
- replace (bool): Whether to replace existing profile
"""
def removeProfile(profile):
"""Remove validation profile"""
def validate(name, value):
"""
Validate CSS property value.
Parameters:
- name (str): Property name
- value (str): Property value
Returns:
bool: True if valid
"""
# Profile Constants
CSS_LEVEL_2: dict # CSS 2.1 properties
CSS3_BASIC_USER_INTERFACE: dict # CSS3 UI properties
CSS3_BOX: dict # CSS3 box properties
CSS3_COLOR: dict # CSS3 color properties
CSS3_PAGED_MEDIA: dict # CSS3 paged media properties
CSS3_BACKGROUNDS_AND_BORDERS: dict # CSS3 background/border properties
class NoSuchProfileException(Exception):
"""Raised when requesting non-existent validation profile"""Entry points for cssutils command-line tools.
def csscapture_main():
"""Entry point for csscapture command-line tool"""
def csscombine_main():
"""Entry point for csscombine command-line tool"""
def cssparse_main():
"""Entry point for cssparse command-line tool"""import cssutils
# CSS with various URLs
css = """
@import url("base.css");
@import "layout.css" screen;
body {
background: url('../images/bg.jpg') no-repeat;
cursor: url('cursors/hand.cur'), pointer;
}
@font-face {
font-family: 'Custom';
src: url('fonts/custom.woff2') format('woff2'),
url('fonts/custom.woff') format('woff');
}
"""
sheet = cssutils.parseString(css)
# Extract all URLs
print("URLs found in stylesheet:")
for url in cssutils.getUrls(sheet):
print(f" {url}")
# Replace URLs with absolute paths
def make_absolute(url):
if url.startswith('http'):
return url # Already absolute
return f"https://example.com/{url.lstrip('../')}"
cssutils.replaceUrls(sheet, make_absolute)
print(f"\nAfter URL replacement:\n{sheet.cssText}")import cssutils
# Main stylesheet with imports
main_css = """
@import "reset.css";
@import "typography.css" screen;
@import "print.css" print;
body {
color: #333;
font-family: Arial;
}
"""
# Simulate imported stylesheets
reset_css = "* { margin: 0; padding: 0; }"
typography_css = "h1 { font-size: 2em; } p { line-height: 1.5; }"
print_css = "body { font-size: 12pt; }"
# Parse main sheet
sheet = cssutils.parseString(main_css)
# Mock fetcher for imports (in real use, this would fetch from URLs)
def mock_fetcher(url):
sheets = {
'reset.css': reset_css,
'typography.css': typography_css,
'print.css': print_css
}
return 'utf-8', sheets.get(url, '')
# Set up parser with custom fetcher
parser = cssutils.CSSParser()
parser.setFetcher(mock_fetcher)
sheet = parser.parseString(main_css)
# Resolve imports into flat stylesheet
flattened = cssutils.resolveImports(sheet)
print("Flattened stylesheet:")
print(flattened.cssText)import cssutils
from cssutils.serialize import CSSSerializer, Preferences
# Create custom serializer for consistent formatting
prefs = Preferences(
indent=' ', # 2-space indentation
omitLastSemicolon=True, # Clean property lists
minimizeColorHash=True, # Compact colors
keepComments=False # Remove comments
)
custom_serializer = CSSSerializer(prefs=prefs)
cssutils.setSerializer(custom_serializer)
# All CSS objects now use custom formatting
css1 = "body{margin:0;color:#ffffff;/* comment */}"
css2 = ".test{padding:10px;background:#ff0000;}"
sheet1 = cssutils.parseString(css1)
sheet2 = cssutils.parseString(css2)
print("Consistently formatted stylesheets:")
print(sheet1.cssText)
print(sheet2.cssText)import cssutils
from cssutils.helper import string, stringvalue, uri, urivalue, normalize
# String utilities
css_string = string("Hello World")
print(f"CSS string: {css_string}") # "Hello World"
unquoted = stringvalue('"Hello World"')
print(f"Unquoted: {unquoted}") # Hello World
# URI utilities
css_uri = uri("images/bg.jpg")
print(f"CSS URI: {css_uri}") # url("images/bg.jpg")
clean_uri = urivalue('url("images/bg.jpg")')
print(f"Clean URI: {clean_uri}") # images/bg.jpg
# Identifier normalization
normalized = normalize("\\43 olor") # Escaped CSS identifier
print(f"Normalized: {normalized}") # colorimport cssutils
import logging
# Set up custom logging
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger('css')
# Configure cssutils error handling
cssutils.log.setLog(logger)
cssutils.log.setLevel(logging.INFO)
cssutils.log.raiseExceptions = False # Log errors instead of raising
# Parse CSS with errors
invalid_css = """
body {
color: invalidcolor;
unknown-property: value;
margin: ; /* missing value */
}
"""
print("Parsing CSS with errors (logged, not raised):")
sheet = cssutils.parseString(invalid_css)
print(f"Parsed {len(sheet.cssRules)} rules despite errors")
# Switch to exception mode
cssutils.log.raiseExceptions = True
try:
cssutils.parseString("invalid { css }")
except Exception as e:
print(f"Exception raised: {e}")import cssutils
from cssutils.profiles import CSS_LEVEL_2, CSS3_COLOR
# Enable validation with specific profiles
cssutils.profile.addProfile(CSS_LEVEL_2)
cssutils.profile.addProfile(CSS3_COLOR)
# Test validation
valid_properties = [
('color', 'red'),
('margin', '10px'),
('background-color', 'rgba(255, 0, 0, 0.5)')
]
invalid_properties = [
('color', 'notacolor'),
('unknown-prop', 'value'),
('margin', 'invalid')
]
print("Valid properties:")
for name, value in valid_properties:
is_valid = cssutils.profile.validate(name, value)
print(f" {name}: {value} -> {is_valid}")
print("\nInvalid properties:")
for name, value in invalid_properties:
is_valid = cssutils.profile.validate(name, value)
print(f" {name}: {value} -> {is_valid}")import cssutils
from cssutils.helper import path2url
import os
# Convert file paths to URLs
file_paths = [
'/home/user/styles.css',
'C:\\Users\\User\\styles.css',
'./relative/path.css'
]
print("File paths to URLs:")
for path in file_paths:
url = path2url(path)
print(f" {path} -> {url}")
# Use with CSS parsing
css_file = '/path/to/styles.css'
file_url = path2url(css_file)
# Parse with proper base URL for relative imports
with open(css_file, 'r') as f:
css_content = f.read()
sheet = cssutils.parseString(css_content, href=file_url)
print(f"\nParsed stylesheet with base URL: {sheet.href}")import cssutils
import os
def process_css_files(directory, output_dir):
"""Utility to process and format multiple CSS files"""
# Configure consistent formatting
prefs = cssutils.serialize.Preferences(
indent=' ',
omitLastSemicolon=True,
minimizeColorHash=True,
keepComments=False
)
cssutils.setSerializer(cssutils.serialize.CSSSerializer(prefs=prefs))
for filename in os.listdir(directory):
if filename.endswith('.css'):
input_path = os.path.join(directory, filename)
output_path = os.path.join(output_dir, f"formatted_{filename}")
# Parse and reformat
sheet = cssutils.parseFile(input_path)
# Resolve imports if needed
if any(rule.type == rule.IMPORT_RULE for rule in sheet):
sheet = cssutils.resolveImports(sheet)
# Write formatted CSS
with open(output_path, 'w') as f:
f.write(sheet.cssText)
print(f"Processed: {filename} -> formatted_{filename}")
# Usage
# process_css_files('input_css/', 'output_css/')Install with Tessl CLI
npx tessl i tessl/pypi-cssutils