Skip to content

Commit 434effd

Browse files
committed
Clean code
1 parent fe7b9e2 commit 434effd

4 files changed

Lines changed: 234 additions & 240 deletions

File tree

cssselect2/__init__.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
"""
2-
cssselect2
3-
==========
1+
"""CSS4 selectors for Python.
42
5-
cssselect2 is a straightforward implementation of CSS3 Selectors for markup
3+
cssselect2 is a straightforward implementation of CSS4 Selectors for markup
64
documents (HTML, XML, etc.) that can be read by ElementTree-like parsers
75
(including cElementTree, lxml, html5lib, etc.)
86
@@ -56,14 +54,14 @@ def add_selector(self, selector, payload):
5654
if selector.id is not None:
5755
self.id_selectors.setdefault(selector.id, []).append(entry)
5856
elif selector.class_name is not None:
59-
self.class_selectors.setdefault(selector.class_name, []) \
60-
.append(entry)
57+
self.class_selectors.setdefault(
58+
selector.class_name, []).append(entry)
6159
elif selector.local_name is not None:
6260
self.lower_local_name_selectors.setdefault(
6361
selector.lower_local_name, []).append(entry)
6462
elif selector.namespace is not None:
65-
self.namespace_selectors.setdefault(selector.namespace, []) \
66-
.append(entry)
63+
self.namespace_selectors.setdefault(
64+
selector.namespace, []).append(entry)
6765
elif selector.requires_lang_attr:
6866
self.lang_attr_selectors.append(entry)
6967
else:

cssselect2/compiler.py

Lines changed: 97 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,17 @@ def __init__(self, parsed_selector):
6767
self.lower_local_name = simple_selector.lower_local_name
6868
elif isinstance(simple_selector, parser.NamespaceSelector):
6969
self.namespace = simple_selector.namespace
70-
elif isinstance(simple_selector, parser.AttributeSelector) and \
71-
simple_selector.name == "lang":
72-
self.requires_lang_attr = True
70+
elif isinstance(simple_selector, parser.AttributeSelector):
71+
if simple_selector.name == 'lang':
72+
self.requires_lang_attr = True
7373

7474

7575
def _compile_node(selector):
7676
"""Return a boolean expression, as a Python source string.
7777
7878
When evaluated in a context where the `el` variable is an
79-
:class:`cssselect2.tree.Element` object,
80-
tells whether the element is a subject of `selector`.
79+
:class:`cssselect2.tree.Element` object, tells whether the element is a
80+
subject of `selector`.
8181
8282
"""
8383
# To avoid precedence-related bugs, any sub-expression that is passed
@@ -101,15 +101,17 @@ def _compile_node(selector):
101101
# Rebind the `el` name inside a generator-expressions (in a new scope)
102102
# so that 'left_inside' applies to different elements.
103103
elif selector.combinator == ' ':
104-
left = 'any((%s) for el in el.ancestors)' % left_inside
104+
left = f'any(({left_inside}) for el in el.ancestors)'
105105
elif selector.combinator == '>':
106-
left = ('next(el is not None and (%s) for el in [el.parent])'
107-
% left_inside)
106+
left = (
107+
f'next(el is not None and ({left_inside}) '
108+
'for el in [el.parent])')
108109
elif selector.combinator == '+':
109-
left = ('next(el is not None and (%s) for el in [el.previous])'
110-
% left_inside)
110+
left = (
111+
f'next(el is not None and ({left_inside}) '
112+
'for el in [el.previous])')
111113
elif selector.combinator == '~':
112-
left = 'any((%s) for el in el.previous_siblings)' % left_inside
114+
left = f'any(({left_inside}) for el in el.previous_siblings)'
113115
else:
114116
raise SelectorError('Unknown combinator', selector.combinator)
115117

@@ -119,8 +121,8 @@ def _compile_node(selector):
119121
elif right == '1':
120122
return left # 1 and x == x
121123
else:
122-
# Evaluate combinators right to left:
123-
return '(%s) and (%s)' % (right, left)
124+
# Evaluate combinators right to left
125+
return f'({right}) and ({left})'
124126

125127
elif isinstance(selector, parser.CompoundSelector):
126128
sub_expressions = [
@@ -131,7 +133,7 @@ def _compile_node(selector):
131133
elif '0' in sub_expressions:
132134
test = '0'
133135
elif sub_expressions:
134-
test = ' and '.join('(%s)' % e for e in sub_expressions)
136+
test = ' and '.join(f'({e})' for e in sub_expressions)
135137
else:
136138
test = '1' # all([]) == True
137139
return test
@@ -176,68 +178,70 @@ def _compile_node(selector):
176178

177179
elif isinstance(selector, parser.LocalNameSelector):
178180
if selector.lower_local_name == selector.local_name:
179-
return 'el.local_name == %r' % selector.local_name
181+
return f'el.local_name == {selector.local_name!r}'
180182
else:
181183
return (
182-
'el.local_name == (%r if el.in_html_document else %r)' %
183-
(selector.lower_local_name, selector.local_name))
184+
f'el.local_name == ({selector.lower_local_name!r} '
185+
f'if el.in_html_document else {selector.local_name!r})')
184186

185187
elif isinstance(selector, parser.NamespaceSelector):
186-
return 'el.namespace_url == %r' % selector.namespace
188+
return f'el.namespace_url == {selector.namespace!r}'
187189

188190
elif isinstance(selector, parser.ClassSelector):
189-
return '%r in el.classes' % selector.class_name
191+
return f'{selector.class_name!r} in el.classes'
190192

191193
elif isinstance(selector, parser.IDSelector):
192-
return 'el.id == %r' % selector.ident
194+
return f'el.id == {selector.ident!r}'
193195

194196
elif isinstance(selector, parser.AttributeSelector):
195197
if selector.namespace is not None:
196198
if selector.namespace:
197199
if selector.name == selector.lower_name:
198-
key = repr('{%s}%s' % (selector.namespace, selector.name))
200+
key = repr(f'{{{selector.namespace}}}{selector.name}')
199201
else:
200-
key = '(%r if el.in_html_document else %r)' % (
201-
'{%s}%s' % (selector.namespace, selector.lower_name),
202-
'{%s}%s' % (selector.namespace, selector.name),
203-
)
202+
lower = f'{{{selector.namespace}}}{selector.lower_name}'
203+
name = f'{{{selector.namespace}}}{selector.name}'
204+
key = f'({lower!r} if el.in_html_document else {name!r})'
204205
else:
205206
if selector.name == selector.lower_name:
206207
key = repr(selector.name)
207208
else:
208-
key = '(%r if el.in_html_document else %r)' % (
209-
selector.lower_name, selector.name)
209+
lower, name = selector.lower_name, selector.name
210+
key = f'({lower!r} if el.in_html_document else {name!r})'
210211
value = selector.value
211212
if selector.operator is None:
212-
return '%s in el.etree_element.attrib' % key
213+
return f'{key} in el.etree_element.attrib'
213214
elif selector.operator == '=':
214-
return 'el.etree_element.get(%s) == %r' % (key, value)
215+
return f'el.etree_element.get({key}) == {value!r}'
215216
elif selector.operator == '~=':
216217
if len(value.split()) != 1 or value.strip() != value:
217218
return '0'
218219
else:
219220
return (
220-
'%r in split_whitespace(el.etree_element.get(%s, ""))'
221-
% (value, key))
221+
f'{value!r} in '
222+
f'split_whitespace(el.etree_element.get({key}, ""))')
222223
elif selector.operator == '|=':
223-
return ('next(v == %r or (v is not None and v.startswith(%r))'
224-
' for v in [el.etree_element.get(%s)])'
225-
% (value, value + '-', key))
224+
return (
225+
f'next(v == {value!r} or '
226+
f' (v is not None and v.startswith({(value + "-")!r}))'
227+
f' for v in [el.etree_element.get({key})])')
226228
elif selector.operator == '^=':
227229
if value:
228-
return 'el.etree_element.get(%s, "").startswith(%r)' % (
229-
key, value)
230+
return (
231+
f'el.etree_element.get({key}, "")'
232+
f'.startswith({value!r})')
230233
else:
231234
return '0'
232235
elif selector.operator == '$=':
233236
if value:
234-
return 'el.etree_element.get(%s, "").endswith(%r)' % (
235-
key, value)
237+
return (
238+
f'el.etree_element.get({key}, "")'
239+
f'.endswith({value!r})')
236240
else:
237241
return '0'
238242
elif selector.operator == '*=':
239243
if value:
240-
return '%r in el.etree_element.get(%s, "")' % (value, key)
244+
return f'{value!r} in el.etree_element.get({key}, "")'
241245
else:
242246
return '0'
243247
else:
@@ -248,44 +252,37 @@ def _compile_node(selector):
248252

249253
elif isinstance(selector, parser.PseudoClassSelector):
250254
if selector.name in ('link', 'any-link', 'local-link'):
251-
test = '%s and el.etree_element.get("href") is not None '
255+
test = html_tag_eq('a', 'area', 'link')
256+
test += ' and el.etree_element.get("href") is not None '
252257
if selector.name == 'local-link':
253258
test += 'and not urlparse(el.etree_element.get("href")).scheme'
254-
return test % html_tag_eq('a', 'area', 'link')
259+
return test
255260
elif selector.name == 'enabled':
261+
input = html_tag_eq(
262+
'button', 'input', 'select', 'textarea', 'option')
263+
group = html_tag_eq('optgroup', 'menuitem', 'fieldset')
264+
a = html_tag_eq('a', 'area', 'link')
256265
return (
257-
'(%s and el.etree_element.get("disabled") is None'
258-
' and not el.in_disabled_fieldset) or'
259-
'(%s and el.etree_element.get("disabled") is None) or '
260-
'(%s and el.etree_element.get("href") is not None)'
261-
% (
262-
html_tag_eq('button', 'input', 'select', 'textarea',
263-
'option'),
264-
html_tag_eq('optgroup', 'menuitem', 'fieldset'),
265-
html_tag_eq('a', 'area', 'link'),
266-
)
267-
)
266+
f'({input} and el.etree_element.get("disabled") is None'
267+
' and not el.in_disabled_fieldset) or'
268+
f'({group} and el.etree_element.get("disabled") is None) or '
269+
f'({a} and el.etree_element.get("href") is not None)')
268270
elif selector.name == 'disabled':
271+
input = html_tag_eq(
272+
'button', 'input', 'select', 'textarea', 'option')
273+
group = html_tag_eq('optgroup', 'menuitem', 'fieldset')
269274
return (
270-
'(%s and (el.etree_element.get("disabled") is not None'
271-
' or el.in_disabled_fieldset)) or'
272-
'(%s and el.etree_element.get("disabled") is not None)' % (
273-
html_tag_eq('button', 'input', 'select', 'textarea',
274-
'option'),
275-
html_tag_eq('optgroup', 'menuitem', 'fieldset'),
276-
)
277-
)
275+
f'({input} and (el.etree_element.get("disabled") is not None'
276+
' or el.in_disabled_fieldset)) or'
277+
f'({group} and el.etree_element.get("disabled") is not None)')
278278
elif selector.name == 'checked':
279+
input = html_tag_eq('input', 'menuitem')
280+
option = html_tag_eq('option')
279281
return (
280-
'(%s and el.etree_element.get("checked") is not None and'
281-
' ascii_lower(el.etree_element.get("type", "")) '
282-
' in ("checkbox", "radio"))'
283-
'or (%s and el.etree_element.get("selected") is not None)'
284-
% (
285-
html_tag_eq('input', 'menuitem'),
286-
html_tag_eq('option'),
287-
)
288-
)
282+
f'({input} and el.etree_element.get("checked") is not None and'
283+
' ascii_lower(el.etree_element.get("type", "")) '
284+
' in ("checkbox", "radio")) or ('
285+
f'{option} and el.etree_element.get("selected") is not None)')
289286
elif selector.name in (
290287
'visited', 'hover', 'active', 'focus', 'focus-within',
291288
'focus-visible', 'target', 'target-within', 'current', 'past',
@@ -301,16 +298,19 @@ def _compile_node(selector):
301298
elif selector.name == 'last-child':
302299
return 'el.index + 1 == len(el.etree_siblings)'
303300
elif selector.name == 'first-of-type':
304-
return ('all(s.tag != el.etree_element.tag'
305-
' for s in el.etree_siblings[:el.index])')
301+
return (
302+
'all(s.tag != el.etree_element.tag'
303+
' for s in el.etree_siblings[:el.index])')
306304
elif selector.name == 'last-of-type':
307-
return ('all(s.tag != el.etree_element.tag'
308-
' for s in el.etree_siblings[el.index + 1:])')
305+
return (
306+
'all(s.tag != el.etree_element.tag'
307+
' for s in el.etree_siblings[el.index + 1:])')
309308
elif selector.name == 'only-child':
310309
return 'len(el.etree_siblings) == 1'
311310
elif selector.name == 'only-of-type':
312-
return ('all(s.tag != el.etree_element.tag or i == el.index'
313-
' for i, s in enumerate(el.etree_siblings))')
311+
return (
312+
'all(s.tag != el.etree_element.tag or i == el.index'
313+
' for i, s in enumerate(el.etree_siblings))')
314314
elif selector.name == 'empty':
315315
return 'not (el.etree_children or el.etree_element.text)'
316316
else:
@@ -335,7 +335,7 @@ def _compile_node(selector):
335335
if token.type != 'ident' and token.value != ',':
336336
raise SelectorError('Invalid arguments for :lang()')
337337
return ' or '.join(
338-
f'el.lang == {lang!r} or el.lang.startswith({lang + "-"!r})'
338+
f'el.lang == {lang!r} or el.lang.startswith({(lang + "-")!r})'
339339
for lang in langs)
340340
else:
341341
nth = []
@@ -379,24 +379,26 @@ def _compile_node(selector):
379379
else:
380380
if current_list is selector_list:
381381
raise SelectorError(
382-
'Invalid arguments for :%s()' % selector.name)
382+
f'Invalid arguments for :{selector.name}()')
383383
if selector.name == 'nth-child':
384384
count = 'el.index'
385385
elif selector.name == 'nth-last-child':
386386
count = 'len(el.etree_siblings) - el.index - 1'
387387
elif selector.name == 'nth-of-type':
388-
count = ('sum(1 for s in el.etree_siblings[:el.index]'
389-
' if s.tag == el.etree_element.tag)')
388+
count = (
389+
'sum(1 for s in el.etree_siblings[:el.index]'
390+
' if s.tag == el.etree_element.tag)')
390391
elif selector.name == 'nth-last-of-type':
391-
count = ('sum(1 for s in el.etree_siblings[el.index + 1:]'
392-
' if s.tag == el.etree_element.tag)')
392+
count = (
393+
'sum(1 for s in el.etree_siblings[el.index + 1:]'
394+
' if s.tag == el.etree_element.tag)')
393395
else:
394396
raise SelectorError('Unknown pseudo-class', selector.name)
395397

396398
result = parse_nth(nth)
397399
if result is None:
398400
raise SelectorError(
399-
'Invalid arguments for :%s()' % selector.name)
401+
f'Invalid arguments for :{selector.name}()')
400402
a, b = result
401403
# x is the number of siblings before/after the element
402404
# Matches if a positive or zero integer n exists so that:
@@ -405,28 +407,28 @@ def _compile_node(selector):
405407
B = b - 1
406408
if a == 0:
407409
# x = B
408-
return '(%s) == %i' % (count, B)
410+
return f'({count}) == {B}'
409411
else:
410412
# n = (x - B) / a
411-
return ('next(r == 0 and n >= 0'
412-
' for n, r in [divmod((%s) - %i, %i)])'
413-
% (count, B, a))
413+
return (
414+
'next(r == 0 and n >= 0'
415+
f' for n, r in [divmod(({count}) - {B}, {a})])')
414416

415417
else:
416418
raise TypeError(type(selector), selector)
417419

418420

419421
def html_tag_eq(*local_names):
422+
"""Generate expression testing equality with HTML local names."""
420423
if len(local_names) == 1:
424+
tag = '{http://www.w3.org/1999/xhtml}' + local_names[0]
421425
return (
422-
'((el.local_name == %r) if el.in_html_document else '
423-
'(el.etree_element.tag == %r))' % (
424-
local_names[0],
425-
'{http://www.w3.org/1999/xhtml}' + local_names[0]))
426+
f'((el.local_name == {local_names[0]!r}) if el.in_html_document '
427+
f'else (el.etree_element.tag == {tag!r}))')
426428
else:
429+
names = ', '.join(repr(n) for n in local_names)
430+
tags = ', '.join(
431+
repr('{http://www.w3.org/1999/xhtml}' + n) for n in local_names)
427432
return (
428-
'((el.local_name in (%s)) if el.in_html_document else '
429-
'(el.etree_element.tag in (%s)))' % (
430-
', '.join(repr(n) for n in local_names),
431-
', '.join(repr('{http://www.w3.org/1999/xhtml}' + n)
432-
for n in local_names)))
433+
f'((el.local_name in ({names})) if el.in_html_document '
434+
f'else (el.etree_element.tag in ({tags})))')

0 commit comments

Comments
 (0)