Hello community, here is the log from the commit of package python-pony for openSUSE:Factory checked in at 2019-04-30 13:01:06 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pony (Old) and /work/SRC/openSUSE:Factory/.python-pony.new.5536 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-pony" Tue Apr 30 13:01:06 2019 rev:3 rq:697456 version:0.7.10 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pony/python-pony.changes 2019-03-01 16:50:20.589733034 +0100 +++ /work/SRC/openSUSE:Factory/.python-pony.new.5536/python-pony.changes 2019-04-30 13:01:09.262160292 +0200 @@ -1,0 +2,14 @@ +Wed Apr 24 11:04:20 UTC 2019 - pgajdos@suse.com + +- version update to 0.7.10 + * Python3.7 and PyPy decompiling fixes + * Fix reading NULL from Optional nullable array column + * Fix handling of empty arrays in queries + * #415: error message typo + * #432: PonyFlask - request object can trigger teardown_request + without real request + * Fix GROUP CONCAT separator for MySQL +- deleted sources + - LICENSE (not needed) + +------------------------------------------------------------------- Old: ---- LICENSE pony-0.7.9.tar.gz New: ---- pony-0.7.10.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pony.spec ++++++ --- /var/tmp/diff_new_pack.8nZ3sg/_old 2019-04-30 13:01:09.810160127 +0200 +++ /var/tmp/diff_new_pack.8nZ3sg/_new 2019-04-30 13:01:09.810160127 +0200 @@ -18,14 +18,13 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pony -Version: 0.7.9 +Version: 0.7.10 Release: 0 Summary: Pony Object-Relational Mapper License: Apache-2.0 Group: Development/Languages/Python URL: https://ponyorm.com Source: https://files.pythonhosted.org/packages/source/p/pony/pony-%{version}.tar.gz -Source99: https://raw.githubusercontent.com/ponyorm/pony/orm/LICENSE BuildRequires: %{python_module setuptools} BuildRequires: dos2unix BuildRequires: fdupes @@ -45,7 +44,6 @@ %prep %setup -q -n pony-%{version} -cp %{SOURCE99} . dos2unix README.md %build ++++++ pony-0.7.9.tar.gz -> pony-0.7.10.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/LICENSE new/pony-0.7.10/LICENSE --- old/pony-0.7.9/LICENSE 1970-01-01 01:00:00.000000000 +0100 +++ new/pony-0.7.10/LICENSE 2017-05-21 00:14:35.000000000 +0200 @@ -0,0 +1,179 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2016 Alexander Kozlovsky, Alexey Malashkevich diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/MANIFEST.in new/pony-0.7.10/MANIFEST.in --- old/pony-0.7.9/MANIFEST.in 2018-12-09 11:02:22.000000000 +0100 +++ new/pony-0.7.10/MANIFEST.in 2019-04-20 16:42:08.000000000 +0200 @@ -1,2 +1,3 @@ include pony/orm/tests/queries.txt include pony/flask/example/templates *.html +include LICENSE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/PKG-INFO new/pony-0.7.10/PKG-INFO --- old/pony-0.7.9/PKG-INFO 2019-01-21 08:24:10.000000000 +0100 +++ new/pony-0.7.10/PKG-INFO 2019-04-20 17:48:23.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pony -Version: 0.7.9 +Version: 0.7.10 Summary: Pony Object-Relational Mapper Home-page: https://ponyorm.com Author: Alexander Kozlovsky, Alexey Malashkevich diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/__init__.py new/pony-0.7.10/pony/__init__.py --- old/pony-0.7.9/pony/__init__.py 2019-01-21 08:21:36.000000000 +0100 +++ new/pony-0.7.10/pony/__init__.py 2019-04-20 17:42:59.000000000 +0200 @@ -3,7 +3,7 @@ import os, sys from os.path import dirname -__version__ = '0.7.9' +__version__ = '0.7.10' def detect_mode(): try: import google.appengine diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/flask/__init__.py new/pony-0.7.10/pony/flask/__init__.py --- old/pony-0.7.9/pony/flask/__init__.py 2018-12-09 11:02:22.000000000 +0100 +++ new/pony-0.7.10/pony/flask/__init__.py 2019-04-20 16:42:08.000000000 +0200 @@ -8,9 +8,8 @@ def _exit_session(exception): session = getattr(request, 'pony_session', None) - if session is None: - raise RuntimeError('Request object lost db_session') - session.__exit__(exc=exception) + if session is not None: + session.__exit__(exc=exception) class Pony(object): def __init__(self, app=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/core.py new/pony-0.7.10/pony/orm/core.py --- old/pony-0.7.9/pony/orm/core.py 2019-01-19 16:53:06.000000000 +0100 +++ new/pony-0.7.10/pony/orm/core.py 2019-04-20 16:42:08.000000000 +0200 @@ -767,13 +767,17 @@ # argument 'self' cannot be named 'database', because 'database' can be in kwargs if self.provider is not None: throw(BindingError, 'Database object was already bound to %s provider' % self.provider.dialect) + if len(args) == 1 and not kwargs and hasattr(args[0], 'keys'): + args, kwargs = (), args[0] + provider = None if args: provider, args = args[0], args[1:] elif 'provider' not in kwargs: throw(TypeError, 'Database provider is not specified') else: provider = kwargs.pop('provider') if isinstance(provider, type) and issubclass(provider, DBAPIProvider): provider_cls = provider else: - if not isinstance(provider, basestring): throw(TypeError) + if not isinstance(provider, basestring): + throw(TypeError, 'Provider name should be string. Got: %r' % type(provider).__name__) if provider == 'pygresql': throw(TypeError, 'Pony no longer supports PyGreSQL module. Please use psycopg2 instead.') self.provider_name = provider @@ -834,7 +838,7 @@ def disconnect(database): provider = database.provider if provider is None: return - if local.db_context_counter: throw(TransactionError, 'disconnect() cannot be called inside of db_sesison') + if local.db_context_counter: throw(TransactionError, 'disconnect() cannot be called inside of db_session') cache = local.db2cache.get(database) if cache is not None: cache.rollback() provider.disconnect() @@ -974,7 +978,8 @@ is_subclass = entity._root_ is not entity if is_subclass: - if table_name is not None: throw(NotImplementedError) + if table_name is not None: throw(NotImplementedError, + 'Cannot specify table name for entity %r which is subclass of %r' % (entity.__name__, entity._root_.__name__)) table_name = entity._root_._table_ entity._table_ = table_name elif table_name is None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/dbproviders/oracle.py new/pony-0.7.10/pony/orm/dbproviders/oracle.py --- old/pony-0.7.9/pony/orm/dbproviders/oracle.py 2019-01-17 07:53:21.000000000 +0100 +++ new/pony-0.7.10/pony/orm/dbproviders/oracle.py 2019-04-20 17:40:09.000000000 +0200 @@ -276,7 +276,11 @@ throw(TranslationError, 'Oracle does not provide `length` function for JSON arrays') def GROUP_CONCAT(builder, distinct, expr, sep=None): assert distinct in (None, True, False) - result = 'LISTAGG(', builder(expr) + if distinct and builder.provider.server_version >= (19,): + distinct = 'DISTINCT ' + else: + distinct = '' + result = 'LISTAGG(', distinct, builder(expr) if sep is not None: result = result, ', ', builder(sep) else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/dbproviders/sqlite.py new/pony-0.7.10/pony/orm/dbproviders/sqlite.py --- old/pony-0.7.9/pony/orm/dbproviders/sqlite.py 2019-01-17 08:02:10.000000000 +0100 +++ new/pony-0.7.10/pony/orm/dbproviders/sqlite.py 2019-04-20 17:40:09.000000000 +0200 @@ -173,6 +173,9 @@ return dbapiprovider.IntConverter.sql_type(converter) class SQLiteDecimalConverter(dbapiprovider.DecimalConverter): + inf = Decimal('infinity') + neg_inf = Decimal('-infinity') + NaN = Decimal('NaN') def sql2py(converter, val): try: val = Decimal(str(val)) except: return val @@ -182,7 +185,10 @@ def py2sql(converter, val): if type(val) is not Decimal: val = Decimal(val) exp = converter.exp - if exp is not None: val = val.quantize(exp) + if exp is not None: + if val in (converter.inf, converter.neg_inf, converter.NaN): + throw(ValueError, 'Cannot store %s Decimal value in database' % val) + val = val.quantize(exp) return str(val) class SQLiteDateConverter(dbapiprovider.DateConverter): @@ -296,6 +302,7 @@ def __init__(provider, *args, **kwargs): DBAPIProvider.__init__(provider, *args, **kwargs) + provider.pre_transaction_lock = Lock() provider.transaction_lock = Lock() @wrap_dbapi_exceptions @@ -308,11 +315,21 @@ try: reraise(*provider.local_exceptions.exc_info) finally: provider.local_exceptions.exc_info = None + def acquire_lock(provider): + provider.pre_transaction_lock.acquire() + try: + provider.transaction_lock.acquire() + finally: + provider.pre_transaction_lock.release() + + def release_lock(provider): + provider.transaction_lock.release() + @wrap_dbapi_exceptions def set_transaction_mode(provider, connection, cache): assert not cache.in_transaction if cache.immediate: - provider.transaction_lock.acquire() + provider.acquire_lock() try: cursor = connection.cursor() @@ -336,7 +353,7 @@ elif core.local.debug: log_orm('SWITCH TO AUTOCOMMIT MODE') finally: if cache.immediate and not cache.in_transaction: - provider.transaction_lock.release() + provider.release_lock() def commit(provider, connection, cache=None): in_transaction = cache is not None and cache.in_transaction @@ -345,7 +362,7 @@ finally: if in_transaction: cache.in_transaction = False - provider.transaction_lock.release() + provider.release_lock() def rollback(provider, connection, cache=None): in_transaction = cache is not None and cache.in_transaction @@ -354,7 +371,7 @@ finally: if in_transaction: cache.in_transaction = False - provider.transaction_lock.release() + provider.release_lock() def drop(provider, connection, cache=None): in_transaction = cache is not None and cache.in_transaction @@ -363,7 +380,7 @@ finally: if in_transaction: cache.in_transaction = False - provider.transaction_lock.release() + provider.release_lock() @wrap_dbapi_exceptions def release(provider, connection, cache=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/decompiling.py new/pony-0.7.10/pony/orm/decompiling.py --- old/pony-0.7.9/pony/orm/decompiling.py 2018-12-09 11:02:23.000000000 +0100 +++ new/pony-0.7.10/pony/orm/decompiling.py 2019-04-20 16:42:08.000000000 +0200 @@ -1,9 +1,10 @@ from __future__ import absolute_import, print_function, division -from pony.py23compat import PY2, izip, xrange +from pony.py23compat import PY2, izip, xrange, PY37, PYPY import sys, types, inspect from opcode import opname as opnames, HAVE_ARGUMENT, EXTENDED_ARG, cmp_op from opcode import hasconst, hasname, hasjrel, haslocal, hascompare, hasfree +from collections import defaultdict from pony.thirdparty.compiler import ast, parse @@ -47,8 +48,6 @@ class InvalidQuery(Exception): pass -class AstGenerated(Exception): pass - def binop(node_type, args_holder=tuple): def method(decompiler): oper2 = decompiler.stack.pop() @@ -65,61 +64,114 @@ if end is None: end = len(code.co_code) decompiler.end = end decompiler.stack = [] + decompiler.jump_map = defaultdict(list) decompiler.targets = {} decompiler.ast = None decompiler.names = set() decompiler.assnames = set() + decompiler.conditions_end = 0 + decompiler.instructions = [] + decompiler.instructions_map = {} + decompiler.or_jumps = set() + decompiler.get_instructions() + decompiler.analyze_jumps() decompiler.decompile() decompiler.ast = decompiler.stack.pop() decompiler.external_names = decompiler.names - decompiler.assnames assert not decompiler.stack, decompiler.stack - def decompile(decompiler): + def get_instructions(decompiler): PY36 = sys.version_info >= (3, 6) + before_yield = True code = decompiler.code co_code = code.co_code free = code.co_cellvars + code.co_freevars - try: - while decompiler.pos < decompiler.end: - i = decompiler.pos - if i in decompiler.targets: decompiler.process_target(i) - op = ord(code.co_code[i]) - if PY36: - extended_arg = 0 + decompiler.abs_jump_to_top = decompiler.for_iter_pos = -1 + while decompiler.pos < decompiler.end: + i = decompiler.pos + op = ord(code.co_code[i]) + if PY36: + extended_arg = 0 + oparg = ord(code.co_code[i+1]) + while op == EXTENDED_ARG: + extended_arg = (extended_arg | oparg) << 8 + i += 2 + op = ord(code.co_code[i]) oparg = ord(code.co_code[i+1]) - while op == EXTENDED_ARG: - extended_arg = (extended_arg | oparg) << 8 - i += 2 - op = ord(code.co_code[i]) - oparg = ord(code.co_code[i+1]) - oparg = None if op < HAVE_ARGUMENT else oparg | extended_arg + oparg = None if op < HAVE_ARGUMENT else oparg | extended_arg + i += 2 + else: + i += 1 + if op >= HAVE_ARGUMENT: + oparg = ord(co_code[i]) + ord(co_code[i + 1]) * 256 i += 2 - else: - i += 1 - if op >= HAVE_ARGUMENT: - oparg = ord(co_code[i]) + ord(co_code[i + 1]) * 256 + if op == EXTENDED_ARG: + op = ord(code.co_code[i]) + i += 1 + oparg = ord(co_code[i]) + ord(co_code[i + 1]) * 256 + oparg * 65536 i += 2 - if op == EXTENDED_ARG: - op = ord(code.co_code[i]) - i += 1 - oparg = ord(co_code[i]) + ord(co_code[i + 1]) * 256 + oparg * 65536 - i += 2 - if op >= HAVE_ARGUMENT: - if op in hasconst: arg = [code.co_consts[oparg]] - elif op in hasname: arg = [code.co_names[oparg]] - elif op in hasjrel: arg = [i + oparg] - elif op in haslocal: arg = [code.co_varnames[oparg]] - elif op in hascompare: arg = [cmp_op[oparg]] - elif op in hasfree: arg = [free[oparg]] - else: arg = [oparg] - else: arg = [] - opname = opnames[op].replace('+', '_') - # print(opname, arg, decompiler.stack) - method = getattr(decompiler, opname, None) - if method is None: throw(NotImplementedError('Unsupported operation: %s' % opname)) - decompiler.pos = i - x = method(*arg) - if x is not None: decompiler.stack.append(x) - except AstGenerated: pass + if op >= HAVE_ARGUMENT: + if op in hasconst: arg = [code.co_consts[oparg]] + elif op in hasname: arg = [code.co_names[oparg]] + elif op in hasjrel: arg = [i + oparg] + elif op in haslocal: arg = [code.co_varnames[oparg]] + elif op in hascompare: arg = [cmp_op[oparg]] + elif op in hasfree: arg = [free[oparg]] + else: arg = [oparg] + else: arg = [] + opname = opnames[op].replace('+', '_') + if opname == 'FOR_ITER': + decompiler.for_iter_pos = decompiler.pos + if opname == 'JUMP_ABSOLUTE' and arg[0] == decompiler.for_iter_pos: + decompiler.abs_jump_to_top = decompiler.pos + + if before_yield: + if 'JUMP' in opname: + endpos = arg[0] + if endpos < decompiler.pos: + decompiler.conditions_end = i + decompiler.jump_map[endpos].append(decompiler.pos) + decompiler.instructions_map[decompiler.pos] = len(decompiler.instructions) + decompiler.instructions.append((decompiler.pos, i, opname, arg)) + if opname == 'YIELD_VALUE': + before_yield = False + decompiler.pos = i + def analyze_jumps(decompiler): + if PYPY: + targets = decompiler.jump_map.pop(decompiler.abs_jump_to_top, []) + decompiler.jump_map[decompiler.for_iter_pos] = targets + for i, (x, y, opname, arg) in enumerate(decompiler.instructions): + if 'JUMP' in opname: + target = arg[0] + if target == decompiler.abs_jump_to_top: + decompiler.instructions[i] = (x, y, opname, [decompiler.for_iter_pos]) + decompiler.conditions_end = y + + i = decompiler.instructions_map[decompiler.conditions_end] + while i > 0: + pos, next_pos, opname, arg = decompiler.instructions[i] + if pos in decompiler.jump_map: + for jump_start_pos in decompiler.jump_map[pos]: + if jump_start_pos > pos: + continue + for or_jump_start_pos in decompiler.or_jumps: + if pos > or_jump_start_pos > jump_start_pos: + break # And jump + else: + decompiler.or_jumps.add(jump_start_pos) + i -= 1 + def decompile(decompiler): + for pos, next_pos, opname, arg in decompiler.instructions: + if pos in decompiler.targets: + decompiler.process_target(pos) + method = getattr(decompiler, opname, None) + if method is None: + throw(NotImplementedError('Unsupported operation: %s' % opname)) + decompiler.pos = pos + decompiler.next_pos = next_pos + x = method(*arg) + if x is not None: + decompiler.stack.append(x) + def pop_items(decompiler, size): if not size: return () result = decompiler.stack[-size:] @@ -154,8 +206,15 @@ def BINARY_SUBSCR(decompiler): oper2 = decompiler.stack.pop() oper1 = decompiler.stack.pop() - if isinstance(oper2, ast.Tuple): return ast.Subscript(oper1, 'OP_APPLY', list(oper2.nodes)) - else: return ast.Subscript(oper1, 'OP_APPLY', [ oper2 ]) + if isinstance(oper2, ast.Sliceobj) and len(oper2.nodes) == 2: + a, b = oper2.nodes + a = None if isinstance(a, ast.Const) and a.value == None else a + b = None if isinstance(b, ast.Const) and b.value == None else b + return ast.Slice(oper1, 'OP_APPLY', a, b) + elif isinstance(oper2, ast.Tuple): + return ast.Subscript(oper1, 'OP_APPLY', list(oper2.nodes)) + else: + return ast.Subscript(oper1, 'OP_APPLY', [ oper2 ]) def BUILD_CONST_KEY_MAP(decompiler, length): keys = decompiler.stack.pop() @@ -174,7 +233,7 @@ data = decompiler.pop_items(2 * length) # [key1, value1, key2, value2, ...] it = iter(data) pairs = list(izip(it, it)) # [(key1, value1), (key2, value2), ...] - return ast.Dict(pairs) + return ast.Dict(tuple(pairs)) def BUILD_SET(decompiler, size): return ast.Set(decompiler.pop_items(size)) @@ -245,6 +304,15 @@ def CALL_METHOD(decompiler, argc): pop = decompiler.stack.pop args = [] + if argc >= 256: + kwargc = argc // 256 + argc = argc % 256 + for i in range(kwargc): + v = pop() + k = pop() + assert isinstance(k, ast.Const) + k = k.value # ast.Name(k.value) + args.append(ast.Keyword(k, v)) for i in range(argc): args.append(pop()) args.reverse() @@ -278,18 +346,48 @@ pass def JUMP_IF_FALSE(decompiler, endpos): - return decompiler.conditional_jump(endpos, ast.And) + return decompiler.conditional_jump(endpos, False) JUMP_IF_FALSE_OR_POP = JUMP_IF_FALSE def JUMP_IF_TRUE(decompiler, endpos): - return decompiler.conditional_jump(endpos, ast.Or) + return decompiler.conditional_jump(endpos, True) JUMP_IF_TRUE_OR_POP = JUMP_IF_TRUE - def conditional_jump(decompiler, endpos, clausetype): - i = decompiler.pos # next instruction - if i in decompiler.targets: decompiler.process_target(i) + def conditional_jump(decompiler, endpos, if_true): + if PY37 or PYPY: + return decompiler.conditional_jump_new(endpos, if_true) + return decompiler.conditional_jump_old(endpos, if_true) + + def conditional_jump_old(decompiler, endpos, if_true): + i = decompiler.next_pos + if i in decompiler.targets: + decompiler.process_target(i) + expr = decompiler.stack.pop() + clausetype = ast.Or if if_true else ast.And + clause = clausetype([expr]) + clause.endpos = endpos + decompiler.targets.setdefault(endpos, clause) + return clause + + def conditional_jump_new(decompiler, endpos, if_true): + expr = decompiler.stack.pop() + if decompiler.pos >= decompiler.conditions_end: + clausetype = ast.Or if if_true else ast.And + elif decompiler.pos in decompiler.or_jumps: + clausetype = ast.Or + if not if_true: + expr = ast.Not(expr) + else: + clausetype = ast.And + if if_true: + expr = ast.Not(expr) + decompiler.stack.append(expr) + + if decompiler.next_pos in decompiler.targets: + decompiler.process_target(decompiler.next_pos) + expr = decompiler.stack.pop() clause = clausetype([ expr ]) clause.endpos = endpos @@ -305,7 +403,7 @@ top = simplify(top) if top is limit: break if isinstance(top, ast.GenExprFor): break - + if not decompiler.stack: break top2 = decompiler.stack[-1] if isinstance(top2, ast.GenExprFor): break if partial and hasattr(top2, 'endpos') and top2.endpos == pos: break @@ -324,7 +422,7 @@ decompiler.stack.append(top) def JUMP_FORWARD(decompiler, endpos): - i = decompiler.pos # next instruction + i = decompiler.next_pos # next instruction decompiler.process_target(i, True) then = decompiler.stack.pop() decompiler.process_target(i, False) @@ -418,10 +516,9 @@ pass def RETURN_VALUE(decompiler): - if decompiler.pos != decompiler.end: throw(NotImplementedError) + if decompiler.next_pos != decompiler.end: throw(NotImplementedError) expr = decompiler.stack.pop() - decompiler.stack.append(simplify(expr)) - raise AstGenerated() + return simplify(expr) def ROT_TWO(decompiler): tos = decompiler.stack.pop() @@ -523,8 +620,7 @@ fors.append(top) else: fors.append(top) fors.reverse() - decompiler.stack.append(ast.GenExpr(ast.GenExprInner(simplify(expr), fors))) - raise AstGenerated() + return ast.GenExpr(ast.GenExprInner(simplify(expr), fors)) test_lines = """ (a and b if c and d else e and f for i in T if (A and B if C and D else E and F)) @@ -539,8 +635,13 @@ (a for b in T if f == 5 and r or t) (a for b in T if f and r and t) - (a for b in T if f == 5 and +r or not t) - (a for b in T if -t and ~r or `f`) + # (a for b in T if f == 5 and +r or not t) + # (a for b in T if -t and ~r or `f`) + + (a for b in T if x and not y and z) + (a for b in T if not x and y) + (a for b in T if not x and y and z) + (a for b in T if not x and y or z) #FIXME! (a**2 for b in T if t * r > y / 3) (a + 2 for b in T if t + r > y // 3) @@ -574,10 +675,12 @@ (s for s in T if s.a > 20 and (s.x.y == 123 or 'ABC' in s.p.q.r)) (a for b in T1 if c > d for e in T2 if f < g) - (func1(a, a.attr, keyarg=123) for s in T) - (func1(a, a.attr, keyarg=123, *e) for s in T) - (func1(a, b, a.attr1, a.b.c, keyarg1=123, keyarg2='mx', *e, **f) for s in T) - (func(a, a.attr, keyarg=123) for a in T if a.method(x, *y, **z) == 4) + (func1(a, a.attr, x=123) for s in T) + # (func1(a, a.attr, *args) for s in T) + # (func1(a, a.attr, x=123, **kwargs) for s in T) + (func1(a, b, a.attr1, a.b.c, x=123, y='foo') for s in T) + # (func1(a, b, a.attr1, a.b.c, x=123, y='foo', **kwargs) for s in T) + # (func(a, a.attr, keyarg=123) for a in T if a.method(x, *args, **kwargs) == 4) ((x or y) and (p or q) for a in T if (a or b) and (c or d)) (x.y for x in T if (a and (b or (c and d))) or X) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/examples/university1.py new/pony-0.7.10/pony/orm/examples/university1.py --- old/pony-0.7.9/pony/orm/examples/university1.py 2019-01-21 07:43:48.000000000 +0100 +++ new/pony-0.7.10/pony/orm/examples/university1.py 2019-04-20 16:42:08.000000000 +0200 @@ -39,7 +39,6 @@ gpa = Required(float, default=0) group = Required(Group) courses = Set(Course) - scores = Optional(IntArray, nullable=True) sql_debug(True) # Output all SQL queries to stdout @@ -49,7 +48,7 @@ postgres=dict(provider='postgres', user='pony', password='pony', host='localhost', database='pony'), oracle=dict(provider='oracle', user='c##pony', password='pony', dsn='localhost/orcl') ) -db.bind(**params['postgres']) +db.bind(**params['sqlite']) db.generate_mapping(create_tables=True) @@ -85,11 +84,11 @@ g106 = Group(number=106, major='B.S./M.S. in Nuclear Engineering', dept=d3) s1 = Student(name='John Smith', dob=date(1991, 3, 20), tel='123-456', gpa=3, group=g101, - courses=[c1, c2, c4, c6], scores=[1, 2, 3]) + courses=[c1, c2, c4, c6]) s2 = Student(name='Matthew Reed', dob=date(1990, 11, 26), gpa=3.5, group=g101, - courses=[c1, c3, c4, c5], scores=[2, 3, 4]) + courses=[c1, c3, c4, c5]) s3 = Student(name='Chuan Qin', dob=date(1989, 2, 5), gpa=4, group=g101, - courses=[c3, c5, c6], scores=[]) + courses=[c3, c5, c6]) s4 = Student(name='Rebecca Lawson', dob=date(1990, 4, 18), tel='234-567', gpa=3.3, group=g102, courses=[c1, c4, c5, c6]) s5 = Student(name='Maria Ionescu', dob=date(1991, 4, 23), gpa=3.9, group=g102, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/sqlbuilding.py new/pony-0.7.10/pony/orm/sqlbuilding.py --- old/pony-0.7.9/pony/orm/sqlbuilding.py 2018-12-09 11:02:23.000000000 +0100 +++ new/pony-0.7.10/pony/orm/sqlbuilding.py 2019-04-20 17:40:09.000000000 +0200 @@ -480,7 +480,10 @@ assert distinct in (None, True, False) result = distinct and 'GROUP_CONCAT(DISTINCT ' or 'GROUP_CONCAT(', builder(expr) if sep is not None: - result = result, ', ', builder(sep) + if builder.provider.dialect == 'MySQL': + result = result, ' SEPARATOR ', builder(sep) + else: + result = result, ', ', builder(sep) return result, ')' UPPER = make_unary_func('upper') LOWER = make_unary_func('lower') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/tests/test_declarative_func_monad.py new/pony-0.7.10/pony/orm/tests/test_declarative_func_monad.py --- old/pony-0.7.9/pony/orm/tests/test_declarative_func_monad.py 2018-12-09 11:02:23.000000000 +0100 +++ new/pony-0.7.10/pony/orm/tests/test_declarative_func_monad.py 2019-04-20 17:40:09.000000000 +0200 @@ -115,6 +115,7 @@ self.assertEqual(result, {Student[1], Student[2], Student[3], Student[4], Student[5]}) @raises_exception(ExprEvalError, "`1 < datetime.now()` raises TypeError: " + ( "can't compare 'datetime' to 'int'" if PYPY2 else + "'<' not supported between instances of 'int' and 'datetime'" if PYPY and sys.version_info >= (3, 6) else "unorderable types: int < datetime" if PYPY else "can't compare datetime.datetime to int" if PY2 else "unorderable types: int() < datetime.datetime()" if sys.version_info < (3, 6) else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/tests/test_decompiler.py new/pony-0.7.10/pony/orm/tests/test_decompiler.py --- old/pony-0.7.9/pony/orm/tests/test_decompiler.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pony-0.7.10/pony/orm/tests/test_decompiler.py 2019-04-20 16:42:08.000000000 +0200 @@ -0,0 +1,103 @@ +import unittest + +from pony.thirdparty.compiler.transformer import parse +from pony.orm.decompiling import Decompiler +from pony.orm.asttranslation import ast2src + + +def generate_gens(): + patterns = [ + '(x * y) * [z * j)', + '([x * y) * z) * j', + '(x * [y * z)) * j', + 'x * ([y * z) * j)', + 'x * (y * [z * j))' + ] + + ops = ('and', 'or') + nots = (True, False) + + result = [] + + for pat in patterns: + cur = pat + for op1 in ops: + for op2 in ops: + for op3 in ops: + res = cur.replace('*', op1, 1) + res = res.replace('*', op2, 1) + res = res.replace('*', op3, 1) + result.append(res) + + final = [] + + for res in result: + for par1 in nots: + for par2 in nots: + for a in nots: + for b in nots: + for c in nots: + for d in nots: + cur = res.replace('(', 'not(') if not par1 else res + if not par2: + cur = cur.replace('[', 'not(') + else: + cur = cur.replace('[', '(') + if not a: cur = cur.replace('x', 'not x') + if not b: cur = cur.replace('y', 'not y') + if not c: cur = cur.replace('z', 'not z') + if not d: cur = cur.replace('j', 'not j') + final.append(cur) + + return final + +def create_test(gen): + def wrapped_test(self): + def get_condition_values(cond): + result = [] + vals = (True, False) + for x in vals: + for y in vals: + for z in vals: + for j in vals: + result.append(eval(cond, {'x': x, 'y': y, 'z': z, 'j': j})) + return result + src1 = '(a for a in [] if %s)' % gen + src2 = 'lambda x, y, z, j: (%s)' % gen + src3 = '(m for m in [] if %s for n in [] if %s)' % (gen, gen) + + code1 = compile(src1, '<?>', 'eval').co_consts[0] + ast1 = Decompiler(code1).ast + res1 = ast2src(ast1).replace('.0', '[]') + res1 = res1[res1.find('if')+2:-1] + + code2 = compile(src2, '<?>', 'eval').co_consts[0] + ast2 = Decompiler(code2).ast + res2 = ast2src(ast2).replace('.0', '[]') + res2 = res2[res2.find(':')+1:] + + code3 = compile(src3, '<?>', 'eval').co_consts[0] + ast3 = Decompiler(code3).ast + res3 = ast2src(ast3).replace('.0', '[]') + res3 = res3[res3.find('if')+2: res3.rfind('for')-1] + + if get_condition_values(gen) != get_condition_values(res1): + self.fail("Incorrect generator decompilation: %s -> %s" % (gen, res1)) + + if get_condition_values(gen) != get_condition_values(res2): + self.fail("Incorrect lambda decompilation: %s -> %s" % (gen, res2)) + + if get_condition_values(gen) != get_condition_values(res3): + self.fail("Incorrect multi-for generator decompilation: %s -> %s" % (gen, res3)) + + return wrapped_test + + +class TestDecompiler(unittest.TestCase): + pass + + +for i, gen in enumerate(generate_gens()): + test_method = create_test(gen) + test_method.__name__ = 'test_decompiler_%d' % i + setattr(TestDecompiler, test_method.__name__, test_method) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/orm/tests/test_select_from_select_queries.py new/pony-0.7.10/pony/orm/tests/test_select_from_select_queries.py --- old/pony-0.7.9/pony/orm/tests/test_select_from_select_queries.py 2018-12-09 11:02:23.000000000 +0100 +++ new/pony-0.7.10/pony/orm/tests/test_select_from_select_queries.py 2019-04-20 17:40:09.000000000 +0200 @@ -2,6 +2,7 @@ from pony.orm import * from pony.orm.tests.testutils import * +from pony.py23compat import PYPY2 db = Database('sqlite', ':memory:') @@ -88,7 +89,8 @@ self.assertEqual(db.last_sql.count('SELECT'), 1) @db_session - @raises_exception(ExprEvalError, "`s.scholarship > 0` raises NameError: name 's' is not defined") + @raises_exception(ExprEvalError, "`s.scholarship > 0` raises NameError: name 's' is not defined" if not PYPY2 + else "`s.scholarship > 0` raises NameError: global name 's' is not defined") def test_7(self): # test access to original query var name from the new query q = select(s.first_name for s in Student if s.scholarship < 500) q2 = select(x for x in q if s.scholarship > 0) @@ -377,7 +379,7 @@ @db_session def test_45(self): - q = select(s for s in Student).order_by(Student.first_name).limit(3, offset=1) + q = select(s for s in Student).order_by(Student.first_name, Student.id).limit(3, offset=1) q2 = select(s for s in q if s.age > 18).limit(2, offset=1) q3 = select(s.last_name for s in q2).limit(2, offset=1) self.assertEqual(set(q3), {'Brown'}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony/py23compat.py new/pony-0.7.10/pony/py23compat.py --- old/pony-0.7.9/pony/py23compat.py 2018-12-09 11:02:23.000000000 +0100 +++ new/pony-0.7.10/pony/py23compat.py 2019-04-20 16:42:08.000000000 +0200 @@ -3,6 +3,7 @@ PY2 = sys.version_info[0] == 2 PYPY = platform.python_implementation() == 'PyPy' PYPY2 = PYPY and PY2 +PY37 = sys.version_info[:2] >= (3, 7) if PY2: from future_builtins import zip as izip, map as imap diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony.egg-info/PKG-INFO new/pony-0.7.10/pony.egg-info/PKG-INFO --- old/pony-0.7.9/pony.egg-info/PKG-INFO 2019-01-21 08:24:08.000000000 +0100 +++ new/pony-0.7.10/pony.egg-info/PKG-INFO 2019-04-20 17:48:21.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: pony -Version: 0.7.9 +Version: 0.7.10 Summary: Pony Object-Relational Mapper Home-page: https://ponyorm.com Author: Alexander Kozlovsky, Alexey Malashkevich diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pony-0.7.9/pony.egg-info/SOURCES.txt new/pony-0.7.10/pony.egg-info/SOURCES.txt --- old/pony-0.7.9/pony.egg-info/SOURCES.txt 2019-01-21 08:24:09.000000000 +0100 +++ new/pony-0.7.10/pony.egg-info/SOURCES.txt 2019-04-20 17:48:21.000000000 +0200 @@ -1,3 +1,4 @@ +LICENSE MANIFEST.in README.md setup.py @@ -78,6 +79,7 @@ pony/orm/tests/test_declarative_sqltranslator.py pony/orm/tests/test_declarative_sqltranslator2.py pony/orm/tests/test_declarative_strings.py +pony/orm/tests/test_decompiler.py pony/orm/tests/test_deduplication.py pony/orm/tests/test_diagram.py pony/orm/tests/test_diagram_attribute.py