ahassany avatar ahassany committed 4377ed6

Better packaging

Comments (0)

Files changed (8)

 .pyc
+.egg*
+dist

__init__.py

-# coding=utf-8
-# Copyright (c) 2012 Structured Abstraction Inc.
-# 
-# See LICENSE for licensing details.
-"""PYRQLATE - Python interpretation of RQL queries."""
-
-import datetime
-import decimal
-import urllib
-
-
-#-------------------------------------------------------------------------------
-class Converters(object):
-
-    #---------------------------------------------------------------------------
-    class Timezone(datetime.tzinfo):
-        """Timezone class based on a constant offset."""
-
-        #-----------------------------------------------------------------------
-        def __init__(self, offset=0):
-            """Timezone[offset=<minutes>]"""
-            self._offset = offset
-            self._timedelta = datetime.timedelta(seconds=offset*60)
-
-        #-----------------------------------------------------------------------
-        def utcoffset(self, dt):
-            return self._timedelta
-
-        #-----------------------------------------------------------------------
-        def dst(self, dt):
-            return datetime.timedelta(0)
-
-        #-----------------------------------------------------------------------
-        def tzname(self, dt):
-            minutes = self._offset
-            sign = 1
-            if minutes < 0:
-                sign = -1
-            minutes = minutes * sign
-
-            hours = minutes / 60
-            minutes = minutes % 60
-            return u'UTC%+03d:%02d' % (hours * sign, minutes)
-
-    #-----------------------------------------------------------------------
-    """Runtime cache of Timezones, see get_timezone for usage."""
-    zones = {
-        0: Timezone(0) # UTC
-    }
-
-    #---------------------------------------------------------------------------
-    @classmethod
-    def get_timezone(cls, offset=0):
-        """
-        Returns a tzinfo instance representing the timezone matching offset.
-
-        Params:
-            -- offset: The number of minutes from GMT. [-720, 720]
-        """
-
-        if offset < -720:
-            offset = -720
-        if offset > 720:
-            offset = 720
-
-        zone = cls.zones.get(offset, None)
-        if zone is None:
-            zone = cls.Timezone(offset)
-            cls.zones[offset] = zone
-        return zone
-
-    #---------------------------------------------------------------------------
-    @classmethod
-    def datetime_from_iso(cls, time):
-        """Produces a datetime and zone from a string.
-
-        Allowed formats:
-            YYYY-MM-DDThh:mm:ss.mmmZ
-            YYYY-MM-DDThh:mm:ss.mmm-HH:MM
-            YYYY-MM-DDThh:mm:ss.mmm+HH:MM
-        """
-
-        try:
-            time = str(time)
-            offset = time[23:29]
-
-            if offset[:1] == '-':
-                offset = offset[1:].split(':')
-                offset = -(int(offset[0]) * 60 + int(offset[1]))
-                if offset < -720:
-                    offset = -720
-            elif offset[:1] == '+':
-                offset = offset[1:].split(':')
-                offset = int(offset[0]) * 60 + int(offset[1])
-                if offset > 720:
-                    offset = 720
-            else:
-                offset = 0
-
-            zone = cls.get_timezone(offset)
-
-            # Extract the date & time component
-            time = time[0:23]
-            date = datetime.datetime.strptime(time, "%Y-%m-%dT%H:%M:%S.%f")
-
-            # Make the time, zone aware.
-            date = date.replace(tzinfo=zone)
-
-        except Exception, e:
-            raise ValueError(str(e))
-
-        return date
-
-    #---------------------------------------------------------------------------
-    @classmethod
-    def datetime_to_iso(cls, time):
-        """Produces an iso representation from a datetime object.
-
-        Format:
-            YYYY-MM-DDTMM:HH:SS.mmm±HH:MM
-        """
-
-        if time.tzinfo is None:
-            time = time.astimezone(cls.get_timezone(0))
-
-        string = time.strftime("%Y-%m-%dT%H:%M:%S")
-
-        # We use milliseconds, not microseconds for wider compatibility.
-        milliseconds = int(time.strftime("%f")) / 1000
-        string = "%s.%03d" % (string, milliseconds)
-
-        # Slap the timezone at the end of the string.
-        offset = time.tzinfo.utcoffset(time)
-        minutes = (offset.days * 1440) + (offset.seconds / 60)
-        string = "%s%+03d:%02d" % (string, (minutes / 60), abs(minutes) % 60)
-        return string
-
-    #---------------------------------------------------------------------------
-    @classmethod
-    def to_python(cls, type, value):
-
-        def _number(x):
-            try:
-                return int(x)
-            except (TypeError, ValueError), e:
-                return decimal.Decimal(x)
-
-        lookup = {
-            'string': lambda x: unicode(x),
-            'boolean': lambda x: bool(x),
-            'number': _number, 
-            'datetime': lambda x: cls.datetime_from_iso(x)
-        }
-        converter = lookup.get(type)
-        if not converter:
-            raise ParseError(u'Unrecognized variable type %s' % type)
-        return converter(value)
-
-    #---------------------------------------------------------------------------
-    @classmethod
-    def from_python(cls, value):
-        lookup = {
-            unicode: lambda x: urllib.quote(x.encode('utf8')),
-            bool: lambda x: unicode(x).lower(),
-            int: lambda x: unicode(x),
-            decimal.Decimal: lambda x: unicode(x),
-            datetime.datetime: lambda x: cls.datetime_to_iso(x),
-            type(None): lambda x: 'null'
-        }
-
-        converter = lookup.get(type(value))
-        if not converter:
-            raise ParseError(u'Unrecognized variable type %s' % type(value))
-        return converter(value)
-
-
-#-------------------------------------------------------------------------------
-class Query(object):
-
-    operations = ['eq', 'lt', 'le', 'gt', 'ge', 'ne', 'in', 'out']
-    conjunctions = ['and', 'or']
-
-    #---------------------------------------------------------------------------
-    def __init__(self, name='and', args=None, cache=None, parent=None):
-        self.name = name
-
-        self.args = args
-        if self.args is None:
-            self.args = []
-
-        self.cache = cache
-        if self.cache is None:
-            self.cache = {}
-
-        self.parent = parent
-
-    #---------------------------------------------------------------------------
-    def ops(self):
-        """Return a flattened list of the all operations performed by this query.
-
-        Operations are composed of ('eq', 'lt', 'le', 'gt', 'ge', 'ne', 'in', 'out')
-        """
-
-        ops = [x for x in self.args if type(x) == Query and x.name in self.operations]
-        for x in self.args:
-            if type(x) == Query:
-                ops += x.ops()
-        return ops
-
-    #---------------------------------------------------------------------------
-    def expand(self):
-        return self.cache.get('expand')
-
-    #---------------------------------------------------------------------------
-    def sort(self):
-        return self.cache.get('sort')
-
-    #---------------------------------------------------------------------------
-    def select(self):
-        return self.cache.get('select')
-
-    #---------------------------------------------------------------------------
-    def limit(self):
-        return self.cache.get('limit')
-
-    #---------------------------------------------------------------------------
-    def __unicode__(self):
-        return u'%s(%s)' % (self.name, u','.join([unicode(arg) for arg in self.args]))
-
-    #---------------------------------------------------------------------------
-    def eq(self, var, val):
-        self.args.append(Query(name='eq', args=[var, val], parent=self))
-        return self
-
-    #---------------------------------------------------------------------------
-    def aand(self, query):
-        parent = Query(name='and', args=[self, query], parent=self.parent)
-        self.parent = parent
-        query.parent = parent
-        return parent
-
-    #---------------------------------------------------------------------------
-    def oor(self, query):
-        parent = Query(name='or', args=[self, query], parent=self.parent)
-        self.parent = parent
-        query.parent = parent
-        return parent
-
-    #---------------------------------------------------------------------------
-    def walk(self, visitor):
-        if self.name == 'and':
-            return visitor.visit_and(self)
-        elif self.name == 'or':
-            return visitor.visit_or(self)
-        elif self.name == 'eq':
-            return visitor.visit_eq(self)
-        elif self.name == 'lt':
-            return visitor.visit_lt(self)
-        elif self.name == 'le':
-            return visitor.visit_le(self)
-        elif self.name == 'gt':
-            return visitor.visit_gt(self)
-        elif self.name == 'ge':
-            return visitor.visit_ge(self)
-        elif self.name == 'ne':
-            return visitor.visit_ne(self)
-        elif self.name == 'in':
-            return visitor.visit_in(self)
-        elif self.name == 'out':
-            return visitor.visit_out(self)
-        elif self.name == 'expand':
-            return visitor.visit_expand(self)
-        elif self.name == 'sort':
-            return visitor.visit_sort(self)
-        elif self.name == 'select':
-            return visitor.visit_select(self)
-        elif self.name == 'array':
-            return visitor.visit_array(self)
-        elif self.name == 'limit':
-            return visitor.visit_limit(self)
-
-
-#-------------------------------------------------------------------------------
-class ParseError(Exception):
-    pass
-
-
-#-------------------------------------------------------------------------------
-def __pre_parse(rql):
-    """Converts the RQL url format into a normalized format.
-
-    Example:
-        id=10 => eq(id,10)
-        id=lt=10 => lt(id,10)
-        id=10&title=eagles => eq(id,10)&eq(title,eagles)
-    """
-
-    operators = [
-        (u'=lt=', u'lt'),
-        (u'=le=', u'le'),
-        (u'=ge=', u'ge'),
-        (u'=gt=', u'gt'),
-        (u'=ne=', u'ne'),
-        (u'=eq=', u'eq'),
-        (u'=in=', u'in'),
-        (u'=out=', u'out'),
-        (u'<=', u'le'),
-        (u'>=', u'ge'),
-        (u'!=', u'ne'),
-        (u'==', u'eq'),
-        (u'<', u'lt'),
-        (u'>', u'gt'),
-        (u'=', u'eq')
-    ]
-
-    if not isinstance(rql, unicode):
-        # Note that we are assuming unicode characters are encoded as utf8.
-        rql = rql.decode('utf8')
-
-    # Search left to right for an appropriate value.
-    def find_value(uni, start):
-        next = start
-        while next < len(uni):
-            if uni[next] in u'|&=':
-                break
-            next += 1
-        return uni[start:next]
-
-    # Search right to left for an appropriate variable.
-    def find_variable(uni, start):
-        prev = start
-        while prev > 0:
-            prev -= 1
-            if uni[prev] in u'()&|,=':
-                prev += 1
-                break
-
-        return uni[prev:start]
-
-    for op in operators:
-        current = 0
-        while True:
-            current = rql.find(op[0], current + 1)
-            if current < 0:
-                break
-
-            # Find the variable name before this operation.
-            variable = find_variable(rql, current)
-
-            # Find the value after the operation.
-            current += len(op[0])
-            value = find_value(rql, current)
-
-            if op[1] == u'in' or op[1] == u'out':
-                if value[0] != u'(':
-                    raise ParseError('The in operator requires an array as its only argument.')
-
-            rql = rql.replace(
-                    u'%s%s%s' % (variable, op[0], value),
-                    u'%s(%s,%s)' % (op[1], variable, value)
-                )
-
-            # Reset the search for the operator as the string has changed length.
-            current = 0
-
-    return rql
-
-
-#-------------------------------------------------------------------------------
-def __convert_to_python(value):
-    """Covert an RQL parsed value into a python equivalent"""
-
-    pieces = value.split(':')
-    if len(pieces) == 2:
-        type, value = pieces
-    else:
-        type = None
-
-    value = urllib.unquote(value.encode('utf8'))
-    value = value.decode('utf8')
-
-    if type:
-        return Converters.to_python(type, value)
-    else:
-        if value == 'Infinity':
-            return decimal.Decimal(value)
-        elif value == '-Infinity':
-            return decimal.Decimal(value)
-        elif value == 'true':
-            return True
-        elif value == 'false':
-            return False
-        elif value == 'null':
-            return None
-        else:
-            try:
-                return Converters.to_python('number', value)
-            except Exception, e:
-                pass
-
-            try:
-                return Converters.to_python('datetime', value)
-            except Exception:
-                pass
-
-    return value
-
-
-#-------------------------------------------------------------------------------
-def parse(rql):
-
-    rql = __pre_parse(rql)
-    term = Query(name=None)
-    root = term
-
-    property_or_value = u''
-    for current in range(len(rql)):
-
-        # Conjunction delimiters
-        if rql[current] == u'&':
-            if not term.name:
-                term.name = u'and'
-            elif term.name != u'and':
-                raise ParseError('Cannot mix conjunctions within a group, use parentheses to break up expression.')
-            continue
-
-        if rql[current] == u'|':
-            if not term.name:
-                term.name = u'or'
-            elif term.name != u'or':
-                raise ParseError('Cannot mix conjunctions within a group, use parentheses to break up expression.')
-            continue
-
-        # Opening parenthesis
-        if rql[current] == u'(':
-            new_term = Query(name=property_or_value, args=[])
-            property_or_value = u''
-            new_term.parent = term
-            term = new_term
-
-            if (term.name == u'sort' or
-                term.name == u'select' or
-                term.name == u'expand' or
-                term.name == u'limit'):
-                root.cache[term.name] = term
-            continue
-
-        # Closing parenthesis
-        elif rql[current] == u')':
-            if not term.name:
-                term.name = u'array'
-                if term.parent and (term.parent.name == u'and' or term.parent.name == u'or'):
-                    raise ParseError('Conjunctions require operations as arguments, not an array.')
-
-            if property_or_value:
-                if term.name == u'and' or term.name == u'or':
-                    raise ParseError('Conjunctions require operations as arguments, not values.')
-                term.args.append(__convert_to_python(property_or_value))
-                property_or_value = u''
-
-            if not term.parent:
-                raise ParseError('Closing parenthesis without an opening parenthesis is not allowed.')
-            term.parent.args.append(term)
-            term = term.parent
-            continue
-
-        if rql[current] in u',=':
-            if property_or_value:
-                term.args.append(__convert_to_python(property_or_value))
-                property_or_value = u''
-        else:
-            property_or_value += rql[current]
-
-    if term.parent:
-        raise ParseError('Opening parenthesis without a closing parenthesis.')
-    if not term.name:
-        term.name = u'and'
-
-    # Fix some degenerate conjunction cases.
-    # ex: id=10&(|people=>20) => id=10|people=20
-    outer = {'term': term}
-    def fix(local):
-        for arg in local.args:
-            if local.name != u'and' and local.name != u'or':
-                continue
-
-            if len(arg.args) == 1 and (arg.name == u'and' or arg.name == u'or'):
-                local.args.pop(local.args.index(arg))
-                arg.parent = local.parent
-                local.parent = arg
-
-                if len(local.args) == 1:
-                    arg.args.insert(0, local.args[0])
-                else:
-                    arg.args.insert(0, local)
-
-                if not arg.parent:
-                    outer['term'] = arg
-                return True
-
-            if fix(arg):
-                return True
-        return False
-
-    while fix(term):
-        pass
-    term = outer['term']
-
-    return term
-
-#-------------------------------------------------------------------------------
-class DeparseVisitor(object):
-
-    #---------------------------------------------------------------------------
-    def visit_and(self, query):
-        sub = []
-        for s in query.args:
-            sub.append(s.walk(self))
-
-        if query.parent:
-            return u'(%s)' % (u'&'.join(sub))
-        else:
-            return u'&'.join(sub)
-
-    #---------------------------------------------------------------------------
-    def visit_or(self, query):
-        sub = []
-        for s in query.args:
-            sub.append(s.walk(self))
-
-        if query.parent:
-            return u'(%s)' % (u'|'.join(sub))
-        else:
-            return u'|'.join(sub)
-
-    #---------------------------------------------------------------------------
-    def visit_eq(self, query):
-        return u'%s=%s' % (query.args[0], Converters.from_python(query.args[1]))
-
-    #---------------------------------------------------------------------------
-    def visit_ne(self, query):
-        return u'%s=ne=%s' % (query.args[0], Converters.from_python(query.args[1]))
-
-    #---------------------------------------------------------------------------
-    def visit_lt(self, query):
-        return u'%s=lt=%s' % (query.args[0], Converters.from_python(query.args[1]))
-
-    #---------------------------------------------------------------------------
-    def visit_le(self, query):
-        return u'%s=le=%s' % (query.args[0], Converters.from_python(query.args[1]))
-
-    #---------------------------------------------------------------------------
-    def visit_gt(self, query):
-        return u'%s=gt=%s' % (query.args[0], Converters.from_python(query.args[1]))
-
-    #---------------------------------------------------------------------------
-    def visit_ge(self, query):
-        return u'%s=ge=%s' % (query.args[0], Converters.from_python(query.args[1]))
-
-    #---------------------------------------------------------------------------
-    def visit_sort(self, query):
-        return u'sort(%s)' % (','.join(query.args))
-
-    #---------------------------------------------------------------------------
-    def visit_expand(self, query):
-        return u'expand(%s)' % (','.join(query.args))
-
-    #---------------------------------------------------------------------------
-    def visit_select(self, query):
-        return u'select(%s)' % (','.join(query.args))
-
-
-#-------------------------------------------------------------------------------
-def deparse(query):
-    """Deparse an RQL query into its url encoding form."""
-
-    return query.walk(DeparseVisitor())
-

pyrqlate/__init__.py

+# coding=utf-8
+# Copyright (c) 2012 Structured Abstraction Inc.
+# 
+# See LICENSE for licensing details.
+"""PYRQLATE - Python interpretation of RQL queries."""
+
+import datetime
+import decimal
+import urllib
+
+
+#-------------------------------------------------------------------------------
+class Converters(object):
+
+    #---------------------------------------------------------------------------
+    class Timezone(datetime.tzinfo):
+        """Timezone class based on a constant offset."""
+
+        #-----------------------------------------------------------------------
+        def __init__(self, offset=0):
+            """Timezone[offset=<minutes>]"""
+            self._offset = offset
+            self._timedelta = datetime.timedelta(seconds=offset*60)
+
+        #-----------------------------------------------------------------------
+        def utcoffset(self, dt):
+            return self._timedelta
+
+        #-----------------------------------------------------------------------
+        def dst(self, dt):
+            return datetime.timedelta(0)
+
+        #-----------------------------------------------------------------------
+        def tzname(self, dt):
+            minutes = self._offset
+            sign = 1
+            if minutes < 0:
+                sign = -1
+            minutes = minutes * sign
+
+            hours = minutes / 60
+            minutes = minutes % 60
+            return u'UTC%+03d:%02d' % (hours * sign, minutes)
+
+    #-----------------------------------------------------------------------
+    """Runtime cache of Timezones, see get_timezone for usage."""
+    zones = {
+        0: Timezone(0) # UTC
+    }
+
+    #---------------------------------------------------------------------------
+    @classmethod
+    def get_timezone(cls, offset=0):
+        """
+        Returns a tzinfo instance representing the timezone matching offset.
+
+        Params:
+            -- offset: The number of minutes from GMT. [-720, 720]
+        """
+
+        if offset < -720:
+            offset = -720
+        if offset > 720:
+            offset = 720
+
+        zone = cls.zones.get(offset, None)
+        if zone is None:
+            zone = cls.Timezone(offset)
+            cls.zones[offset] = zone
+        return zone
+
+    #---------------------------------------------------------------------------
+    @classmethod
+    def datetime_from_iso(cls, time):
+        """Produces a datetime and zone from a string.
+
+        Allowed formats:
+            YYYY-MM-DDThh:mm:ss.mmmZ
+            YYYY-MM-DDThh:mm:ss.mmm-HH:MM
+            YYYY-MM-DDThh:mm:ss.mmm+HH:MM
+        """
+
+        try:
+            time = str(time)
+            offset = time[23:29]
+
+            if offset[:1] == '-':
+                offset = offset[1:].split(':')
+                offset = -(int(offset[0]) * 60 + int(offset[1]))
+                if offset < -720:
+                    offset = -720
+            elif offset[:1] == '+':
+                offset = offset[1:].split(':')
+                offset = int(offset[0]) * 60 + int(offset[1])
+                if offset > 720:
+                    offset = 720
+            else:
+                offset = 0
+
+            zone = cls.get_timezone(offset)
+
+            # Extract the date & time component
+            time = time[0:23]
+            date = datetime.datetime.strptime(time, "%Y-%m-%dT%H:%M:%S.%f")
+
+            # Make the time, zone aware.
+            date = date.replace(tzinfo=zone)
+
+        except Exception, e:
+            raise ValueError(str(e))
+
+        return date
+
+    #---------------------------------------------------------------------------
+    @classmethod
+    def datetime_to_iso(cls, time):
+        """Produces an iso representation from a datetime object.
+
+        Format:
+            YYYY-MM-DDTMM:HH:SS.mmm±HH:MM
+        """
+
+        if time.tzinfo is None:
+            time = time.astimezone(cls.get_timezone(0))
+
+        string = time.strftime("%Y-%m-%dT%H:%M:%S")
+
+        # We use milliseconds, not microseconds for wider compatibility.
+        milliseconds = int(time.strftime("%f")) / 1000
+        string = "%s.%03d" % (string, milliseconds)
+
+        # Slap the timezone at the end of the string.
+        offset = time.tzinfo.utcoffset(time)
+        minutes = (offset.days * 1440) + (offset.seconds / 60)
+        string = "%s%+03d:%02d" % (string, (minutes / 60), abs(minutes) % 60)
+        return string
+
+    #---------------------------------------------------------------------------
+    @classmethod
+    def to_python(cls, type, value):
+
+        def _number(x):
+            try:
+                return int(x)
+            except (TypeError, ValueError), e:
+                return decimal.Decimal(x)
+
+        lookup = {
+            'string': lambda x: unicode(x),
+            'boolean': lambda x: bool(x),
+            'number': _number, 
+            'datetime': lambda x: cls.datetime_from_iso(x)
+        }
+        converter = lookup.get(type)
+        if not converter:
+            raise ParseError(u'Unrecognized variable type %s' % type)
+        return converter(value)
+
+    #---------------------------------------------------------------------------
+    @classmethod
+    def from_python(cls, value):
+        lookup = {
+            unicode: lambda x: urllib.quote(x.encode('utf8')),
+            bool: lambda x: unicode(x).lower(),
+            int: lambda x: unicode(x),
+            decimal.Decimal: lambda x: unicode(x),
+            datetime.datetime: lambda x: cls.datetime_to_iso(x),
+            type(None): lambda x: 'null'
+        }
+
+        converter = lookup.get(type(value))
+        if not converter:
+            raise ParseError(u'Unrecognized variable type %s' % type(value))
+        return converter(value)
+
+
+#-------------------------------------------------------------------------------
+class Query(object):
+
+    operations = ['eq', 'lt', 'le', 'gt', 'ge', 'ne', 'in', 'out']
+    conjunctions = ['and', 'or']
+
+    #---------------------------------------------------------------------------
+    def __init__(self, name='and', args=None, cache=None, parent=None):
+        self.name = name
+
+        self.args = args
+        if self.args is None:
+            self.args = []
+
+        self.cache = cache
+        if self.cache is None:
+            self.cache = {}
+
+        self.parent = parent
+
+    #---------------------------------------------------------------------------
+    def ops(self):
+        """Return a flattened list of the all operations performed by this query.
+
+        Operations are composed of ('eq', 'lt', 'le', 'gt', 'ge', 'ne', 'in', 'out')
+        """
+
+        ops = [x for x in self.args if type(x) == Query and x.name in self.operations]
+        for x in self.args:
+            if type(x) == Query:
+                ops += x.ops()
+        return ops
+
+    #---------------------------------------------------------------------------
+    def expand(self):
+        return self.cache.get('expand')
+
+    #---------------------------------------------------------------------------
+    def sort(self):
+        return self.cache.get('sort')
+
+    #---------------------------------------------------------------------------
+    def select(self):
+        return self.cache.get('select')
+
+    #---------------------------------------------------------------------------
+    def limit(self):
+        return self.cache.get('limit')
+
+    #---------------------------------------------------------------------------
+    def __unicode__(self):
+        return u'%s(%s)' % (self.name, u','.join([unicode(arg) for arg in self.args]))
+
+    #---------------------------------------------------------------------------
+    def eq(self, var, val):
+        self.args.append(Query(name='eq', args=[var, val], parent=self))
+        return self
+
+    #---------------------------------------------------------------------------
+    def aand(self, query):
+        parent = Query(name='and', args=[self, query], parent=self.parent)
+        self.parent = parent
+        query.parent = parent
+        return parent
+
+    #---------------------------------------------------------------------------
+    def oor(self, query):
+        parent = Query(name='or', args=[self, query], parent=self.parent)
+        self.parent = parent
+        query.parent = parent
+        return parent
+
+    #---------------------------------------------------------------------------
+    def walk(self, visitor):
+        if self.name == 'and':
+            return visitor.visit_and(self)
+        elif self.name == 'or':
+            return visitor.visit_or(self)
+        elif self.name == 'eq':
+            return visitor.visit_eq(self)
+        elif self.name == 'lt':
+            return visitor.visit_lt(self)
+        elif self.name == 'le':
+            return visitor.visit_le(self)
+        elif self.name == 'gt':
+            return visitor.visit_gt(self)
+        elif self.name == 'ge':
+            return visitor.visit_ge(self)
+        elif self.name == 'ne':
+            return visitor.visit_ne(self)
+        elif self.name == 'in':
+            return visitor.visit_in(self)
+        elif self.name == 'out':
+            return visitor.visit_out(self)
+        elif self.name == 'expand':
+            return visitor.visit_expand(self)
+        elif self.name == 'sort':
+            return visitor.visit_sort(self)
+        elif self.name == 'select':
+            return visitor.visit_select(self)
+        elif self.name == 'array':
+            return visitor.visit_array(self)
+        elif self.name == 'limit':
+            return visitor.visit_limit(self)
+
+
+#-------------------------------------------------------------------------------
+class ParseError(Exception):
+    pass
+
+
+#-------------------------------------------------------------------------------
+def __pre_parse(rql):
+    """Converts the RQL url format into a normalized format.
+
+    Example:
+        id=10 => eq(id,10)
+        id=lt=10 => lt(id,10)
+        id=10&title=eagles => eq(id,10)&eq(title,eagles)
+    """
+
+    operators = [
+        (u'=lt=', u'lt'),
+        (u'=le=', u'le'),
+        (u'=ge=', u'ge'),
+        (u'=gt=', u'gt'),
+        (u'=ne=', u'ne'),
+        (u'=eq=', u'eq'),
+        (u'=in=', u'in'),
+        (u'=out=', u'out'),
+        (u'<=', u'le'),
+        (u'>=', u'ge'),
+        (u'!=', u'ne'),
+        (u'==', u'eq'),
+        (u'<', u'lt'),
+        (u'>', u'gt'),
+        (u'=', u'eq')
+    ]
+
+    if not isinstance(rql, unicode):
+        # Note that we are assuming unicode characters are encoded as utf8.
+        rql = rql.decode('utf8')
+
+    # Search left to right for an appropriate value.
+    def find_value(uni, start):
+        next = start
+        while next < len(uni):
+            if uni[next] in u'|&=':
+                break
+            next += 1
+        return uni[start:next]
+
+    # Search right to left for an appropriate variable.
+    def find_variable(uni, start):
+        prev = start
+        while prev > 0:
+            prev -= 1
+            if uni[prev] in u'()&|,=':
+                prev += 1
+                break
+
+        return uni[prev:start]
+
+    for op in operators:
+        current = 0
+        while True:
+            current = rql.find(op[0], current + 1)
+            if current < 0:
+                break
+
+            # Find the variable name before this operation.
+            variable = find_variable(rql, current)
+
+            # Find the value after the operation.
+            current += len(op[0])
+            value = find_value(rql, current)
+
+            if op[1] == u'in' or op[1] == u'out':
+                if value[0] != u'(':
+                    raise ParseError('The in operator requires an array as its only argument.')
+
+            rql = rql.replace(
+                    u'%s%s%s' % (variable, op[0], value),
+                    u'%s(%s,%s)' % (op[1], variable, value)
+                )
+
+            # Reset the search for the operator as the string has changed length.
+            current = 0
+
+    return rql
+
+
+#-------------------------------------------------------------------------------
+def __convert_to_python(value):
+    """Covert an RQL parsed value into a python equivalent"""
+
+    pieces = value.split(':')
+    if len(pieces) == 2:
+        type, value = pieces
+    else:
+        type = None
+
+    value = urllib.unquote(value.encode('utf8'))
+    value = value.decode('utf8')
+
+    if type:
+        return Converters.to_python(type, value)
+    else:
+        if value == 'Infinity':
+            return decimal.Decimal(value)
+        elif value == '-Infinity':
+            return decimal.Decimal(value)
+        elif value == 'true':
+            return True
+        elif value == 'false':
+            return False
+        elif value == 'null':
+            return None
+        else:
+            try:
+                return Converters.to_python('number', value)
+            except Exception, e:
+                pass
+
+            try:
+                return Converters.to_python('datetime', value)
+            except Exception:
+                pass
+
+    return value
+
+
+#-------------------------------------------------------------------------------
+def parse(rql):
+
+    rql = __pre_parse(rql)
+    term = Query(name=None)
+    root = term
+
+    property_or_value = u''
+    for current in range(len(rql)):
+
+        # Conjunction delimiters
+        if rql[current] == u'&':
+            if not term.name:
+                term.name = u'and'
+            elif term.name != u'and':
+                raise ParseError('Cannot mix conjunctions within a group, use parentheses to break up expression.')
+            continue
+
+        if rql[current] == u'|':
+            if not term.name:
+                term.name = u'or'
+            elif term.name != u'or':
+                raise ParseError('Cannot mix conjunctions within a group, use parentheses to break up expression.')
+            continue
+
+        # Opening parenthesis
+        if rql[current] == u'(':
+            new_term = Query(name=property_or_value, args=[])
+            property_or_value = u''
+            new_term.parent = term
+            term = new_term
+
+            if (term.name == u'sort' or
+                term.name == u'select' or
+                term.name == u'expand' or
+                term.name == u'limit'):
+                root.cache[term.name] = term
+            continue
+
+        # Closing parenthesis
+        elif rql[current] == u')':
+            if not term.name:
+                term.name = u'array'
+                if term.parent and (term.parent.name == u'and' or term.parent.name == u'or'):
+                    raise ParseError('Conjunctions require operations as arguments, not an array.')
+
+            if property_or_value:
+                if term.name == u'and' or term.name == u'or':
+                    raise ParseError('Conjunctions require operations as arguments, not values.')
+                term.args.append(__convert_to_python(property_or_value))
+                property_or_value = u''
+
+            if not term.parent:
+                raise ParseError('Closing parenthesis without an opening parenthesis is not allowed.')
+            term.parent.args.append(term)
+            term = term.parent
+            continue
+
+        if rql[current] in u',=':
+            if property_or_value:
+                term.args.append(__convert_to_python(property_or_value))
+                property_or_value = u''
+        else:
+            property_or_value += rql[current]
+
+    if term.parent:
+        raise ParseError('Opening parenthesis without a closing parenthesis.')
+    if not term.name:
+        term.name = u'and'
+
+    # Fix some degenerate conjunction cases.
+    # ex: id=10&(|people=>20) => id=10|people=20
+    outer = {'term': term}
+    def fix(local):
+        for arg in local.args:
+            if local.name != u'and' and local.name != u'or':
+                continue
+
+            if len(arg.args) == 1 and (arg.name == u'and' or arg.name == u'or'):
+                local.args.pop(local.args.index(arg))
+                arg.parent = local.parent
+                local.parent = arg
+
+                if len(local.args) == 1:
+                    arg.args.insert(0, local.args[0])
+                else:
+                    arg.args.insert(0, local)
+
+                if not arg.parent:
+                    outer['term'] = arg
+                return True
+
+            if fix(arg):
+                return True
+        return False
+
+    while fix(term):
+        pass
+    term = outer['term']
+
+    return term
+
+#-------------------------------------------------------------------------------
+class DeparseVisitor(object):
+
+    #---------------------------------------------------------------------------
+    def visit_and(self, query):
+        sub = []
+        for s in query.args:
+            sub.append(s.walk(self))
+
+        if query.parent:
+            return u'(%s)' % (u'&'.join(sub))
+        else:
+            return u'&'.join(sub)
+
+    #---------------------------------------------------------------------------
+    def visit_or(self, query):
+        sub = []
+        for s in query.args:
+            sub.append(s.walk(self))
+
+        if query.parent:
+            return u'(%s)' % (u'|'.join(sub))
+        else:
+            return u'|'.join(sub)
+
+    #---------------------------------------------------------------------------
+    def visit_eq(self, query):
+        return u'%s=%s' % (query.args[0], Converters.from_python(query.args[1]))
+
+    #---------------------------------------------------------------------------
+    def visit_ne(self, query):
+        return u'%s=ne=%s' % (query.args[0], Converters.from_python(query.args[1]))
+
+    #---------------------------------------------------------------------------
+    def visit_lt(self, query):
+        return u'%s=lt=%s' % (query.args[0], Converters.from_python(query.args[1]))
+
+    #---------------------------------------------------------------------------
+    def visit_le(self, query):
+        return u'%s=le=%s' % (query.args[0], Converters.from_python(query.args[1]))
+
+    #---------------------------------------------------------------------------
+    def visit_gt(self, query):
+        return u'%s=gt=%s' % (query.args[0], Converters.from_python(query.args[1]))
+
+    #---------------------------------------------------------------------------
+    def visit_ge(self, query):
+        return u'%s=ge=%s' % (query.args[0], Converters.from_python(query.args[1]))
+
+    #---------------------------------------------------------------------------
+    def visit_sort(self, query):
+        return u'sort(%s)' % (','.join(query.args))
+
+    #---------------------------------------------------------------------------
+    def visit_expand(self, query):
+        return u'expand(%s)' % (','.join(query.args))
+
+    #---------------------------------------------------------------------------
+    def visit_select(self, query):
+        return u'select(%s)' % (','.join(query.args))
+
+
+#-------------------------------------------------------------------------------
+def deparse(query):
+    """Deparse an RQL query into its url encoding form."""
+
+    return query.walk(DeparseVisitor())
+
Add a comment to this file

pyrqlate/tests/__init__.py

Empty file added.

pyrqlate/tests/test.py

+#encoding: utf8
+
+import unittest
+import pyrqlate
+import urllib
+
+#-------------------------------------------------------------------------------
+class TestParse(unittest.TestCase):
+
+    #---------------------------------------------------------------------------
+    def test_eq_parse(self):
+
+        q = pyrqlate.parse('id=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=1')
+        q = pyrqlate.parse('id=eagles')
+        self.assertEqual(pyrqlate.deparse(q), 'id=eagles')
+        q = pyrqlate.parse('id=true')
+        self.assertEqual(pyrqlate.deparse(q), 'id=true')
+        q = pyrqlate.parse('id=false')
+        self.assertEqual(pyrqlate.deparse(q), 'id=false')
+        q = pyrqlate.parse('id=null')
+        self.assertEqual(pyrqlate.deparse(q), 'id=null')
+        q = pyrqlate.parse('id=2011-01-01T00:00:00.000-07:00')
+        self.assertEqual(pyrqlate.deparse(q), 'id=2011-01-01T00:00:00.000-07:00')
+        q = pyrqlate.parse('id=1.56')
+        self.assertEqual(pyrqlate.deparse(q), 'id=1.56')
+
+        # UTF-8 encoded unicode.
+        q = pyrqlate.parse('id=орлов')
+        self.assertEqual(pyrqlate.deparse(q), 'id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # UTF-8 encoded and URL quoted.
+        q = pyrqlate.parse('id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+        self.assertEqual(pyrqlate.deparse(q), 'id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # Pre decoded unicode.
+        q = pyrqlate.parse(u'id=орлов')
+        self.assertEqual(pyrqlate.deparse(q), u'id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+    #---------------------------------------------------------------------------
+    def test_ne_parse(self):
+
+        q = pyrqlate.parse('id!=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=1')
+
+        q = pyrqlate.parse('id=ne=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=1')
+        q = pyrqlate.parse('id=ne=eagles')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=eagles')
+        q = pyrqlate.parse('id=ne=true')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=true')
+        q = pyrqlate.parse('id=ne=false')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=false')
+        q = pyrqlate.parse('id=ne=null')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=null')
+        q = pyrqlate.parse('id=ne=2011-01-01T00:00:00.000-07:00')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=2011-01-01T00:00:00.000-07:00')
+        q = pyrqlate.parse('id=ne=1.56')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=1.56')
+
+        # UTF-8 encoded unicode.
+        q = pyrqlate.parse('id=ne=орлов')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # UTF-8 encoded and URL quoted.
+        q = pyrqlate.parse('id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # Pre decoded unicode.
+        q = pyrqlate.parse(u'id=ne=орлов')
+        self.assertEqual(pyrqlate.deparse(q), u'id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+    #---------------------------------------------------------------------------
+    def test_lt_parse(self):
+
+        q = pyrqlate.parse('id<1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=1')
+
+        q = pyrqlate.parse('id=lt=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=1')
+        q = pyrqlate.parse('id=lt=eagles')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=eagles')
+        q = pyrqlate.parse('id=lt=true')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=true')
+        q = pyrqlate.parse('id=lt=false')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=false')
+        q = pyrqlate.parse('id=lt=null')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=null')
+        q = pyrqlate.parse('id=lt=2011-01-01T00:00:00.000-07:00')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=2011-01-01T00:00:00.000-07:00')
+        q = pyrqlate.parse('id=lt=1.56')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=1.56')
+
+        # UTF-8 encoded unicode.
+        q = pyrqlate.parse('id=lt=орлов')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # UTF-8 encoded and URL quoted.
+        q = pyrqlate.parse('id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+        self.assertEqual(pyrqlate.deparse(q), 'id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # Pre decoded unicode.
+        q = pyrqlate.parse(u'id=lt=орлов')
+        self.assertEqual(pyrqlate.deparse(q), u'id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+    #---------------------------------------------------------------------------
+    def test_le_parse(self):
+
+        q = pyrqlate.parse('id<=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=1')
+
+        q = pyrqlate.parse('id=le=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=1')
+        q = pyrqlate.parse('id=le=eagles')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=eagles')
+        q = pyrqlate.parse('id=le=true')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=true')
+        q = pyrqlate.parse('id=le=false')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=false')
+        q = pyrqlate.parse('id=le=null')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=null')
+        q = pyrqlate.parse('id=le=2011-01-01T00:00:00.000-07:00')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=2011-01-01T00:00:00.000-07:00')
+        q = pyrqlate.parse('id=le=1.56')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=1.56')
+
+        # UTF-8 encoded unicode.
+        q = pyrqlate.parse('id=le=орлов')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # UTF-8 encoded and URL quoted.
+        q = pyrqlate.parse('id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+        self.assertEqual(pyrqlate.deparse(q), 'id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # Pre decoded unicode.
+        q = pyrqlate.parse(u'id=le=орлов')
+        self.assertEqual(pyrqlate.deparse(q), u'id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+    #---------------------------------------------------------------------------
+    def test_gt_parse(self):
+
+        q = pyrqlate.parse('id>1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=1')
+
+        q = pyrqlate.parse('id=gt=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=1')
+        q = pyrqlate.parse('id=gt=eagles')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=eagles')
+        q = pyrqlate.parse('id=gt=true')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=true')
+        q = pyrqlate.parse('id=gt=false')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=false')
+        q = pyrqlate.parse('id=gt=null')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=null')
+        q = pyrqlate.parse('id=gt=2011-01-01T00:00:00.000-07:00')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=2011-01-01T00:00:00.000-07:00')
+        q = pyrqlate.parse('id=gt=1.56')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=1.56')
+
+        # UTF-8 encoded unicode.
+        q = pyrqlate.parse('id=gt=орлов')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # UTF-8 encoded and URL quoted.
+        q = pyrqlate.parse('id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+        self.assertEqual(pyrqlate.deparse(q), 'id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # Pre decoded unicode.
+        q = pyrqlate.parse(u'id=gt=орлов')
+        self.assertEqual(pyrqlate.deparse(q), u'id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+    #---------------------------------------------------------------------------
+    def test_ge_parse(self):
+
+        q = pyrqlate.parse('id>=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=1')
+
+        q = pyrqlate.parse('id=ge=1')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=1')
+        q = pyrqlate.parse('id=ge=eagles')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=eagles')
+        q = pyrqlate.parse('id=ge=true')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=true')
+        q = pyrqlate.parse('id=ge=false')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=false')
+        q = pyrqlate.parse('id=ge=null')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=null')
+        q = pyrqlate.parse('id=ge=2011-01-01T00:00:00.000-07:00')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=2011-01-01T00:00:00.000-07:00')
+        q = pyrqlate.parse('id=ge=1.56')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=1.56')
+
+        # UTF-8 encoded unicode.
+        q = pyrqlate.parse('id=ge=орлов')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # UTF-8 encoded and URL quoted.
+        q = pyrqlate.parse('id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+        self.assertEqual(pyrqlate.deparse(q), 'id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+        # Pre decoded unicode.
+        q = pyrqlate.parse(u'id=ge=орлов')
+        self.assertEqual(pyrqlate.deparse(q), u'id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
+
+    #---------------------------------------------------------------------------
+    def test_and_parse(self):
+
+        q = pyrqlate.parse('id=1&name=eagle')
+        self.assertEqual(pyrqlate.deparse(q), 'id=1&name=eagle')
+
+        q = pyrqlate.parse('id=1&(name=eagle&age=1)')
+        self.assertEqual(pyrqlate.deparse(q), 'id=1&(name=eagle&age=1)')
+
+    #---------------------------------------------------------------------------
+    def test_or_parse(self):
+
+        q = pyrqlate.parse('id=1|name=eagle')
+        self.assertEqual(pyrqlate.deparse(q), 'id=1|name=eagle')
+
+        q = pyrqlate.parse('id=1|(name=eagle|age=1)')
+        self.assertEqual(pyrqlate.deparse(q), 'id=1|(name=eagle|age=1)')
+
+    #---------------------------------------------------------------------------
+    def test_empty_parse(self):
+        q = pyrqlate.parse('')
+        self.assertEqual(pyrqlate.Query, type(q))
+
+#!/usr/bin/env python
+from setuptools import setup
+
+version = "0.1.dev"
+
+setup(
+    name="pyrqlate",
+    version=version,
+    packages=["pyrqlate", "pyrqlate.tests"],
+    package_data={},
+)
Add a comment to this file

tests/__init__.py

Empty file removed.

tests/test.py

-#encoding: utf8
-
-import unittest
-import pyrqlate
-import urllib
-
-#-------------------------------------------------------------------------------
-class TestParse(unittest.TestCase):
-
-    #---------------------------------------------------------------------------
-    def test_eq_parse(self):
-
-        q = pyrqlate.parse('id=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=1')
-        q = pyrqlate.parse('id=eagles')
-        self.assertEqual(pyrqlate.deparse(q), 'id=eagles')
-        q = pyrqlate.parse('id=true')
-        self.assertEqual(pyrqlate.deparse(q), 'id=true')
-        q = pyrqlate.parse('id=false')
-        self.assertEqual(pyrqlate.deparse(q), 'id=false')
-        q = pyrqlate.parse('id=null')
-        self.assertEqual(pyrqlate.deparse(q), 'id=null')
-        q = pyrqlate.parse('id=2011-01-01T00:00:00.000-07:00')
-        self.assertEqual(pyrqlate.deparse(q), 'id=2011-01-01T00:00:00.000-07:00')
-        q = pyrqlate.parse('id=1.56')
-        self.assertEqual(pyrqlate.deparse(q), 'id=1.56')
-
-        # UTF-8 encoded unicode.
-        q = pyrqlate.parse('id=орлов')
-        self.assertEqual(pyrqlate.deparse(q), 'id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # UTF-8 encoded and URL quoted.
-        q = pyrqlate.parse('id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-        self.assertEqual(pyrqlate.deparse(q), 'id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # Pre decoded unicode.
-        q = pyrqlate.parse(u'id=орлов')
-        self.assertEqual(pyrqlate.deparse(q), u'id=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-    #---------------------------------------------------------------------------
-    def test_ne_parse(self):
-
-        q = pyrqlate.parse('id!=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=1')
-
-        q = pyrqlate.parse('id=ne=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=1')
-        q = pyrqlate.parse('id=ne=eagles')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=eagles')
-        q = pyrqlate.parse('id=ne=true')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=true')
-        q = pyrqlate.parse('id=ne=false')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=false')
-        q = pyrqlate.parse('id=ne=null')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=null')
-        q = pyrqlate.parse('id=ne=2011-01-01T00:00:00.000-07:00')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=2011-01-01T00:00:00.000-07:00')
-        q = pyrqlate.parse('id=ne=1.56')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=1.56')
-
-        # UTF-8 encoded unicode.
-        q = pyrqlate.parse('id=ne=орлов')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # UTF-8 encoded and URL quoted.
-        q = pyrqlate.parse('id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # Pre decoded unicode.
-        q = pyrqlate.parse(u'id=ne=орлов')
-        self.assertEqual(pyrqlate.deparse(q), u'id=ne=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-    #---------------------------------------------------------------------------
-    def test_lt_parse(self):
-
-        q = pyrqlate.parse('id<1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=1')
-
-        q = pyrqlate.parse('id=lt=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=1')
-        q = pyrqlate.parse('id=lt=eagles')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=eagles')
-        q = pyrqlate.parse('id=lt=true')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=true')
-        q = pyrqlate.parse('id=lt=false')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=false')
-        q = pyrqlate.parse('id=lt=null')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=null')
-        q = pyrqlate.parse('id=lt=2011-01-01T00:00:00.000-07:00')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=2011-01-01T00:00:00.000-07:00')
-        q = pyrqlate.parse('id=lt=1.56')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=1.56')
-
-        # UTF-8 encoded unicode.
-        q = pyrqlate.parse('id=lt=орлов')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # UTF-8 encoded and URL quoted.
-        q = pyrqlate.parse('id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-        self.assertEqual(pyrqlate.deparse(q), 'id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # Pre decoded unicode.
-        q = pyrqlate.parse(u'id=lt=орлов')
-        self.assertEqual(pyrqlate.deparse(q), u'id=lt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-    #---------------------------------------------------------------------------
-    def test_le_parse(self):
-
-        q = pyrqlate.parse('id<=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=1')
-
-        q = pyrqlate.parse('id=le=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=1')
-        q = pyrqlate.parse('id=le=eagles')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=eagles')
-        q = pyrqlate.parse('id=le=true')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=true')
-        q = pyrqlate.parse('id=le=false')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=false')
-        q = pyrqlate.parse('id=le=null')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=null')
-        q = pyrqlate.parse('id=le=2011-01-01T00:00:00.000-07:00')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=2011-01-01T00:00:00.000-07:00')
-        q = pyrqlate.parse('id=le=1.56')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=1.56')
-
-        # UTF-8 encoded unicode.
-        q = pyrqlate.parse('id=le=орлов')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # UTF-8 encoded and URL quoted.
-        q = pyrqlate.parse('id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-        self.assertEqual(pyrqlate.deparse(q), 'id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # Pre decoded unicode.
-        q = pyrqlate.parse(u'id=le=орлов')
-        self.assertEqual(pyrqlate.deparse(q), u'id=le=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-    #---------------------------------------------------------------------------
-    def test_gt_parse(self):
-
-        q = pyrqlate.parse('id>1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=1')
-
-        q = pyrqlate.parse('id=gt=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=1')
-        q = pyrqlate.parse('id=gt=eagles')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=eagles')
-        q = pyrqlate.parse('id=gt=true')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=true')
-        q = pyrqlate.parse('id=gt=false')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=false')
-        q = pyrqlate.parse('id=gt=null')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=null')
-        q = pyrqlate.parse('id=gt=2011-01-01T00:00:00.000-07:00')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=2011-01-01T00:00:00.000-07:00')
-        q = pyrqlate.parse('id=gt=1.56')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=1.56')
-
-        # UTF-8 encoded unicode.
-        q = pyrqlate.parse('id=gt=орлов')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # UTF-8 encoded and URL quoted.
-        q = pyrqlate.parse('id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-        self.assertEqual(pyrqlate.deparse(q), 'id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # Pre decoded unicode.
-        q = pyrqlate.parse(u'id=gt=орлов')
-        self.assertEqual(pyrqlate.deparse(q), u'id=gt=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-    #---------------------------------------------------------------------------
-    def test_ge_parse(self):
-
-        q = pyrqlate.parse('id>=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=1')
-
-        q = pyrqlate.parse('id=ge=1')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=1')
-        q = pyrqlate.parse('id=ge=eagles')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=eagles')
-        q = pyrqlate.parse('id=ge=true')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=true')
-        q = pyrqlate.parse('id=ge=false')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=false')
-        q = pyrqlate.parse('id=ge=null')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=null')
-        q = pyrqlate.parse('id=ge=2011-01-01T00:00:00.000-07:00')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=2011-01-01T00:00:00.000-07:00')
-        q = pyrqlate.parse('id=ge=1.56')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=1.56')
-
-        # UTF-8 encoded unicode.
-        q = pyrqlate.parse('id=ge=орлов')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # UTF-8 encoded and URL quoted.
-        q = pyrqlate.parse('id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-        self.assertEqual(pyrqlate.deparse(q), 'id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-        # Pre decoded unicode.
-        q = pyrqlate.parse(u'id=ge=орлов')
-        self.assertEqual(pyrqlate.deparse(q), u'id=ge=%D0%BE%D1%80%D0%BB%D0%BE%D0%B2')
-
-    #---------------------------------------------------------------------------
-    def test_and_parse(self):
-
-        q = pyrqlate.parse('id=1&name=eagle')
-        self.assertEqual(pyrqlate.deparse(q), 'id=1&name=eagle')
-
-        q = pyrqlate.parse('id=1&(name=eagle&age=1)')
-        self.assertEqual(pyrqlate.deparse(q), 'id=1&(name=eagle&age=1)')
-
-    #---------------------------------------------------------------------------
-    def test_or_parse(self):
-
-        q = pyrqlate.parse('id=1|name=eagle')
-        self.assertEqual(pyrqlate.deparse(q), 'id=1|name=eagle')
-
-        q = pyrqlate.parse('id=1|(name=eagle|age=1)')
-        self.assertEqual(pyrqlate.deparse(q), 'id=1|(name=eagle|age=1)')
-
-    #---------------------------------------------------------------------------
-    def test_empty_parse(self):
-        q = pyrqlate.parse('')
-        self.assertEqual(pyrqlate.Query, type(q))
-
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.