@@ -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
7575def _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
419421def 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