Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Make pseudo-elements lower-case in the ASCII range.

See http://www.w3.org/TR/selectors/#casesens

Pseudo-classes were already case-insensitive, but the
lower-casing was moved to the parser.
  • Loading branch information...
commit c192fcb38d4a147b83cc46e32cfad4ba800ed180 1 parent 13023ed
@SimonSapin authored
Showing with 26 additions and 16 deletions.
  1. +11 −3 cssselect/parser.py
  2. +13 −11 cssselect/tests.py
  3. +2 −2 cssselect/xpath.py
View
14 cssselect/parser.py
@@ -25,6 +25,11 @@
_unichr = chr
+def ascii_lower(string):
+ """Lower-case, but only in the ASCII range."""
+ return string.encode('utf8').lower().decode('utf8')
+
+
class SelectorError(Exception):
"""Common parent for :class:`SelectorSyntaxError` and
:class:`ExpressionError`.
@@ -52,6 +57,8 @@ class Selector(object):
"""
def __init__(self, tree, pseudo_element=None):
self.parsed_tree = tree
+ if pseudo_element is not None:
+ pseudo_element = ascii_lower(pseudo_element)
#: The identifier for the pseudo-element as a string, or ``None``.
#:
#: +-------------------------+----------------+----------------+
@@ -114,7 +121,7 @@ class Function(object):
"""
def __init__(self, selector, name, arguments):
self.selector = selector
- self.name = name
+ self.name = ascii_lower(name)
self.arguments = arguments
def __repr__(self):
@@ -137,7 +144,7 @@ class Pseudo(object):
"""
def __init__(self, selector, ident):
self.selector = selector
- self.ident = ident
+ self.ident = ascii_lower(ident)
def __repr__(self):
return '%s[%r:%s]' % (
@@ -393,7 +400,8 @@ def parse_simple_selector(stream, inside_negation=False):
pseudo_element = stream.next_ident()
continue
ident = stream.next_ident()
- if ident in ('first-line', 'first-letter', 'before', 'after'):
+ if ident.lower() in ('first-line', 'first-letter',
+ 'before', 'after'):
# Special case: CSS 2.1 pseudo-elements can have a single ':'
# Any new pseudo-element must have two.
pseudo_element = _unicode(ident)
View
24 cssselect/tests.py
@@ -167,17 +167,17 @@ def parse_one(css):
assert parse_one(':empty') == ('Pseudo[Element[*]:empty]', None)
# Special cases for CSS 2.1 pseudo-elements
- assert parse_one(':before') == ('Element[*]', 'before')
- assert parse_one(':after') == ('Element[*]', 'after')
- assert parse_one(':first-line') == ('Element[*]', 'first-line')
- assert parse_one(':first-letter') == ('Element[*]', 'first-letter')
+ assert parse_one(':BEfore') == ('Element[*]', 'before')
+ assert parse_one(':aftER') == ('Element[*]', 'after')
+ assert parse_one(':First-Line') == ('Element[*]', 'first-line')
+ assert parse_one(':First-Letter') == ('Element[*]', 'first-letter')
- assert parse_one('::before') == ('Element[*]', 'before')
- assert parse_one('::after') == ('Element[*]', 'after')
- assert parse_one('::first-line') == ('Element[*]', 'first-line')
- assert parse_one('::first-letter') == ('Element[*]', 'first-letter')
+ assert parse_one('::befoRE') == ('Element[*]', 'before')
+ assert parse_one('::AFter') == ('Element[*]', 'after')
+ assert parse_one('::firsT-linE') == ('Element[*]', 'first-line')
+ assert parse_one('::firsT-letteR') == ('Element[*]', 'first-letter')
- assert parse_one('::selection') == ('Element[*]', 'selection')
+ assert parse_one('::Selection') == ('Element[*]', 'selection')
assert parse_one('foo:after') == ('Element[foo]', 'after')
assert parse_one('foo::selection') == ('Element[foo]', 'selection')
assert parse_one('lorem#ipsum ~ a#b.c[href]:empty::selection') == (
@@ -346,13 +346,15 @@ def xpath(css):
"e[last() = 1]")
assert xpath('e:empty') == (
"e[not(*) and not(normalize-space())]")
+ assert xpath('e:EmPTY') == (
+ "e[not(*) and not(normalize-space())]")
assert xpath('e:root') == (
"e[not(parent::*)]")
assert xpath('e:hover') == (
"e[0]") # never matches
assert xpath('e:contains("foo")') == (
"e[contains(string(.), 'foo')]")
- assert xpath('e:contains(foo)') == (
+ assert xpath('e:ConTains(foo)') == (
"e[contains(string(.), 'foo')]")
assert xpath('e.warning') == (
"e[@class and contains("
@@ -361,7 +363,7 @@ def xpath(css):
"e[@id = 'myid']")
assert xpath('e:not(:nth-child(odd))') == (
"e[not((position() -1) mod 2 = 0 and position() >= 1)]")
- assert xpath('e:not(*)') == (
+ assert xpath('e:nOT(*)') == (
"e[0]") # never matches
assert xpath('e f') == (
"e/descendant-or-self::*/f")
View
4 cssselect/xpath.py
@@ -234,7 +234,7 @@ def xpath_negation(self, negation):
def xpath_function(self, function):
"""Translate a functional pseudo-class."""
- method = 'xpath_%s_function' % function.name.replace('-', '_').lower()
+ method = 'xpath_%s_function' % function.name.replace('-', '_')
method = getattr(self, method, None)
if not method:
raise ExpressionError(
@@ -243,7 +243,7 @@ def xpath_function(self, function):
def xpath_pseudo(self, pseudo):
"""Translate a pseudo-class."""
- method = 'xpath_%s_pseudo' % pseudo.ident.replace('-', '_').lower()
+ method = 'xpath_%s_pseudo' % pseudo.ident.replace('-', '_')
method = getattr(self, method, None)
if not method:
# TODO: better error message for pseudo-elements?
Please sign in to comment.
Something went wrong with that request. Please try again.