Skip to content

Commit 754b701

Browse files
committed
Use parentheses when joining with AND potentially-complex expressions
1 parent c053272 commit 754b701

File tree

2 files changed

+17
-12
lines changed

2 files changed

+17
-12
lines changed

cssselect/xpath.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __repr__(self):
5656

5757
def add_condition(self, condition):
5858
if self.condition:
59-
self.condition = '%s and (%s)' % (self.condition, condition)
59+
self.condition = '(%s) and (%s)' % (self.condition, condition)
6060
else:
6161
self.condition = condition
6262
return self
@@ -457,19 +457,19 @@ def xpath_nth_child_function(self, xpath, function, last=False,
457457
if a == 0:
458458
return xpath.add_condition('%s = %s' % (siblings_count, b_min_1))
459459

460-
expr = []
460+
expressions = []
461461

462462
if a > 0:
463463
# siblings count, an+b-1, is always >= 0,
464464
# so if a>0, and (b-1)<=0, an "n" exists to satisfy this,
465465
# therefore, the predicate is only interesting if (b-1)>0
466466
if b_min_1 > 0:
467-
expr.append('%s >= %s' % (siblings_count, b_min_1))
467+
expressions.append('%s >= %s' % (siblings_count, b_min_1))
468468
else:
469469
# if a<0, and (b-1)<0, no "n" satisfies this,
470470
# this is tested above as an early exist condition
471471
# otherwise,
472-
expr.append('%s <= %s' % (siblings_count, b_min_1))
472+
expressions.append('%s <= %s' % (siblings_count, b_min_1))
473473

474474
# operations modulo 1 or -1 are simpler, one only needs to verify:
475475
#
@@ -495,9 +495,14 @@ def xpath_nth_child_function(self, xpath, function, last=False,
495495
b_neg = '+%s' % b_neg
496496
left = '(%s %s)' % (left, b_neg)
497497

498-
expr.append('%s mod %s = 0' % (left, a))
498+
expressions.append('%s mod %s = 0' % (left, a))
499499

500-
xpath.add_condition(' and '.join(expr))
500+
if len(expressions) > 1:
501+
template = '(%s)'
502+
else:
503+
template = '%s'
504+
xpath.add_condition(' and '.join(template % expression
505+
for expression in expressions))
501506
return xpath
502507

503508
def xpath_nth_last_child_function(self, xpath, function):

tests/test_cssselect.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,8 @@ def xpath(css):
428428
"e[count(preceding-sibling::*) <= 0]")
429429

430430
assert xpath('e:nth-child(3n+2)') == (
431-
"e[count(preceding-sibling::*) >= 1 and "
432-
"(count(preceding-sibling::*) +2) mod 3 = 0]")
431+
"e[(count(preceding-sibling::*) >= 1) and "
432+
"((count(preceding-sibling::*) +2) mod 3 = 0)]")
433433
assert xpath('e:nth-child(3n-2)') == (
434434
"e[count(preceding-sibling::*) mod 3 = 0]")
435435
assert xpath('e:nth-child(-n+6)') == (
@@ -442,8 +442,8 @@ def xpath(css):
442442
assert xpath('e:nth-last-child(2n+1)') == (
443443
"e[count(following-sibling::*) mod 2 = 0]")
444444
assert xpath('e:nth-last-child(2n+2)') == (
445-
"e[count(following-sibling::*) >= 1 and "
446-
"(count(following-sibling::*) +1) mod 2 = 0]")
445+
"e[(count(following-sibling::*) >= 1) and "
446+
"((count(following-sibling::*) +1) mod 2 = 0)]")
447447
assert xpath('e:nth-last-child(3n+1)') == (
448448
"e[count(following-sibling::*) mod 3 = 0]")
449449
# represents the two last e elements
@@ -497,7 +497,7 @@ def xpath(css):
497497
assert xpath('e > f') == (
498498
"e/f")
499499
assert xpath('e + f') == (
500-
"e/following-sibling::*[name() = 'f' and (position() = 1)]")
500+
"e/following-sibling::*[(name() = 'f') and (position() = 1)]")
501501
assert xpath('e ~ f') == (
502502
"e/following-sibling::f")
503503
assert xpath('e ~ f:nth-child(3)') == (
@@ -642,7 +642,7 @@ def xpath(css):
642642
"descendant-or-self::*[(@id = 'first' or @id = 'second') "
643643
"and (@href)]")
644644

645-
assert str(XPathExpr('', '', condition='@href')) == "[(@href)]"
645+
assert str(XPathExpr('', '', condition='@href')) == "[@href]"
646646

647647
document = etree.fromstring(OPERATOR_PRECEDENCE_IDS)
648648
sort_key = dict(

0 commit comments

Comments
 (0)