From 331739a02ccde5a68674e80e20181f687abd13a2 Mon Sep 17 00:00:00 2001 From: comfuture Date: Wed, 25 Mar 2020 17:40:29 +0900 Subject: [PATCH 01/11] Fixes exception due to ast parser changes. > since version 3.8: Methods visit_Num(), visit_Str(), visit_Bytes(), visit_NameConstant() and visit_Ellipsis() are deprecated now and will not be called in future Python versions. Add the visit_Constant() method to handle all constant nodes. related https://github.com/alonho/pql/issues/29 --- pql/matching.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pql/matching.py b/pql/matching.py index 22625b2..9c542db 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -111,6 +111,8 @@ def __init__(self, *a, **k): super(SchemaAwareParser, self).__init__(SchemaAwareOperatorMap(*a, **k)) class FieldName(AstHandler): + def handle_Constant(self, node): + return node.value def handle_Str(self, node): return node.s def handle_Name(self, name): @@ -338,7 +340,7 @@ class Field(AstHandler): SPECIAL_VALUES = {'None': None, 'null': None} - def handle_NameConstant(self,node): + def handle_Constant(self, node): try: return self.SPECIAL_VALUES[str(node.value)] except KeyError: @@ -365,8 +367,12 @@ def handle_Call(self, node): return StringFunc().handle(node) def handle_Str(self, node): return node.s + def handle_Constant(self, node): + return node.value class IntField(AlgebricField): + def handle_Constant(self, node): + return node.value def handle_Num(self, node): return node.n def handle_Call(self, node): @@ -395,6 +401,8 @@ def handle_Dict(self, node): for key, value in zip(node.keys, node.values)) class DateTimeField(AlgebricField): + def handle_Constant(self, node): + return parse_date(node.s) def handle_Str(self, node): return parse_date(node) def handle_Num(self, node): From 3e22354b90b5d70c56cc3a520e2be7c43a9bdbca Mon Sep 17 00:00:00 2001 From: comfuture Date: Thu, 26 Mar 2020 14:41:07 +0900 Subject: [PATCH 02/11] add ast handler for date fuctions --- pql/matching.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pql/matching.py b/pql/matching.py index 9c542db..e182121 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -402,7 +402,7 @@ def handle_Dict(self, node): class DateTimeField(AlgebricField): def handle_Constant(self, node): - return parse_date(node.s) + return parse_date(node.value) def handle_Str(self, node): return parse_date(node) def handle_Num(self, node): @@ -411,6 +411,8 @@ def handle_Call(self, node): return DateTimeFunc().handle(node) class EpochField(AlgebricField): + def handle_Constant(self, node): + return float(parse_date(node).strftime('%s.%f')) def handle_Str(self, node): return float(parse_date(node).strftime('%s.%f')) def handle_Num(self, node): @@ -419,6 +421,8 @@ def handle_Call(self, node): return EpochFunc().handle(node) class EpochUTCField(AlgebricField): + def handle_Constant(self, node): + return timegm(parse_date(node).timetuple()) def handle_Str(self, node): return timegm(parse_date(node).timetuple()) def handle_Num(self, node): @@ -427,6 +431,8 @@ def handle_Call(self, node): return EpochUTCFunc().handle(node) class IdField(AlgebricField): + def handle_Constant(self, node): + return bson.ObjectId(node.value) def handle_Str(self, node): return bson.ObjectId(node.s) def handle_Call(self, node): From 86e8e9448e5cb79a33b68ebb0cad45f7a8e2cd59 Mon Sep 17 00:00:00 2001 From: comfuture Date: Thu, 26 Mar 2020 14:44:35 +0900 Subject: [PATCH 03/11] implement type check method for parse_date function --- pql/matching.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pql/matching.py b/pql/matching.py index e182121..57e26bd 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -26,10 +26,10 @@ def parse_date(node): - if hasattr(node, 'n'): # it's a number! + if type(node.value) in (int, float): # it's a number! return datetime.datetime.fromtimestamp(node.n) try: - return dateutil.parser.parse(node.s) + return dateutil.parser.parse(node.value) except Exception as e: raise ParseError('Error parsing date: ' + str(e), col_offset=node.col_offset) From 5d4fe793de5ea59d9539ffb23707f352f78c4f2b Mon Sep 17 00:00:00 2001 From: ChangJoo Park Date: Wed, 24 Jun 2020 20:21:48 +0900 Subject: [PATCH 04/11] Add handle_UnaryOp to IntField --- find_tests.py | 3 +++ pql/matching.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/find_tests.py b/find_tests.py index 6fb03ca..5dc3c89 100644 --- a/find_tests.py +++ b/find_tests.py @@ -184,6 +184,9 @@ def compare(self, string, expected): def test_sanity(self): self.compare('a == 3', {'a': 3}) + def test_minus(self): + self.compare('a == -1', {'a': -1}) + def test_invalid_field(self): with self.assertRaises(pql.ParseError) as context: self.compare('b == 3', None) diff --git a/pql/matching.py b/pql/matching.py index 57e26bd..a682fa1 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -375,6 +375,13 @@ def handle_Constant(self, node): return node.value def handle_Num(self, node): return node.n + + def handle_UnaryOp(self, node): + if (node.op.__class__.__name__ == 'USub'): + return - node.operand.value + else: + raise NotImplementedError() + def handle_Call(self, node): return IntFunc().handle(node) From 89a66f4f8eae373d2e741deae81ae898ad4c3d1c Mon Sep 17 00:00:00 2001 From: ChangJoo Park Date: Wed, 24 Jun 2020 20:27:09 +0900 Subject: [PATCH 05/11] Add test for PqlSchemaLessTestCase when using USub operator --- find_tests.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/find_tests.py b/find_tests.py index 5dc3c89..914f4a4 100644 --- a/find_tests.py +++ b/find_tests.py @@ -17,6 +17,15 @@ def test_hyphenated(self): def test_equal_int(self): self.compare('a == 1', {'a': 1}) + def test_minus(self): + self.compare('a == -1', {'a': -1}) + + def test_more_than_minus(self): + self.compare('a > -1', {'a': {'$gt': -1}}) + + def test_less_than_minus(self): + self.compare('a < -1', {'a': {'$lt': -1}}) + def test_not_equal_string(self): self.compare('a != "foo"', {'a': {'$ne': 'foo'}}) From 9a52198c728a6ab8354181e27f1a4470d005f247 Mon Sep 17 00:00:00 2001 From: ChangJoo Park Date: Wed, 24 Jun 2020 20:29:53 +0900 Subject: [PATCH 06/11] Using `type` for check USub --- pql/matching.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pql/matching.py b/pql/matching.py index a682fa1..a147e48 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -375,9 +375,8 @@ def handle_Constant(self, node): return node.value def handle_Num(self, node): return node.n - def handle_UnaryOp(self, node): - if (node.op.__class__.__name__ == 'USub'): + if (type(node.op) == ast.USub): return - node.operand.value else: raise NotImplementedError() From 4da8e8da3406c70d39ce1ca03541888c3ec162da Mon Sep 17 00:00:00 2001 From: ChangJoo Park Date: Wed, 24 Jun 2020 20:33:18 +0900 Subject: [PATCH 07/11] Return operand value when not USub --- pql/matching.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pql/matching.py b/pql/matching.py index a147e48..be40117 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -376,10 +376,11 @@ def handle_Constant(self, node): def handle_Num(self, node): return node.n def handle_UnaryOp(self, node): - if (type(node.op) == ast.USub): + op_type = type(node.op) + if (op_type == ast.USub): return - node.operand.value else: - raise NotImplementedError() + return node.operand.value def handle_Call(self, node): return IntFunc().handle(node) From 4c0c01ae9aed5716a890eaa8b87c83053b0eb473 Mon Sep 17 00:00:00 2001 From: ChangJoo Park Date: Wed, 24 Jun 2020 20:33:50 +0900 Subject: [PATCH 08/11] add test for `+` operator --- find_tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/find_tests.py b/find_tests.py index 914f4a4..7e93dfa 100644 --- a/find_tests.py +++ b/find_tests.py @@ -17,6 +17,9 @@ def test_hyphenated(self): def test_equal_int(self): self.compare('a == 1', {'a': 1}) + def test_plus_operator(self): + self.compare('a == +1', {'a': 1}) + def test_minus(self): self.compare('a == -1', {'a': -1}) From d5c1c7e393bfceafaebcf1d9df87e2ae652ad6f9 Mon Sep 17 00:00:00 2001 From: comfuture Date: Wed, 24 Jun 2020 22:32:02 +0900 Subject: [PATCH 09/11] version bump, skip tox when env not available --- setup.py | 7 +++++-- tox.ini | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 43d78e6..91696fd 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup -__version__ = '0.5.0' +__version__ = '0.5.1' setup(name='pql', version=__version__, @@ -12,6 +12,9 @@ "Intended Audience :: Developers", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Operating System :: POSIX :: Linux", "Operating System :: MacOS :: MacOS X"], license='BSD', @@ -19,4 +22,4 @@ # require the bson.ObjectId type, It's safe to assume it won't change (famous last words) install_requires=['pymongo', 'python-dateutil'], - packages=['pql']) \ No newline at end of file + packages=['pql']) diff --git a/tox.ini b/tox.ini index 21caa3a..8dcd39c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,7 @@ [tox] -envlist = py33,py35 +envlist = py{py3,27,35,36,37,38} +skip_missing_interpreters = true + [testenv] deps=nose -commands=nosetests \ No newline at end of file +commands=nosetests From e9e408b6c20bebf244460c936fec706e34e05db5 Mon Sep 17 00:00:00 2001 From: sam Date: Mon, 14 Sep 2020 16:19:29 +0900 Subject: [PATCH 10/11] Add a function that recognizes the version and converts it to a number --- find_tests.py | 4 ++++ pql/matching.py | 26 ++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/find_tests.py b/find_tests.py index 7e93dfa..524a883 100644 --- a/find_tests.py +++ b/find_tests.py @@ -185,6 +185,10 @@ def test_polygon_and_box(self): {'$geoWithin': {'$' + shape: [[1, 2], [3, 4], [5, 6]]}}}) + def test_symver(self): + self.compare('version == symver("1.0.0 (0)")', {'version': 1000000000.0}) + + class PqlSchemaAwareTestCase(BasePqlTestCase): def compare(self, string, expected): diff --git a/pql/matching.py b/pql/matching.py index be40117..d22c238 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -22,6 +22,7 @@ import bson import datetime import dateutil.parser +import re from calendar import timegm @@ -286,8 +287,12 @@ def handle_geoIntersects(self, node): def handle_geoWithin(self, node): return {'$geoWithin': GeoShapeParser().handle(self.get_arg(node, 0))} +class SymverFunc(Func): + def handle_symver(self, node): + return self.parse_arg(node, 0, SymverField()) + class GenericFunc(StringFunc, IntFunc, ListFunc, DateTimeFunc, - IdFunc, EpochFunc, EpochUTCFunc, GeoFunc): + IdFunc, EpochFunc, EpochUTCFunc, GeoFunc, SymverFunc): pass #---Operators---# @@ -445,6 +450,23 @@ def handle_Str(self, node): def handle_Call(self, node): return IdFunc().handle(node) +class SymverField(AlgebricField): + re_version = r'(\d+)\.(\d+)\.(\d+)\s*\((\d+)\)' + + def handle_Constant(self, node): + print(self.re_version) + print(node.value) + d = re.findall(self.re_version, node.value)[0] + print(d) + return (int(d[0]) * 1e9) + (int(d[1]) * 1e6) + (int(d[2]) * 1e3) + int(d[3]) + + def handle_Num(self, node): + d = re.findall(self.re_version, node.value)[0] + return (int(d[0]) * 1e9) + (int(d[1]) * 1e6) + (int(d[2]) * 1e3) + int(d[3]) + + def handle_Call(self, node): + return SymverFunc().handle(node) + class GenericField(IntField, BoolField, StringField, ListField, DictField, GeoField): def handle_Call(self, node): - return GenericFunc().handle(node) + return GenericFunc().handle(node) \ No newline at end of file From 358c42b7aa15634268c41850a01a4ff9f5c0994f Mon Sep 17 00:00:00 2001 From: comfuture Date: Tue, 15 Sep 2020 12:32:36 +0900 Subject: [PATCH 11/11] remove print()s --- pql/matching.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pql/matching.py b/pql/matching.py index d22c238..a9e6e4c 100644 --- a/pql/matching.py +++ b/pql/matching.py @@ -454,10 +454,7 @@ class SymverField(AlgebricField): re_version = r'(\d+)\.(\d+)\.(\d+)\s*\((\d+)\)' def handle_Constant(self, node): - print(self.re_version) - print(node.value) d = re.findall(self.re_version, node.value)[0] - print(d) return (int(d[0]) * 1e9) + (int(d[1]) * 1e6) + (int(d[2]) * 1e3) + int(d[3]) def handle_Num(self, node): @@ -469,4 +466,4 @@ def handle_Call(self, node): class GenericField(IntField, BoolField, StringField, ListField, DictField, GeoField): def handle_Call(self, node): - return GenericFunc().handle(node) \ No newline at end of file + return GenericFunc().handle(node)