Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pony for openSUSE:Factory checked in at 2026-07-07 21:06:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pony (Old) and /work/SRC/openSUSE:Factory/.python-pony.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pony" Tue Jul 7 21:06:17 2026 rev:17 rq:1364309 version:0.7.19 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pony/python-pony.changes 2025-10-29 21:07:31.984836012 +0100 +++ /work/SRC/openSUSE:Factory/.python-pony.new.1982/python-pony.changes 2026-07-07 21:08:52.234056098 +0200 @@ -1,0 +2,7 @@ +Tue Jul 7 10:41:32 UTC 2026 - Nico Krapp <[email protected]> + +- Add py313.patch and py314.patch to support Python 3.13 and Python 3.14 +- remove skip for Python 3.13 and Python 3.14 +- add skip for Python 3.15 + +------------------------------------------------------------------- New: ---- py313.patch py314.patch ----------(New B)---------- New: - Add py313.patch and py314.patch to support Python 3.13 and Python 3.14 - remove skip for Python 3.13 and Python 3.14 New: - Add py313.patch and py314.patch to support Python 3.13 and Python 3.14 - remove skip for Python 3.13 and Python 3.14 ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pony.spec ++++++ --- /var/tmp/diff_new_pack.YoV6Fa/_old 2026-07-07 21:08:52.922079877 +0200 +++ /var/tmp/diff_new_pack.YoV6Fa/_new 2026-07-07 21:08:52.926080016 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-pony # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,8 +17,7 @@ # not compatible: https://github.com/ponyorm/pony/blob/v0.7.17/setup.py#L112 -%define skip_python313 1 -%define skip_python314 1 +%define skip_python315 1 Name: python-pony Version: 0.7.19 Release: 0 @@ -26,6 +25,10 @@ License: Apache-2.0 URL: https://ponyorm.com Source: https://files.pythonhosted.org/packages/source/p/pony/pony-%{version}.tar.gz +# PATCH-FIX-UPSTREAM py313.patch to support Python 3.13 gh#ponyorm/pony@179cb05 +Patch0: py313.patch +# PATCH-FIX-UPSTREAM py314.patch to support Python 3.14 gh#ponyorm/pony#754 +Patch1: py314.patch BuildRequires: %{python_module base} BuildRequires: %{python_module pip} BuildRequires: %{python_module setuptools} ++++++ py313.patch ++++++ >From a52bdba67123e2f7be78e04bb67339411c46df8e Mon Sep 17 00:00:00 2001 From: black-sliver <[email protected]> Date: Thu, 21 Aug 2025 00:29:59 +0200 Subject: [PATCH 1/6] Py3.13: add missing iter to ResultIterator --- pony/orm/core.py | 2 ++ 1 file changed, 2 insertions(+) Index: pony-0.7.19/pony/orm/core.py =================================================================== --- pony-0.7.19.orig/pony/orm/core.py +++ pony-0.7.19/pony/orm/core.py @@ -6282,6 +6282,8 @@ class QueryResultIterator(object): self._position += 1 return item __next__ = next + def __iter__(self): + return self def __length_hint__(self): return len(self._query_result) - self._position Index: pony-0.7.19/pony/orm/decompiling.py =================================================================== --- pony-0.7.19.orig/pony/orm/decompiling.py +++ pony-0.7.19/pony/orm/decompiling.py @@ -1,5 +1,5 @@ from __future__ import absolute_import, print_function, division -from pony.py23compat import PY36, PY37, PY38, PY39, PY310, PY311, PY312, PYPY +from pony.py23compat import PY36, PY37, PY38, PY39, PY310, PY311, PY312, PY313, PYPY import sys, types, inspect from opcode import opname as opnames, HAVE_ARGUMENT, EXTENDED_ARG, cmp_op @@ -194,6 +194,10 @@ class Decompiler(object): oparg = co_code[i] + co_code[i + 1] * 256 + oparg * 65536 i += 2 + # CACHE bytes have inconvenient placement in py3.13, so we need to skip them + while i < len(code.co_code) and opnames[code.co_code[i]] == 'CACHE': + i += 2 + opname = opnames[op].replace('+', '_') if op >= HAVE_ARGUMENT: if op in hasconst: @@ -217,9 +221,18 @@ class Decompiler(object): arg = [i + oparg * (2 if PY310 else 1) * (-1 if 'BACKWARD' in opname else 1)] elif op in haslocal: - arg = [code.co_varnames[oparg]] + if opname in {'LOAD_FAST_LOAD_FAST', 'STORE_FAST_LOAD_FAST', 'STORE_FAST_STORE_FAST'}: + # py3.13: 2 4bit args + arg = [code._varname_from_oparg(oparg >> 4), code._varname_from_oparg(oparg & 0x0f)] + elif PY313: + # co_varnames is incomplete now + arg = [code._varname_from_oparg(oparg)] + else: + arg = [code.co_varnames[oparg]] elif op in hascompare: - if PY312: + if PY313: + oparg >>= 5 + elif PY312: oparg >>= 4 arg = [cmp_op[oparg]] elif op in hasfree: @@ -230,6 +243,10 @@ class Decompiler(object): arg = [oparg * (2 if PY310 else 1)] else: arg = [oparg] + elif PY313 and opname == 'MAKE_FUNCTION' and opnames[code.co_code[i]] == 'SET_FUNCTION_ATTRIBUTE': + # pull attributes from next instruction + arg = [code.co_code[i+1]] + i += 2 else: arg = [] if opname == 'FOR_ITER': decompiler.for_iter_pos = decompiler.pos @@ -390,6 +407,11 @@ class Decompiler(object): end = decompiler.stack.pop() start = decompiler.stack.pop() node1 = decompiler.stack.pop() + if PY313: + if isinstance(end, ast.Constant) and end.value is None: + end = None + if isinstance(start, ast.Constant) and start.value is None: + start = None node2 = ast.Slice(start, end, ctx=ast.Load()) return ast.Subscript(value=node1, slice=node2, ctx=ast.Load()) @@ -502,12 +524,19 @@ class Decompiler(object): args = values[:-len(keys)] keywords = [ast.keyword(k, v) for k, v in zip(keys, values[-len(keys):])] - self = decompiler.stack.pop() - callable_ = decompiler.stack.pop() - if callable_ is None: - callable_ = self + if PY313: + # self/NULL and callable are swapped + self_ = decompiler.stack.pop() + callable_ = decompiler.stack.pop() + if self_ is not None: + args.insert(0, self_) else: - args.insert(0, self) + self_ = decompiler.stack.pop() + callable_ = decompiler.stack.pop() + if callable_ is None: + callable_ = self_ + else: + args.insert(0, self_) decompiler.stack.append(callable_) return decompiler._call_function(args, keywords) @@ -545,6 +574,12 @@ class Decompiler(object): star = decompiler.stack.pop() args = [ast.Starred(value=star)] if star else None keywords = [ast.keyword(value=star2)] if star2 else None + if PY313: + # self/NULL and callable are swapped; FIXME: this leaves NULL on the stack? + self_ = decompiler.stack.pop() + callable_ = decompiler.stack.pop() + decompiler.stack.append(self_) + decompiler.stack.append(callable_) return decompiler._call_function(args, keywords) def CALL_METHOD(decompiler, argc): @@ -566,12 +601,24 @@ class Decompiler(object): method = pop() return ast.Call(method, args, keywords) + def CALL_KW(decompiler, argc): + names = decompiler.stack.pop() + if isinstance(names, ast.Constant): + decompiler.kw_names = names.value + else: + raise NotImplementedError(f"CALL_KW for {names.__class__.__name__} not implemented") + return decompiler.CALL(argc) + def COMPARE_OP(decompiler, op): oper2 = decompiler.stack.pop() oper1 = decompiler.stack.pop() op = operator_mapping[op]() return ast.Compare(oper1, [op], [oper2]) + def CONVERT_VALUE(decompiler, conversion): + value = decompiler.stack.pop() + return value, [-1, ord('s'), ord('r'), ord('a')][conversion] + def COPY(decompiler, _): pass # this is not great, but stack is not the same as during runtime # actual queries are hopefully covered by tests @@ -609,6 +656,25 @@ class Decompiler(object): value = decompiler.stack.pop() return ast.FormattedValue(value=value, conversion=conversion, format_spec=format_spec) + def FORMAT_SIMPLE(decompiler): + # see CONVERT_VALUE + args = decompiler.stack.pop() + if isinstance(args, tuple): + value, conversion = args + else: + value, conversion = args, -1 + return ast.FormattedValue(value=value, conversion=conversion) + + def FORMAT_WITH_SPEC(decompiler): + spec = decompiler.stack.pop() + args = decompiler.stack.pop() + if isinstance(args, tuple): + # FIXME: is this correct here? should we look for ast.FormattedValue instead? + value, conversion = args + else: + value, conversion = args, -1 + return ast.FormattedValue(value=value, conversion=conversion, format_spec=spec) + def GEN_START(decompiler, kind): assert kind == 0 # only support sync @@ -791,7 +857,12 @@ class Decompiler(object): def LOAD_ATTR(decompiler, attr_name, push_null): res = ast.Attribute(decompiler.stack.pop(), attr_name, ast.Load()) - if push_null: + if push_null and PY313: + # NULL and attr swapped + decompiler.stack.append(res) + decompiler.stack.append(None) + return None + elif push_null: decompiler.stack.append(None) return res @@ -810,13 +881,24 @@ class Decompiler(object): decompiler.names.add(varname) return ast.Name(varname, ast.Load()) + def LOAD_FAST_LOAD_FAST(decompiler, varname1, varname2): + decompiler.names.add(varname1) + decompiler.stack.append(ast.Name(varname1, ast.Load())) + return decompiler.LOAD_FAST(varname2) + LOAD_FAST_AND_CLEAR = LOAD_FAST def LOAD_GLOBAL(decompiler, varname, push_null): - if push_null: - decompiler.stack.append(None) + res = ast.Name(varname, ast.Load()) decompiler.names.add(varname) - return ast.Name(varname, ast.Load()) + if push_null and PY313: + # NULL and global swapped + decompiler.stack.append(res) + decompiler.stack.append(None) + return None + elif push_null and not PY313: + decompiler.stack.append(None) + return res def LOAD_METHOD(decompiler, methname): return decompiler.LOAD_ATTR(methname, PY311) @@ -834,7 +916,7 @@ class Decompiler(object): decompiler.stack[-3:-2] = [] # ignore freevars return decompiler.MAKE_FUNCTION(argc) - def MAKE_FUNCTION(decompiler, argc): + def MAKE_FUNCTION(decompiler, argc=0): defaults = [] if sys.version_info >= (3, 6): if not PY311: @@ -923,6 +1005,12 @@ class Decompiler(object): decompiler.stack.append(tos2) decompiler.stack.append(tos1) + def SET_FUNCTION_ATTRIBUTE(decompiler, flag): + """This replaces the argument to MAKE_FUNCTION in py 3.13""" + # This actually needs special handling in get_instructions because + # the func will not be at the top of the stack when we get here. + throw(NotImplementedError) + def SETUP_LOOP(decompiler, endpos): pass @@ -940,6 +1028,14 @@ class Decompiler(object): decompiler.assnames.add(varname) decompiler.store(ast.Name(varname, ast.Store())) + def STORE_FAST_STORE_FAST(decompiler, varname1, varname2): + decompiler.STORE_FAST(varname1) + decompiler.STORE_FAST(varname2) + + def STORE_FAST_LOAD_FAST(decompiler, varname1, varname2): + decompiler.STORE_FAST(varname1) + return decompiler.LOAD_FAST(varname2) + def STORE_MAP(decompiler): tos = decompiler.stack.pop() tos1 = decompiler.stack.pop() @@ -964,6 +1060,9 @@ class Decompiler(object): pass # this is not great, but stack is not the same as during runtime # actual queries are hopefully covered by tests + def TO_BOOL(decompiler): + pass + def UNARY_POSITIVE(decompiler): return ast.UnaryOp(op=ast.UAdd(), operand=decompiler.stack.pop()) Index: pony-0.7.19/pony/py23compat.py =================================================================== --- pony-0.7.19.orig/pony/py23compat.py +++ pony-0.7.19/pony/py23compat.py @@ -8,6 +8,7 @@ PY39 = sys.version_info[:2] >= (3, 9) PY310 = sys.version_info[:2] >= (3, 10) PY311 = sys.version_info[:2] >= (3, 11) PY312 = sys.version_info[:2] >= (3, 12) +PY313 = sys.version_info[:2] >= (3, 13) unicode = str buffer = bytes Index: pony-0.7.19/pony/utils/tests/test_utils.py =================================================================== --- /dev/null +++ pony-0.7.19/pony/utils/tests/test_utils.py @@ -0,0 +1,17 @@ +import ast +import unittest +from pony.py23compat import PY36 +from pony.utils import pickle_ast, unpickle_ast + + +class PickleTest(unittest.TestCase): + @unittest.skipUnless(PY36, "requires Python 3") + def test_persistent_id(self): + src = ast.literal_eval("...") + res = unpickle_ast(pickle_ast(src)) + self.assertEqual(res, src) + + def test_simple_ast(self): + src = ast.parse("(x for x in [])") + res = unpickle_ast(pickle_ast(src)) + self.assertEqual(ast.dump(res), ast.dump(src)) Index: pony-0.7.19/pony/utils/utils.py =================================================================== --- pony-0.7.19.orig/pony/utils/utils.py +++ pony-0.7.19/pony/utils/utils.py @@ -368,26 +368,30 @@ def between(x, a, b): def is_utf8(encoding): return encoding.upper().replace('_', '').replace('-', '') in ('UTF8', 'UTF', 'U8') -def _persistent_id(obj): - if obj is Ellipsis: - return "Ellipsis" - -def _persistent_load(persid): - if persid == "Ellipsis": - return Ellipsis - raise pickle.UnpicklingError("unsupported persistent object") + +class PersistentPickler(pickle.Pickler): + def persistent_id(self, obj): + if obj is Ellipsis: + return "Ellipsis" + return None + + +class PersistentUnpickler(pickle.Unpickler): + def persistent_load(self, persid): + if persid == "Ellipsis": + return Ellipsis + raise pickle.UnpicklingError("unsupported persistent object") + def pickle_ast(val): pickled = io.BytesIO() - pickler = pickle.Pickler(pickled) - pickler.persistent_id = _persistent_id + pickler = PersistentPickler(pickled) pickler.dump(val) return pickled def unpickle_ast(pickled): pickled.seek(0) - unpickler = pickle.Unpickler(pickled) - unpickler.persistent_load = _persistent_load + unpickler = PersistentUnpickler(pickled) return unpickler.load() def copy_ast(tree): Index: pony-0.7.19/setup.py =================================================================== --- pony-0.7.19.orig/setup.py +++ pony-0.7.19/setup.py @@ -70,6 +70,7 @@ classifiers = [ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries', 'Topic :: Database' @@ -110,8 +111,8 @@ download_url = "http://pypi.python.org/p if __name__ == "__main__": pv = sys.version_info[:2] - if pv < (3, 8) or pv > (3, 12): - s = "Sorry, but %s %s requires Python of one of the following versions: 3.8-3.12." \ + if pv < (3, 8) or pv > (3, 13): + s = "Sorry, but %s %s requires Python of one of the following versions: 3.8-3.13." \ " You have version %s" print(s % (name, version, sys.version.split(' ', 1)[0])) sys.exit(1) ++++++ py314.patch ++++++ Index: pony-0.7.19/pony/orm/core.py =================================================================== --- pony-0.7.19.orig/pony/orm/core.py +++ pony-0.7.19/pony/orm/core.py @@ -3871,7 +3871,8 @@ class EntityMeta(type): comprehension = ast.comprehension( target=ast.Name(iter_name, ast.Store()), iter=ast.Name('.0', ast.Load()), - ifs=[] + ifs=[], + is_async=False ) entity._default_genexpr_ = ast.GeneratorExp(ast.Name(iter_name, ast.Load()), [comprehension]) @@ -4393,7 +4394,11 @@ class EntityMeta(type): name = names[0] for_expr = ast.comprehension( - target=ast.Name(name, ast.Store()), iter=ast.Name('.0', ast.Load()), ifs=[cond_expr]) + target=ast.Name(name, ast.Store()), + iter=ast.Name('.0', ast.Load()), + ifs=[cond_expr], + is_async = False + ) inner_expr = ast.GeneratorExp(elt=ast.Name(name, ast.Load()), generators=[for_expr]) locals = locals.copy() if locals is not None else {} Index: pony-0.7.19/pony/orm/decompiling.py =================================================================== --- pony-0.7.19.orig/pony/orm/decompiling.py +++ pony-0.7.19/pony/orm/decompiling.py @@ -1,5 +1,5 @@ from __future__ import absolute_import, print_function, division -from pony.py23compat import PY36, PY37, PY38, PY39, PY310, PY311, PY312, PY313, PYPY +from pony.py23compat import PY36, PY37, PY38, PY39, PY310, PY311, PY312, PY313, PY314, PYPY import sys, types, inspect from opcode import opname as opnames, HAVE_ARGUMENT, EXTENDED_ARG, cmp_op @@ -89,6 +89,12 @@ operator_mapping = { 'not in': ast.NotIn } +double_load_store_ops = { + 'LOAD_FAST_LOAD_FAST', + 'STORE_FAST_LOAD_FAST', + 'STORE_FAST_STORE_FAST', + 'LOAD_FAST_BORROW_LOAD_FAST_BORROW', +} def clean_assign(node): if isinstance(node, ast.Assign): @@ -99,6 +105,9 @@ def clean_assign(node): def make_const(value): if is_const(value): return value + if PY314: + if isinstance(value, slice): + return slice_to_ast(value) if PY39: return ast.Constant(value) elif PY38: @@ -140,6 +149,13 @@ def unwrap_str(key): return key.s +def slice_to_ast(value): + start = ast.Constant(value.start) if value.start is not None else None + stop = ast.Constant(value.stop) if value.stop is not None else None + step = ast.Constant(value.step) if value.step is not None else None + return ast.Slice(start, stop, step) + + class Decompiler(object): def __init__(decompiler, code, start=0, end=None): decompiler.code = code @@ -221,7 +237,7 @@ class Decompiler(object): arg = [i + oparg * (2 if PY310 else 1) * (-1 if 'BACKWARD' in opname else 1)] elif op in haslocal: - if opname in {'LOAD_FAST_LOAD_FAST', 'STORE_FAST_LOAD_FAST', 'STORE_FAST_STORE_FAST'}: + if opname in double_load_store_ops: # py3.13: 2 4bit args arg = [code._varname_from_oparg(oparg >> 4), code._varname_from_oparg(oparg & 0x0f)] elif PY313: @@ -254,6 +270,10 @@ class Decompiler(object): and arg[0] == decompiler.for_iter_pos): decompiler.abs_jump_to_top = decompiler.pos + # NOT_TAKEN has a similar problem as CACHE, but does not change rel jump addresses, so needs to be here + while i < len(code.co_code) and opnames[code.co_code[i]] == 'NOT_TAKEN': + i += 2 + if before_yield: merge = False if opname == 'JUMP_BACKWARD': @@ -379,6 +399,9 @@ class Decompiler(object): inplace = opname.startswith('NB_INPLACE_') opname = opname.split('_', 2 if inplace else 1)[-1] + if opname == "SUBSCR": + return decompiler.BINARY_SUBSCR() + op = { "ADD": ast.Add, "AND": ast.BitAnd, @@ -412,7 +435,7 @@ class Decompiler(object): end = None if isinstance(start, ast.Constant) and start.value is None: start = None - node2 = ast.Slice(start, end, ctx=ast.Load()) + node2 = ast.Slice(start, end) return ast.Subscript(value=node1, slice=node2, ctx=ast.Load()) def BINARY_SUBSCR(decompiler): @@ -423,6 +446,13 @@ class Decompiler(object): node2.lower = None if isinstance(node2.upper, ast.Constant) and node2.upper.value is None: node2.upper = None + elif ( + isinstance(node2, ast.Constant) + and isinstance(node2.value, tuple) + and any(isinstance(value, slice) for value in node2.value) + ): + # py3.14 has a different format for constant tuple of slices + node2 = ast.Tuple(elts=[slice_to_ast(value) for value in node2.value]) elif not PY38: if isinstance(node2, ast.Tuple) and any(isinstance(item, ast.Slice) for item in node2.elts): node2 = ast.ExtSlice(node2.elts) @@ -565,7 +595,7 @@ class Decompiler(object): star = decompiler.stack.pop() return decompiler.CALL_FUNCTION(argc, star, star2) - def CALL_FUNCTION_EX(decompiler, argc): + def CALL_FUNCTION_EX(decompiler, argc=1): star2 = None if argc: if argc != 1: @@ -881,12 +911,15 @@ class Decompiler(object): decompiler.names.add(varname) return ast.Name(varname, ast.Load()) + LOAD_FAST_AND_CLEAR = LOAD_FAST + LOAD_FAST_BORROW = LOAD_FAST + def LOAD_FAST_LOAD_FAST(decompiler, varname1, varname2): decompiler.names.add(varname1) decompiler.stack.append(ast.Name(varname1, ast.Load())) return decompiler.LOAD_FAST(varname2) - LOAD_FAST_AND_CLEAR = LOAD_FAST + LOAD_FAST_BORROW_LOAD_FAST_BORROW = LOAD_FAST_LOAD_FAST def LOAD_GLOBAL(decompiler, varname, push_null): res = ast.Name(varname, ast.Load()) @@ -909,6 +942,9 @@ class Decompiler(object): decompiler.names.add(varname) return ast.Name(varname, ast.Load()) + def LOAD_SMALL_INT(decompiler, value): + return make_const(value) + def MAKE_CELL(decompiler, freevar): pass Index: pony-0.7.19/pony/orm/sqltranslation.py =================================================================== --- pony-0.7.19.orig/pony/orm/sqltranslation.py +++ pony-0.7.19/pony/orm/sqltranslation.py @@ -10,7 +10,7 @@ from functools import update_wrapper from uuid import UUID from pony import options, utils -from pony.utils import localbase, is_ident, throw, reraise, copy_ast, between, concat, coalesce +from pony.utils import localbase, is_ident, throw, reraise, copy_ast, between, concat, coalesce, IntegerGenerator from pony.orm.asttranslation import ASTTranslator, ast2src, TranslationError, create_extractors, get_child_nodes from pony.orm.decompiling import decompile, DecompileError, operator_mapping from pony.orm.ormtypes import \ @@ -1241,7 +1241,7 @@ class SqlQuery(object): sqlquery.tablerefs = {} if parent_sqlquery is None: sqlquery.alias_counters = {} - sqlquery.expr_counter = itertools.count(1) + sqlquery.expr_counter = IntegerGenerator(1) else: sqlquery.alias_counters = parent_sqlquery.alias_counters.copy() sqlquery.expr_counter = parent_sqlquery.expr_counter Index: pony-0.7.19/pony/orm/tests/test_declarative_exceptions.py =================================================================== --- pony-0.7.19.orig/pony/orm/tests/test_declarative_exceptions.py +++ pony-0.7.19/pony/orm/tests/test_declarative_exceptions.py @@ -1,5 +1,5 @@ from __future__ import absolute_import, print_function, division -from pony.py23compat import PYPY +from pony.py23compat import PY314, PYPY import sys, unittest from datetime import date @@ -77,7 +77,8 @@ class TestSQLTranslatorExceptions(unitte def test5(self): select(s for s in Student if s.name.upper(**{'a':'b', 'c':'d'})) - @raises_exception(ExprEvalError, "`1 in 2` raises TypeError: argument of type 'int' is not iterable" if not PYPY else + @raises_exception(ExprEvalError, "`1 in 2` raises TypeError: argument of type 'int' is not a container or iterable" if PY314 else + "`1 in 2` raises TypeError: argument of type 'int' is not iterable" if not PYPY else "`1 in 2` raises TypeError: 'int' object is not iterable") def test6(self): select(s for s in Student if 1 in 2) Index: pony-0.7.19/pony/py23compat.py =================================================================== --- pony-0.7.19.orig/pony/py23compat.py +++ pony-0.7.19/pony/py23compat.py @@ -9,6 +9,7 @@ PY310 = sys.version_info[:2] >= (3, 10) PY311 = sys.version_info[:2] >= (3, 11) PY312 = sys.version_info[:2] >= (3, 12) PY313 = sys.version_info[:2] >= (3, 13) +PY314 = sys.version_info[:2] >= (3, 14) unicode = str buffer = bytes Index: pony-0.7.19/pony/utils/tests/test_utils.py =================================================================== --- pony-0.7.19.orig/pony/utils/tests/test_utils.py +++ pony-0.7.19/pony/utils/tests/test_utils.py @@ -1,7 +1,9 @@ import ast import unittest +from copy import deepcopy + from pony.py23compat import PY36 -from pony.utils import pickle_ast, unpickle_ast +from pony.utils import pickle_ast, unpickle_asti, IntegerGenerator class PickleTest(unittest.TestCase): @@ -15,3 +17,21 @@ class PickleTest(unittest.TestCase): src = ast.parse("(x for x in [])") res = unpickle_ast(pickle_ast(src)) self.assertEqual(ast.dump(res), ast.dump(src)) + + +class IntegerGeneratorTest(unittest.TestCase): + def test_from_zero(self): + c = IntegerGenerator() + self.assertEqual(next(c), 0) + self.assertEqual(next(c), 1) + + def test_from_start(self): + c = IntegerGenerator(42) + self.assertEqual(next(c), 42) + self.assertEqual(next(c), 43) + + def test_copy(self): + c = IntegerGenerator(42) + self.assertEqual(next(c), 42) + d = deepcopy(c) + self.assertEqual(next(c), next(d)) Index: pony-0.7.19/pony/utils/utils.py =================================================================== --- pony-0.7.19.orig/pony/utils/utils.py +++ pony-0.7.19/pony/utils/utils.py @@ -445,3 +445,17 @@ def deduplicate(value, deduplication_cac return deduplication_cache[t].setdefault(value, value) except: return value + + +class IntegerGenerator: + """pickleable version of itertools.count() with fixed step=1""" + def __init__(self, firstval=0): + self.val = firstval + + def __iter__(self): + return self + + def __next__(self): + res = self.val + self.val += 1 + return res Index: pony-0.7.19/setup.py =================================================================== --- pony-0.7.19.orig/setup.py +++ pony-0.7.19/setup.py @@ -71,6 +71,7 @@ classifiers = [ 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries', 'Topic :: Database' @@ -111,8 +112,8 @@ download_url = "http://pypi.python.org/p if __name__ == "__main__": pv = sys.version_info[:2] - if pv < (3, 8) or pv > (3, 13): - s = "Sorry, but %s %s requires Python of one of the following versions: 3.8-3.13." \ + if pv < (3, 8) or pv > (3, 14): + s = "Sorry, but %s %s requires Python of one of the following versions: 3.8-3.14." \ " You have version %s" print(s % (name, version, sys.version.split(' ', 1)[0])) sys.exit(1)
