# HG changeset patch # User Jim Baker # Date 1454384221 25200 # Node ID d06e29d100c04576735e86c75a26c5f33669bb72 # Parent b6735606c13df95f770527e629954407f82808c5 Do not deserialize PyFunction objects. Fixes #2454 Instead use standard Python pickling; or subclass PyFunction. diff --git a/Lib/test/test_java_integration.py b/Lib/test/test_java_integration.py --- a/Lib/test/test_java_integration.py +++ b/Lib/test/test_java_integration.py @@ -14,8 +14,9 @@ import re from collections import deque from test import test_support -from java.lang import (ClassCastException, ExceptionInInitializerError, String, Runnable, System, - Runtime, Math, Byte) +from java.lang import ( + ClassCastException, ExceptionInInitializerError, UnsupportedOperationException, + String, Runnable, System, Runtime, Math, Byte) from java.math import BigDecimal, BigInteger from java.net import URI from java.io import (ByteArrayInputStream, ByteArrayOutputStream, File, FileInputStream, @@ -656,13 +657,30 @@ class SerializationTest(unittest.TestCas self.assertEqual(date_list, roundtrip_serialization(date_list)) def test_java_serialization_pycode(self): - def universal_answer(): return 42 serialized_code = roundtrip_serialization(universal_answer.func_code) self.assertEqual(eval(serialized_code), universal_answer()) + def test_java_serialization_pyfunction(self): + # Not directly supported due to lack of general utility + # (globals will usually be in the function object in + # func_globals), and problems with unserialization + # vulnerabilities. Users can always subclass from PyFunction + # for specific cases, as seen in PyCascading + import new + def f(): + return 6 * 7 + max(0, 1, 2) + # However, using the new module, it's possible to create a + # function with no globals, which means the globals will come + # from the current context + g = new.function(f.func_code, {}, "g") + # But still forbid Java deserialization of this function + # object. Use pickling or other support instead. + with self.assertRaises(UnsupportedOperationException): + roundtrip_serialization(g) + def test_builtin_names(self): import __builtin__ names = [x for x in dir(__builtin__)] @@ -872,7 +890,7 @@ class SingleMethodInterfaceTest(unittest future.get() self.assertEqual(x, [42]) - @unittest.skip("FIXME: not working") + @unittest.skip("FIXME: not working; see http://bugs.jython.org/issue2115") def test_callable_object(self): callable_obj = CallableObject() future = self.executor.submit(callable_obj) diff --git a/Lib/test/test_new.py b/Lib/test/test_new.py --- a/Lib/test/test_new.py +++ b/Lib/test/test_new.py @@ -24,18 +24,10 @@ class NewTest(unittest.TestCase): c = new.instance(C, {'yolks': 3}) o = new.instance(C) - - # __dict__ is a non dict mapping in Jython - if test_support.is_jython: - self.assertEqual(len(o.__dict__), 0, "new __dict__ should be empty") - else: - self.assertEqual(o.__dict__, {}, "new __dict__ should be empty") + self.assertEqual(o.__dict__, {}, "new __dict__ should be empty") del o o = new.instance(C, None) - if test_support.is_jython: - self.assertEqual(len(o.__dict__), 0, "new __dict__ should be empty") - else: - self.assertEqual(o.__dict__, {}, "new __dict__ should be empty") + self.assertEqual(o.__dict__, {}, "new __dict__ should be empty") del o def break_yolks(self): @@ -109,7 +101,14 @@ class NewTest(unittest.TestCase): test_closure(g, (1, 1), ValueError) # closure is wrong size test_closure(f, g.func_closure, ValueError) # no closure needed - if hasattr(new, 'code') and not test_support.is_jython: + # [Obsolete] Note: Jython will never have new.code() + # + # Who said that?!!! guess what, we do! :) + # + # Unfortunately we still need a way to compile to Python bytecode, + # so support is still incomplete, as seen in the fact that we need + # to get values from CPython 2.7. + if hasattr(new, 'code'): def test_code(self): # bogus test of new.code() def f(a): pass @@ -117,16 +116,16 @@ class NewTest(unittest.TestCase): c = f.func_code argcount = c.co_argcount nlocals = c.co_nlocals - stacksize = c.co_stacksize + stacksize = 1 # TODO c.co_stacksize flags = c.co_flags - codestring = c.co_code - constants = c.co_consts - names = c.co_names + codestring = 'd\x00\x00S' # TODO c.co_code + constants = (None,) # TODO c.co_consts + names = () # TODO c.co_names varnames = c.co_varnames filename = c.co_filename name = c.co_name firstlineno = c.co_firstlineno - lnotab = c.co_lnotab + lnotab = '' # TODO c.co_lnotab, but also see http://bugs.jython.org/issue1638 freevars = c.co_freevars cellvars = c.co_cellvars diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java --- a/src/org/python/core/PyBytecode.java +++ b/src/org/python/core/PyBytecode.java @@ -66,6 +66,12 @@ public class PyBytecode extends PyBaseCo debug = defaultDebug; + if (argcount < 0) { + throw Py.ValueError("code: argcount must not be negative"); + } else if (nlocals < 0) { + throw Py.ValueError("code: nlocals must not be negative"); + } + co_argcount = nargs = argcount; co_varnames = varnames; co_nlocals = nlocals; // maybe assert = varnames.length; diff --git a/src/org/python/core/PyFunction.java b/src/org/python/core/PyFunction.java --- a/src/org/python/core/PyFunction.java +++ b/src/org/python/core/PyFunction.java @@ -545,6 +545,9 @@ public class PyFunction extends PyObject @Override public boolean isSequenceType() { return false; } + private Object readResolve() { + throw new UnsupportedOperationException(); + } /* Traverseproc implementation */ @Override