jaraco / jaraco/cssutils

CSS file of 320 bytes takes 10+ seconds to parse

Open
#18 4 comments 0 reactions 0 assignees View on GitHub
help wanted
Dominant language
Python
Stars
92
Forks
30
PR merge metrics
No merged PRs in 30d

Description

I've encountered CSS file which `cssutils` library takes lots of seconds to parse (in fact, almost infinite).
After some investigation I've minified problematic sample (as possible) and prepared reproducible code example.
On my laptop (core i7-8550U) it takes **14 seconds** to parse file with just one line and 320 bytes of text.

Original file has 20kBytes of text and parser does not finish it in several hours.

Code:
https://gist.github.com/vdmit/ef9007170fa1c616cf5aba1fcebfce87
I'm using:
- latest stable `cssutils` release 2.3.0 from pypi repo
- python 3.8.10
- Ubuntu 20.04 linux distribution.

Notes:
- original CSS file was malformed and even contained non-printable characters. My minified example is still not valid CSS file, but it is a plain ANSI file.
- Adding line break in any place fixes problem;
- Removing of last style expression (`.s11...` reduces execution time from 14 seconds to 3 seconds).
- Adding of one more style expression increases execution time from 14 seconds to 50, 2 more styles yields ~260 seconds.

So, there is a **exponential** complexity somewhere, which is strange.

Traceback of interrupted script is following:

```python3 cssutils_infinite_loop_bug_example.py
Traceback (most recent call last):
File "cssutils_infinite_loop_bug_example.py", line 10, in
sheet = css_parser.parseString(cssText=css_text)
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/parse.py", line 147, in parseString
sheet._setCssTextWithEncodingOverride(
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/css/cssstylesheet.py", line 408, in _setCssTextWithEncodingOverride
self.cssText = cssText
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/css/cssstylesheet.py", line 331, in _setCssText
wellformed, expected = self._parse(
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/util.py", line 484, in _parse
expected = p(expected, seq, token, tokenizer)
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/css/cssstylesheet.py", line 313, in ruleset
rule.cssText = self._tokensupto2(tokenizer, token)
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/util.py", line 343, in _tokensupto2
for token in tokenizer:
File "/home/vdmit/.local/lib/python3.8/site-packages/cssutils/tokenize2.py", line 172, in tokenize
match = matcher(text, pos) # if no match try next production
KeyboardInterrupt
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the linked reproducible example and trace the reported path through cssutils/parse.py, cssutils/css/cssstylesheet.py, util.py, and tokenize2.py. Benchmark the sample while varying line breaks and style expressions, then add a regression test showing that the input no longer has exponential parsing time.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.