From f3457b49f140d00faf412e5a53c52f42c3dfcd0e Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Mon, 1 May 2017 22:46:54 +0800 Subject: [PATCH 01/13] bpo-15987: Add AST nodes eq & ne compare methods --- Lib/ast.py | 37 ++++++++++++++++++++++++++++ Lib/test/test_ast.py | 57 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/Lib/ast.py b/Lib/ast.py index 070c2bee7f9dee..7174a4700b11e6 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -24,9 +24,46 @@ :copyright: Copyright 2008 by Armin Ronacher. :license: Python License. """ +import _ast from _ast import * +# Add EQ and NE compare to _ast Nodes +_NODES = [vars(_ast)[k] for k in filter( + lambda x: not x.startswith('_') and x not in ('AST' 'PyCF_ONLY_AST'), vars(_ast))] + + +def ast_eq_compare(a, b): + if type(a) != type(b): + return False + if type(a) not in _NODES: + return a == b + + ret = True + for field in a._fields: + af = vars(a)[field] + bf = vars(b)[field] + if isinstance(af, list): + if len(af) != len(bf): + return False + for i, j in zip(af, bf): + ret &= ast_eq_compare(i, j) + elif type(af) in _NODES: + ret &= ast_eq_compare(af, bf) + elif af != bf: + return False + return ret + + +def ast_ne_compare(a, b): + return not ast_eq_compare(a, b) + + +for n in _NODES: + n.__eq__ = ast_eq_compare + n.__ne__ = ast_ne_compare + + def parse(source, filename='', mode='exec'): """ Parse the source into an AST node. diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 2f59527cb8a450..cf9c5de883a1dd 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -6,6 +6,8 @@ import weakref from test import support +from test.support import findfile + def to_tuple(t): if t is None or isinstance(t, (str, int, complex)): @@ -432,6 +434,61 @@ def test_empty_yield_from(self): self.assertIn("field value is required", str(cm.exception)) +class ASTCompareTest(unittest.TestCase): + def test_literals_compare(self): + self.assertEqual(ast.Num(-20), ast.Num(-20)) + self.assertEqual(ast.Num(10), ast.Num(10)) + self.assertEqual(ast.Num(2048), ast.Num(2048)) + self.assertEqual(ast.Str("ABCD"), ast.Str("ABCD")) + self.assertEqual(ast.Str("中文字"), ast.Str("中文字")) + + self.assertNotEqual(ast.Num(10), ast.Num(20)) + self.assertNotEqual(ast.Num(-10), ast.Num(10)) + self.assertNotEqual(ast.Str("AAAA"), ast.Str("BBBB")) + self.assertNotEqual(ast.Str("一二三"), ast.Str("中文字")) + + def test_operator_compare(self): + self.assertEqual(ast.Add(), ast.Add()) + self.assertEqual(ast.Sub(), ast.Sub()) + + self.assertNotEqual(ast.Add(), ast.Sub()) + self.assertNotEqual(ast.Add(), ast.Num()) + + def test_complex_ast(self): + fps = [findfile('test_asyncgen.py'), + findfile('test_generators.py'), + findfile('test_unicode.py')] + + for fp in fps: + with open(fp) as f: + source = f.read() + a = ast.parse(source) + b = ast.parse(source) + self.assertEqual(a, b) + self.assertFalse(a != b) + + def test_exec_compare(self): + for source in exec_tests: + a = ast.parse(source, mode='exec') + b = ast.parse(source, mode='exec') + self.assertEqual(a, b) + self.assertFalse(a != b) + + def test_single_compare(self): + for source in single_tests: + a = ast.parse(source, mode='single') + b = ast.parse(source, mode='single') + self.assertEqual(a, b) + self.assertFalse(a != b) + + def test_eval_compare(self): + for source in eval_tests: + a = ast.parse(source, mode='eval') + b = ast.parse(source, mode='eval') + self.assertEqual(a, b) + self.assertFalse(a != b) + + class ASTHelpers_Test(unittest.TestCase): def test_parse(self): From 84241a383e3f721ee178aa82844d12d2a5829b54 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Mon, 1 May 2017 23:01:57 +0800 Subject: [PATCH 02/13] Skip complex ast compare when get unicode decode error --- Lib/test/test_ast.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index cf9c5de883a1dd..6bb608b4bf025f 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -461,7 +461,11 @@ def test_complex_ast(self): for fp in fps: with open(fp) as f: - source = f.read() + try: + source = f.read() + except UnicodeDecodeError: + continue + a = ast.parse(source) b = ast.parse(source) self.assertEqual(a, b) From b47c6fd9b20181d7464ae81aaa1d95181dca1164 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 12:22:30 +0800 Subject: [PATCH 03/13] Add base type richcompare methods --- Lib/ast.py | 37 -------------------------------- Lib/test/test_ast.py | 6 ++++++ Python/Python-ast.c | 51 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 38 deletions(-) diff --git a/Lib/ast.py b/Lib/ast.py index 7174a4700b11e6..070c2bee7f9dee 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -24,46 +24,9 @@ :copyright: Copyright 2008 by Armin Ronacher. :license: Python License. """ -import _ast from _ast import * -# Add EQ and NE compare to _ast Nodes -_NODES = [vars(_ast)[k] for k in filter( - lambda x: not x.startswith('_') and x not in ('AST' 'PyCF_ONLY_AST'), vars(_ast))] - - -def ast_eq_compare(a, b): - if type(a) != type(b): - return False - if type(a) not in _NODES: - return a == b - - ret = True - for field in a._fields: - af = vars(a)[field] - bf = vars(b)[field] - if isinstance(af, list): - if len(af) != len(bf): - return False - for i, j in zip(af, bf): - ret &= ast_eq_compare(i, j) - elif type(af) in _NODES: - ret &= ast_eq_compare(af, bf) - elif af != bf: - return False - return ret - - -def ast_ne_compare(a, b): - return not ast_eq_compare(a, b) - - -for n in _NODES: - n.__eq__ = ast_eq_compare - n.__ne__ = ast_ne_compare - - def parse(source, filename='', mode='exec'): """ Parse the source into an AST node. diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 6bb608b4bf025f..4782fa1aaa78ab 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -435,6 +435,12 @@ def test_empty_yield_from(self): class ASTCompareTest(unittest.TestCase): + def test_normal_compare(self): + self.assertEqual(ast.parse('x = 10'), ast.parse('x = 10')) + self.assertNotEqual(ast.parse('x = 10'), ast.parse('')) + self.assertNotEqual(ast.parse('x = 10'), ast.parse('x')) + self.assertNotEqual(ast.parse('x = 10;y = 20'), ast.parse('class C:pass')) + def test_literals_compare(self): self.assertEqual(ast.Num(-20), ast.Num(-20)) self.assertEqual(ast.Num(10), ast.Num(10)) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 2759b2fe9c4b33..4ce256a1dc5619 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -617,6 +617,55 @@ static PyGetSetDef ast_type_getsets[] = { {NULL} }; +static PyObject * +ast_richcompare(PyObject *self, PyObject *other, int op) +{ + int i, len; + PyObject *fields, *key, *a, *b; + + /* Check operator */ + if ((op != Py_EQ && op != Py_NE) || + !PyAST_Check(self) || + !PyAST_Check(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + if (Py_TYPE(self) != Py_TYPE(other)) { + if (op == Py_EQ) + Py_RETURN_FALSE; + else + Py_RETURN_TRUE; + } + + fields = PyObject_GetAttrString(self, "_fields"); + len = PySequence_Size(fields); + for (i = 0; i < len; ++i) { + key = PySequence_GetItem(fields, i); + a = PyObject_GetAttr(self, key); + b = PyObject_GetAttr(other, key); + if (Py_TYPE(a) != Py_TYPE(b)) { + if (op == Py_EQ) + Py_RETURN_FALSE; + } + + if (op == Py_EQ) { + if (!PyObject_RichCompareBool(a, b, Py_EQ)) { + Py_RETURN_FALSE; + } + } + else if (op == Py_NE) { + if (PyObject_RichCompareBool(a, b, Py_NE)) { + Py_RETURN_TRUE; + } + } + } + + if (op == Py_EQ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + static PyTypeObject AST_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "_ast.AST", @@ -641,7 +690,7 @@ static PyTypeObject AST_type = { 0, /* tp_doc */ (traverseproc)ast_traverse, /* tp_traverse */ (inquiry)ast_clear, /* tp_clear */ - 0, /* tp_richcompare */ + ast_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ From f2c3655c1991040ede6ff0c7f8b63a34b7139efd Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 13:41:58 +0800 Subject: [PATCH 04/13] Add comments --- Python/Python-ast.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 4ce256a1dc5619..2512823085862f 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -630,6 +630,7 @@ ast_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_NOTIMPLEMENTED; } + /* Compare types */ if (Py_TYPE(self) != Py_TYPE(other)) { if (op == Py_EQ) Py_RETURN_FALSE; @@ -637,6 +638,7 @@ ast_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_TRUE; } + /* Compare fields */ fields = PyObject_GetAttrString(self, "_fields"); len = PySequence_Size(fields); for (i = 0; i < len; ++i) { From aa92d325e42e23e5009118709b35ac997971e6e8 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 14:31:59 +0800 Subject: [PATCH 05/13] Add corner case and fix richcompare --- Lib/test/test_ast.py | 5 +++++ Python/Python-ast.c | 34 ++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 4782fa1aaa78ab..3026da7085f60f 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -442,9 +442,11 @@ def test_normal_compare(self): self.assertNotEqual(ast.parse('x = 10;y = 20'), ast.parse('class C:pass')) def test_literals_compare(self): + self.assertEqual(ast.Num(), ast.Num()) self.assertEqual(ast.Num(-20), ast.Num(-20)) self.assertEqual(ast.Num(10), ast.Num(10)) self.assertEqual(ast.Num(2048), ast.Num(2048)) + self.assertEqual(ast.Str(), ast.Str()) self.assertEqual(ast.Str("ABCD"), ast.Str("ABCD")) self.assertEqual(ast.Str("中文字"), ast.Str("中文字")) @@ -453,6 +455,9 @@ def test_literals_compare(self): self.assertNotEqual(ast.Str("AAAA"), ast.Str("BBBB")) self.assertNotEqual(ast.Str("一二三"), ast.Str("中文字")) + self.assertNotEqual(ast.Num(10), ast.Num()) + self.assertNotEqual(ast.Str("AB"), ast.Str()) + def test_operator_compare(self): self.assertEqual(ast.Add(), ast.Add()) self.assertEqual(ast.Sub(), ast.Sub()) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 2512823085862f..b5bf1283054b7c 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -607,21 +607,11 @@ ast_type_reduce(PyObject *self, PyObject *unused) return Py_BuildValue("O()", Py_TYPE(self)); } -static PyMethodDef ast_type_methods[] = { - {"__reduce__", ast_type_reduce, METH_NOARGS, NULL}, - {NULL} -}; - -static PyGetSetDef ast_type_getsets[] = { - {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, - {NULL} -}; - static PyObject * ast_richcompare(PyObject *self, PyObject *other, int op) { int i, len; - PyObject *fields, *key, *a, *b; + PyObject *fields, *key, *a = Py_None, *b = Py_None; /* Check operator */ if ((op != Py_EQ && op != Py_NE) || @@ -643,11 +633,17 @@ ast_richcompare(PyObject *self, PyObject *other, int op) len = PySequence_Size(fields); for (i = 0; i < len; ++i) { key = PySequence_GetItem(fields, i); - a = PyObject_GetAttr(self, key); - b = PyObject_GetAttr(other, key); + + if (PyObject_HasAttr(self, key)) + a = PyObject_GetAttr(self, key); + if (PyObject_HasAttr(other, key)) + b = PyObject_GetAttr(other, key); + + if (Py_TYPE(a) != Py_TYPE(b)) { - if (op == Py_EQ) + if (op == Py_EQ) { Py_RETURN_FALSE; + } } if (op == Py_EQ) { @@ -668,6 +664,16 @@ ast_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_FALSE; } +static PyMethodDef ast_type_methods[] = { + {"__reduce__", ast_type_reduce, METH_NOARGS, NULL}, + {NULL} +}; + +static PyGetSetDef ast_type_getsets[] = { + {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}, + {NULL} +}; + static PyTypeObject AST_type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "_ast.AST", From 886fc478ca14f5fda770107d70f82b4b8203c9f2 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 15:32:41 +0800 Subject: [PATCH 06/13] Add more descriptive assertEqual for debugging --- Lib/test/test_ast.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 3026da7085f60f..6b65e66a84d3bd 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -479,28 +479,28 @@ def test_complex_ast(self): a = ast.parse(source) b = ast.parse(source) - self.assertEqual(a, b) + self.assertEqual(a, b, "%s != %s" % (ast.dump(a), ast.dump(b))) self.assertFalse(a != b) def test_exec_compare(self): for source in exec_tests: a = ast.parse(source, mode='exec') b = ast.parse(source, mode='exec') - self.assertEqual(a, b) + self.assertEqual(a, b, "%s != %s" % (ast.dump(a), ast.dump(b))) self.assertFalse(a != b) def test_single_compare(self): for source in single_tests: a = ast.parse(source, mode='single') b = ast.parse(source, mode='single') - self.assertEqual(a, b) + self.assertEqual(a, b, "%s != %s" % (ast.dump(a), ast.dump(b))) self.assertFalse(a != b) def test_eval_compare(self): for source in eval_tests: a = ast.parse(source, mode='eval') b = ast.parse(source, mode='eval') - self.assertEqual(a, b) + self.assertEqual(a, b, "%s != %s" % (ast.dump(a), ast.dump(b))) self.assertFalse(a != b) From 5ec28b25a64b2395b484f31af54b2491e6165fe4 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 15:52:34 +0800 Subject: [PATCH 07/13] Debugging with travis --- Lib/test/test_ast.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 6b65e66a84d3bd..05becb64a3f1b2 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -435,6 +435,10 @@ def test_empty_yield_from(self): class ASTCompareTest(unittest.TestCase): + def setUp(self): + import imp + imp.reload(ast) + def test_normal_compare(self): self.assertEqual(ast.parse('x = 10'), ast.parse('x = 10')) self.assertNotEqual(ast.parse('x = 10'), ast.parse('')) From 213111c7bc1015191169bc79477b5bf98709e43c Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 16:15:39 +0800 Subject: [PATCH 08/13] Debugging test_ast --- Lib/test/test_ast.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 05becb64a3f1b2..e6ca60faee5761 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -436,8 +436,10 @@ def test_empty_yield_from(self): class ASTCompareTest(unittest.TestCase): def setUp(self): - import imp - imp.reload(ast) + import subprocess + p = subprocess.Popen('./python -m unittest test.test_ast'.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = p.communicate() + self.assertTrue(False, (out, err)) def test_normal_compare(self): self.assertEqual(ast.parse('x = 10'), ast.parse('x = 10')) From 8a36d80d84dc76c5cabc37f5d8381eec9c0e9aba Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 16:59:26 +0800 Subject: [PATCH 09/13] Remove debugging --- Lib/test/test_ast.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index e6ca60faee5761..6b65e66a84d3bd 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -435,12 +435,6 @@ def test_empty_yield_from(self): class ASTCompareTest(unittest.TestCase): - def setUp(self): - import subprocess - p = subprocess.Popen('./python -m unittest test.test_ast'.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = p.communicate() - self.assertTrue(False, (out, err)) - def test_normal_compare(self): self.assertEqual(ast.parse('x = 10'), ast.parse('x = 10')) self.assertNotEqual(ast.parse('x = 10'), ast.parse('')) From b42207c25e8aeb6ba89babad5938047277615fba Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 17:24:16 +0800 Subject: [PATCH 10/13] Move code to asdl_c.py --- Parser/asdl_c.py | 59 ++++++++++++++++++++++++++++++++++++++++++++- Python/Python-ast.c | 59 +-------------------------------------------- 2 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 096f5f8c7494f7..a9d4029243cfa9 100644 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -720,6 +720,63 @@ def visitModule(self, mod): return Py_BuildValue("O()", Py_TYPE(self)); } +static PyObject * +ast_richcompare(PyObject *self, PyObject *other, int op) +{ + int i, len; + PyObject *fields, *key, *a = Py_None, *b = Py_None; + + /* Check operator */ + if ((op != Py_EQ && op != Py_NE) || + !PyAST_Check(self) || + !PyAST_Check(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + /* Compare types */ + if (Py_TYPE(self) != Py_TYPE(other)) { + if (op == Py_EQ) + Py_RETURN_FALSE; + else + Py_RETURN_TRUE; + } + + /* Compare fields */ + fields = PyObject_GetAttrString(self, "_fields"); + len = PySequence_Size(fields); + for (i = 0; i < len; ++i) { + key = PySequence_GetItem(fields, i); + + if (PyObject_HasAttr(self, key)) + a = PyObject_GetAttr(self, key); + if (PyObject_HasAttr(other, key)) + b = PyObject_GetAttr(other, key); + + + if (Py_TYPE(a) != Py_TYPE(b)) { + if (op == Py_EQ) { + Py_RETURN_FALSE; + } + } + + if (op == Py_EQ) { + if (!PyObject_RichCompareBool(a, b, Py_EQ)) { + Py_RETURN_FALSE; + } + } + else if (op == Py_NE) { + if (PyObject_RichCompareBool(a, b, Py_NE)) { + Py_RETURN_TRUE; + } + } + } + + if (op == Py_EQ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + static PyMethodDef ast_type_methods[] = { {"__reduce__", ast_type_reduce, METH_NOARGS, NULL}, {NULL} @@ -754,7 +811,7 @@ def visitModule(self, mod): 0, /* tp_doc */ (traverseproc)ast_traverse, /* tp_traverse */ (inquiry)ast_clear, /* tp_clear */ - 0, /* tp_richcompare */ + ast_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ diff --git a/Python/Python-ast.c b/Python/Python-ast.c index b5bf1283054b7c..87d6971c232eff 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -607,63 +607,6 @@ ast_type_reduce(PyObject *self, PyObject *unused) return Py_BuildValue("O()", Py_TYPE(self)); } -static PyObject * -ast_richcompare(PyObject *self, PyObject *other, int op) -{ - int i, len; - PyObject *fields, *key, *a = Py_None, *b = Py_None; - - /* Check operator */ - if ((op != Py_EQ && op != Py_NE) || - !PyAST_Check(self) || - !PyAST_Check(other)) { - Py_RETURN_NOTIMPLEMENTED; - } - - /* Compare types */ - if (Py_TYPE(self) != Py_TYPE(other)) { - if (op == Py_EQ) - Py_RETURN_FALSE; - else - Py_RETURN_TRUE; - } - - /* Compare fields */ - fields = PyObject_GetAttrString(self, "_fields"); - len = PySequence_Size(fields); - for (i = 0; i < len; ++i) { - key = PySequence_GetItem(fields, i); - - if (PyObject_HasAttr(self, key)) - a = PyObject_GetAttr(self, key); - if (PyObject_HasAttr(other, key)) - b = PyObject_GetAttr(other, key); - - - if (Py_TYPE(a) != Py_TYPE(b)) { - if (op == Py_EQ) { - Py_RETURN_FALSE; - } - } - - if (op == Py_EQ) { - if (!PyObject_RichCompareBool(a, b, Py_EQ)) { - Py_RETURN_FALSE; - } - } - else if (op == Py_NE) { - if (PyObject_RichCompareBool(a, b, Py_NE)) { - Py_RETURN_TRUE; - } - } - } - - if (op == Py_EQ) - Py_RETURN_TRUE; - else - Py_RETURN_FALSE; -} - static PyMethodDef ast_type_methods[] = { {"__reduce__", ast_type_reduce, METH_NOARGS, NULL}, {NULL} @@ -698,7 +641,7 @@ static PyTypeObject AST_type = { 0, /* tp_doc */ (traverseproc)ast_traverse, /* tp_traverse */ (inquiry)ast_clear, /* tp_clear */ - ast_richcompare, /* tp_richcompare */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ From 033f245b789a1f8ae7f8be539954765406f361fb Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 17:26:40 +0800 Subject: [PATCH 11/13] Revert change for Python-ast.c --- Python/Python-ast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 87d6971c232eff..2759b2fe9c4b33 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -641,7 +641,7 @@ static PyTypeObject AST_type = { 0, /* tp_doc */ (traverseproc)ast_traverse, /* tp_traverse */ (inquiry)ast_clear, /* tp_clear */ - 0, /* tp_richcompare */ + 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ From b85ff63dfe5be5a36bbeadb5f359ec6ccabe16f4 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 17:38:25 +0800 Subject: [PATCH 12/13] Fix style and add comment --- Parser/asdl_c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index a9d4029243cfa9..08d9839e653b6d 100644 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -752,7 +752,7 @@ def visitModule(self, mod): if (PyObject_HasAttr(other, key)) b = PyObject_GetAttr(other, key); - + /* Check filed value type */ if (Py_TYPE(a) != Py_TYPE(b)) { if (op == Py_EQ) { Py_RETURN_FALSE; From acd7ab74cb01839950977abf84f8f1664d4f1327 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Tue, 2 May 2017 17:50:59 +0800 Subject: [PATCH 13/13] Add re-generated Python-ast.c --- Python/Python-ast.c | 59 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 2759b2fe9c4b33..5ba9435b317e8d 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -607,6 +607,63 @@ ast_type_reduce(PyObject *self, PyObject *unused) return Py_BuildValue("O()", Py_TYPE(self)); } +static PyObject * +ast_richcompare(PyObject *self, PyObject *other, int op) +{ + int i, len; + PyObject *fields, *key, *a = Py_None, *b = Py_None; + + /* Check operator */ + if ((op != Py_EQ && op != Py_NE) || + !PyAST_Check(self) || + !PyAST_Check(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + /* Compare types */ + if (Py_TYPE(self) != Py_TYPE(other)) { + if (op == Py_EQ) + Py_RETURN_FALSE; + else + Py_RETURN_TRUE; + } + + /* Compare fields */ + fields = PyObject_GetAttrString(self, "_fields"); + len = PySequence_Size(fields); + for (i = 0; i < len; ++i) { + key = PySequence_GetItem(fields, i); + + if (PyObject_HasAttr(self, key)) + a = PyObject_GetAttr(self, key); + if (PyObject_HasAttr(other, key)) + b = PyObject_GetAttr(other, key); + + /* Check filed value type */ + if (Py_TYPE(a) != Py_TYPE(b)) { + if (op == Py_EQ) { + Py_RETURN_FALSE; + } + } + + if (op == Py_EQ) { + if (!PyObject_RichCompareBool(a, b, Py_EQ)) { + Py_RETURN_FALSE; + } + } + else if (op == Py_NE) { + if (PyObject_RichCompareBool(a, b, Py_NE)) { + Py_RETURN_TRUE; + } + } + } + + if (op == Py_EQ) + Py_RETURN_TRUE; + else + Py_RETURN_FALSE; +} + static PyMethodDef ast_type_methods[] = { {"__reduce__", ast_type_reduce, METH_NOARGS, NULL}, {NULL} @@ -641,7 +698,7 @@ static PyTypeObject AST_type = { 0, /* tp_doc */ (traverseproc)ast_traverse, /* tp_traverse */ (inquiry)ast_clear, /* tp_clear */ - 0, /* tp_richcompare */ + ast_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */