Browse Source

Initial code commit

tags/0.1
Johann Schmitz 4 years ago
parent
commit
28b3dfddc7
Signed by: ercpe <johann@j-schmitz.net> GPG Key ID: A084064277C501ED
6 changed files with 248 additions and 1 deletions
  1. +2
    -0
      .gitignore
  2. +2
    -1
      requirements.txt
  3. +2
    -0
      src/phylter/__init__.py
  4. +51
    -0
      src/phylter/conditions.py
  5. +83
    -0
      src/phylter/parser.py
  6. +108
    -0
      tests/test_parser.py

+ 2
- 0
.gitignore View File

@@ -59,3 +59,5 @@ docs/_build/
target/
# PyCharm
.idea

src/test.py

+ 2
- 1
requirements.txt View File

@@ -1 +1,2 @@
# Project dependencies
# Project dependencies
pyparsing

+ 2
- 0
src/phylter/__init__.py View File

@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-


+ 51
- 0
src/phylter/conditions.py View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-

class Condition(object):

def __init__(self, left, right):
self.left = left
self.right = right


class EqualsCondition(Condition):

def __str__(self):
return "%s == %s" % (self.left, self.right)


class GreaterThanCondition(Condition):

def __str__(self):
return "%s > %s" % (self.left, self.right)


class GreaterThanOrEqualCondition(Condition):

def __str__(self):
return "%s >= %s" % (self.left, self.right)


class LessThanCondition(Condition):

def __str__(self):
return "%s < %s" % (self.left, self.right)


class LessThanOrEqualCondition(Condition):

def __str__(self):
return "%s <= %s" % (self.left, self.right)


class Operator(object):
def __init__(self, left, right):
self.left = left
self.right = right


class AndOperator(Operator):
pass

class OrOperator(Operator):
pass


+ 83
- 0
src/phylter/parser.py View File

@@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
import pyparsing

from phylter.conditions import EqualsCondition, GreaterThanCondition, LessThanCondition, GreaterThanOrEqualCondition, \
LessThanOrEqualCondition

field = pyparsing.Word(pyparsing.alphanums)
operator = pyparsing.oneOf(('==', '!=', '>', '<', '>=', '<='))
value = pyparsing.quotedString | pyparsing.Word(pyparsing.alphanums)

#and_or = pyparsing.oneOf(['and', 'or'], caseless=True)

field_op_value = field + operator + value

#andor_field_op_value = and_or + field_op_value

pattern = field_op_value #+ pyparsing.Optional(pyparsing.OneOrMore(andor_field_op_value))


class ConsumableIter(object):
def __init__(self, iterable):
self.iterable = iterable or []
self.length = len(self.iterable)
self.pos = 0

@property
def remaining(self):
return self.length - self.pos

@property
def has_more(self):
return self.remaining > 0

@property
def current(self):
if self.pos >= self.length-1:
return None

return self.iterable[self.pos]

def consume(self, length=1):
if length is None or length < 0 or length > self.length:
raise ValueError("'length' argument must be 0 <= length")

if length > self.remaining:
raise ValueError("Cannot consume more than %s remaining elements" % self.remaining)

if length == 0:
return None

if length == 1:
element = self.iterable[self.pos]
self.pos += length
return element

elements = self.iterable[self.pos:self.pos+length]
self.pos += length
return elements


class Parser(object):

def __init__(self, *args, **kwargs):
pass

def parse(self, query):
chunks = ConsumableIter(pattern.parseString(query, parseAll=True))

while chunks.has_more:
left, operator, right = tuple(chunks.consume(3))
return [self._get_condition_class(operator)(left, right)]

def _get_condition_class(self, operator):
d = {
'==': EqualsCondition,
'>': GreaterThanCondition,
'<': LessThanCondition,
'>=': GreaterThanOrEqualCondition,
'<=': LessThanOrEqualCondition
}
if not operator in d:
raise Exception("Unknown operator '%s'" % operator)
return d[operator]

+ 108
- 0
tests/test_parser.py View File

@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
import pytest

from phylter.conditions import EqualsCondition, GreaterThanCondition, GreaterThanOrEqualCondition, LessThanCondition, \
LessThanOrEqualCondition
from phylter.parser import ConsumableIter, Parser


class TestConsumableIter(object):

def test_constructor(self):
for v in (None, []):
ci = ConsumableIter(v)
assert ci.length == 0
assert ci.remaining == 0

ci = ConsumableIter([1, 2, 3])
assert ci.length == 3
assert ci.remaining == 3

def test_current(self):
ci = ConsumableIter([1, 2, 3])
assert ci.current == 1

def test_current_empty(self):
ci = ConsumableIter([])
assert ci.current is None

def test_has_more(self):
ci = ConsumableIter([1, 2, 3])
assert ci.has_more

def test_has_more_empty(self):
ci = ConsumableIter([])
assert not ci.has_more

def test_consume(self):
ci = ConsumableIter([1, 2, 3])
with pytest.raises(ValueError):
ci.consume(None)

ci = ConsumableIter([1, 2, 3])
with pytest.raises(ValueError):
ci.consume(-1)

ci = ConsumableIter([1, 2, 3])
with pytest.raises(ValueError):
ci.consume(100)

ci = ConsumableIter([1, 2, 3])
ci.consume(1)
with pytest.raises(ValueError):
ci.consume(3)

ci = ConsumableIter([1, 2, 3])
assert ci.consume(0) is None
assert ci.consume(1) == 1
assert ci.consume(1) == 2
assert ci.consume(1) == 3
assert ci.remaining == 0
assert not ci.has_more

ci = ConsumableIter([1, 2, 3])
assert ci.consume(3) == [1, 2, 3]
assert ci.remaining == 0
assert not ci.has_more

def test_consume_empty(self):
ci = ConsumableIter([])

with pytest.raises(ValueError):
ci.consume(1)


class TestParser(object):

def test_constructor(self):
p = Parser()
p = Parser('foo')
p = Parser('foo', bar='baz')

def test_get_condition_class(self):
p = Parser()

assert p._get_condition_class('==') == EqualsCondition
assert p._get_condition_class('>') == GreaterThanCondition
assert p._get_condition_class('<') == LessThanCondition
assert p._get_condition_class('>=') == GreaterThanOrEqualCondition
assert p._get_condition_class('<=') == LessThanOrEqualCondition

for x in (None, 'foobar'):
with pytest.raises(Exception) as e:
p._get_condition_class(x)
assert e

def test_parse(self):
for query, left, right, clazz in (
('foo == bar', 'foo', 'bar', EqualsCondition),
('foo > bar', 'foo', 'bar', GreaterThanCondition),
('foo < bar', 'foo', 'bar', LessThanCondition),
('foo >= bar', 'foo', 'bar', GreaterThanOrEqualCondition),
('foo <= bar', 'foo', 'bar', LessThanOrEqualCondition),
):
q = Parser().parse(query)
assert len(q) == 1
assert isinstance(q[0], clazz)
assert q[0].left == left
assert q[0].right == right

Loading…
Cancel
Save