mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Framework for testing cascade.py
This commit is contained in:
parent
c2ef186d9f
commit
176b6b248d
@ -29,40 +29,55 @@ def html_css_stylesheet(container):
|
||||
return _html_css_stylesheet
|
||||
|
||||
def media_allowed(media):
|
||||
if not media or not media.mediaText:
|
||||
return True
|
||||
return media_ok(media.mediaText)
|
||||
|
||||
def iterrules(container, rules, sheet_name, media_rule_ok=media_allowed, rule_index_counter=None, rule_type=None):
|
||||
def iterrules(container, sheet_name, rules=None, media_rule_ok=media_allowed, rule_index_counter=None, rule_type=None, importing=None):
|
||||
''' Iterate over all style rules in the specified sheet. Import and Media rules are
|
||||
automatically resolved. Yields (rule, sheet_name, rule_number).
|
||||
|
||||
:param rules: List of CSSRules or a CSSStyleSheet instance
|
||||
:param rules: List of CSSRules or a CSSStyleSheet instance or None in which case it is read from container using sheet_name
|
||||
:param sheet_name: The name of the sheet in the container (in case of inline style sheets, the name of the html file)
|
||||
:param media_rule_ok: A function to test if a @media rule is allowed
|
||||
:param rule_index_counter: A counter object, rule numbers will be calculated by incrementing the counter.
|
||||
:param rule_type: Only yield rules of this type (by default all rules are yielded
|
||||
:param rule_type: Only yield rules of this type, where type is a string type name, see cssutils.css.CSSRule for the names (by default all rules are yielded)
|
||||
:return: (CSSRule object, the name of the sheet from which it comes, rule index - a monotonically increasing number)
|
||||
'''
|
||||
|
||||
rule_index_counter = rule_index_counter or count()
|
||||
riter = partial(iterrules, container, rule_index_counter=rule_index_counter, media_rule_ok=media_rule_ok, rule_type=rule_type)
|
||||
if importing is None:
|
||||
importing = set()
|
||||
importing.add(sheet_name)
|
||||
riter = partial(iterrules, container, rule_index_counter=rule_index_counter, media_rule_ok=media_rule_ok, rule_type=rule_type, importing=importing)
|
||||
if rules is None:
|
||||
rules = container.parsed(sheet_name)
|
||||
if rule_type is not None:
|
||||
rule_type = getattr(CSSRule, rule_type)
|
||||
|
||||
for rule in rules:
|
||||
if rule.type == CSSRule.IMPORT_RULE:
|
||||
name = container.href_to_name(rule.href, sheet_name)
|
||||
if container.has_name(name):
|
||||
csheet = container.parsed(name)
|
||||
if isinstance(csheet, CSSStyleSheet):
|
||||
for cr in riter(csheet, name):
|
||||
yield cr
|
||||
if media_rule_ok(rule.media):
|
||||
name = container.href_to_name(rule.href, sheet_name)
|
||||
if container.has_name(name):
|
||||
if name in importing:
|
||||
container.log.error('Recursive import of {} from {}, ignoring'.format(name, sheet_name))
|
||||
else:
|
||||
csheet = container.parsed(name)
|
||||
if isinstance(csheet, CSSStyleSheet):
|
||||
for cr in riter(name, rules=csheet):
|
||||
yield cr
|
||||
elif rule.type == CSSRule.MEDIA_RULE:
|
||||
if media_rule_ok(rule.media):
|
||||
for cr in riter(rule.cssRules, sheet_name):
|
||||
for cr in riter(sheet_name, rules=rule.cssRules):
|
||||
yield cr
|
||||
|
||||
elif rule_type is None or rule.type == rule_type:
|
||||
num = next(rule_index_counter)
|
||||
yield rule, sheet_name, num
|
||||
|
||||
importing.discard(sheet_name)
|
||||
|
||||
StyleDeclaration = namedtuple('StyleDeclaration', 'index declaration pseudo_element')
|
||||
Specificity = namedtuple('Specificity', 'is_style num_id num_class num_elem rule_index')
|
||||
|
||||
@ -123,7 +138,7 @@ def resolve_styles(container, name):
|
||||
pseudo_pat = re.compile(ur':{1,2}(%s)' % ('|'.join(INAPPROPRIATE_PSEUDO_CLASSES)), re.I)
|
||||
|
||||
def process_sheet(sheet, sheet_name):
|
||||
for rule, sheet_name, rule_index in iterrules(container, sheet, sheet_name, rule_index_counter=rule_index_counter, rule_type=CSSRule.STYLE_RULE):
|
||||
for rule, sheet_name, rule_index in iterrules(container, sheet_name, rules=sheet, rule_index_counter=rule_index_counter, rule_type=CSSRule.STYLE_RULE):
|
||||
for selector in rule.selectorList:
|
||||
text = selector.selectorText
|
||||
try:
|
||||
|
@ -186,7 +186,6 @@ class ContainerBase(object): # {{{
|
||||
css_preprocessor=(None if self.tweak_mode else self.css_preprocessor))
|
||||
# }}}
|
||||
|
||||
|
||||
class Container(ContainerBase): # {{{
|
||||
|
||||
'''
|
||||
|
63
src/calibre/ebooks/oeb/polish/tests/cascade.py
Normal file
63
src/calibre/ebooks/oeb/polish/tests/cascade.py
Normal file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from calibre.constants import iswindows
|
||||
|
||||
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
||||
from calibre.ebooks.oeb.polish.cascade import iterrules
|
||||
from calibre.ebooks.oeb.polish.container import ContainerBase, href_to_name
|
||||
from calibre.ebooks.oeb.polish.tests.base import BaseTest
|
||||
from calibre.utils.logging import Log, Stream
|
||||
|
||||
class VirtualContainer(ContainerBase):
|
||||
|
||||
tweak_mode = True
|
||||
|
||||
def __init__(self, files):
|
||||
s = Stream()
|
||||
self.log_stream = s.stream
|
||||
log = Log()
|
||||
log.outputs = [s]
|
||||
ContainerBase.__init__(self, log=log)
|
||||
self.mime_map = {k:self.guess_type(k) for k in files}
|
||||
self.files = files
|
||||
|
||||
def has_name(self, name):
|
||||
return name in self.mime_map
|
||||
|
||||
def href_to_name(self, href, base=None):
|
||||
return href_to_name(href, ('C:\\root' if iswindows else '/root'), base)
|
||||
|
||||
def parsed(self, name):
|
||||
if name not in self.parsed_cache:
|
||||
mt = self.mime_map[name]
|
||||
if mt in OEB_STYLES:
|
||||
self.parsed_cache[name] = self.parse_css(self.files[name], name)
|
||||
elif mt in OEB_DOCS:
|
||||
self.parsed_cache[name] = self.parse_xhtml(self.files[name], name)
|
||||
else:
|
||||
self.parsed_cache[name] = self.files[name]
|
||||
return self.parsed_cache[name]
|
||||
|
||||
class CascadeTest(BaseTest):
|
||||
|
||||
def test_iterrules(self):
|
||||
def get_rules(files, name='x/one.css', l=1, rule_type=None):
|
||||
c = VirtualContainer(files)
|
||||
rules = tuple(iterrules(c, name, rule_type=rule_type))
|
||||
self.assertEqual(len(rules), l)
|
||||
return rules, c
|
||||
get_rules({'x/one.css':'@import "../two.css";', 'two.css':'body { color: red; }'})
|
||||
get_rules({'x/one.css':'@import "../two.css" screen;', 'two.css':'body { color: red; }'})
|
||||
get_rules({'x/one.css':'@import "../two.css" xyz;', 'two.css':'body { color: red; }'}, l=0)
|
||||
get_rules({'x/one.css':'@import "../two.css";', 'two.css':'body { color: red; }'}, l=0, rule_type='FONT_FACE_RULE')
|
||||
get_rules({'x/one.css':'@import "../two.css";', 'two.css':'body { color: red; }'}, rule_type='STYLE_RULE')
|
||||
get_rules({'x/one.css':'@media screen { body { color: red; } }'})
|
||||
get_rules({'x/one.css':'@media xyz { body { color: red; } }'}, l=0)
|
||||
c = get_rules({'x/one.css':'@import "../two.css";', 'two.css':'@import "x/one.css"; body { color: red; }'})[1]
|
||||
self.assertIn('Recursive import', c.log_stream.getvalue().decode('utf-8'))
|
@ -50,7 +50,6 @@ FONT_SIZE_NAMES = {
|
||||
'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', 'xx-large'
|
||||
}
|
||||
|
||||
ALL_MEDIA_TYPES = frozenset('all aural braille handheld print projection screen tty tv embossed amzn-mobi amzn-kf8'.split())
|
||||
ALLOWED_MEDIA_TYPES = frozenset({'screen', 'all', 'aural', 'amzn-kf8'})
|
||||
IGNORED_MEDIA_FEATURES = frozenset('width min-width max-width height min-height max-height device-width min-device-width max-device-width device-height min-device-height max-device-height aspect-ratio min-aspect-ratio max-aspect-ratio device-aspect-ratio min-device-aspect-ratio max-device-aspect-ratio color min-color max-color color-index min-color-index max-color-index monochrome min-monochrome max-monochrome -webkit-min-device-pixel-ratio resolution min-resolution max-resolution scan grid'.split()) # noqa
|
||||
|
||||
|
@ -27,6 +27,10 @@ class Stream(object):
|
||||
def flush(self):
|
||||
self.stream.flush()
|
||||
|
||||
def prints(self, level, *args, **kwargs):
|
||||
self._prints(*args, **kwargs)
|
||||
|
||||
|
||||
class ANSIStream(Stream):
|
||||
|
||||
def __init__(self, stream=sys.stdout):
|
||||
|
Loading…
x
Reference in New Issue
Block a user