Skip to content

Commit

Permalink
Merge pull request #52 from jiaaro/master
Browse files Browse the repository at this point in the history
delete python 2 compatibility stuff and add cython
  • Loading branch information
BevMS authored Nov 21, 2017
2 parents eab78d3 + e22d644 commit 447cf34
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 299 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ python:
- "3.6"
install:
- pip install django==${DJANGO}
- "pip install psycopg2 git+https://github.com/mediapredict/[email protected] future==0.16.0"
- "pip install psycopg2 git+https://github.com/mediapredict/[email protected] cython"
- "pip install ."
addons:
postgresql: "9.3"
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,16 @@ Arrays:

### Functions

- `timestamp(YYYY-MM-DD)` this is a helper function which generates the unix timestamp corresponding to the date entered. When the daffodil is evaluated the timestamp is functionally a number, but this lets you write the daffodil in a way that is easier to read and understand.

Example: people who began `mystudy` after halloween 2017: `mystudy__started >= timestamp(2017-10-31)`
- `timestamp(YYYY-MM-DD)` or `timestamp(YYYY-MM-DD HH:MM)` this is a helper function which generates the unix timestamp corresponding to the date (and optionally, time) entered. When the daffodil is evaluated the datetime is functionally a number, but this lets you write the daffodil in a way that is easier to read and understand. If hours and minutes are entered they are interpreted as 24 hour time UTC.

Examples:
- people who began `mystudy` after halloween 2017:
`mystudy__started >= timestamp(2017-10-31)`
- people who participated in `balloonstudy` while the Macy's thanksgiving day parade was on tv (Nov 23, 9AM to 12PM Eastern time):
```
balloonstudy__started >= timestamp(2017-11-23 2:00)
balloonstudy__started < timestamp(2017-11-23 17:00)
```

### Comparison operators

Expand Down
2 changes: 1 addition & 1 deletion daffodil/__init__.py → daffodil/__init__.pyx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .parser import Daffodil
from .predicate import DictionaryPredicateDelegate
from .hstore_predicate import HStoreQueryDelegate
from .pretty_print import PrettyPrintDelegate
from .key_expectation_delegate import KeyExpectationDelegate
from .simulation_delegate import SimulationMatchingDelegate
from .parser import Daffodil
30 changes: 1 addition & 29 deletions daffodil/base_delegate.py
Original file line number Diff line number Diff line change
@@ -1,29 +1 @@
from builtins import object


class BaseDaffodilDelegate(object):

def mk_any(self, children):
raise NotImplementedError()

def mk_all(self, children):
raise NotImplementedError()

def mk_not_any(self, children):
raise NotImplementedError()

def mk_not_all(self, children):
raise NotImplementedError()

def mk_test(self, test_str):
raise NotImplementedError()

def mk_comment(self, comment, is_inline):
raise NotImplementedError()

def mk_cmp(self, key, val, test):
raise NotImplementedError()

def call(self, predicate, iterable):
raise NotImplementedError()

from .parser import BaseDaffodilDelegate
3 changes: 1 addition & 2 deletions daffodil/django_integration.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from builtins import str
from django.core.exceptions import ValidationError
from daffodil import Daffodil
from daffodil.exceptions import ParseError
Expand All @@ -8,4 +7,4 @@ def validate_daffodil_fltr(value):
try:
Daffodil(value)
except ParseError as e:
raise ValidationError("Invalid Daffodil filter. %s" % str(e))
raise ValidationError("Invalid Daffodil filter. %s" % str(e))
File renamed without changes.
35 changes: 19 additions & 16 deletions daffodil/hstore_predicate.py → daffodil/hstore_predicate.pyx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
from future import standard_library
standard_library.install_aliases()
from builtins import str
from past.builtins import basestring
from collections import UserString

from .base_delegate import BaseDaffodilDelegate
from .parser cimport Token, BaseDaffodilDelegate


RE_CASE = "CASE WHEN ({0}->'{1}' ~ E'"
Expand All @@ -29,7 +24,7 @@ def breaks_optimizer(expr):
return (
expr.daff_test in {"!=", "!in"} or
(expr.daff_test == "?=" and expr.daff_val == False) or
(expr.daff_test == "=" and isinstance(expr.daff_val, basestring))
(expr.daff_test == "=" and isinstance(expr.daff_val, str))
)

def escape_string_sql(s):
Expand All @@ -40,9 +35,10 @@ def make_sql_array(*strings):
",".join(escape_string_sql(s) for s in strings)
)

class HStoreQueryDelegate(BaseDaffodilDelegate):
cdef class HStoreQueryDelegate(BaseDaffodilDelegate):
cdef public str field

def __init__(self, hstore_field_name):
def __cinit__(self, hstore_field_name):
self.field = hstore_field_name

def mk_any(self, children):
Expand Down Expand Up @@ -125,7 +121,7 @@ def mk_test(self, daff_test_str):
if test_str == "!=":
test_fn.is_NE_test = True
elif test_str == "=":
test_fn = lambda k, v, t: "({0} {1}))".format(k, v) if t == basestring else "{0} {1} {2}".format(k, test_str, v)
test_fn = lambda k, v, t: "({0} {1}))".format(k, v) if t == str else "{0} {1} {2}".format(k, test_str, v)
test_fn.is_EQ_test = True
elif test_str == "!in":
test_fn.is_NOT_IN_test = True
Expand All @@ -136,7 +132,14 @@ def mk_test(self, daff_test_str):
test_fn.test_str = daff_test_str
return test_fn

def mk_cmp(self, key, val, test):
cdef mk_cmp(self, Token key, Token test, Token val):
return self._mk_cmp(
key.content,
val,
self.mk_test(test.content)
)

def _mk_cmp(self, key, val, test):
val = val.content
daff_key = key
daff_test = test.test_str
Expand All @@ -160,7 +163,7 @@ def mk_cmp(self, key, val, test):
# if its cast - exclude those not matching type
key_format = key_format % (" NOT " + type_check[0] + " OR " if cast else "")

elif is_eq_test and _type == basestring:
elif is_eq_test and _type == str:
# here we convert '=' to '@>'
# instead of:
# (hs_data->'univisionlanguage1') = 'both'
Expand All @@ -187,12 +190,12 @@ def mk_cmp(self, key, val, test):

def cond_cast(self, val):
def escape_single_quote(val):
if isinstance(val, basestring):
if isinstance(val, str):
return val.replace("'", "''")
return val

def format_list(lst):
delimiter = "'" if isinstance(lst[0], basestring) else ""
delimiter = "'" if isinstance(lst[0], str) else ""
formatted_list = ",".join(
["{1}{0}{1}".format(escape_single_quote(elem), delimiter) for elem in lst]
)
Expand Down Expand Up @@ -225,7 +228,7 @@ def get_cast_attr(val, attr=None):
"type_check": lambda v: NUMERIC_TYPE_CHECK,
},
{
"type": basestring,
"type": str,
"cast": lambda v: "",
"value": lambda v: "'{}'".format(escape_single_quote(v)),
"type_check": lambda v: ["", ""],
Expand All @@ -242,4 +245,4 @@ def get_cast_attr(val, attr=None):


def call(self, predicate, queryset):
return queryset.extra(where=[predicate]) if predicate else queryset
return queryset.extra(where=[predicate]) if predicate else queryset
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .base_delegate import BaseDaffodilDelegate
from .parser cimport Token, BaseDaffodilDelegate


class KeyExpectationDelegate(BaseDaffodilDelegate):
cdef class KeyExpectationDelegate(BaseDaffodilDelegate):
"""
Determines which keys in a daffodil are required in data dictionaries
in order to match and which keys have to be omitted to match.
Expand Down Expand Up @@ -42,7 +42,14 @@ def mk_test(self, test_str):
def mk_comment(self, comment, is_inline):
return set(), set()

def mk_cmp(self, key, val, test):
cdef mk_cmp(self, Token key, Token test, Token val):
return self._mk_cmp(
key.content,
val,
self.mk_test(test.content)
)

def _mk_cmp(self, key, val, test):
val = val.content
if test == "?=" and val is False:
return set(), {key}
Expand Down
40 changes: 40 additions & 0 deletions daffodil/parser.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
cdef class Token:
cpdef public object content

cdef class TimeStamp(Token):
cpdef public str raw_content

cdef class GroupStart(Token): pass
cdef class GroupEnd(Token): pass
cdef class LineComment(Token): pass
cdef class TrailingComment(Token): pass
cdef class Operator(Token): pass
cdef class String(Token): pass
cdef class Number(Token): pass
cdef class Boolean(Token): pass
cdef class ArrayStart(Token): pass
cdef class ArrayEnd(Token): pass

cdef class _ArrayToken(Token):
cpdef public object raw_content

cdef class BaseDaffodilDelegate:
cdef mk_cmp(self, Token key, Token test, Token val)

cdef class DaffodilParser:
cdef public str src
cdef public tokens
cdef int pos, end

cdef str char(self, int offset=*)
cdef str chars(self, int n, pos=*)
cdef consume_whitespace(self, bint newlines=*)
cdef str read_quoted_string(self)


cdef object _read_val(tokens)

cdef class Daffodil:
cdef public DaffodilParser parse_result
cdef public BaseDaffodilDelegate delegate
cdef public object keys, predicate
Loading

0 comments on commit 447cf34

Please sign in to comment.