From jython-checkins at python.org Mon Nov 3 23:29:01 2014 From: jython-checkins at python.org (jeff.allen) Date: Mon, 03 Nov 2014 22:29:01 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Revert_PyJavaType=2Ejava_ch?= =?utf-8?q?anges_in_7a70d824e7bd=2E?= Message-ID: <20141103222843.120722.10908@psf.io> https://hg.python.org/jython/rev/7c731ca90075 changeset: 7405:7c731ca90075 user: Jeff Allen date: Mon Nov 03 22:22:25 2014 +0000 summary: Revert PyJavaType.java changes in 7a70d824e7bd. This file was included accidentally (evidently by the project, and not by the apparent author of the change set). files: src/org/python/core/PyJavaType.java | 31 ----------------- 1 files changed, 0 insertions(+), 31 deletions(-) diff --git a/src/org/python/core/PyJavaType.java b/src/org/python/core/PyJavaType.java --- a/src/org/python/core/PyJavaType.java +++ b/src/org/python/core/PyJavaType.java @@ -1053,37 +1053,6 @@ } } }; - PyBuiltinMethodNarrow mapLeProxy = new MapMethod("__le__", 1) { - @Override - public PyObject __call__(PyObject other) { - if (other.getType().isSubType(PyDictionary.TYPE)) { - PyDictionary oDict = (PyDictionary) other; - if (asMap().size() != oDict.size()) { - return Py.False; - } - for (Object jkey : asMap().keySet()) { - Object jval = asMap().get(jkey); - PyObject oVal = oDict.__finditem__(Py.java2py(jkey)); - if (oVal == null) { - return Py.False; - } - if (!Py.java2py(jval)._eq(oVal).__nonzero__()) { - return Py.False; - } - } - return Py.True; - } else { - Object oj = other.getJavaProxy(); - if (oj instanceof Map) { - Map oMap = (Map) oj; - return asMap().equals(oMap) ? Py.True : Py.False; - } else { - return null; - } - } - } - }; - PyBuiltinMethodNarrow mapIterProxy = new MapMethod("__iter__", 0) { @Override public PyObject __call__() { -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 5 16:57:33 2014 From: jython-checkins at python.org (jim.baker) Date: Wed, 05 Nov 2014 15:57:33 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Defer_fixing_test=5Fasyncha?= =?utf-8?q?t=2C_test=5Fasyncore?= Message-ID: <20141105155715.699.53029@psf.io> https://hg.python.org/jython/rev/7f0ba06ef74e changeset: 7406:7f0ba06ef74e user: Jim Baker date: Wed Nov 05 16:57:04 2014 +0100 summary: Defer fixing test_asynchat, test_asyncore These flaky tests should be reevaluated after 2.7.0. files: Lib/test/regrtest.py | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -1262,6 +1262,10 @@ # Requires Python bytecode compilation support test_longexp + # Nonreliable tests + test_asynchat + test_asyncore + # Tests that should work with socket-reboot, but currently hang test_ftplib test_httplib -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 5 17:44:41 2014 From: jython-checkins at python.org (jim.baker) Date: Wed, 05 Nov 2014 16:44:41 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_runpy_and_corresponding?= =?utf-8?q?_zipimport_support=2E?= Message-ID: <20141105164440.85214.96965@psf.io> https://hg.python.org/jython/rev/78eed6864f6b changeset: 7407:78eed6864f6b user: Jim Baker date: Wed Nov 05 17:44:22 2014 +0100 summary: Fix runpy and corresponding zipimport support. No longer skips some test_runpy tests that were erroneously (via script_helpers) that Jython uses .pyc instead of $py.class. Adds zipimport.get_filename, new with 2.7. files: Lib/test/script_helper.py | 167 ++++ Lib/test/test_import_jy.py | 3 +- Lib/test/test_runpy.py | 413 ---------- src/org/python/modules/Setup.java | 2 +- src/org/python/modules/imp.java | 2 +- src/org/python/modules/zipimport/zipimport.java | 3 +- src/org/python/modules/zipimport/zipimporter.java | 13 + 7 files changed, 185 insertions(+), 418 deletions(-) diff --git a/Lib/test/script_helper.py b/Lib/test/script_helper.py new file mode 100644 --- /dev/null +++ b/Lib/test/script_helper.py @@ -0,0 +1,167 @@ +# Common utility functions used by various script execution tests +# e.g. test_cmd_line, test_cmd_line_script and test_runpy + +import sys +import os +import re +import os.path +import tempfile +import subprocess +import py_compile +import contextlib +import shutil +try: + import zipfile +except ImportError: + # If Python is build without Unicode support, importing _io will + # fail, which, in turn, means that zipfile cannot be imported + # Most of this module can then still be used. + pass + +from test.test_support import strip_python_stderr + +# Executing the interpreter in a subprocess +def _assert_python(expected_success, *args, **env_vars): + cmd_line = [sys.executable] + if not env_vars: + cmd_line.append('-E') + cmd_line.extend(args) + # Need to preserve the original environment, for in-place testing of + # shared library builds. + env = os.environ.copy() + env.update(env_vars) + p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + env=env) + try: + out, err = p.communicate() + finally: + subprocess._cleanup() + p.stdout.close() + p.stderr.close() + rc = p.returncode + err = strip_python_stderr(err) + if (rc and expected_success) or (not rc and not expected_success): + raise AssertionError( + "Process return code is %d, " + "stderr follows:\n%s" % (rc, err.decode('ascii', 'ignore'))) + return rc, out, err + +def assert_python_ok(*args, **env_vars): + """ + Assert that running the interpreter with `args` and optional environment + variables `env_vars` is ok and return a (return code, stdout, stderr) tuple. + """ + return _assert_python(True, *args, **env_vars) + +def assert_python_failure(*args, **env_vars): + """ + Assert that running the interpreter with `args` and optional environment + variables `env_vars` fails and return a (return code, stdout, stderr) tuple. + """ + return _assert_python(False, *args, **env_vars) + +def python_exit_code(*args): + cmd_line = [sys.executable, '-E'] + cmd_line.extend(args) + with open(os.devnull, 'w') as devnull: + return subprocess.call(cmd_line, stdout=devnull, + stderr=subprocess.STDOUT) + +def spawn_python(*args, **kwargs): + cmd_line = [sys.executable, '-E'] + cmd_line.extend(args) + return subprocess.Popen(cmd_line, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + **kwargs) + +def kill_python(p): + p.stdin.close() + data = p.stdout.read() + p.stdout.close() + # try to cleanup the child so we don't appear to leak when running + # with regrtest -R. + p.wait() + subprocess._cleanup() + return data + +def run_python(*args, **kwargs): + if __debug__: + p = spawn_python(*args, **kwargs) + else: + p = spawn_python('-O', *args, **kwargs) + stdout_data = kill_python(p) + return p.wait(), stdout_data + +# Script creation utilities + at contextlib.contextmanager +def temp_dir(): + dirname = tempfile.mkdtemp() + dirname = os.path.realpath(dirname) + try: + yield dirname + finally: + shutil.rmtree(dirname) + +def make_script(script_dir, script_basename, source): + script_filename = script_basename+os.extsep+'py' + script_name = os.path.join(script_dir, script_filename) + script_file = open(script_name, 'w') + script_file.write(source) + script_file.close() + return script_name + +def compile_script(script_name): + py_compile.compile(script_name, doraise=True) + compiled_name = script_name[:-3] + '$py.class' + return compiled_name + +def make_zip_script(zip_dir, zip_basename, script_name, name_in_zip=None): + zip_filename = zip_basename+os.extsep+'zip' + zip_name = os.path.join(zip_dir, zip_filename) + zip_file = zipfile.ZipFile(zip_name, 'w') + if name_in_zip is None: + name_in_zip = os.path.basename(script_name) + zip_file.write(script_name, name_in_zip) + zip_file.close() + #if test.test_support.verbose: + # zip_file = zipfile.ZipFile(zip_name, 'r') + # print 'Contents of %r:' % zip_name + # zip_file.printdir() + # zip_file.close() + return zip_name, os.path.join(zip_name, name_in_zip) + +def make_pkg(pkg_dir): + os.mkdir(pkg_dir) + make_script(pkg_dir, '__init__', '') + +def make_zip_pkg(zip_dir, zip_basename, pkg_name, script_basename, + source, depth=1, compiled=False): + unlink = [] + init_name = make_script(zip_dir, '__init__', '') + unlink.append(init_name) + init_basename = os.path.basename(init_name) + script_name = make_script(zip_dir, script_basename, source) + unlink.append(script_name) + if compiled: + init_name = compile_script(init_name) + script_name = compile_script(script_name) + unlink.extend((init_name, script_name)) + pkg_names = [os.sep.join([pkg_name]*i) for i in range(1, depth+1)] + script_name_in_zip = os.path.join(pkg_names[-1], os.path.basename(script_name)) + zip_filename = zip_basename+os.extsep+'zip' + zip_name = os.path.join(zip_dir, zip_filename) + zip_file = zipfile.ZipFile(zip_name, 'w') + for name in pkg_names: + init_name_in_zip = os.path.join(name, init_basename) + zip_file.write(init_name, init_name_in_zip) + zip_file.write(script_name, script_name_in_zip) + zip_file.close() + for name in unlink: + os.unlink(name) + #if test.test_support.verbose: + # zip_file = zipfile.ZipFile(zip_name, 'r') + # print 'Contents of %r:' % zip_name + # zip_file.printdir() + # zip_file.close() + return zip_name, os.path.join(zip_name, script_name_in_zip) diff --git a/Lib/test/test_import_jy.py b/Lib/test/test_import_jy.py --- a/Lib/test/test_import_jy.py +++ b/Lib/test/test_import_jy.py @@ -144,11 +144,10 @@ self.assertEqual(imp.find_module('sys'), (None, 'sys', ('', '', 6))) self.assertEqual(imp.find_module('__builtin__'), (None, '__builtin__', ('', '', 6))) - self.assertEqual(imp.find_module('imp'), (None, 'imp', ('', '', 6))) def test_imp_is_builtin(self): self.assertTrue(all(imp.is_builtin(mod) - for mod in ['sys', '__builtin__', 'imp'])) + for mod in ['sys', '__builtin__'])) self.assertFalse(imp.is_builtin('os')) def test_load_compiled(self): diff --git a/Lib/test/test_runpy.py b/Lib/test/test_runpy.py deleted file mode 100644 --- a/Lib/test/test_runpy.py +++ /dev/null @@ -1,413 +0,0 @@ -# Test the runpy module -import unittest -import os -import os.path -import sys -import re -import tempfile -from test.test_support import verbose, run_unittest, forget, is_jython -from test.script_helper import (temp_dir, make_script, compile_script, - make_pkg, make_zip_script, make_zip_pkg) - - -from runpy import _run_code, _run_module_code, run_module, run_path -# Note: This module can't safely test _run_module_as_main as it -# runs its tests in the current process, which would mess with the -# real __main__ module (usually test.regrtest) -# See test_cmd_line_script for a test that executes that code path - -# Set up the test code and expected results - -class RunModuleCodeTest(unittest.TestCase): - """Unit tests for runpy._run_code and runpy._run_module_code""" - - expected_result = ["Top level assignment", "Lower level reference"] - test_source = ( - "# Check basic code execution\n" - "result = ['Top level assignment']\n" - "def f():\n" - " result.append('Lower level reference')\n" - "f()\n" - "# Check the sys module\n" - "import sys\n" - "run_argv0 = sys.argv[0]\n" - "run_name_in_sys_modules = __name__ in sys.modules\n" - "if run_name_in_sys_modules:\n" - " module_in_sys_modules = globals() is sys.modules[__name__].__dict__\n" - "# Check nested operation\n" - "import runpy\n" - "nested = runpy._run_module_code('x=1\\n', mod_name='')\n" - ) - - def test_run_code(self): - saved_argv0 = sys.argv[0] - d = _run_code(self.test_source, {}) - self.assertEqual(d["result"], self.expected_result) - self.assertIs(d["__name__"], None) - self.assertIs(d["__file__"], None) - self.assertIs(d["__loader__"], None) - self.assertIs(d["__package__"], None) - self.assertIs(d["run_argv0"], saved_argv0) - self.assertNotIn("run_name", d) - self.assertIs(sys.argv[0], saved_argv0) - - def test_run_module_code(self): - initial = object() - name = "" - file = "Some other nonsense" - loader = "Now you're just being silly" - package = '' # Treat as a top level module - d1 = dict(initial=initial) - saved_argv0 = sys.argv[0] - d2 = _run_module_code(self.test_source, - d1, - name, - file, - loader, - package) - self.assertNotIn("result", d1) - self.assertIs(d2["initial"], initial) - self.assertEqual(d2["result"], self.expected_result) - self.assertEqual(d2["nested"]["x"], 1) - self.assertIs(d2["__name__"], name) - self.assertTrue(d2["run_name_in_sys_modules"]) - self.assertTrue(d2["module_in_sys_modules"]) - self.assertIs(d2["__file__"], file) - self.assertIs(d2["run_argv0"], file) - self.assertIs(d2["__loader__"], loader) - self.assertIs(d2["__package__"], package) - self.assertIs(sys.argv[0], saved_argv0) - self.assertNotIn(name, sys.modules) - - -class RunModuleTest(unittest.TestCase): - """Unit tests for runpy.run_module""" - - def expect_import_error(self, mod_name): - try: - run_module(mod_name) - except ImportError: - pass - else: - self.fail("Expected import error for " + mod_name) - - def test_invalid_names(self): - # Builtin module - self.expect_import_error("sys") - # Non-existent modules - self.expect_import_error("sys.imp.eric") - self.expect_import_error("os.path.half") - self.expect_import_error("a.bee") - self.expect_import_error(".howard") - self.expect_import_error("..eaten") - # Package without __main__.py - self.expect_import_error("multiprocessing") - - def test_library_module(self): - run_module("runpy") - - def _add_pkg_dir(self, pkg_dir): - os.mkdir(pkg_dir) - pkg_fname = os.path.join(pkg_dir, "__init__"+os.extsep+"py") - pkg_file = open(pkg_fname, "w") - pkg_file.close() - return pkg_fname - - def _make_pkg(self, source, depth, mod_base="runpy_test"): - pkg_name = "__runpy_pkg__" - test_fname = mod_base+os.extsep+"py" - pkg_dir = sub_dir = tempfile.mkdtemp() - if verbose: print " Package tree in:", sub_dir - sys.path.insert(0, pkg_dir) - if verbose: print " Updated sys.path:", sys.path[0] - for i in range(depth): - sub_dir = os.path.join(sub_dir, pkg_name) - pkg_fname = self._add_pkg_dir(sub_dir) - if verbose: print " Next level in:", sub_dir - if verbose: print " Created:", pkg_fname - mod_fname = os.path.join(sub_dir, test_fname) - mod_file = open(mod_fname, "w") - mod_file.write(source) - mod_file.close() - if verbose: print " Created:", mod_fname - mod_name = (pkg_name+".")*depth + mod_base - return pkg_dir, mod_fname, mod_name - - def _del_pkg(self, top, depth, mod_name): - for entry in list(sys.modules): - if entry.startswith("__runpy_pkg__"): - del sys.modules[entry] - if verbose: print " Removed sys.modules entries" - del sys.path[0] - if verbose: print " Removed sys.path entry" - for root, dirs, files in os.walk(top, topdown=False): - for name in files: - try: - os.remove(os.path.join(root, name)) - except OSError, ex: - if verbose: print ex # Persist with cleaning up - for name in dirs: - fullname = os.path.join(root, name) - try: - os.rmdir(fullname) - except OSError, ex: - if verbose: print ex # Persist with cleaning up - try: - os.rmdir(top) - if verbose: print " Removed package tree" - except OSError, ex: - if verbose: print ex # Persist with cleaning up - - def _check_module(self, depth): - pkg_dir, mod_fname, mod_name = ( - self._make_pkg("x=1\n", depth)) - forget(mod_name) - try: - if verbose: print "Running from source:", mod_name - d1 = run_module(mod_name) # Read from source - self.assertIn("x", d1) - self.assertTrue(d1["x"] == 1) - del d1 # Ensure __loader__ entry doesn't keep file open - __import__(mod_name) - os.remove(mod_fname) - if verbose: print "Running from compiled:", mod_name - d2 = run_module(mod_name) # Read from bytecode - self.assertIn("x", d2) - self.assertTrue(d2["x"] == 1) - del d2 # Ensure __loader__ entry doesn't keep file open - finally: - self._del_pkg(pkg_dir, depth, mod_name) - if verbose: print "Module executed successfully" - - def _check_package(self, depth): - pkg_dir, mod_fname, mod_name = ( - self._make_pkg("x=1\n", depth, "__main__")) - pkg_name, _, _ = mod_name.rpartition(".") - forget(mod_name) - try: - if verbose: print "Running from source:", pkg_name - d1 = run_module(pkg_name) # Read from source - self.assertIn("x", d1) - self.assertTrue(d1["x"] == 1) - del d1 # Ensure __loader__ entry doesn't keep file open - __import__(mod_name) - os.remove(mod_fname) - if verbose: print "Running from compiled:", pkg_name - d2 = run_module(pkg_name) # Read from bytecode - self.assertIn("x", d2) - self.assertTrue(d2["x"] == 1) - del d2 # Ensure __loader__ entry doesn't keep file open - finally: - self._del_pkg(pkg_dir, depth, pkg_name) - if verbose: print "Package executed successfully" - - def _add_relative_modules(self, base_dir, source, depth): - if depth <= 1: - raise ValueError("Relative module test needs depth > 1") - pkg_name = "__runpy_pkg__" - module_dir = base_dir - for i in range(depth): - parent_dir = module_dir - module_dir = os.path.join(module_dir, pkg_name) - # Add sibling module - sibling_fname = os.path.join(module_dir, "sibling"+os.extsep+"py") - sibling_file = open(sibling_fname, "w") - sibling_file.close() - if verbose: print " Added sibling module:", sibling_fname - # Add nephew module - uncle_dir = os.path.join(parent_dir, "uncle") - self._add_pkg_dir(uncle_dir) - if verbose: print " Added uncle package:", uncle_dir - cousin_dir = os.path.join(uncle_dir, "cousin") - self._add_pkg_dir(cousin_dir) - if verbose: print " Added cousin package:", cousin_dir - nephew_fname = os.path.join(cousin_dir, "nephew"+os.extsep+"py") - nephew_file = open(nephew_fname, "w") - nephew_file.close() - if verbose: print " Added nephew module:", nephew_fname - - def _check_relative_imports(self, depth, run_name=None): - contents = r"""\ -from __future__ import absolute_import -from . import sibling -from ..uncle.cousin import nephew -""" - pkg_dir, mod_fname, mod_name = ( - self._make_pkg(contents, depth)) - try: - self._add_relative_modules(pkg_dir, contents, depth) - pkg_name = mod_name.rpartition('.')[0] - if verbose: print "Running from source:", mod_name - d1 = run_module(mod_name, run_name=run_name) # Read from source - self.assertIn("__package__", d1) - self.assertTrue(d1["__package__"] == pkg_name) - self.assertIn("sibling", d1) - self.assertIn("nephew", d1) - del d1 # Ensure __loader__ entry doesn't keep file open - __import__(mod_name) - os.remove(mod_fname) - if verbose: print "Running from compiled:", mod_name - d2 = run_module(mod_name, run_name=run_name) # Read from bytecode - self.assertIn("__package__", d2) - self.assertTrue(d2["__package__"] == pkg_name) - self.assertIn("sibling", d2) - self.assertIn("nephew", d2) - del d2 # Ensure __loader__ entry doesn't keep file open - finally: - self._del_pkg(pkg_dir, depth, mod_name) - if verbose: print "Module executed successfully" - - def test_run_module(self): - for depth in range(4): - if verbose: print "Testing package depth:", depth - self._check_module(depth) - - def test_run_package(self): - for depth in range(1, 4): - if verbose: print "Testing package depth:", depth - self._check_package(depth) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_explicit_relative_import(self): - for depth in range(2, 5): - if verbose: print "Testing relative imports at depth:", depth - self._check_relative_imports(depth) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_main_relative_import(self): - for depth in range(2, 5): - if verbose: print "Testing main relative imports at depth:", depth - self._check_relative_imports(depth, "__main__") - - -class RunPathTest(unittest.TestCase): - """Unit tests for runpy.run_path""" - # Based on corresponding tests in test_cmd_line_script - - test_source = """\ -# Script may be run with optimisation enabled, so don't rely on assert -# statements being executed -def assertEqual(lhs, rhs): - if lhs != rhs: - raise AssertionError('%r != %r' % (lhs, rhs)) -def assertIs(lhs, rhs): - if lhs is not rhs: - raise AssertionError('%r is not %r' % (lhs, rhs)) -# Check basic code execution -result = ['Top level assignment'] -def f(): - result.append('Lower level reference') -f() -assertEqual(result, ['Top level assignment', 'Lower level reference']) -# Check the sys module -import sys -assertIs(globals(), sys.modules[__name__].__dict__) -argv0 = sys.argv[0] -""" - - def _make_test_script(self, script_dir, script_basename, source=None): - if source is None: - source = self.test_source - return make_script(script_dir, script_basename, source) - - def _check_script(self, script_name, expected_name, expected_file, - expected_argv0, expected_package): - result = run_path(script_name) - self.assertEqual(result["__name__"], expected_name) - self.assertEqual(result["__file__"], expected_file) - self.assertIn("argv0", result) - self.assertEqual(result["argv0"], expected_argv0) - self.assertEqual(result["__package__"], expected_package) - - def _check_import_error(self, script_name, msg): - msg = re.escape(msg) - self.assertRaisesRegexp(ImportError, msg, run_path, script_name) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_basic_script(self): - with temp_dir() as script_dir: - mod_name = 'script' - script_name = self._make_test_script(script_dir, mod_name) - self._check_script(script_name, "", script_name, - script_name, None) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_script_compiled(self): - with temp_dir() as script_dir: - mod_name = 'script' - script_name = self._make_test_script(script_dir, mod_name) - compiled_name = compile_script(script_name) - os.remove(script_name) - self._check_script(compiled_name, "", compiled_name, - compiled_name, None) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_directory(self): - with temp_dir() as script_dir: - mod_name = '__main__' - script_name = self._make_test_script(script_dir, mod_name) - self._check_script(script_dir, "", script_name, - script_dir, '') - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_directory_compiled(self): - with temp_dir() as script_dir: - mod_name = '__main__' - script_name = self._make_test_script(script_dir, mod_name) - compiled_name = compile_script(script_name) - os.remove(script_name) - self._check_script(script_dir, "", compiled_name, - script_dir, '') - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_directory_error(self): - with temp_dir() as script_dir: - mod_name = 'not_main' - script_name = self._make_test_script(script_dir, mod_name) - msg = "can't find '__main__' module in %r" % script_dir - self._check_import_error(script_dir, msg) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_zipfile(self): - with temp_dir() as script_dir: - mod_name = '__main__' - script_name = self._make_test_script(script_dir, mod_name) - zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) - self._check_script(zip_name, "", fname, zip_name, '') - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_zipfile_compiled(self): - with temp_dir() as script_dir: - mod_name = '__main__' - script_name = self._make_test_script(script_dir, mod_name) - compiled_name = compile_script(script_name) - zip_name, fname = make_zip_script(script_dir, 'test_zip', compiled_name) - self._check_script(zip_name, "", fname, zip_name, '') - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_zipfile_error(self): - with temp_dir() as script_dir: - mod_name = 'not_main' - script_name = self._make_test_script(script_dir, mod_name) - zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) - msg = "can't find '__main__' module in %r" % zip_name - self._check_import_error(zip_name, msg) - - @unittest.skipIf(is_jython, "FIXME: not working in Jython") - def test_main_recursion_error(self): - with temp_dir() as script_dir, temp_dir() as dummy_dir: - mod_name = '__main__' - source = ("import runpy\n" - "runpy.run_path(%r)\n") % dummy_dir - script_name = self._make_test_script(script_dir, mod_name, source) - zip_name, fname = make_zip_script(script_dir, 'test_zip', script_name) - msg = "recursion depth exceeded" - self.assertRaisesRegexp(RuntimeError, msg, run_path, zip_name) - - - -def test_main(): - run_unittest(RunModuleCodeTest, RunModuleTest, RunPathTest) - -if __name__ == "__main__": - test_main() diff --git a/src/org/python/modules/Setup.java b/src/org/python/modules/Setup.java --- a/src/org/python/modules/Setup.java +++ b/src/org/python/modules/Setup.java @@ -51,7 +51,7 @@ "errno", "exceptions:org.python.core.exceptions", "gc", - "imp", + "_imp:org.python.modules._imp", "itertools:org.python.modules.itertools.itertools", "jarray", "jffi:org.python.modules.jffi.jffi", diff --git a/src/org/python/modules/imp.java b/src/org/python/modules/_imp.java rename from src/org/python/modules/imp.java rename to src/org/python/modules/_imp.java --- a/src/org/python/modules/imp.java +++ b/src/org/python/modules/_imp.java @@ -22,7 +22,7 @@ * be implemented under Jython. */ -public class imp { +public class _imp { public static PyString __doc__ = new PyString( "This module provides the components needed to build your own\n"+ "__import__ function. Undocumented functions are obsolete.\n" diff --git a/src/org/python/modules/zipimport/zipimport.java b/src/org/python/modules/zipimport/zipimport.java --- a/src/org/python/modules/zipimport/zipimport.java +++ b/src/org/python/modules/zipimport/zipimport.java @@ -36,7 +36,8 @@ return new PyException(ZipImportError, message); } - // XXX: Ideally this cache would be per PySystemState + // FIXME this cache should be per PySystemState, but at the very least it should also be weakly referenced! + // FIXME could also do this via a loading cache instead public static PyDictionary _zip_directory_cache = new PyDictionary(); public static void classDictInit(PyObject dict) { diff --git a/src/org/python/modules/zipimport/zipimporter.java b/src/org/python/modules/zipimport/zipimporter.java --- a/src/org/python/modules/zipimport/zipimporter.java +++ b/src/org/python/modules/zipimport/zipimporter.java @@ -235,6 +235,19 @@ return Py.None; } + public PyObject get_filename(String fullname) { + return zipimporter_get_filename(fullname); + } + + @ExposedMethod + final PyObject zipimporter_get_filename(String fullname) { + ModuleCodeData moduleCodeData = getModuleCode(fullname); + if (moduleCodeData != null) { + return new PyString(moduleCodeData.path); + } + return Py.None; + } + /** * Return the source code for the module as a string (using * newline characters for line endings) -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Thu Nov 6 09:57:16 2014 From: jython-checkins at python.org (jim.baker) Date: Thu, 06 Nov 2014 08:57:16 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_isinstance_no_longer_checks?= =?utf-8?q?_sys=2Egetrecursionlimit?= Message-ID: <20141106085711.85212.94502@psf.io> https://hg.python.org/jython/rev/fd59d7a80042 changeset: 7408:fd59d7a80042 user: Jim Baker date: Thu Nov 06 09:57:04 2014 +0100 summary: isinstance no longer checks sys.getrecursionlimit files: Lib/test/test_isinstance.py | 277 ++++++++++++++++++++++++ 1 files changed, 277 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_isinstance.py b/Lib/test/test_isinstance.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_isinstance.py @@ -0,0 +1,277 @@ +# Tests some corner cases with isinstance() and issubclass(). While these +# tests use new style classes and properties, they actually do whitebox +# testing of error conditions uncovered when using extension types. + +import unittest +from test import test_support +import sys + + + +class TestIsInstanceExceptions(unittest.TestCase): + # Test to make sure that an AttributeError when accessing the instance's + # class's bases is masked. This was actually a bug in Python 2.2 and + # 2.2.1 where the exception wasn't caught but it also wasn't being cleared + # (leading to an "undetected error" in the debug build). Set up is, + # isinstance(inst, cls) where: + # + # - inst isn't an InstanceType + # - cls isn't a ClassType, a TypeType, or a TupleType + # - cls has a __bases__ attribute + # - inst has a __class__ attribute + # - inst.__class__ as no __bases__ attribute + # + # Sounds complicated, I know, but this mimics a situation where an + # extension type raises an AttributeError when its __bases__ attribute is + # gotten. In that case, isinstance() should return False. + def test_class_has_no_bases(self): + class I(object): + def getclass(self): + # This must return an object that has no __bases__ attribute + return None + __class__ = property(getclass) + + class C(object): + def getbases(self): + return () + __bases__ = property(getbases) + + self.assertEqual(False, isinstance(I(), C())) + + # Like above except that inst.__class__.__bases__ raises an exception + # other than AttributeError + def test_bases_raises_other_than_attribute_error(self): + class E(object): + def getbases(self): + raise RuntimeError + __bases__ = property(getbases) + + class I(object): + def getclass(self): + return E() + __class__ = property(getclass) + + class C(object): + def getbases(self): + return () + __bases__ = property(getbases) + + self.assertRaises(RuntimeError, isinstance, I(), C()) + + # Here's a situation where getattr(cls, '__bases__') raises an exception. + # If that exception is not AttributeError, it should not get masked + def test_dont_mask_non_attribute_error(self): + class I: pass + + class C(object): + def getbases(self): + raise RuntimeError + __bases__ = property(getbases) + + self.assertRaises(RuntimeError, isinstance, I(), C()) + + # Like above, except that getattr(cls, '__bases__') raises an + # AttributeError, which /should/ get masked as a TypeError + def test_mask_attribute_error(self): + class I: pass + + class C(object): + def getbases(self): + raise AttributeError + __bases__ = property(getbases) + + self.assertRaises(TypeError, isinstance, I(), C()) + + + +# These tests are similar to above, but tickle certain code paths in +# issubclass() instead of isinstance() -- really PyObject_IsSubclass() +# vs. PyObject_IsInstance(). +class TestIsSubclassExceptions(unittest.TestCase): + def test_dont_mask_non_attribute_error(self): + class C(object): + def getbases(self): + raise RuntimeError + __bases__ = property(getbases) + + class S(C): pass + + self.assertRaises(RuntimeError, issubclass, C(), S()) + + def test_mask_attribute_error(self): + class C(object): + def getbases(self): + raise AttributeError + __bases__ = property(getbases) + + class S(C): pass + + self.assertRaises(TypeError, issubclass, C(), S()) + + # Like above, but test the second branch, where the __bases__ of the + # second arg (the cls arg) is tested. This means the first arg must + # return a valid __bases__, and it's okay for it to be a normal -- + # unrelated by inheritance -- class. + def test_dont_mask_non_attribute_error_in_cls_arg(self): + class B: pass + + class C(object): + def getbases(self): + raise RuntimeError + __bases__ = property(getbases) + + self.assertRaises(RuntimeError, issubclass, B, C()) + + def test_mask_attribute_error_in_cls_arg(self): + class B: pass + + class C(object): + def getbases(self): + raise AttributeError + __bases__ = property(getbases) + + self.assertRaises(TypeError, issubclass, B, C()) + + + +# meta classes for creating abstract classes and instances +class AbstractClass(object): + def __init__(self, bases): + self.bases = bases + + def getbases(self): + return self.bases + __bases__ = property(getbases) + + def __call__(self): + return AbstractInstance(self) + +class AbstractInstance(object): + def __init__(self, klass): + self.klass = klass + + def getclass(self): + return self.klass + __class__ = property(getclass) + +# abstract classes +AbstractSuper = AbstractClass(bases=()) + +AbstractChild = AbstractClass(bases=(AbstractSuper,)) + +# normal classes +class Super: + pass + +class Child(Super): + pass + +# new-style classes +class NewSuper(object): + pass + +class NewChild(NewSuper): + pass + + + +class TestIsInstanceIsSubclass(unittest.TestCase): + # Tests to ensure that isinstance and issubclass work on abstract + # classes and instances. Before the 2.2 release, TypeErrors were + # raised when boolean values should have been returned. The bug was + # triggered by mixing 'normal' classes and instances were with + # 'abstract' classes and instances. This case tries to test all + # combinations. + + def test_isinstance_normal(self): + # normal instances + self.assertEqual(True, isinstance(Super(), Super)) + self.assertEqual(False, isinstance(Super(), Child)) + self.assertEqual(False, isinstance(Super(), AbstractSuper)) + self.assertEqual(False, isinstance(Super(), AbstractChild)) + + self.assertEqual(True, isinstance(Child(), Super)) + self.assertEqual(False, isinstance(Child(), AbstractSuper)) + + def test_isinstance_abstract(self): + # abstract instances + self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper)) + self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild)) + self.assertEqual(False, isinstance(AbstractSuper(), Super)) + self.assertEqual(False, isinstance(AbstractSuper(), Child)) + + self.assertEqual(True, isinstance(AbstractChild(), AbstractChild)) + self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper)) + self.assertEqual(False, isinstance(AbstractChild(), Super)) + self.assertEqual(False, isinstance(AbstractChild(), Child)) + + def test_subclass_normal(self): + # normal classes + self.assertEqual(True, issubclass(Super, Super)) + self.assertEqual(False, issubclass(Super, AbstractSuper)) + self.assertEqual(False, issubclass(Super, Child)) + + self.assertEqual(True, issubclass(Child, Child)) + self.assertEqual(True, issubclass(Child, Super)) + self.assertEqual(False, issubclass(Child, AbstractSuper)) + + def test_subclass_abstract(self): + # abstract classes + self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper)) + self.assertEqual(False, issubclass(AbstractSuper, AbstractChild)) + self.assertEqual(False, issubclass(AbstractSuper, Child)) + + self.assertEqual(True, issubclass(AbstractChild, AbstractChild)) + self.assertEqual(True, issubclass(AbstractChild, AbstractSuper)) + self.assertEqual(False, issubclass(AbstractChild, Super)) + self.assertEqual(False, issubclass(AbstractChild, Child)) + + def test_subclass_tuple(self): + # test with a tuple as the second argument classes + self.assertEqual(True, issubclass(Child, (Child,))) + self.assertEqual(True, issubclass(Child, (Super,))) + self.assertEqual(False, issubclass(Super, (Child,))) + self.assertEqual(True, issubclass(Super, (Child, Super))) + self.assertEqual(False, issubclass(Child, ())) + self.assertEqual(True, issubclass(Super, (Child, (Super,)))) + + self.assertEqual(True, issubclass(NewChild, (NewChild,))) + self.assertEqual(True, issubclass(NewChild, (NewSuper,))) + self.assertEqual(False, issubclass(NewSuper, (NewChild,))) + self.assertEqual(True, issubclass(NewSuper, (NewChild, NewSuper))) + self.assertEqual(False, issubclass(NewChild, ())) + self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,)))) + + self.assertEqual(True, issubclass(int, (long, (float, int)))) + if test_support.have_unicode: + self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring)))) + + def test_subclass_recursion_limit(self): + # make sure that issubclass raises RuntimeError before the C stack is + # blown + self.assertRaises(RuntimeError, blowstack, issubclass, str, str) + + def test_isinstance_recursion_limit(self): + # make sure that issubclass raises RuntimeError before the C stack is + # blown + self.assertRaises(RuntimeError, blowstack, isinstance, '', str) + +def blowstack(fxn, arg, compare_to): + # Make sure that calling isinstance with a deeply nested tuple for its + # argument will raise RuntimeError eventually. + tuple_arg = (compare_to,) + while True: + tuple_arg = (tuple_arg,) + fxn(arg, tuple_arg) + + +def test_main(): + test_support.run_unittest( + TestIsInstanceExceptions, + TestIsSubclassExceptions, + TestIsInstanceIsSubclass + ) + + +if __name__ == '__main__': + test_main() -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Thu Nov 6 11:05:18 2014 From: jython-checkins at python.org (jim.baker) Date: Thu, 06 Nov 2014 10:05:18 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_imp_now_in_Python=2C_wrappi?= =?utf-8?q?ng_=5Fimp?= Message-ID: <20141106100455.108379.21800@psf.io> https://hg.python.org/jython/rev/923157581df6 changeset: 7409:923157581df6 user: Jim Baker date: Thu Nov 06 11:04:50 2014 +0100 summary: imp now in Python, wrapping _imp files: Lib/imp.py | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/Lib/imp.py b/Lib/imp.py new file mode 100644 --- /dev/null +++ b/Lib/imp.py @@ -0,0 +1,18 @@ +import os.path + +from _imp import (C_BUILTIN, C_EXTENSION, IMP_HOOK, PKG_DIRECTORY, PY_COMPILED, PY_FROZEN, PY_SOURCE, + __doc__, acquire_lock, find_module, getClass, get_magic, get_suffixes, + is_builtin, is_frozen, + load_compiled, load_dynamic, load_module, load_source, + lock_held, new_module, release_lock, reload) + + +class NullImporter(object): + + def __init__(self, path): + if os.path.isdir(path): + raise ImportError() + + def find_module(self, fullname, path=None): + return None + -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Thu Nov 6 11:06:48 2014 From: jython-checkins at python.org (jim.baker) Date: Thu, 06 Nov 2014 10:06:48 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Allow_one_*bit*_of_precisio?= =?utf-8?q?n_difference_in_strtod?= Message-ID: <20141106100631.719.22253@psf.io> https://hg.python.org/jython/rev/629c75459e0f changeset: 7410:629c75459e0f user: Jim Baker date: Thu Nov 06 11:06:26 2014 +0100 summary: Allow one *bit* of precision difference in strtod files: Lib/test/test_strtod.py | 420 ++++++++++++++++++++++++++++ 1 files changed, 420 insertions(+), 0 deletions(-) diff --git a/Lib/test/test_strtod.py b/Lib/test/test_strtod.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_strtod.py @@ -0,0 +1,420 @@ +# Tests for the correctly-rounded string -> float conversions +# introduced in Python 2.7 and 3.1. + +import random +import struct +import unittest +import re +import sys +from test import test_support + +if getattr(sys, 'float_repr_style', '') != 'short': + raise unittest.SkipTest('correctly-rounded string->float conversions ' + 'not available on this system') + +# Correctly rounded str -> float in pure Python, for comparison. + +strtod_parser = re.compile(r""" # A numeric string consists of: + (?P[-+])? # an optional sign, followed by + (?=\d|\.\d) # a number with at least one digit + (?P\d*) # having a (possibly empty) integer part + (?:\.(?P\d*))? # followed by an optional fractional part + (?:E(?P[-+]?\d+))? # and an optional exponent + \Z +""", re.VERBOSE | re.IGNORECASE).match + +# Pure Python version of correctly rounded string->float conversion. +# Avoids any use of floating-point by returning the result as a hex string. +def strtod(s, mant_dig=53, min_exp = -1021, max_exp = 1024): + """Convert a finite decimal string to a hex string representing an + IEEE 754 binary64 float. Return 'inf' or '-inf' on overflow. + This function makes no use of floating-point arithmetic at any + stage.""" + + # parse string into a pair of integers 'a' and 'b' such that + # abs(decimal value) = a/b, along with a boolean 'negative'. + m = strtod_parser(s) + if m is None: + raise ValueError('invalid numeric string') + fraction = m.group('frac') or '' + intpart = int(m.group('int') + fraction) + exp = int(m.group('exp') or '0') - len(fraction) + negative = m.group('sign') == '-' + a, b = intpart*10**max(exp, 0), 10**max(0, -exp) + + # quick return for zeros + if not a: + return '-0x0.0p+0' if negative else '0x0.0p+0' + + # compute exponent e for result; may be one too small in the case + # that the rounded value of a/b lies in a different binade from a/b + d = a.bit_length() - b.bit_length() + d += (a >> d if d >= 0 else a << -d) >= b + e = max(d, min_exp) - mant_dig + + # approximate a/b by number of the form q * 2**e; adjust e if necessary + a, b = a << max(-e, 0), b << max(e, 0) + q, r = divmod(a, b) + if 2*r > b or 2*r == b and q & 1: + q += 1 + if q.bit_length() == mant_dig+1: + q //= 2 + e += 1 + + # double check that (q, e) has the right form + assert q.bit_length() <= mant_dig and e >= min_exp - mant_dig + assert q.bit_length() == mant_dig or e == min_exp - mant_dig + + # check for overflow and underflow + if e + q.bit_length() > max_exp: + return '-inf' if negative else 'inf' + if not q: + return '-0x0.0p+0' if negative else '0x0.0p+0' + + # for hex representation, shift so # bits after point is a multiple of 4 + hexdigs = 1 + (mant_dig-2)//4 + shift = 3 - (mant_dig-2)%4 + q, e = q << shift, e - shift + return '{}0x{:x}.{:0{}x}p{:+d}'.format( + '-' if negative else '', + q // 16**hexdigs, + q % 16**hexdigs, + hexdigs, + e + 4*hexdigs) + +TEST_SIZE = 10 + +class StrtodTests(unittest.TestCase): + + def assertApproxHexEqual(self, a, b): + # Jython specific - we allow for one bit difference due to what + # the underlying JVM provides. Revisit if we ever implement Apache + # Commons Math. + # Example: expected 0x1.e2bb05c458496p-1022, got 0x1.e2bb05c458495p-1022 + am, ae = a.split('p') + bm, be = b.split('p') + self.assertEqual(ae, be) + am = int(am.replace('.', ''), 16) + bm = int(bm.replace('.', ''), 16) + self.assertTrue(abs(am - bm) <= 1, "Expected %s, got %s with more than one bit precision loss" % (a, b)) + + def check_strtod(self, s, approx=False): + """Compare the result of Python's builtin correctly rounded + string->float conversion (using float) to a pure Python + correctly rounded string->float implementation. Fail if the + two methods give different results.""" + + try: + fs = float(s) + except OverflowError: + got = '-inf' if s[0] == '-' else 'inf' + except MemoryError: + got = 'memory error' + else: + got = fs.hex() + expected = strtod(s) + if approx: + self.assertApproxHexEqual(expected, got) + else: + self.assertEqual(expected, got, + "Incorrectly rounded str->float conversion for {}: " + "expected {}, got {}".format(s, expected, got)) + + def test_short_halfway_cases(self): + # exact halfway cases with a small number of significant digits + for k in 0, 5, 10, 15, 20: + # upper = smallest integer >= 2**54/5**k + upper = -(-2**54//5**k) + # lower = smallest odd number >= 2**53/5**k + lower = -(-2**53//5**k) + if lower % 2 == 0: + lower += 1 + for i in xrange(TEST_SIZE): + # Select a random odd n in [2**53/5**k, + # 2**54/5**k). Then n * 10**k gives a halfway case + # with small number of significant digits. + n, e = random.randrange(lower, upper, 2), k + + # Remove any additional powers of 5. + while n % 5 == 0: + n, e = n // 5, e + 1 + assert n % 10 in (1, 3, 7, 9) + + # Try numbers of the form n * 2**p2 * 10**e, p2 >= 0, + # until n * 2**p2 has more than 20 significant digits. + digits, exponent = n, e + while digits < 10**20: + s = '{}e{}'.format(digits, exponent) + self.check_strtod(s) + # Same again, but with extra trailing zeros. + s = '{}e{}'.format(digits * 10**40, exponent - 40) + self.check_strtod(s) + digits *= 2 + + # Try numbers of the form n * 5**p2 * 10**(e - p5), p5 + # >= 0, with n * 5**p5 < 10**20. + digits, exponent = n, e + while digits < 10**20: + s = '{}e{}'.format(digits, exponent) + self.check_strtod(s) + # Same again, but with extra trailing zeros. + s = '{}e{}'.format(digits * 10**40, exponent - 40) + self.check_strtod(s) + digits *= 5 + exponent -= 1 + + def test_halfway_cases(self): + # test halfway cases for the round-half-to-even rule + for i in xrange(100 * TEST_SIZE): + # bit pattern for a random finite positive (or +0.0) float + bits = random.randrange(2047*2**52) + + # convert bit pattern to a number of the form m * 2**e + e, m = divmod(bits, 2**52) + if e: + m, e = m + 2**52, e - 1 + e -= 1074 + + # add 0.5 ulps + m, e = 2*m + 1, e - 1 + + # convert to a decimal string + if e >= 0: + digits = m << e + exponent = 0 + else: + # m * 2**e = (m * 5**-e) * 10**e + digits = m * 5**-e + exponent = e + s = '{}e{}'.format(digits, exponent) + self.check_strtod(s, approx=True) + + def test_boundaries(self): + # boundaries expressed as triples (n, e, u), where + # n*10**e is an approximation to the boundary value and + # u*10**e is 1ulp + boundaries = [ + (10000000000000000000, -19, 1110), # a power of 2 boundary (1.0) + (17976931348623159077, 289, 1995), # overflow boundary (2.**1024) + (22250738585072013831, -327, 4941), # normal/subnormal (2.**-1022) + (0, -327, 4941), # zero + ] + for n, e, u in boundaries: + for j in xrange(1000): + digits = n + random.randrange(-3*u, 3*u) + exponent = e + s = '{}e{}'.format(digits, exponent) + self.check_strtod(s) + n *= 10 + u *= 10 + e -= 1 + + def test_underflow_boundary(self): + # test values close to 2**-1075, the underflow boundary; similar + # to boundary_tests, except that the random error doesn't scale + # with n + for exponent in xrange(-400, -320): + base = 10**-exponent // 2**1075 + for j in xrange(TEST_SIZE): + digits = base + random.randrange(-1000, 1000) + s = '{}e{}'.format(digits, exponent) + self.check_strtod(s) + + def test_bigcomp(self): + for ndigs in 5, 10, 14, 15, 16, 17, 18, 19, 20, 40, 41, 50: + dig10 = 10**ndigs + for i in xrange(10 * TEST_SIZE): + digits = random.randrange(dig10) + exponent = random.randrange(-400, 400) + s = '{}e{}'.format(digits, exponent) + self.check_strtod(s) + + def test_parsing(self): + # make '0' more likely to be chosen than other digits + digits = '000000123456789' + signs = ('+', '-', '') + + # put together random short valid strings + # \d*[.\d*]?e + for i in xrange(1000): + for j in xrange(TEST_SIZE): + s = random.choice(signs) + intpart_len = random.randrange(5) + s += ''.join(random.choice(digits) for _ in xrange(intpart_len)) + if random.choice([True, False]): + s += '.' + fracpart_len = random.randrange(5) + s += ''.join(random.choice(digits) + for _ in xrange(fracpart_len)) + else: + fracpart_len = 0 + if random.choice([True, False]): + s += random.choice(['e', 'E']) + s += random.choice(signs) + exponent_len = random.randrange(1, 4) + s += ''.join(random.choice(digits) + for _ in xrange(exponent_len)) + + if intpart_len + fracpart_len: + self.check_strtod(s) + else: + try: + float(s) + except ValueError: + pass + else: + assert False, "expected ValueError" + + def test_particular(self): + # inputs that produced crashes or incorrectly rounded results with + # previous versions of dtoa.c, for various reasons + test_strings = [ + # issue 7632 bug 1, originally reported failing case + '2183167012312112312312.23538020374420446192e-370', + # 5 instances of issue 7632 bug 2 + '12579816049008305546974391768996369464963024663104e-357', + '17489628565202117263145367596028389348922981857013e-357', + '18487398785991994634182916638542680759613590482273e-357', + '32002864200581033134358724675198044527469366773928e-358', + '94393431193180696942841837085033647913224148539854e-358', + '73608278998966969345824653500136787876436005957953e-358', + '64774478836417299491718435234611299336288082136054e-358', + '13704940134126574534878641876947980878824688451169e-357', + '46697445774047060960624497964425416610480524760471e-358', + # failing case for bug introduced by METD in r77451 (attempted + # fix for issue 7632, bug 2), and fixed in r77482. + '28639097178261763178489759107321392745108491825303e-311', + # two numbers demonstrating a flaw in the bigcomp 'dig == 0' + # correction block (issue 7632, bug 3) + '1.00000000000000001e44', + '1.0000000000000000100000000000000000000001e44', + # dtoa.c bug for numbers just smaller than a power of 2 (issue + # 7632, bug 4) + '99999999999999994487665465554760717039532578546e-47', + # failing case for off-by-one error introduced by METD in + # r77483 (dtoa.c cleanup), fixed in r77490 + '965437176333654931799035513671997118345570045914469' #... + '6213413350821416312194420007991306908470147322020121018368e0', + # incorrect lsb detection for round-half-to-even when + # bc->scale != 0 (issue 7632, bug 6). + + # Java does not correctly handle, so we don't either + # '104308485241983990666713401708072175773165034278685' #... + # '682646111762292409330928739751702404658197872319129' #... + # '036519947435319418387839758990478549477777586673075' #... + # '945844895981012024387992135617064532141489278815239' #... + # '849108105951619997829153633535314849999674266169258' #... + # '928940692239684771590065027025835804863585454872499' #... + # '320500023126142553932654370362024104462255244034053' #... + # '203998964360882487378334860197725139151265590832887' #... + # '433736189468858614521708567646743455601905935595381' #... + # '852723723645799866672558576993978025033590728687206' #... + # '296379801363024094048327273913079612469982585674824' #... + # '156000783167963081616214710691759864332339239688734' #... + # '656548790656486646106983450809073750535624894296242' #... + # '072010195710276073042036425579852459556183541199012' #... + # '652571123898996574563824424330960027873516082763671875e-1075', + + # demonstration that original fix for issue 7632 bug 1 was + # buggy; the exit condition was too strong + '247032822920623295e-341', + # demonstrate similar problem to issue 7632 bug1: crash + # with 'oversized quotient in quorem' message. + '99037485700245683102805043437346965248029601286431e-373', + '99617639833743863161109961162881027406769510558457e-373', + '98852915025769345295749278351563179840130565591462e-372', + '99059944827693569659153042769690930905148015876788e-373', + '98914979205069368270421829889078356254059760327101e-372', + # issue 7632 bug 5: the following 2 strings convert differently + '1000000000000000000000000000000000000000e-16', + '10000000000000000000000000000000000000000e-17', + # issue 7632 bug 7 + '991633793189150720000000000000000000000000000000000000000e-33', + # And another, similar, failing halfway case + '4106250198039490000000000000000000000000000000000000000e-38', + # issue 7632 bug 8: the following produced 10.0 + '10.900000000000000012345678912345678912345', + + # two humongous values from issue 7743 + + # Java does not correctly handle, so we don't either + # '116512874940594195638617907092569881519034793229385' #... + # '228569165191541890846564669771714896916084883987920' #... + # '473321268100296857636200926065340769682863349205363' #... + # '349247637660671783209907949273683040397979984107806' #... + # '461822693332712828397617946036239581632976585100633' #... + # '520260770761060725403904123144384571612073732754774' #... + # '588211944406465572591022081973828448927338602556287' #... + # '851831745419397433012491884869454462440536895047499' #... + # '436551974649731917170099387762871020403582994193439' #... + # '761933412166821484015883631622539314203799034497982' #... + # '130038741741727907429575673302461380386596501187482' #... + # '006257527709842179336488381672818798450229339123527' #... + # '858844448336815912020452294624916993546388956561522' #... + # '161875352572590420823607478788399460162228308693742' #... + # '05287663441403533948204085390898399055004119873046875e-1075', + + '525440653352955266109661060358202819561258984964913' #... + '892256527849758956045218257059713765874251436193619' #... + '443248205998870001633865657517447355992225852945912' #... + '016668660000210283807209850662224417504752264995360' #... + '631512007753855801075373057632157738752800840302596' #... + '237050247910530538250008682272783660778181628040733' #... + '653121492436408812668023478001208529190359254322340' #... + '397575185248844788515410722958784640926528544043090' #... + '115352513640884988017342469275006999104519620946430' #... + '818767147966495485406577703972687838176778993472989' #... + '561959000047036638938396333146685137903018376496408' #... + '319705333868476925297317136513970189073693314710318' #... + '991252811050501448326875232850600451776091303043715' #... + '157191292827614046876950225714743118291034780466325' #... + '085141343734564915193426994587206432697337118211527' #... + '278968731294639353354774788602467795167875117481660' #... + '4738791256853675690543663283782215866825e-1180', + + # exercise exit conditions in bigcomp comparison loop + '2602129298404963083833853479113577253105939995688e2', + '260212929840496308383385347911357725310593999568896e0', + '26021292984049630838338534791135772531059399956889601e-2', + '260212929840496308383385347911357725310593999568895e0', + '260212929840496308383385347911357725310593999568897e0', + '260212929840496308383385347911357725310593999568996e0', + '260212929840496308383385347911357725310593999568866e0', + # 2**53 + '9007199254740992.00', + # 2**1024 - 2**970: exact overflow boundary. All values + # smaller than this should round to something finite; any value + # greater than or equal to this one overflows. + '179769313486231580793728971405303415079934132710037' #... + '826936173778980444968292764750946649017977587207096' #... + '330286416692887910946555547851940402630657488671505' #... + '820681908902000708383676273854845817711531764475730' #... + '270069855571366959622842914819860834936475292719074' #... + '168444365510704342711559699508093042880177904174497792', + # 2**1024 - 2**970 - tiny + '179769313486231580793728971405303415079934132710037' #... + '826936173778980444968292764750946649017977587207096' #... + '330286416692887910946555547851940402630657488671505' #... + '820681908902000708383676273854845817711531764475730' #... + '270069855571366959622842914819860834936475292719074' #... + '168444365510704342711559699508093042880177904174497791.999', + # 2**1024 - 2**970 + tiny + '179769313486231580793728971405303415079934132710037' #... + '826936173778980444968292764750946649017977587207096' #... + '330286416692887910946555547851940402630657488671505' #... + '820681908902000708383676273854845817711531764475730' #... + '270069855571366959622842914819860834936475292719074' #... + '168444365510704342711559699508093042880177904174497792.001', + # 1 - 2**-54, +-tiny + '999999999999999944488848768742172978818416595458984375e-54', + '9999999999999999444888487687421729788184165954589843749999999e-54', + '9999999999999999444888487687421729788184165954589843750000001e-54', + ] + for s in test_strings: + self.check_strtod(s) + +def test_main(): + test_support.run_unittest(StrtodTests) + +if __name__ == "__main__": + test_main() -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Thu Nov 6 23:37:04 2014 From: jython-checkins at python.org (jim.baker) Date: Thu, 06 Nov 2014 22:37:04 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_pkgutil=2Eread=5Fcode_needs?= =?utf-8?q?_to_dispatch_to_read=5Fjython=5Fcode?= Message-ID: <20141106223701.85198.13159@psf.io> https://hg.python.org/jython/rev/301e2a08b0c9 changeset: 7411:301e2a08b0c9 user: Jim Baker date: Thu Nov 06 23:36:56 2014 +0100 summary: pkgutil.read_code needs to dispatch to read_jython_code files: Lib/pkgutil.py | 15 +++++++++++---- 1 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Lib/pkgutil.py b/Lib/pkgutil.py --- a/Lib/pkgutil.py +++ b/Lib/pkgutil.py @@ -8,6 +8,7 @@ import imp import os.path from types import ModuleType +from java.lang import IllegalArgumentException from org.python.core import imp as _imp, BytecodeLoader __all__ = [ @@ -21,11 +22,17 @@ # diff args to pass into our underlying imp implementation, as # accessed by _imp here -def read_jython_code(fullname, file, filename): - data = _imp.readCode(filename, file, False) - return BytecodeLoader.makeCode(fullname + "$py", data, filename) +def read_jython_code(fullname, stream, filename): + try: + data = _imp.readCode(filename, stream, False) + return BytecodeLoader.makeCode(fullname + "$py", data, filename) + except IllegalArgumentException: + return None -read_code = read_jython_code +def read_code(stream): + filename = stream.name + fullname = os.path.splitext(os.path.split(filename)[1])[0] + return read_jython_code(fullname, stream, filename) def simplegeneric(func): """Make a trivial single-dispatch generic function""" -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sun Nov 9 19:02:16 2014 From: jython-checkins at python.org (alan.kennedy) Date: Sun, 09 Nov 2014 18:02:16 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixing_an_issue_relating_to?= =?utf-8?q?_the_site_module?= Message-ID: <20141109180214.113484.16271@psf.io> https://hg.python.org/jython/rev/fcd4dfc05813 changeset: 7412:fcd4dfc05813 user: Alan Kennedy date: Sun Nov 09 18:01:46 2014 +0000 summary: Fixing an issue relating to the site module files: NEWS | 1 + src/com/xhaus/modjy/ModjyJServlet.java | 41 ++++++++----- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -2,6 +2,7 @@ Jython 2.7b3 Bugs Fixed + - [ 2225 ] Jython+django-jython - no module named site - [ 2108 ] Cannot set attribute to instances of AST/PythonTree (blocks pyflakes) - [ 1497 ] ast classes do not have appropiate base classes - [ 1980 ] ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq and ast.NotIn should be subclasses of ast.cmpop diff --git a/src/com/xhaus/modjy/ModjyJServlet.java b/src/com/xhaus/modjy/ModjyJServlet.java --- a/src/com/xhaus/modjy/ModjyJServlet.java +++ b/src/com/xhaus/modjy/ModjyJServlet.java @@ -33,7 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.python.core.imp; +import org.python.core.Options; import org.python.core.Py; import org.python.core.PyException; import org.python.core.PyObject; @@ -103,6 +103,9 @@ public void init() throws ServletException { try { Properties props = readConfiguration(); + // We check for site packages settings before initialising the runtime + // https://hg.python.org/jython/rev/51b28cc2c43d + checkSitePackages(props); PythonInterpreter.initialize(System.getProperties(), props, new String[0]); PySystemState systemState = new PySystemState(); interp = new PythonInterpreter(null, systemState); @@ -149,8 +152,9 @@ } /** - * Setup the modjy environment, i.e. 1. Find the location of the modjy.jar file and add it to - * sys.path 2. Process the WEB-INF/lib-python directory, if it exists + * Setup the modjy environment, i.e. + * 1. Find the location of the modjy.jar file and add it to sys.path + * 2. Process the WEB-INF/lib-python directory, if it exists * * @param interp * - The PythonInterpreter used to service requests @@ -163,22 +167,22 @@ Properties props, PySystemState systemState) throws PyException { processPythonLib(interp, systemState); - checkSitePackages(props); } /** * Check if the user has requested to initialise the jython installation "site-packages". + * The value defaults to true, i.e. load site packages * * @param props * - The properties from which config options are found */ protected void checkSitePackages(Properties props) throws PyException { + boolean loadSitePackages = true; String loadSitePackagesParam = props.getProperty(LOAD_SITE_PACKAGES_PARAM); - boolean loadSitePackages = true; - if (loadSitePackagesParam != null && loadSitePackagesParam.trim().compareTo("0") == 0) + if (loadSitePackagesParam != null && loadSitePackagesParam.trim().compareTo("0") == 0) { loadSitePackages = false; - if (loadSitePackages) - imp.load("site"); + } + Options.importSite = loadSitePackages; } /** @@ -192,17 +196,21 @@ protected void processPythonLib(PythonInterpreter interp, PySystemState systemState) { // Add the lib-python directory to sys.path String pythonLibPath = getServletContext().getRealPath(LIB_PYTHON); - if (pythonLibPath == null) + if (pythonLibPath == null) { return; + } File pythonLib = new File(pythonLibPath); - if (!pythonLib.exists()) + if (!pythonLib.exists()) { return; + } systemState.path.append(new PyString(pythonLibPath)); // Now check for .pth files in lib-python and process each one String[] libPythonContents = pythonLib.list(); - for (String libPythonContent : libPythonContents) - if (libPythonContent.endsWith(PTH_FILE_EXTENSION)) + for (String libPythonContent : libPythonContents) { + if (libPythonContent.endsWith(PTH_FILE_EXTENSION)) { processPthFile(interp, systemState, pythonLibPath, libPythonContent); + } + } } /** @@ -227,12 +235,13 @@ String line; while ((line = lineReader.readLine()) != null) { line = line.trim(); - if (line.length() == 0) + if (line.length() == 0) { continue; - if (line.startsWith("#")) + } + if (line.startsWith("#")) { continue; - if (line.startsWith("import")) - { + } + if (line.startsWith("import")) { interp.exec(line); continue; } -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Tue Nov 18 00:41:05 2014 From: jython-checkins at python.org (jim.baker) Date: Mon, 17 Nov 2014 23:41:05 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Support_pickling_of_xrange_?= =?utf-8?q?and_ensure_limits_match_CPython?= Message-ID: <20141117234103.13351.55937@psf.io> https://hg.python.org/jython/rev/ac8baa059974 changeset: 7413:ac8baa059974 user: Jim Baker date: Mon Nov 17 16:40:45 2014 -0700 summary: Support pickling of xrange and ensure limits match CPython files: Lib/test/test_xrange.py | 146 ------------------ src/org/python/core/PyXRange.java | 52 ++++-- 2 files changed, 33 insertions(+), 165 deletions(-) diff --git a/Lib/test/test_xrange.py b/Lib/test/test_xrange.py deleted file mode 100644 --- a/Lib/test/test_xrange.py +++ /dev/null @@ -1,146 +0,0 @@ -# Python test set -- built-in functions - -import test.test_support, unittest -from test.test_support import is_jython -import sys -import pickle -import itertools - -import warnings -warnings.filterwarnings("ignore", "integer argument expected", - DeprecationWarning, "unittest") - -# pure Python implementations (3 args only), for comparison -def pyrange(start, stop, step): - if (start - stop) // step < 0: - # replace stop with next element in the sequence of integers - # that are congruent to start modulo step. - stop += (start - stop) % step - while start != stop: - yield start - start += step - -def pyrange_reversed(start, stop, step): - stop += (start - stop) % step - return pyrange(stop - step, start - step, -step) - - -class XrangeTest(unittest.TestCase): - def assert_iterators_equal(self, xs, ys, test_id, limit=None): - # check that an iterator xs matches the expected results ys, - # up to a given limit. - if limit is not None: - xs = itertools.islice(xs, limit) - ys = itertools.islice(ys, limit) - sentinel = object() - pairs = itertools.izip_longest(xs, ys, fillvalue=sentinel) - for i, (x, y) in enumerate(pairs): - if x == y: - continue - elif x == sentinel: - self.fail('{}: iterator ended unexpectedly ' - 'at position {}; expected {}'.format(test_id, i, y)) - elif y == sentinel: - self.fail('{}: unexpected excess element {} at ' - 'position {}'.format(test_id, x, i)) - else: - self.fail('{}: wrong element at position {};' - 'expected {}, got {}'.format(test_id, i, y, x)) - - def test_xrange(self): - self.assertEqual(list(xrange(3)), [0, 1, 2]) - self.assertEqual(list(xrange(1, 5)), [1, 2, 3, 4]) - self.assertEqual(list(xrange(0)), []) - self.assertEqual(list(xrange(-3)), []) - self.assertEqual(list(xrange(1, 10, 3)), [1, 4, 7]) - self.assertEqual(list(xrange(5, -5, -3)), [5, 2, -1, -4]) - - a = 10 - b = 100 - c = 50 - - self.assertEqual(list(xrange(a, a+2)), [a, a+1]) - self.assertEqual(list(xrange(a+2, a, -1L)), [a+2, a+1]) - self.assertEqual(list(xrange(a+4, a, -2)), [a+4, a+2]) - - seq = list(xrange(a, b, c)) - self.assertIn(a, seq) - self.assertNotIn(b, seq) - self.assertEqual(len(seq), 2) - - seq = list(xrange(b, a, -c)) - self.assertIn(b, seq) - self.assertNotIn(a, seq) - self.assertEqual(len(seq), 2) - - seq = list(xrange(-a, -b, -c)) - self.assertIn(-a, seq) - self.assertNotIn(-b, seq) - self.assertEqual(len(seq), 2) - - self.assertRaises(TypeError, xrange) - self.assertRaises(TypeError, xrange, 1, 2, 3, 4) - self.assertRaises(ValueError, xrange, 1, 2, 0) - - self.assertRaises(OverflowError, xrange, 10**100, 10**101, 10**101) - - self.assertRaises(TypeError, xrange, 0, "spam") - self.assertRaises(TypeError, xrange, 0, 42, "spam") - - self.assertEqual(len(xrange(0, sys.maxint, sys.maxint-1)), 2) - - self.assertRaises(OverflowError, xrange, -sys.maxint, sys.maxint) - self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint) - - r = xrange(-sys.maxint, sys.maxint, 2) - self.assertEqual(len(r), sys.maxint) - self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2) - - @unittest.skipIf(is_jython, "FIXME: pickling range not working in Jython") - def test_pickling(self): - testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), - (13, 21, 3), (-2, 2, 2)] - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - for t in testcases: - r = xrange(*t) - self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))), - list(r)) - - @unittest.skipIf(is_jython, "FIXME: range iter not working in Jython") - def test_range_iterators(self): - # see issue 7298 - limits = [base + jiggle - for M in (2**32, 2**64) - for base in (-M, -M//2, 0, M//2, M) - for jiggle in (-2, -1, 0, 1, 2)] - test_ranges = [(start, end, step) - for start in limits - for end in limits - for step in (-2**63, -2**31, -2, -1, 1, 2)] - - for start, end, step in test_ranges: - try: - iter1 = xrange(start, end, step) - except OverflowError: - pass - else: - iter2 = pyrange(start, end, step) - test_id = "xrange({}, {}, {})".format(start, end, step) - # check first 100 entries - self.assert_iterators_equal(iter1, iter2, test_id, limit=100) - - try: - iter1 = reversed(xrange(start, end, step)) - except OverflowError: - pass - else: - iter2 = pyrange_reversed(start, end, step) - test_id = "reversed(xrange({}, {}, {}))".format(start, end, step) - self.assert_iterators_equal(iter1, iter2, test_id, limit=100) - - -def test_main(): - test.test_support.run_unittest(XrangeTest) - -if __name__ == "__main__": - test_main() diff --git a/src/org/python/core/PyXRange.java b/src/org/python/core/PyXRange.java --- a/src/org/python/core/PyXRange.java +++ b/src/org/python/core/PyXRange.java @@ -14,11 +14,10 @@ public static final PyType TYPE = PyType.fromClass(PyXRange.class); - private final int start; - - private final int step; - - private final int len; + private final long start; + private final long step; + private final long stop; + private final long len; public PyXRange(int ihigh) { this(0, ihigh, 1); @@ -36,18 +35,19 @@ } int n; - if (istep > 0) { - n = getLenOfRange(ilow, ihigh, istep); + long listep = istep; + if (listep > 0) { + n = getLenOfRange(ilow, ihigh, listep); } else { - n = getLenOfRange(ihigh, ilow, -istep); + n = getLenOfRange(ihigh, ilow, -listep); } if (n < 0) { throw Py.OverflowError("xrange() result has too many items"); } - start = ilow; len = n; step = istep; + stop = ihigh; } @ExposedNew @@ -79,16 +79,16 @@ * @param step int value (> 0) * @return int length of range */ - static int getLenOfRange(int lo, int hi, int step) { - int n = 0; + static int getLenOfRange(long lo, long hi, long step) { if (lo < hi) { // the base difference may be > Integer.MAX_VALUE - long diff = (long)hi - (long)lo - 1; - // any long > Integer.MAX_VALUE or < Integer.MIN_VALUE gets casted to a + long diff = hi - lo - 1; + // any long > Integer.MAX_VALUE or < Integer.MIN_VALUE gets cast to a // negative number - n = (int)((diff / step) + 1); + return (int)((diff / step) + 1); + } else { + return 0; } - return n; } @Override @@ -98,7 +98,7 @@ @ExposedMethod(doc = BuiltinDocs.xrange___len___doc) final int xrange___len__() { - return len; + return (int)len; } @Override @@ -136,11 +136,22 @@ private final PyXRangeIter range_reverse() { return new PyXRangeIter(0, - (start + (long)(len - 1) * step), // start - (long)(0 - step), // step (negative value) + (start + (len - 1) * step), // start + (0 - step), // step (negative value) len); } + @ExposedMethod + public PyObject xrange___reduce__() { + return new PyTuple(getType(), + new PyTuple(Py.newInteger(start), Py.newInteger(stop), Py.newInteger(step))); + } + + @Override + public PyObject __reduce__() { + return xrange___reduce__(); + } + @Override protected PyObject pyget(int i) { return Py.newInteger(start + (i % len) * step); @@ -165,7 +176,10 @@ @Override public String toString() { - int stop = start + len * step; + long lstop = start + len * step; + if (lstop > PySystemState.maxint) { lstop = PySystemState.maxint; } + else if (lstop < PySystemState.minint) { lstop = PySystemState.minint; } + int stop = (int)lstop; if (start == 0 && step == 1) { return String.format("xrange(%d)", stop); } else if (step == 1) { -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 19 06:36:36 2014 From: jython-checkins at python.org (jim.baker) Date: Wed, 19 Nov 2014 05:36:36 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_concurrency_issues_with?= =?utf-8?q?_weakref_dicts_and_update_tests=2E?= Message-ID: <20141119053632.113292.39076@psf.io> https://hg.python.org/jython/rev/8fed9df0ccd8 changeset: 7414:8fed9df0ccd8 user: Jim Baker date: Tue Nov 18 22:36:17 2014 -0700 summary: Fix concurrency issues with weakref dicts and update tests. WeakKeyDictionary and WeakValueDictionary now build their dictionaries by using Google Guava, as supported by jythonlib.dict_builder, which now also supports providing such backing for dictionaries derived from dict. Update weakref tests to 2.7, with appropriate skips for Jython. Added gc.collect() (with sleeps) as necessary so that weak reference lifecycle can be observed in tests, including callbacks (sleeps allow us to observe the reaper thread in action). files: Lib/test/test_weakref.py | 490 ++++++--- Lib/test/test_weakset.py | 12 +- Lib/weakref.py | 358 +------ src/org/python/core/PyDictionary.java | 5 + src/org/python/core/PyDictionaryDerived.java | 10 + src/org/python/modules/_jythonlib/dict_builder.java | 16 +- src/org/python/modules/_weakref/AbstractReference.java | 18 + src/org/python/modules/_weakref/ProxyType.java | 6 +- src/org/python/modules/_weakref/ReferenceType.java | 7 + 9 files changed, 444 insertions(+), 478 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -11,7 +11,7 @@ import time def extra_collect(): - """Kick Java's GC into gear""" + """Kick Java's GC into gear; also callbacks need to be called by our reaper thread""" gc.collect() time.sleep(0.2) gc.collect() @@ -19,7 +19,8 @@ gc.collect() else: def extra_collect(): - pass + gc.collect() + # Used in ReferencesTestCase.test_ref_created_during_del() . ref_from_del = None @@ -47,6 +48,27 @@ return C.method +class Object: + def __init__(self, arg): + self.arg = arg + def __repr__(self): + return "" % self.arg + def __eq__(self, other): + if isinstance(other, Object): + return self.arg == other.arg + return NotImplemented + def __ne__(self, other): + if isinstance(other, Object): + return self.arg != other.arg + return NotImplemented + def __hash__(self): + return hash(self.arg) + +class RefCycle: + def __init__(self): + self.cycle = self + + class TestBase(unittest.TestCase): def setUp(self): @@ -71,6 +93,7 @@ repr(wr) # Dead reference: del o + gc.collect() repr(wr) def test_basic_callback(self): @@ -85,11 +108,11 @@ ref2 = weakref.ref(o, self.callback) del o extra_collect() - self.assert_(ref1() is None, + self.assertTrue(ref1() is None, "expected reference to be invalidated") - self.assert_(ref2() is None, + self.assertTrue(ref2() is None, "expected reference to be invalidated") - self.assert_(self.cbcalled == 2, + self.assertTrue(self.cbcalled == 2, "callback not called the right number of times") def test_multiple_selfref_callbacks(self): @@ -109,6 +132,7 @@ self.ref = weakref.ref(c, callback) ref1 = weakref.ref(c, callback) del c + gc.collect() def test_proxy_ref(self): o = C() @@ -116,28 +140,31 @@ ref1 = weakref.proxy(o, self.callback) ref2 = weakref.proxy(o, self.callback) del o + gc.collect() def check(proxy): proxy.bar - extra_collect() self.assertRaises(weakref.ReferenceError, check, ref1) self.assertRaises(weakref.ReferenceError, check, ref2) - # XXX: CPython GC collects C() immediately. use ref1 instead on - # Jython - if test_support.is_jython: - self.assertRaises(weakref.ReferenceError, bool, ref1) - else: - self.assertRaises(weakref.ReferenceError, bool, weakref.proxy(C())) - self.assert_(self.cbcalled == 2) + + # Need to add extra step for Jython - in the orginal test, ref counting had already removed C() + # self.assertRaises(weakref.ReferenceError, bool, weakref.proxy(C())) + o2 = C() + ref3 = weakref.proxy(o2) + del o2 + gc.collect() + self.assertRaises(weakref.ReferenceError, bool, ref3) + + self.assertTrue(self.cbcalled == 2) def check_basic_ref(self, factory): o = factory() ref = weakref.ref(o) - self.assert_(ref() is not None, + self.assertTrue(ref() is not None, "weak reference to live object should be live") o2 = ref() - self.assert_(o is o2, + self.assertTrue(o is o2, "() should return original object if live") def check_basic_callback(self, factory): @@ -145,10 +172,10 @@ o = factory() ref = weakref.ref(o, self.callback) del o - extra_collect() - self.assert_(self.cbcalled == 1, + extra_collect() # Jython - allow for the reaper to take care of the callback + self.assertTrue(self.cbcalled == 1, "callback did not properly set 'cbcalled'") - self.assert_(ref() is None, + self.assertTrue(ref() is None, "ref2 should be dead after deleting object reference") def test_ref_reuse(self): @@ -158,20 +185,20 @@ # between these two; it should make no difference proxy = weakref.proxy(o) ref2 = weakref.ref(o) - self.assert_(ref1 is ref2, + self.assertTrue(ref1 is ref2, "reference object w/out callback should be re-used") o = C() proxy = weakref.proxy(o) ref1 = weakref.ref(o) ref2 = weakref.ref(o) - self.assert_(ref1 is ref2, + self.assertTrue(ref1 is ref2, "reference object w/out callback should be re-used") - self.assert_(weakref.getweakrefcount(o) == 2, + self.assertTrue(weakref.getweakrefcount(o) == 2, "wrong weak ref count for object") del proxy - extra_collect() - self.assert_(weakref.getweakrefcount(o) == 1, + gc.collect() + self.assertTrue(weakref.getweakrefcount(o) == 1, "wrong weak ref count for object after deleting proxy") def test_proxy_reuse(self): @@ -179,7 +206,7 @@ proxy1 = weakref.proxy(o) ref = weakref.ref(o) proxy2 = weakref.proxy(o) - self.assert_(proxy1 is proxy2, + self.assertTrue(proxy1 is proxy2, "proxy object w/out callback should have been re-used") def test_basic_proxy(self): @@ -188,16 +215,15 @@ L = UserList.UserList() p = weakref.proxy(L) - self.failIf(p, "proxy for empty UserList should be false") + self.assertFalse(p, "proxy for empty UserList should be false") p.append(12) self.assertEqual(len(L), 1) - self.failUnless(p, "proxy for non-empty UserList should be true") + self.assertTrue(p, "proxy for non-empty UserList should be true") with test_support.check_py3k_warnings(): p[:] = [2, 3] self.assertEqual(len(L), 2) self.assertEqual(len(p), 2) - self.failUnless(3 in p, - "proxy didn't support __contains__() properly") + self.assertIn(3, p, "proxy didn't support __contains__() properly") p[1] = 5 self.assertEqual(L[1], 5) self.assertEqual(p[1], 5) @@ -213,7 +239,6 @@ self.assertEqual(L3[:5], p3[:5]) self.assertEqual(L3[2:5], p3[2:5]) - @unittest.skip("FIXME: broken") def test_proxy_unicode(self): # See bug 5037 class C(object): @@ -222,10 +247,9 @@ def __unicode__(self): return u"unicode" instance = C() - self.assertTrue("__unicode__" in dir(weakref.proxy(instance))) + self.assertIn("__unicode__", dir(weakref.proxy(instance))) self.assertEqual(unicode(weakref.proxy(instance)), u"unicode") - @unittest.skip("FIXME: broken") def test_proxy_index(self): class C: def __index__(self): @@ -234,7 +258,6 @@ p = weakref.proxy(o) self.assertEqual(operator.index(p), 10) - @unittest.skip("FIXME: broken") def test_proxy_div(self): class C: def __floordiv__(self, other): @@ -265,19 +288,19 @@ o = Object(1) p1 = makeref(o, None) p2 = makeref(o, None) - self.assert_(p1 is p2, "both callbacks were None in the C API") + self.assertTrue(p1 is p2, "both callbacks were None in the C API") del p1, p2 p1 = makeref(o) p2 = makeref(o, None) - self.assert_(p1 is p2, "callbacks were NULL, None in the C API") + self.assertTrue(p1 is p2, "callbacks were NULL, None in the C API") del p1, p2 p1 = makeref(o) p2 = makeref(o) - self.assert_(p1 is p2, "both callbacks were NULL in the C API") + self.assertTrue(p1 is p2, "both callbacks were NULL in the C API") del p1, p2 p1 = makeref(o, None) p2 = makeref(o) - self.assert_(p1 is p2, "callbacks were None, NULL in the C API") + self.assertTrue(p1 is p2, "callbacks were None, NULL in the C API") def test_callable_proxy(self): o = Callable() @@ -285,13 +308,13 @@ self.check_proxy(o, ref1) - self.assert_(type(ref1) is weakref.CallableProxyType, + self.assertTrue(type(ref1) is weakref.CallableProxyType, "proxy is not of callable type") ref1('twinkies!') - self.assert_(o.bar == 'twinkies!', + self.assertTrue(o.bar == 'twinkies!', "call through proxy not passed through to original") ref1(x='Splat.') - self.assert_(o.bar == 'Splat.', + self.assertTrue(o.bar == 'Splat.', "call through proxy not passed through to original") # expect due to too few args @@ -302,24 +325,24 @@ def check_proxy(self, o, proxy): o.foo = 1 - self.assert_(proxy.foo == 1, + self.assertTrue(proxy.foo == 1, "proxy does not reflect attribute addition") o.foo = 2 - self.assert_(proxy.foo == 2, + self.assertTrue(proxy.foo == 2, "proxy does not reflect attribute modification") del o.foo - self.assert_(not hasattr(proxy, 'foo'), + self.assertTrue(not hasattr(proxy, 'foo'), "proxy does not reflect attribute removal") proxy.foo = 1 - self.assert_(o.foo == 1, + self.assertTrue(o.foo == 1, "object does not reflect attribute addition via proxy") proxy.foo = 2 - self.assert_( + self.assertTrue( o.foo == 2, "object does not reflect attribute modification via proxy") del proxy.foo - self.assert_(not hasattr(o, 'foo'), + self.assertTrue(not hasattr(o, 'foo'), "object does not reflect attribute removal via proxy") def test_proxy_deletion(self): @@ -343,22 +366,22 @@ o = C() ref1 = weakref.ref(o) ref2 = weakref.ref(o, self.callback) - self.assert_(weakref.getweakrefcount(o) == 2, + self.assertTrue(weakref.getweakrefcount(o) == 2, "got wrong number of weak reference objects") proxy1 = weakref.proxy(o) proxy2 = weakref.proxy(o, self.callback) - self.assert_(weakref.getweakrefcount(o) == 4, + self.assertTrue(weakref.getweakrefcount(o) == 4, "got wrong number of weak reference objects") del ref1, ref2, proxy1, proxy2 - extra_collect() - self.assert_(weakref.getweakrefcount(o) == 0, + gc.collect() + self.assertTrue(weakref.getweakrefcount(o) == 0, "weak reference objects not unlinked from" " referent when discarded.") # assumes ints do not support weakrefs - self.assert_(weakref.getweakrefcount(1) == 0, + self.assertTrue(weakref.getweakrefcount(1) == 0, "got wrong number of weak reference objects for int") def test_getweakrefs(self): @@ -366,25 +389,25 @@ ref1 = weakref.ref(o, self.callback) ref2 = weakref.ref(o, self.callback) del ref1 - extra_collect() - self.assert_(weakref.getweakrefs(o) == [ref2], + gc.collect() + self.assertTrue(weakref.getweakrefs(o) == [ref2], "list of refs does not match") o = C() ref1 = weakref.ref(o, self.callback) ref2 = weakref.ref(o, self.callback) del ref2 - extra_collect() - self.assert_(weakref.getweakrefs(o) == [ref1], + gc.collect() + self.assertTrue(weakref.getweakrefs(o) == [ref1], "list of refs does not match") del ref1 - extra_collect() - self.assert_(weakref.getweakrefs(o) == [], + gc.collect() + self.assertTrue(weakref.getweakrefs(o) == [], "list of refs not cleared") # assumes ints do not support weakrefs - self.assert_(weakref.getweakrefs(1) == [], + self.assertTrue(weakref.getweakrefs(1) == [], "list of refs does not match for int") def test_newstyle_number_ops(self): @@ -392,8 +415,8 @@ pass f = F(2.0) p = weakref.proxy(f) - self.assert_(p + 1.0 == 3.0) - self.assert_(1.0 + p == 3.0) # this used to SEGV + self.assertTrue(p + 1.0 == 3.0) + self.assertTrue(1.0 + p == 3.0) # this used to SEGV def test_callbacks_protected(self): # Callbacks protected from already-set exceptions? @@ -581,6 +604,7 @@ del c1, c2, C, D gc.collect() + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython without significant rewriting") def test_callback_in_cycle_resurrection(self): import gc @@ -627,6 +651,7 @@ gc.collect() self.assertEqual(alist, []) + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") def test_callbacks_on_callback(self): import gc @@ -646,7 +671,7 @@ c.wr = weakref.ref(d, callback) # this won't trigger d.wr = weakref.ref(callback, d.cb) # ditto external_wr = weakref.ref(callback, safe_callback) # but this will - self.assert_(external_wr() is callback) + self.assertTrue(external_wr() is callback) # The weakrefs attached to c and d should get cleared, so that # C.cb is never called. But external_wr isn't part of the cyclic @@ -658,7 +683,6 @@ del callback, c, d, C self.assertEqual(alist, []) # del isn't enough to clean up cycles gc.collect() - extra_collect() self.assertEqual(alist, ["safe_callback called"]) self.assertEqual(external_wr(), None) @@ -666,17 +690,18 @@ gc.collect() self.assertEqual(alist, []) + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") def test_gc_during_ref_creation(self): self.check_gc_during_creation(weakref.ref) + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") def test_gc_during_proxy_creation(self): self.check_gc_during_creation(weakref.proxy) + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") def check_gc_during_creation(self, makeref): - # XXX: threshold not applicable to Jython - if not test_support.is_jython: - thresholds = gc.get_threshold() - gc.set_threshold(1, 1, 1) + thresholds = gc.get_threshold() + gc.set_threshold(1, 1, 1) gc.collect() class A: pass @@ -697,9 +722,7 @@ weakref.ref(referenced, callback) finally: - # XXX: threshold not applicable to Jython - if not test_support.is_jython: - gc.set_threshold(*thresholds) + gc.set_threshold(*thresholds) def test_ref_created_during_del(self): # Bug #1377858 @@ -713,7 +736,6 @@ w = Target() - @unittest.skip("FIXME: broken") def test_init(self): # Issue 3634 # .__init__() doesn't check errors correctly @@ -722,10 +744,98 @@ # No exception should be raised here gc.collect() + def test_classes(self): + # Check that both old-style classes and new-style classes + # are weakrefable. + class A(object): + pass + class B: + pass + l = [] + weakref.ref(int) + a = weakref.ref(A, l.append) + A = None + gc.collect() + self.assertEqual(a(), None) + self.assertEqual(l, [a]) + b = weakref.ref(B, l.append) + B = None + gc.collect() + self.assertEqual(b(), None) + self.assertEqual(l, [a, b]) + + def test_equality(self): + # Alive weakrefs defer equality testing to their underlying object. + x = Object(1) + y = Object(1) + z = Object(2) + a = weakref.ref(x) + b = weakref.ref(y) + c = weakref.ref(z) + d = weakref.ref(x) + # Note how we directly test the operators here, to stress both + # __eq__ and __ne__. + self.assertTrue(a == b) + self.assertFalse(a != b) + self.assertFalse(a == c) + self.assertTrue(a != c) + self.assertTrue(a == d) + self.assertFalse(a != d) + del x, y, z + gc.collect() + for r in a, b, c: + # Sanity check + self.assertIs(r(), None) + # Dead weakrefs compare by identity: whether `a` and `d` are the + # same weakref object is an implementation detail, since they pointed + # to the same original object and didn't have a callback. + # (see issue #16453). + self.assertFalse(a == b) + self.assertTrue(a != b) + self.assertFalse(a == c) + self.assertTrue(a != c) + self.assertEqual(a == d, a is d) + self.assertEqual(a != d, a is not d) + + def test_hashing(self): + # Alive weakrefs hash the same as the underlying object + x = Object(42) + y = Object(42) + a = weakref.ref(x) + b = weakref.ref(y) + self.assertEqual(hash(a), hash(42)) + del x, y + gc.collect() + # Dead weakrefs: + # - retain their hash is they were hashed when alive; + # - otherwise, cannot be hashed. + self.assertEqual(hash(a), hash(42)) + self.assertRaises(TypeError, hash, b) + + def test_trashcan_16602(self): + # Issue #16602: when a weakref's target was part of a long + # deallocation chain, the trashcan mechanism could delay clearing + # of the weakref and make the target object visible from outside + # code even though its refcount had dropped to 0. A crash ensued. + class C(object): + def __init__(self, parent): + if not parent: + return + wself = weakref.ref(self) + def cb(wparent): + o = wself() + self.wparent = weakref.ref(parent, cb) + + d = weakref.WeakKeyDictionary() + root = c = C(None) + for n in range(100): + d[c] = c = C(c) + del root + gc.collect() + class SubclassableWeakrefTestCase(TestBase): - @unittest.skip("FIXME: broken") def test_subclass_refs(self): class MyRef(weakref.ref): def __init__(self, ob, callback=None, value=42): @@ -736,42 +846,41 @@ return super(MyRef, self).__call__() o = Object("foo") mr = MyRef(o, value=24) - self.assert_(mr() is o) - self.assert_(mr.called) + self.assertTrue(mr() is o) + self.assertTrue(mr.called) self.assertEqual(mr.value, 24) del o - self.assert_(mr() is None) - self.assert_(mr.called) + gc.collect() + self.assertTrue(mr() is None) + self.assertTrue(mr.called) - @unittest.skip("FIXME: broken") def test_subclass_refs_dont_replace_standard_refs(self): class MyRef(weakref.ref): pass o = Object(42) r1 = MyRef(o) r2 = weakref.ref(o) - self.assert_(r1 is not r2) + self.assertTrue(r1 is not r2) self.assertEqual(weakref.getweakrefs(o), [r2, r1]) self.assertEqual(weakref.getweakrefcount(o), 2) r3 = MyRef(o) self.assertEqual(weakref.getweakrefcount(o), 3) refs = weakref.getweakrefs(o) self.assertEqual(len(refs), 3) - self.assert_(r2 is refs[0]) - self.assert_(r1 in refs[1:]) - self.assert_(r3 in refs[1:]) + self.assertIn(r1, refs) + self.assertIn(r2, refs) + self.assertIn(r3, refs) - @unittest.skip("FIXME: broken") def test_subclass_refs_dont_conflate_callbacks(self): class MyRef(weakref.ref): pass o = Object(42) r1 = MyRef(o, id) r2 = MyRef(o, str) - self.assert_(r1 is not r2) - refs = weakref.getweakrefs(o) - self.assert_(r1 in refs) - self.assert_(r2 in refs) + self.assertTrue(r1 is not r2) + refs = list(weakref.getweakrefs(o)) + self.assertIn(r1, refs) + self.assertIn(r2, refs) def test_subclass_refs_with_slots(self): class MyRef(weakref.ref): @@ -788,7 +897,7 @@ self.assertEqual(r.slot1, "abc") self.assertEqual(r.slot2, "def") self.assertEqual(r.meth(), "abcdef") - self.failIf(hasattr(r, "__dict__")) + self.assertFalse(hasattr(r, "__dict__")) def test_subclass_refs_with_cycle(self): # Bug #3110 @@ -829,48 +938,101 @@ self.assertEqual(self.cbcalled, 0) -class Object: - def __init__(self, arg): - self.arg = arg - def __repr__(self): - return "" % self.arg - - class MappingTestCase(TestBase): COUNT = 10 + def check_len_cycles(self, dict_type, cons): + N = 20 + items = [RefCycle() for i in range(N)] + dct = dict_type(cons(o) for o in items) + # Keep an iterator alive + it = dct.iteritems() + try: + next(it) + except StopIteration: + pass + del items + gc.collect() + n1 = len(list(dct)) + del it + gc.collect() + n2 = len(list(dct)) + # one or two items may be kept alive inside the iterator + self.assertIn(n1, (0, 1, 2)) + self.assertEqual(n2, 0) + + def test_weak_keyed_len_cycles(self): + self.check_len_cycles(weakref.WeakKeyDictionary, lambda k: (k, 1)) + + def test_weak_valued_len_cycles(self): + self.check_len_cycles(weakref.WeakValueDictionary, lambda k: (1, k)) + + def check_len_race(self, dict_type, cons): + # Extended sanity checks for len() in the face of cyclic collection + self.addCleanup(gc.set_threshold, *gc.get_threshold()) + for th in range(1, 100): + N = 20 + gc.collect(0) + gc.set_threshold(th, th, th) + items = [RefCycle() for i in range(N)] + dct = dict_type(cons(o) for o in items) + del items + # All items will be collected at next garbage collection pass + it = dct.iteritems() + try: + next(it) + except StopIteration: + pass + n1 = len(dct) + del it + n2 = len(dct) + self.assertGreaterEqual(n1, 0) + self.assertLessEqual(n1, N) + self.assertGreaterEqual(n2, 0) + self.assertLessEqual(n2, n1) + + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") + def test_weak_keyed_len_race(self): + self.check_len_race(weakref.WeakKeyDictionary, lambda k: (k, 1)) + + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") + def test_weak_valued_len_race(self): + self.check_len_race(weakref.WeakValueDictionary, lambda k: (1, k)) + def test_weak_values(self): # # This exercises d.copy(), d.items(), d[], del d[], len(d). # dict, objects = self.make_weak_valued_dict() for o in objects: - self.assert_(weakref.getweakrefcount(o) == 1, - "wrong number of weak references to %r!" % o) - self.assert_(o is dict[o.arg], + if not test_support.is_jython: # Such dictionaries now use MapMaker + self.assertTrue(weakref.getweakrefcount(o) == 1, + "wrong number of weak references to %r!" % o) + self.assertTrue(o is dict[o.arg], "wrong object returned by weak dict!") items1 = dict.items() items2 = dict.copy().items() items1.sort() items2.sort() - self.assert_(items1 == items2, + self.assertTrue(items1 == items2, "cloning of weak-valued dictionary did not work!") del items1, items2 - self.assert_(len(dict) == self.COUNT) + self.assertTrue(len(dict) == self.COUNT) del objects[0] - extra_collect() - self.assert_(len(dict) == (self.COUNT - 1), + gc.collect() + # underlying Map.size is guaranteed only to be eventually consistent for MapMaker + self.assertEqual(len(list(dict.iterkeys())), self.COUNT - 1, "deleting object did not cause dictionary update") del objects, o - extra_collect() - self.assert_(len(dict) == 0, + gc.collect() + self.assertEqual(len(list(dict.iterkeys())), 0, "deleting the values did not clear the dictionary") # regression on SF bug #447152: dict = weakref.WeakValueDictionary() self.assertRaises(KeyError, dict.__getitem__, 1) dict[2] = C() - extra_collect() + gc.collect() self.assertRaises(KeyError, dict.__getitem__, 2) def test_weak_keys(self): @@ -880,28 +1042,29 @@ # dict, objects = self.make_weak_keyed_dict() for o in objects: - self.assert_(weakref.getweakrefcount(o) == 1, - "wrong number of weak references to %r!" % o) - self.assert_(o.arg is dict[o], + if not test_support.is_jython: # Such dictionaries now use MapMaker + self.assertTrue(weakref.getweakrefcount(o) == 1, + "wrong number of weak references to %r!" % o) + self.assertTrue(o.arg is dict[o], "wrong object returned by weak dict!") items1 = dict.items() items2 = dict.copy().items() - self.assert_(set(items1) == set(items2), + self.assertTrue(set(items1) == set(items2), "cloning of weak-keyed dictionary did not work!") del items1, items2 - self.assert_(len(dict) == self.COUNT) + self.assertEqual(len(list(dict.iterkeys())), self.COUNT) del objects[0] - extra_collect() - self.assert_(len(dict) == (self.COUNT - 1), + gc.collect() + self.assertEqual(len(list(dict.iterkeys())), self.COUNT - 1, "deleting object did not cause dictionary update") del objects, o - extra_collect() - self.assert_(len(dict) == 0, + gc.collect() + self.assertEqual(len(list(dict.iterkeys())), 0, "deleting the keys did not clear the dictionary") o = Object(42) dict[o] = "What is the meaning of the universe?" - self.assertTrue(o in dict) - self.assertTrue(34 not in dict) + self.assertIn(o, dict) + self.assertNotIn(34, dict) def test_weak_keyed_iters(self): dict, objects = self.make_weak_keyed_dict() @@ -913,7 +1076,7 @@ objects2 = list(objects) for wr in refs: ob = wr() - self.assertTrue(ob in dict) + self.assertIn(ob, dict) self.assertEqual(ob.arg, dict[ob]) objects2.remove(ob) self.assertEqual(len(objects2), 0) @@ -923,7 +1086,7 @@ self.assertEqual(len(list(dict.iterkeyrefs())), len(objects)) for wr in dict.iterkeyrefs(): ob = wr() - self.assertTrue(ob in dict) + self.assertIn(ob, dict) self.assertEqual(ob.arg, dict[ob]) objects2.remove(ob) self.assertEqual(len(objects2), 0) @@ -958,37 +1121,37 @@ items = dict.items() for item in dict.iteritems(): items.remove(item) - self.assert_(len(items) == 0, "iteritems() did not touch all items") + self.assertTrue(len(items) == 0, "iteritems() did not touch all items") # key iterator, via __iter__(): keys = dict.keys() for k in dict: keys.remove(k) - self.assert_(len(keys) == 0, "__iter__() did not touch all keys") + self.assertTrue(len(keys) == 0, "__iter__() did not touch all keys") # key iterator, via iterkeys(): keys = dict.keys() for k in dict.iterkeys(): keys.remove(k) - self.assert_(len(keys) == 0, "iterkeys() did not touch all keys") + self.assertTrue(len(keys) == 0, "iterkeys() did not touch all keys") # value iterator: values = dict.values() for v in dict.itervalues(): values.remove(v) - self.assert_(len(values) == 0, + self.assertTrue(len(values) == 0, "itervalues() did not touch all values") def test_make_weak_keyed_dict_from_dict(self): o = Object(3) dict = weakref.WeakKeyDictionary({o:364}) - self.assert_(dict[o] == 364) + self.assertTrue(dict[o] == 364) def test_make_weak_keyed_dict_from_weak_keyed_dict(self): o = Object(3) dict = weakref.WeakKeyDictionary({o:364}) dict2 = weakref.WeakKeyDictionary(dict) - self.assert_(dict[o] == 364) + self.assertTrue(dict[o] == 364) def make_weak_keyed_dict(self): dict = weakref.WeakKeyDictionary() @@ -1008,19 +1171,19 @@ weakdict = klass() weakdict[key1] = value1 weakdict[key2] = value2 - self.assert_(len(weakdict) == 2) + self.assertTrue(len(weakdict) == 2) k, v = weakdict.popitem() - self.assert_(len(weakdict) == 1) + self.assertTrue(len(weakdict) == 1) if k is key1: - self.assert_(v is value1) + self.assertTrue(v is value1) else: - self.assert_(v is value2) + self.assertTrue(v is value2) k, v = weakdict.popitem() - self.assert_(len(weakdict) == 0) + self.assertTrue(len(weakdict) == 0) if k is key1: - self.assert_(v is value1) + self.assertTrue(v is value1) else: - self.assert_(v is value2) + self.assertTrue(v is value2) def test_weak_valued_dict_popitem(self): self.check_popitem(weakref.WeakValueDictionary, @@ -1031,21 +1194,21 @@ C(), "value 1", C(), "value 2") def check_setdefault(self, klass, key, value1, value2): - self.assert_(value1 is not value2, + self.assertTrue(value1 is not value2, "invalid test" " -- value parameters must be distinct objects") weakdict = klass() o = weakdict.setdefault(key, value1) - self.assertTrue(o is value1) - self.assertTrue(key in weakdict) - self.assertTrue(weakdict.get(key) is value1) - self.assertTrue(weakdict[key] is value1) + self.assertIs(o, value1) + self.assertIn(key, weakdict) + self.assertIs(weakdict.get(key), value1) + self.assertIs(weakdict[key], value1) o = weakdict.setdefault(key, value2) - self.assertTrue(o is value1) - self.assertTrue(key in weakdict) - self.assertTrue(weakdict.get(key) is value1) - self.assertTrue(weakdict[key] is value1) + self.assertIs(o, value1) + self.assertIn(key, weakdict) + self.assertIs(weakdict.get(key), value1) + self.assertIs(weakdict[key], value1) def test_weak_valued_dict_setdefault(self): self.check_setdefault(weakref.WeakValueDictionary, @@ -1064,17 +1227,17 @@ weakdict.update(dict) self.assertEqual(len(weakdict), len(dict)) for k in weakdict.keys(): - self.assertTrue(k in dict, + self.assertIn(k, dict, "mysterious new key appeared in weak dict") v = dict.get(k) - self.assertTrue(v is weakdict[k]) - self.assertTrue(v is weakdict.get(k)) + self.assertIs(v, weakdict[k]) + self.assertIs(v, weakdict.get(k)) for k in dict.keys(): - self.assertTrue(k in weakdict, + self.assertIn(k, weakdict, "original key disappeared in weak dict") v = dict[k] - self.assertTrue(v is weakdict[k]) - self.assertTrue(v is weakdict.get(k)) + self.assertIs(v, weakdict[k]) + self.assertIs(v, weakdict.get(k)) def test_weak_valued_dict_update(self): self.check_update(weakref.WeakValueDictionary, @@ -1090,10 +1253,10 @@ o2 = Object('2') d[o1] = 'something' d[o2] = 'something' - self.assert_(len(d) == 2) + self.assertTrue(len(d) == 2) del d[o1] - self.assert_(len(d) == 1) - self.assert_(d.keys() == [o2]) + self.assertTrue(len(d) == 1) + self.assertTrue(d.keys() == [o2]) def test_weak_valued_delitem(self): d = weakref.WeakValueDictionary() @@ -1101,10 +1264,10 @@ o2 = Object('2') d['something'] = o1 d['something else'] = o2 - self.assert_(len(d) == 2) + self.assertTrue(len(d) == 2) del d['something'] - self.assert_(len(d) == 1) - self.assert_(d.items() == [('something else', o2)]) + self.assertTrue(len(d) == 1) + self.assertTrue(d.items() == [('something else', o2)]) def test_weak_keyed_bad_delitem(self): d = weakref.WeakKeyDictionary() @@ -1114,14 +1277,15 @@ self.assertRaises(KeyError, d.__delitem__, o) self.assertRaises(KeyError, d.__getitem__, o) - # If a key isn't of a weakly referencable type, __getitem__ and - # __setitem__ raise TypeError. __delitem__ should too. - self.assertRaises(TypeError, d.__delitem__, 13) - self.assertRaises(TypeError, d.__getitem__, 13) - self.assertRaises(TypeError, d.__setitem__, 13, 13) + # In Jython, all objects can be weakly referenced. + self.assertRaises(KeyError, d.__delitem__, 13) + self.assertRaises(KeyError, d.__getitem__, 13) + # Using MapMaker means that __eq__ is not called given that such + # Google Collections based weak maps use identity, not equality. + @unittest.skipIf(test_support.is_jython, "Not a valid test for Jython") def test_weak_keyed_cascading_deletes(self): - # SF bug 742860. For some reason, before 2.3 __delitem__ iterated + # SF bug 742860. For some reason, bef1ore 2.3 __delitem__ iterated # over the keys via self.data.iterkeys(). If things vanished from # the dict during this (or got added), that caused a RuntimeError. @@ -1202,7 +1366,8 @@ >>> o is o2 True >>> del o, o2 ->>> extra_collect() +>>> import gc # Addition for Jython +>>> gc.collect() >>> print r() None @@ -1255,7 +1420,8 @@ >>> id2obj(a_id) is a True >>> del a ->>> extra_collect() +>>> import gc # addition for Jython +>>> gc.collect() >>> try: ... id2obj(a_id) ... except KeyError: @@ -1269,16 +1435,6 @@ __test__ = {'libreftest' : libreftest} def test_main(): - if test_support.is_jython: - # Probably CPython GC specific (possibly even Jython bugs) - del ReferencesTestCase.test_callback_in_cycle_resurrection - del ReferencesTestCase.test_callbacks_on_callback - - # Jython allows types to be weakref'd that CPython doesn't - del MappingTestCase.test_weak_keyed_bad_delitem - - # CPython GC specific - del MappingTestCase.test_weak_keyed_cascading_deletes test_support.run_unittest( ReferencesTestCase, MappingTestCase, diff --git a/Lib/test/test_weakset.py b/Lib/test/test_weakset.py --- a/Lib/test/test_weakset.py +++ b/Lib/test/test_weakset.py @@ -63,14 +63,14 @@ def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) - @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") + #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj self.assertEqual(len(self.fs), 0) - @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") + #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) @@ -167,14 +167,14 @@ self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) - @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") + #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_lt(self): self.assertTrue(self.ab_weakset < self.abcde_weakset) self.assertFalse(self.abcde_weakset < self.def_weakset) self.assertFalse(self.ab_weakset < self.ab_weakset) self.assertFalse(WeakSet() < WeakSet()) - @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") + #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_gt(self): self.assertTrue(self.abcde_weakset > self.ab_weakset) self.assertFalse(self.abcde_weakset > self.def_weakset) @@ -229,7 +229,7 @@ self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) - @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") + #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_add(self): x = SomeClass('Q') self.s.add(x) @@ -356,7 +356,7 @@ self.assertFalse(self.s == tuple(self.items)) self.assertFalse(self.s == 1) - @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") + #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference diff --git a/Lib/weakref.py b/Lib/weakref.py --- a/Lib/weakref.py +++ b/Lib/weakref.py @@ -5,12 +5,12 @@ http://www.python.org/dev/peps/pep-0205/ """ +# Changed for Jython to use MapMaker in Google Collections + # Naming convention: Variables named "wr" are weak reference objects; # they are called this instead of "ref" to avoid name collisions with # the module-global ref() function imported from _weakref. -import UserDict - from _weakref import ( getweakrefcount, getweakrefs, @@ -23,6 +23,7 @@ from _weakrefset import WeakSet from exceptions import ReferenceError +from jythonlib import MapMaker, dict_builder ProxyTypes = (ProxyType, CallableProxyType) @@ -32,107 +33,15 @@ "CallableProxyType", "ProxyTypes", "WeakValueDictionary", 'WeakSet'] -class WeakValueDictionary(UserDict.UserDict): +class WeakValueDictionary(dict): """Mapping class that references values weakly. Entries in the dictionary will be discarded when no strong reference to the value exists anymore """ - # We inherit the constructor without worrying about the input - # dictionary; since it uses our .update() method, we get the right - # checks (if the other dictionary is a WeakValueDictionary, - # objects are unwrapped on the way out, and we always wrap on the - # way in). - def __init__(self, *args, **kw): - def remove(wr, selfref=ref(self)): - self = selfref() - if self is not None: - try: - del self.data[wr.key] - except KeyError: - pass - self._remove = remove - UserDict.UserDict.__init__(self, *args, **kw) - - def __getitem__(self, key): - o = self.data[key]() - if o is None: - raise KeyError, key - else: - return o - - def __contains__(self, key): - try: - o = self.data[key]() - except KeyError: - return False - return o is not None - - def has_key(self, key): - try: - o = self.data[key]() - except KeyError: - return False - return o is not None - - def __repr__(self): - return "" % id(self) - - def __setitem__(self, key, value): - self.data[key] = KeyedRef(value, self._remove, key) - - def copy(self): - new = WeakValueDictionary() - for key, wr in self.data.items(): - o = wr() - if o is not None: - new[key] = o - return new - - __copy__ = copy - - def __deepcopy__(self, memo): - from copy import deepcopy - new = self.__class__() - for key, wr in self.data.items(): - o = wr() - if o is not None: - new[deepcopy(key, memo)] = o - return new - - def get(self, key, default=None): - try: - wr = self.data[key] - except KeyError: - return default - else: - o = wr() - if o is None: - # This should only happen - return default - else: - return o - - def items(self): - L = [] - for key, wr in self.data.items(): - o = wr() - if o is not None: - L.append((key, o)) - return L - - def iteritems(self): - for wr in self.data.itervalues(): - value = wr() - if value is not None: - yield wr.key, value - - def iterkeys(self): - return self.data.iterkeys() - - def __iter__(self): - return self.data.iterkeys() + def __new__(cls, *args, **kw): + return WeakValueDictionaryBuilder(*args, **kw) def itervaluerefs(self): """Return an iterator that yields the weak references to the values. @@ -144,51 +53,8 @@ keep the values around longer than needed. """ - return self.data.itervalues() - - def itervalues(self): - for wr in self.data.itervalues(): - obj = wr() - if obj is not None: - yield obj - - def popitem(self): - while 1: - key, wr = self.data.popitem() - o = wr() - if o is not None: - return key, o - - def pop(self, key, *args): - try: - o = self.data.pop(key)() - except KeyError: - if args: - return args[0] - raise - if o is None: - raise KeyError, key - else: - return o - - def setdefault(self, key, default=None): - try: - wr = self.data[key] - except KeyError: - self.data[key] = KeyedRef(default, self._remove, key) - return default - else: - return wr() - - def update(self, dict=None, **kwargs): - d = self.data - if dict is not None: - if not hasattr(dict, "items"): - dict = type({})(dict) - for key, o in dict.items(): - d[key] = KeyedRef(o, self._remove, key) - if len(kwargs): - self.update(kwargs) + for value in self.itervalues(): + yield ref(value) def valuerefs(self): """Return a list of weak references to the values. @@ -200,17 +66,56 @@ keep the values around longer than needed. """ - return self.data.values() + return [ref(value) for value in self.itervalues()] - def values(self): - L = [] - for wr in self.data.values(): - o = wr() - if o is not None: - L.append(o) - return L +WeakValueDictionaryBuilder = dict_builder(MapMaker().weakValues().makeMap, WeakValueDictionary) +class WeakKeyDictionary(dict): + """ Mapping class that references keys weakly. + + Entries in the dictionary will be discarded when there is no + longer a strong reference to the key. This can be used to + associate additional data with an object owned by other parts of + an application without adding attributes to those objects. This + can be especially useful with objects that override attribute + accesses. + """ + + def __new__(cls, *args, **kw): + return WeakKeyDictionaryBuilder(*args, **kw) + + def iterkeyrefs(self): + """Return an iterator that yields the weak references to the keys. + + The references are not guaranteed to be 'live' at the time + they are used, so the result of calling the references needs + to be checked before being used. This can be used to avoid + creating references that will cause the garbage collector to + keep the keys around longer than needed. + + """ + for key in self.iterkeys(): + yield ref(key) + + def keyrefs(self): + """Return a list of weak references to the keys. + + The references are not guaranteed to be 'live' at the time + they are used, so the result of calling the references needs + to be checked before being used. This can be used to avoid + creating references that will cause the garbage collector to + keep the keys around longer than needed. + + """ + return [ref(key) for key in self.iterkeys()] + +WeakKeyDictionaryBuilder = dict_builder(MapMaker().weakKeys().makeMap, WeakKeyDictionary) + + +# Jython does not use below, however retaining in the case of any user code that might +# be using it. Note that it is not exported. + class KeyedRef(ref): """Specialized reference that includes a key corresponding to the value. @@ -230,156 +135,3 @@ def __init__(self, ob, callback, key): super(KeyedRef, self).__init__(ob, callback) - - -class WeakKeyDictionary(UserDict.UserDict): - """ Mapping class that references keys weakly. - - Entries in the dictionary will be discarded when there is no - longer a strong reference to the key. This can be used to - associate additional data with an object owned by other parts of - an application without adding attributes to those objects. This - can be especially useful with objects that override attribute - accesses. - """ - - def __init__(self, dict=None): - self.data = {} - def remove(k, selfref=ref(self)): - self = selfref() - if self is not None: - try: - del self.data[k] - except KeyError: - pass - self._remove = remove - if dict is not None: self.update(dict) - - def __delitem__(self, key): - del self.data[ref(key)] - - def __getitem__(self, key): - return self.data[ref(key)] - - def __repr__(self): - return "" % id(self) - - def __setitem__(self, key, value): - self.data[ref(key, self._remove)] = value - - def copy(self): - new = WeakKeyDictionary() - for key, value in self.data.items(): - o = key() - if o is not None: - new[o] = value - return new - - __copy__ = copy - - def __deepcopy__(self, memo): - from copy import deepcopy - new = self.__class__() - for key, value in self.data.items(): - o = key() - if o is not None: - new[o] = deepcopy(value, memo) - return new - - def get(self, key, default=None): - return self.data.get(ref(key),default) - - def has_key(self, key): - try: - wr = ref(key) - except TypeError: - return 0 - return wr in self.data - - def __contains__(self, key): - try: - wr = ref(key) - except TypeError: - return 0 - return wr in self.data - - def items(self): - L = [] - for key, value in self.data.items(): - o = key() - if o is not None: - L.append((o, value)) - return L - - def iteritems(self): - for wr, value in self.data.iteritems(): - key = wr() - if key is not None: - yield key, value - - def iterkeyrefs(self): - """Return an iterator that yields the weak references to the keys. - - The references are not guaranteed to be 'live' at the time - they are used, so the result of calling the references needs - to be checked before being used. This can be used to avoid - creating references that will cause the garbage collector to - keep the keys around longer than needed. - - """ - return self.data.iterkeys() - - def iterkeys(self): - for wr in self.data.iterkeys(): - obj = wr() - if obj is not None: - yield obj - - def __iter__(self): - return self.iterkeys() - - def itervalues(self): - return self.data.itervalues() - - def keyrefs(self): - """Return a list of weak references to the keys. - - The references are not guaranteed to be 'live' at the time - they are used, so the result of calling the references needs - to be checked before being used. This can be used to avoid - creating references that will cause the garbage collector to - keep the keys around longer than needed. - - """ - return self.data.keys() - - def keys(self): - L = [] - for wr in self.data.keys(): - o = wr() - if o is not None: - L.append(o) - return L - - def popitem(self): - while 1: - key, value = self.data.popitem() - o = key() - if o is not None: - return o, value - - def pop(self, key, *args): - return self.data.pop(ref(key), *args) - - def setdefault(self, key, default=None): - return self.data.setdefault(ref(key, self._remove),default) - - def update(self, dict=None, **kwargs): - d = self.data - if dict is not None: - if not hasattr(dict, "items"): - dict = type({})(dict) - for key, value in dict.items(): - d[ref(key, self._remove)] = value - if len(kwargs): - self.update(kwargs) diff --git a/src/org/python/core/PyDictionary.java b/src/org/python/core/PyDictionary.java --- a/src/org/python/core/PyDictionary.java +++ b/src/org/python/core/PyDictionary.java @@ -79,6 +79,11 @@ internalMap = backingMap; } + public PyDictionary(PyType type, ConcurrentMap backingMap, boolean useBackingMap) { + super(type); + internalMap = backingMap; + } + /** * Create a new dictionary which is populated with entries the given map. */ diff --git a/src/org/python/core/PyDictionaryDerived.java b/src/org/python/core/PyDictionaryDerived.java --- a/src/org/python/core/PyDictionaryDerived.java +++ b/src/org/python/core/PyDictionaryDerived.java @@ -1,6 +1,7 @@ /* Generated file, do not modify. See jython/src/templates/gderived.py. */ package org.python.core; +import java.util.concurrent.ConcurrentMap; import java.io.Serializable; import org.python.core.finalization.FinalizeTrigger; import org.python.core.finalization.FinalizablePyObjectDerived; @@ -66,6 +67,15 @@ } } + public PyDictionaryDerived(PyType subtype, ConcurrentMap backingMap, boolean useBackingMap) { + super(subtype, backingMap, useBackingMap); + slots=new PyObject[subtype.getNumSlots()]; + dict=subtype.instDict(); + if (subtype.needsFinalizer()) { + finalizeTrigger=FinalizeTrigger.makeTrigger(this); + } + } + public PyString __str__() { PyType self_type=getType(); PyObject impl=self_type.lookup("__str__"); diff --git a/src/org/python/modules/_jythonlib/dict_builder.java b/src/org/python/modules/_jythonlib/dict_builder.java --- a/src/org/python/modules/_jythonlib/dict_builder.java +++ b/src/org/python/modules/_jythonlib/dict_builder.java @@ -2,6 +2,7 @@ package org.python.modules._jythonlib; import org.python.core.PyDictionary; +import org.python.core.PyDictionaryDerived; import org.python.core.PyObject; import org.python.core.PyType; @@ -24,15 +25,28 @@ public static final PyType TYPE = PyType.fromClass(dict_builder.class); private final PyObject factory; + private final PyType dict_type; public dict_builder(PyObject factory) { super(); this.factory = factory; + this.dict_type = null; + } + + public dict_builder(PyObject factory, PyType dict_type) { + super(); + this.factory = factory; + this.dict_type = dict_type; } public PyObject __call__(PyObject[] args, String[] keywords) { ConcurrentMap map = (ConcurrentMap) (factory.__call__().__tojava__(ConcurrentMap.class)); - PyDictionary dict = new PyDictionary(map, true); + PyDictionary dict; + if (dict_type == null) { + dict = new PyDictionary(map, true); + } else { + dict = new PyDictionaryDerived(dict_type, map, true); + } dict.updateCommon(args, keywords, "dict"); return dict; } diff --git a/src/org/python/modules/_weakref/AbstractReference.java b/src/org/python/modules/_weakref/AbstractReference.java --- a/src/org/python/modules/_weakref/AbstractReference.java +++ b/src/org/python/modules/_weakref/AbstractReference.java @@ -39,6 +39,12 @@ return o; } + // Differentiate reference equality (equals) with what is being referred to (__eq__) + @Override + public boolean equals(Object ob_other) { + return ob_other == this; + } + public int hashCode() { return gref.pythonHashCode(); } @@ -54,4 +60,16 @@ } return pythis._eq(pyother); } + + public PyObject __ne__(PyObject other) { + if (other.getClass() != getClass()) { + return Py.True; + } + PyObject pythis = (PyObject)gref.get(); + PyObject pyother = (PyObject)((AbstractReference)other).gref.get(); + if (pythis == null || pyother == null) { + return this == other ? Py.False : Py.True; + } + return pythis._eq(pyother).__not__(); + } } diff --git a/src/org/python/modules/_weakref/ProxyType.java b/src/org/python/modules/_weakref/ProxyType.java --- a/src/org/python/modules/_weakref/ProxyType.java +++ b/src/org/python/modules/_weakref/ProxyType.java @@ -6,6 +6,7 @@ import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyType; +import org.python.core.PyUnicode; import org.python.expose.ExposedType; /** @@ -47,6 +48,7 @@ public PyObject __iter__() { return py().__iter__(); } public PyString __str__() { return py().__str__(); } + public PyUnicode __unicode__() { return py().__unicode__(); } public PyString __hex__() { return py().__hex__(); } public PyString __oct__() { return py().__oct__(); } public PyObject __int__() { return py().__int__(); } @@ -60,6 +62,7 @@ public boolean __contains__(PyObject o) { return py().__contains__(o); } + public PyObject __index__() { return py().__index__(); } public PyObject __add__(PyObject o) { return py().__add__(o); } public PyObject __radd__(PyObject o) { return py().__radd__(o); } @@ -71,8 +74,10 @@ public PyObject __rmul__(PyObject o) { return py().__rmul__(o); } public PyObject __imul__(PyObject o) { return py().__imul__(o); } public PyObject __div__(PyObject o) { return py().__div__(o); } + public PyObject __floordiv__(PyObject o) { return py().__floordiv__(o); } public PyObject __rdiv__(PyObject o) { return py().__rdiv__(o); } public PyObject __idiv__(PyObject o) { return py().__idiv__(o); } + public PyObject __ifloordiv__(PyObject o) { return py().__ifloordiv__(o); } public PyObject __mod__(PyObject o) { return py().__mod__(o); } public PyObject __rmod__(PyObject o) { return py().__rmod__(o); } public PyObject __imod__(PyObject o) { return py().__imod__(o); } @@ -84,7 +89,6 @@ public PyObject __lshift__(PyObject o) { return py().__lshift__(o); } public PyObject __rlshift__(PyObject o) { return py().__rlshift__(o);} public PyObject __ilshift__(PyObject o) { return py().__ilshift__(o);} - public PyObject __rshift__(PyObject o) { return py().__rshift__(o); } public PyObject __rrshift__(PyObject o) { return py().__rrshift__(o);} public PyObject __irshift__(PyObject o) { return py().__irshift__(o);} diff --git a/src/org/python/modules/_weakref/ReferenceType.java b/src/org/python/modules/_weakref/ReferenceType.java --- a/src/org/python/modules/_weakref/ReferenceType.java +++ b/src/org/python/modules/_weakref/ReferenceType.java @@ -3,8 +3,10 @@ import org.python.core.ArgParser; import org.python.core.Py; +import org.python.core.PyList; import org.python.core.PyNewWrapper; import org.python.core.PyObject; +import org.python.core.PyTuple; import org.python.core.PyType; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; @@ -57,6 +59,11 @@ // Just ensure at least one arg, leaving other args alone ArgParser ap = parseInitArgs("__init__", args, keywords); ap.getPyObject(0); + int arglen = ap.getList(2).__len__(); + if (arglen > 2) { + throw Py.TypeError(String.format("__init__ expected at most 2 arguments, got %d", + arglen)); + } } /** -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 19 20:32:29 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Wed, 19 Nov 2014 19:32:29 +0000 Subject: [Jython-checkins] =?utf-8?b?anl0aG9uOiBGaXhlZCBfX2RvY19fIG9mIG9y?= =?utf-8?q?g=2Emodules=2E=5Fcsv=2EPyDialect=2E?= Message-ID: <20141119193223.62766.84498@psf.io> https://hg.python.org/jython/rev/d49d8004256a changeset: 7415:d49d8004256a user: Stefan Richthofer date: Wed Nov 19 20:31:25 2014 +0100 summary: Fixed __doc__ of org.modules._csv.PyDialect. files: src/org/python/modules/_csv/PyDialect.java | 12 ++++----- 1 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/org/python/modules/_csv/PyDialect.java b/src/org/python/modules/_csv/PyDialect.java --- a/src/org/python/modules/_csv/PyDialect.java +++ b/src/org/python/modules/_csv/PyDialect.java @@ -18,15 +18,13 @@ /** * The Python CSV Dialect type. */ - at ExposedType(name = "_csv.Dialect") + at ExposedType(name = "_csv.Dialect", doc = PyDialect.Dialect_doc) public class PyDialect extends PyObject { - public static final PyType TYPE = PyType.fromClass(PyDialect.class); - - public PyString __doc__ = Py.newString( - "CSV dialect\n" + - "\n" + - "The Dialect type records CSV parsing and generation options.\n"); + public static final String Dialect_doc = + "CSV dialect\n" + + "\n" + + "The Dialect type records CSV parsing and generation options.\n"; /** Whether " is represented by "". */ @ExposedGet -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 19 21:16:01 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Wed, 19 Nov 2014 20:16:01 +0000 Subject: [Jython-checkins] =?utf-8?b?anl0aG9uOiBBZGp1c3RlZCBfX2RvY19fIG9m?= =?utf-8?q?_unicode_object_to_match_CPython=27s_variant=2E?= Message-ID: <20141119201526.62768.67948@psf.io> https://hg.python.org/jython/rev/6c398c76abcd changeset: 7416:6c398c76abcd user: Stefan Richthofer date: Wed Nov 19 21:14:57 2014 +0100 summary: Adjusted __doc__ of unicode object to match CPython's variant. files: src/org/python/core/BuiltinDocs.java | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/src/org/python/core/BuiltinDocs.java b/src/org/python/core/BuiltinDocs.java --- a/src/org/python/core/BuiltinDocs.java +++ b/src/org/python/core/BuiltinDocs.java @@ -252,7 +252,8 @@ "x.__delattr__('name') <==> del x.name"; public final static String unicode_doc = - "unicode(string [, encoding[, errors]]) -> object\n" + + "unicode(object='') -> unicode object\n" + + "unicode(string[, encoding[, errors]]) -> unicode object\n" + "\n" + "Create a new Unicode object from the given encoded string.\n" + "encoding defaults to the current default string encoding.\n" + -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 19 21:35:14 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Wed, 19 Nov 2014 20:35:14 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Some_cosmetics_in_PyObject_?= =?utf-8?q?and_PyType=2E?= Message-ID: <20141119203500.113290.24830@psf.io> https://hg.python.org/jython/rev/eb61f21316bc changeset: 7417:eb61f21316bc user: Stefan Richthofer date: Wed Nov 19 21:34:52 2014 +0100 summary: Some cosmetics in PyObject and PyType. Cleaned comments, fixed typos and tabs. files: src/org/python/core/PyObject.java | 10 +++++-- src/org/python/core/PyType.java | 25 ++++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/org/python/core/PyObject.java b/src/org/python/core/PyObject.java --- a/src/org/python/core/PyObject.java +++ b/src/org/python/core/PyObject.java @@ -109,7 +109,7 @@ *

* Note that this empty finalizer implementation is optimized away by the JVM * (See {@link http://www.javaspecialists.eu/archive/Issue170.html}). - * So {@code PyObject}s are not expensively treaded as finalizable objects by the + * So {@code PyObject}s are not expensively treated as finalizable objects by the * GC. Its only intention is to prevent subclasses from having Java-style finalizers. *

*/ @@ -239,10 +239,14 @@ return __repr__(); } + /** + * PyObjects that implement + * org.python.core.finalization.HasFinalizeTrigger + * shall implement this method via:
+ * FinalizeTrigger.ensureFinalizer(this); + **/ @ExposedMethod public void __ensure_finalizer__() { - //PyObjects that implement HasFinalizeTrigger shall implement this method via: - //FinalizeTrigger.ensureFinalizer(this); } public PyUnicode __unicode__() { diff --git a/src/org/python/core/PyType.java b/src/org/python/core/PyType.java --- a/src/org/python/core/PyType.java +++ b/src/org/python/core/PyType.java @@ -573,16 +573,19 @@ * @return a boolean indicating whether the type implements __del__ */ public final boolean needsFinalizer() { - //It might be sluggish to assume that if a finalizer was needed - //once, this would never change. However since an expensive - //FinalizeTrigger was created anyway, it won't hurt to keep it. - //Whether there actually is a __del__ in the dict will be checked - //again when the finalizer runs. - if (needs_finalizer) return true; - else { - needs_finalizer = lookup_mro("__del__") != null; - return needs_finalizer; - } + /* + * It might be sluggish to assume that if a finalizer was needed + * once, this would never change. However since an expensive + * FinalizeTrigger was created anyway, it won't hurt to keep it. + * Whether there actually is a __del__ in the dict, will be checked + * again when the finalizer runs. + */ + if (needs_finalizer) { + return true; + } else { + needs_finalizer = lookup_mro("__del__") != null; + return needs_finalizer; + } } /** @@ -1989,7 +1992,7 @@ } /** - * A thead safe, non-blocking version of Armin Rigo's mro cache. + * A thread safe, non-blocking version of Armin Rigo's mro cache. */ static class MethodCache { -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Wed Nov 19 23:10:43 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Wed, 19 Nov 2014 22:10:43 +0000 Subject: [Jython-checkins] =?utf-8?b?anl0aG9uOiBGaXhlZCBfX2RvY19fIG9mIG9y?= =?utf-8?q?g=2Epython=2Emodules=2Eitertools=2Ecombinations=2E?= Message-ID: <20141119221041.43950.48196@psf.io> https://hg.python.org/jython/rev/6a6ab537b976 changeset: 7418:6a6ab537b976 user: Stefan Richthofer date: Wed Nov 19 23:10:28 2014 +0100 summary: Fixed __doc__ of org.python.modules.itertools.combinations. files: src/org/python/modules/itertools/combinations.java | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/org/python/modules/itertools/combinations.java b/src/org/python/modules/itertools/combinations.java --- a/src/org/python/modules/itertools/combinations.java +++ b/src/org/python/modules/itertools/combinations.java @@ -12,17 +12,16 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.combinations", base = PyObject.class) + at ExposedType(name = "itertools.combinations", base = PyObject.class, doc = combinations.combinations_doc) public class combinations extends PyIterator { public static final PyType TYPE = PyType.fromClass(combinations.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String combinations_doc = "combinations(iterable, r) --> combinations object\n\n" + - "Return successive r-length combinations of elements in the iterable.\n\n" + - "combinations(range(4), 3) --> (0,1,2), (0,1,3), (0,2,3), (1,2,3)"); + "Return successive r-length combinations of elements in the iterable.\n\n" + + "combinations(range(4), 3) --> (0,1,2), (0,1,3), (0,2,3), (1,2,3)"; public combinations() { super(); -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Thu Nov 20 00:46:57 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Wed, 19 Nov 2014 23:46:57 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixed_various_other_=5F=5Fd?= =?utf-8?q?oc=5F=5F-issues_in_org=2Epython=2Emodules=2Eitertools=2E?= Message-ID: <20141119234649.43942.37430@psf.io> https://hg.python.org/jython/rev/f9cc96d0f517 changeset: 7419:f9cc96d0f517 user: Stefan Richthofer date: Thu Nov 20 00:46:39 2014 +0100 summary: Fixed various other __doc__-issues in org.python.modules.itertools. files: src/org/python/modules/itertools/chain.java | 14 ++-- src/org/python/modules/itertools/combinationsWithReplacement.java | 12 ++-- src/org/python/modules/itertools/compress.java | 13 ++-- src/org/python/modules/itertools/count.java | 10 +-- src/org/python/modules/itertools/cycle.java | 11 ++-- src/org/python/modules/itertools/dropwhile.java | 11 ++-- src/org/python/modules/itertools/groupby.java | 7 +- src/org/python/modules/itertools/ifilter.java | 11 ++-- src/org/python/modules/itertools/ifilterfalse.java | 12 ++-- src/org/python/modules/itertools/imap.java | 15 +++-- src/org/python/modules/itertools/islice.java | 7 +- src/org/python/modules/itertools/itertools.java | 2 +- src/org/python/modules/itertools/izip.java | 17 +++--- src/org/python/modules/itertools/izipLongest.java | 18 +++--- src/org/python/modules/itertools/permutations.java | 9 +-- src/org/python/modules/itertools/product.java | 25 ++++----- src/org/python/modules/itertools/repeat.java | 10 ++-- src/org/python/modules/itertools/starmap.java | 11 ++-- src/org/python/modules/itertools/takewhile.java | 11 ++-- 19 files changed, 107 insertions(+), 119 deletions(-) diff --git a/src/org/python/modules/itertools/chain.java b/src/org/python/modules/itertools/chain.java --- a/src/org/python/modules/itertools/chain.java +++ b/src/org/python/modules/itertools/chain.java @@ -15,17 +15,17 @@ import java.util.ArrayList; - at ExposedType(name = "itertools.chain", base = PyObject.class) + at ExposedType(name = "itertools.chain", base = PyObject.class, doc = chain.chain_doc) public class chain extends PyIterator { public static final PyType TYPE = PyType.fromClass(chain.class); private itertools.ItertoolsIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( - "chain(*iterables) --> chain object\n\nReturn a chain object " - + "whose .next() method returns elements from the\nfirst iterable until it is exhausted, then elements" - + " from the next\niterable, until all of the iterables are exhausted."); + public static final String chain_doc = + "chain(*iterables) --> chain object\n\n" + + "Return a chain object whose .next() method returns elements from the\n" + + "first iterable until it is exhausted, then elements from the next\n" + + "iterable, until all of the iterables are exhausted."; public chain() { super(); @@ -94,4 +94,4 @@ return doNext(__iternext__()); } -} \ No newline at end of file +} diff --git a/src/org/python/modules/itertools/combinationsWithReplacement.java b/src/org/python/modules/itertools/combinationsWithReplacement.java --- a/src/org/python/modules/itertools/combinationsWithReplacement.java +++ b/src/org/python/modules/itertools/combinationsWithReplacement.java @@ -13,18 +13,18 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.combinations_with_replacement", base = PyObject.class) + at ExposedType(name = "itertools.combinations_with_replacement", base = PyObject.class, + doc = combinationsWithReplacement.combinations_with_replacement_doc) public class combinationsWithReplacement extends PyIterator { public static final PyType TYPE = PyType.fromClass(combinationsWithReplacement.class); private itertools.ItertoolsIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String combinations_with_replacement_doc = "combinations_with_replacement(iterable, r) --> combinations_with_replacement object\n\n" + - "Return successive r-length combinations of elements in the iterable\n" + - "allowing individual elements to have successive repeats.\n" + - "combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC"); + "Return successive r-length combinations of elements in the iterable\n" + + "allowing individual elements to have successive repeats.\n" + + "combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC"; public combinationsWithReplacement() { super(); diff --git a/src/org/python/modules/itertools/compress.java b/src/org/python/modules/itertools/compress.java --- a/src/org/python/modules/itertools/compress.java +++ b/src/org/python/modules/itertools/compress.java @@ -12,18 +12,17 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.compress", base = PyObject.class) + at ExposedType(name = "itertools.compress", base = PyObject.class, doc = compress.compress_doc) public class compress extends PyIterator { public static final PyType TYPE = PyType.fromClass(compress.class); private itertools.ItertoolsIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( - "compress(data, selectors) --> iterator over selected data\n\n" + - "Return data elements corresponding to true selector elements.\n" + - "Forms a shorter iterator from selected data elements using the\n" + - "selectors to choose the data elements."); + public static final String compress_doc = + "compress(data, selectors) --> iterator over selected data\n\n" + + "Return data elements corresponding to true selector elements.\n" + + "Forms a shorter iterator from selected data elements using the\n" + + "selectors to choose the data elements."; public compress() { super(); diff --git a/src/org/python/modules/itertools/count.java b/src/org/python/modules/itertools/count.java --- a/src/org/python/modules/itertools/count.java +++ b/src/org/python/modules/itertools/count.java @@ -9,15 +9,12 @@ import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedClassMethod; import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; -import java.util.ArrayList; - - at ExposedType(name = "itertools.count", base = PyObject.class) + at ExposedType(name = "itertools.count", base = PyObject.class, doc = count.count_doc) public class count extends PyIterator { public static final PyType TYPE = PyType.fromClass(count.class); @@ -25,8 +22,7 @@ private int counter; private int stepper; - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String count_doc = "count(start=0, step=1) --> count object\n\n" + "Return a count object whose .next() method returns consecutive values.\n" + " Equivalent to:\n" + @@ -35,7 +31,7 @@ " x = firstval\n" + " while 1:\n" + " yield x\n" + - " x += step\n"); + " x += step\n"; public count(PyType subType) { super(subType); diff --git a/src/org/python/modules/itertools/cycle.java b/src/org/python/modules/itertools/cycle.java --- a/src/org/python/modules/itertools/cycle.java +++ b/src/org/python/modules/itertools/cycle.java @@ -13,17 +13,16 @@ import java.util.ArrayList; import java.util.List; - at ExposedType(name = "itertools.count", base = PyObject.class) + at ExposedType(name = "itertools.count", base = PyObject.class, doc = cycle.cycle_doc) public class cycle extends PyIterator { public static final PyType TYPE = PyType.fromClass(cycle.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( - "cycle(iterable) --> cycle object\n\n" + - "Return elements from the iterable until it is exhausted.\n" + - "Then repeat the sequence indefinitely."); + public static final String cycle_doc = + "cycle(iterable) --> cycle object\n\n" + + "Return elements from the iterable until it is exhausted.\n" + + "Then repeat the sequence indefinitely."; public cycle() { super(); diff --git a/src/org/python/modules/itertools/dropwhile.java b/src/org/python/modules/itertools/dropwhile.java --- a/src/org/python/modules/itertools/dropwhile.java +++ b/src/org/python/modules/itertools/dropwhile.java @@ -11,7 +11,7 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.dropwhile", base = PyObject.class) + at ExposedType(name = "itertools.dropwhile", base = PyObject.class, doc = dropwhile.dropwhile_doc) public class dropwhile extends PyIterator { public static final PyType TYPE = PyType.fromClass(dropwhile.class); @@ -30,11 +30,10 @@ dropwhile___init__(predicate, iterable); } - @ExposedGet - public static PyString __doc__ = new PyString( - "dropwhile(predicate, iterable) --> dropwhile object\n\n" - + "Drop items from the iterable while predicate(item) is true.\n" - + "Afterwards, return every element until the iterable is exhausted."); + public static final String dropwhile_doc = + "dropwhile(predicate, iterable) --> dropwhile object\n\n" + + "Drop items from the iterable while predicate(item) is true.\n" + + "Afterwards, return every element until the iterable is exhausted."; /** * Create an iterator that drops items from the iterable while predicate(item) diff --git a/src/org/python/modules/itertools/groupby.java b/src/org/python/modules/itertools/groupby.java --- a/src/org/python/modules/itertools/groupby.java +++ b/src/org/python/modules/itertools/groupby.java @@ -14,7 +14,7 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.groupby", base = PyObject.class) + at ExposedType(name = "itertools.groupby", base = PyObject.class, doc = groupby.groupby_doc) public class groupby extends PyIterator { public static final PyType TYPE = PyType.fromClass(groupby.class); @@ -38,10 +38,9 @@ groupby___init__(iterable, keyfunc); } - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String groupby_doc = "groupby(iterable[, keyfunc]) -> create an iterator which returns\n" + - "(key, sub-iterator) grouped by each value of key(value)."); + "(key, sub-iterator) grouped by each value of key(value)."; /** * Creates an iterator that returns the items of the iterable for which diff --git a/src/org/python/modules/itertools/ifilter.java b/src/org/python/modules/itertools/ifilter.java --- a/src/org/python/modules/itertools/ifilter.java +++ b/src/org/python/modules/itertools/ifilter.java @@ -12,7 +12,7 @@ import org.python.expose.ExposedType; - at ExposedType(name = "itertools.ifilter", base = PyObject.class) + at ExposedType(name = "itertools.ifilter", base = PyObject.class, doc = ifilter.ifilter_doc) public class ifilter extends PyIterator { public static final PyType TYPE = PyType.fromClass(ifilter.class); @@ -31,11 +31,10 @@ ifilter___init__(predicate, iterable); } - @ExposedGet - public static PyString __doc__ = new PyString( - "ifilter(function or None, sequence) --> ifilter object\n\n" - + "Return those items of sequence for which function(item) is true.\nIf function is None, " - + "return the items that are true."); + public static final String ifilter_doc = + "ifilter(function or None, sequence) --> ifilter object\n\n" + + "Return those items of sequence for which function(item) is true.\n" + + "If function is None, return the items that are true."; /** * Creates an iterator that returns the items of the iterable for which diff --git a/src/org/python/modules/itertools/ifilterfalse.java b/src/org/python/modules/itertools/ifilterfalse.java --- a/src/org/python/modules/itertools/ifilterfalse.java +++ b/src/org/python/modules/itertools/ifilterfalse.java @@ -10,7 +10,8 @@ import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.ifilterfalse", base = PyObject.class) + at ExposedType(name = "itertools.ifilterfalse", base = PyObject.class, + doc = ifilterfalse.ifilterfalse_doc) public class ifilterfalse extends PyIterator { public static final PyType TYPE = PyType.fromClass(ifilterfalse.class); @@ -29,11 +30,10 @@ ifilterfalse___init__(predicate, iterable); } - @ExposedGet - public static PyString __doc__ = new PyString( - "'ifilterfalse(function or None, sequence) --> ifilterfalse object\n\n" - + "Return those items of sequence for which function(item) is false.\nIf function is None, " - + "return the items that are false.'"); + public static final String ifilterfalse_doc = + "'ifilterfalse(function or None, sequence) --> ifilterfalse object\n\n" + + "Return those items of sequence for which function(item) is false.\n" + + "If function is None, return the items that are false.'"; /** * Creates an iterator that returns the items of the iterable for which diff --git a/src/org/python/modules/itertools/imap.java b/src/org/python/modules/itertools/imap.java --- a/src/org/python/modules/itertools/imap.java +++ b/src/org/python/modules/itertools/imap.java @@ -12,18 +12,19 @@ import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.imap", base = PyObject.class) + at ExposedType(name = "itertools.imap", base = PyObject.class, doc = imap.imap_doc) public class imap extends PyIterator { public static final PyType TYPE = PyType.fromClass(imap.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( - "'map(func, *iterables) --> imap object\n\nMake an iterator that computes the " - + "function using arguments from\neach of the iterables.\tLike map() except that it returns\n" - + "an iterator instead of a list and that it stops when the shortest\niterable is exhausted " - + "instead of filling in None for shorter\niterables."); + public static final String imap_doc = + "'map(func, *iterables) --> imap object\n\n" + + "Make an iterator that computes the function using arguments from\n" + + "each of the iterables.\tLike map() except that it returns\n" + + "an iterator instead of a list and that it stops when the shortest\n" + + "iterable is exhausted instead of filling in None for shorter\n" + + "iterables."; public imap() { super(); diff --git a/src/org/python/modules/itertools/islice.java b/src/org/python/modules/itertools/islice.java --- a/src/org/python/modules/itertools/islice.java +++ b/src/org/python/modules/itertools/islice.java @@ -13,21 +13,20 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.islice", base = PyObject.class) + at ExposedType(name = "itertools.islice", base = PyObject.class, doc = islice.islice_doc) public class islice extends PyIterator { public static final PyType TYPE = PyType.fromClass(islice.class); private itertools.ItertoolsIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String islice_doc = "islice(iterable, [start,] stop [, step]) --> islice object\n\n" + "Return an iterator whose next() method returns selected values from an\n" + "iterable. If start is specified, will skip all preceding elements;\n" + "otherwise, start defaults to zero. Step defaults to one. If\n" + "specified as another value, step determines how many values are \n" + "skipped between successive calls. Works like a slice() on a list\n" + - "but returns an iterator."); + "but returns an iterator."; public islice() { super(); diff --git a/src/org/python/modules/itertools/itertools.java b/src/org/python/modules/itertools/itertools.java --- a/src/org/python/modules/itertools/itertools.java +++ b/src/org/python/modules/itertools/itertools.java @@ -18,7 +18,7 @@ */ public class itertools implements ClassDictInit { - public static PyString __doc__ = new PyString( + public static final PyString __doc__ = new PyString( "Functional tools for creating and using iterators.\n\nInfinite iterators:\n" + "count([n]) --> n, n+1, n+2, ...\n" + "cycle(p) --> p0, p1, ... plast, p0, p1, ...\n" diff --git a/src/org/python/modules/itertools/izip.java b/src/org/python/modules/itertools/izip.java --- a/src/org/python/modules/itertools/izip.java +++ b/src/org/python/modules/itertools/izip.java @@ -13,19 +13,20 @@ import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.izip", base = PyObject.class) + at ExposedType(name = "itertools.izip", base = PyObject.class, doc = izip.izip_doc) public class izip extends PyIterator { public static final PyType TYPE = PyType.fromClass(izip.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( - "izip(iter1 [,iter2 [...]]) --> izip object\n\nReturn an izip object " - + "whose .next() method returns a tuple where\nthe i-th element comes from the i-th iterable argument. " - + "The .next()\nmethod continues until the shortest iterable in the argument sequence\nis exhausted and then it " - + "raises StopIteration. Works like the zip()\nfunction but consumes less memory by returning an iterator " - + "instead of\na list."); + public static final String izip_doc = + "izip(iter1 [,iter2 [...]]) --> izip object\n\n" + + "Return an izip object whose .next() method returns a tuple where\n" + + "the i-th element comes from the i-th iterable argument. The .next()\n" + + "method continues until the shortest iterable in the argument sequence\n" + + "is exhausted and then it raises StopIteration. Works like the zip()\n" + + "function but consumes less memory by returning an iterator instead of\n" + + "a list."; public izip() { super(); diff --git a/src/org/python/modules/itertools/izipLongest.java b/src/org/python/modules/itertools/izipLongest.java --- a/src/org/python/modules/itertools/izipLongest.java +++ b/src/org/python/modules/itertools/izipLongest.java @@ -12,7 +12,8 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.izip_longest", base = PyObject.class) + at ExposedType(name = "itertools.izip_longest", base = PyObject.class, + doc = izipLongest.izip_longest_doc) public class izipLongest extends PyIterator { public static final PyType TYPE = PyType.fromClass(izipLongest.class); @@ -31,15 +32,14 @@ izipLongest___init__(iterables, fillvalue); } - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String izip_longest_doc = "izip_longest(iter1 [,iter2 [...]], [fillvalue=None]) --> izip_longest object\n\n" + - "Return an izip_longest object whose .next() method returns a tuple where\n" + - "the i-th element comes from the i-th iterable argument. The .next()\n" + - "method continues until the longest iterable in the argument sequence\n" + - "is exhausted and then it raises StopIteration. When the shorter iterables\n" + - "are exhausted, the fillvalue is substituted in their place. The fillvalue\n" + - "defaults to None or can be specified by a keyword argument."); + "Return an izip_longest object whose .next() method returns a tuple where\n" + + "the i-th element comes from the i-th iterable argument. The .next()\n" + + "method continues until the longest iterable in the argument sequence\n" + + "is exhausted and then it raises StopIteration. When the shorter iterables\n" + + "are exhausted, the fillvalue is substituted in their place. The fillvalue\n" + + "defaults to None or can be specified by a keyword argument."; /** * Create an iterator that returns items from the iterable while predicate(item) diff --git a/src/org/python/modules/itertools/permutations.java b/src/org/python/modules/itertools/permutations.java --- a/src/org/python/modules/itertools/permutations.java +++ b/src/org/python/modules/itertools/permutations.java @@ -13,16 +13,15 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.permutations", base = PyObject.class) + at ExposedType(name = "itertools.permutations", base = PyObject.class, doc = permutations.permutations_doc) public class permutations extends PyIterator { public static final PyType TYPE = PyType.fromClass(permutations.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String permutations_doc = "permutations(iterable[, r]) --> permutations object\n\n" + - "Return successive r-length permutations of elements in the iterable.\n\n" + - "permutations(range(3), 2) --> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)"); + "Return successive r-length permutations of elements in the iterable.\n\n" + + "permutations(range(3), 2) --> (0,1), (0,2), (1,0), (1,2), (2,0), (2,1)"; public permutations() { super(); diff --git a/src/org/python/modules/itertools/product.java b/src/org/python/modules/itertools/product.java --- a/src/org/python/modules/itertools/product.java +++ b/src/org/python/modules/itertools/product.java @@ -12,25 +12,24 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.product", base = PyObject.class) + at ExposedType(name = "itertools.product", base = PyObject.class, doc = product.product_doc) public class product extends PyIterator { public static final PyType TYPE = PyType.fromClass(product.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( + public static final String product_doc = "product(*iterables) --> product object\n\n" + - "Cartesian product of input iterables. Equivalent to nested for-loops.\n\n" + - "For example, product(A, B) returns the same as: ((x,y) for x in A for y in B).\n" + - "The leftmost iterators are in the outermost for-loop, so the output tuples\n" + - "cycle in a manner similar to an odometer (with the rightmost element changing\n" + - "on every iteration).\n\n" + - "To compute the product of an iterable with itself, specify the number\n" + - "of repetitions with the optional repeat keyword argument. For example,\n" + - "product(A, repeat=4) means the same as product(A, A, A, A).\n\n" + - "product('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\n" + - "product((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ..."); + "Cartesian product of input iterables. Equivalent to nested for-loops.\n\n" + + "For example, product(A, B) returns the same as: ((x,y) for x in A for y in B).\n" + + "The leftmost iterators are in the outermost for-loop, so the output tuples\n" + + "cycle in a manner similar to an odometer (with the rightmost element changing\n" + + "on every iteration).\n\n" + + "To compute the product of an iterable with itself, specify the number\n" + + "of repetitions with the optional repeat keyword argument. For example,\n" + + "product(A, repeat=4) means the same as product(A, A, A, A).\n\n" + + "product('ab', range(3)) --> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)\n" + + "product((0,1), (0,1), (0,1)) --> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0) ..."; public product() { super(); diff --git a/src/org/python/modules/itertools/repeat.java b/src/org/python/modules/itertools/repeat.java --- a/src/org/python/modules/itertools/repeat.java +++ b/src/org/python/modules/itertools/repeat.java @@ -13,7 +13,7 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.repeat", base = PyObject.class) + at ExposedType(name = "itertools.repeat", base = PyObject.class, doc = repeat.repeat_doc) public class repeat extends PyIterator { public static final PyType TYPE = PyType.fromClass(repeat.class); @@ -21,10 +21,10 @@ private PyObject object; private int counter; - @ExposedGet - public static PyString __doc__ = new PyString( - "'repeat(element [,times]) -> create an iterator which returns the element\n" - + "for the specified number of times. If not specified, returns the element\nendlessly."); + public static final String repeat_doc = + "'repeat(element [,times]) -> create an iterator which returns the element\n" + + "for the specified number of times. If not specified, returns the element\n" + + "endlessly."; public repeat() { super(); diff --git a/src/org/python/modules/itertools/starmap.java b/src/org/python/modules/itertools/starmap.java --- a/src/org/python/modules/itertools/starmap.java +++ b/src/org/python/modules/itertools/starmap.java @@ -12,17 +12,16 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.starmap", base = PyObject.class) + at ExposedType(name = "itertools.starmap", base = PyObject.class, doc = starmap.starmap_doc) public class starmap extends PyIterator { public static final PyType TYPE = PyType.fromClass(starmap.class); private PyIterator iter; - @ExposedGet - public static PyString __doc__ = new PyString( - "starmap(function, sequence) --> starmap object\n\nReturn an " - + "iterator whose values are returned from the function evaluated\nwith an argument tuple taken from the " - + "given sequence."); + public static final String starmap_doc = + "starmap(function, sequence) --> starmap object\n\n" + + "Return an iterator whose values are returned from the function evaluated\n" + + "with an argument tuple taken from the given sequence."; public starmap() { super(); diff --git a/src/org/python/modules/itertools/takewhile.java b/src/org/python/modules/itertools/takewhile.java --- a/src/org/python/modules/itertools/takewhile.java +++ b/src/org/python/modules/itertools/takewhile.java @@ -11,7 +11,7 @@ import org.python.expose.ExposedNew; import org.python.expose.ExposedType; - at ExposedType(name = "itertools.takewhile", base = PyObject.class) + at ExposedType(name = "itertools.takewhile", base = PyObject.class, doc = takewhile.takewhile_doc) public class takewhile extends PyIterator { public static final PyType TYPE = PyType.fromClass(takewhile.class); @@ -30,11 +30,10 @@ takewhile___init__(predicate, iterable); } - @ExposedGet - public static PyString __doc__ = new PyString( - "takewhile(predicate, iterable) --> takewhile object\n\n" - + "Return successive entries from an iterable as long as the \n" - + "predicate evaluates to true for each entry."); + public static final String takewhile_doc = + "takewhile(predicate, iterable) --> takewhile object\n\n" + + "Return successive entries from an iterable as long as the \n" + + "predicate evaluates to true for each entry."; /** * Create an iterator that returns items from the iterable while predicate(item) -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Fri Nov 21 03:18:25 2014 From: jython-checkins at python.org (jim.baker) Date: Fri, 21 Nov 2014 02:18:25 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Now_uses_java=2Eutil=2EWeak?= =?utf-8?q?HashMap_to_back_weakref=2EWeakSet?= Message-ID: <20141121021824.7142.92011@psf.io> https://hg.python.org/jython/rev/f51940218cf2 changeset: 7420:f51940218cf2 user: Jim Baker date: Thu Nov 20 19:17:46 2014 -0700 summary: Now uses java.util.WeakHashMap to back weakref.WeakSet Adds jythonlib.set_builder so that Python sets/derived sets can be built with an arbitrary backing java.util.Set. Updates test_weakset to 2.7 and fixes all outstanding skips. Updates gderived.py so that it can include arbitrary additional imports, based on a new import directive. files: Lib/_weakrefset.py | 213 +--------- Lib/test/test_set.py | 3 +- Lib/test/test_weakset.py | 71 ++- src/org/python/core/BaseSet.java | 8 +- src/org/python/core/PyDictionaryDerived.java | 18 +- src/org/python/core/PySet.java | 9 + src/org/python/core/PySetDerived.java | 10 + src/org/python/modules/_jythonlib/_jythonlib.java | 2 + src/org/python/modules/_jythonlib/set_builder.java | 48 ++ src/templates/dict.derived | 10 + src/templates/gderived.py | 25 + src/templates/set.derived | 11 + 12 files changed, 186 insertions(+), 242 deletions(-) diff --git a/Lib/_weakrefset.py b/Lib/_weakrefset.py --- a/Lib/_weakrefset.py +++ b/Lib/_weakrefset.py @@ -2,211 +2,20 @@ # This code is separated-out because it is needed # by abc.py to load everything else at startup. -from _weakref import ref +from java.util import WeakHashMap +from java.util.Collections import newSetFromMap, synchronizedMap +from jythonlib import set_builder, MapMaker __all__ = ['WeakSet'] -class _IterationGuard(object): - # This context manager registers itself in the current iterators of the - # weak container, such as to delay all removals until the context manager - # exits. - # This technique should be relatively thread-safe (since sets are). +class WeakSet(set): - def __init__(self, weakcontainer): - # Don't create cycles - self.weakcontainer = ref(weakcontainer) + def __new__(cls, data=None): + def _build(): + # Although not specified in the docs, WeakSet supports equality on + # keys, as seen in test_weakset and its use of testing class + # SomeClass. Therefore we cannot use MapMaker in this case. + return newSetFromMap(synchronizedMap(WeakHashMap())) - def __enter__(self): - w = self.weakcontainer() - if w is not None: - w._iterating.add(self) - return self - - def __exit__(self, e, t, b): - w = self.weakcontainer() - if w is not None: - s = w._iterating - s.remove(self) - if not s: - w._commit_removals() - - -class WeakSet(object): - def __init__(self, data=None): - self.data = set() - def _remove(item, selfref=ref(self)): - self = selfref() - if self is not None: - if self._iterating: - self._pending_removals.append(item) - else: - self.data.discard(item) - self._remove = _remove - # A list of keys to be removed - self._pending_removals = [] - self._iterating = set() - if data is not None: - self.update(data) - - def _commit_removals(self): - l = self._pending_removals - discard = self.data.discard - while l: - discard(l.pop()) - - def __iter__(self): - with _IterationGuard(self): - for itemref in self.data: - item = itemref() - if item is not None: - yield item - - def __len__(self): - return sum(x() is not None for x in self.data) - - def __contains__(self, item): - return ref(item) in self.data - - def __reduce__(self): - return (self.__class__, (list(self),), - getattr(self, '__dict__', None)) - - __hash__ = None - - def add(self, item): - if self._pending_removals: - self._commit_removals() - self.data.add(ref(item, self._remove)) - - def clear(self): - if self._pending_removals: - self._commit_removals() - self.data.clear() - - def copy(self): - return self.__class__(self) - - def pop(self): - if self._pending_removals: - self._commit_removals() - while True: - try: - itemref = self.data.pop() - except KeyError: - raise KeyError('pop from empty WeakSet') - item = itemref() - if item is not None: - return item - - def remove(self, item): - if self._pending_removals: - self._commit_removals() - self.data.remove(ref(item)) - - def discard(self, item): - if self._pending_removals: - self._commit_removals() - self.data.discard(ref(item)) - - def update(self, other): - if self._pending_removals: - self._commit_removals() - if isinstance(other, self.__class__): - self.data.update(other.data) - else: - for element in other: - self.add(element) - - def __ior__(self, other): - self.update(other) - return self - - # Helper functions for simple delegating methods. - def _apply(self, other, method): - if not isinstance(other, self.__class__): - other = self.__class__(other) - newdata = method(other.data) - newset = self.__class__() - newset.data = newdata - return newset - - def difference(self, other): - return self._apply(other, self.data.difference) - __sub__ = difference - - def difference_update(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.difference_update(ref(item) for item in other) - def __isub__(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.difference_update(ref(item) for item in other) - return self - - def intersection(self, other): - return self._apply(other, self.data.intersection) - __and__ = intersection - - def intersection_update(self, other): - if self._pending_removals: - self._commit_removals() - self.data.intersection_update(ref(item) for item in other) - def __iand__(self, other): - if self._pending_removals: - self._commit_removals() - self.data.intersection_update(ref(item) for item in other) - return self - - def issubset(self, other): - return self.data.issubset(ref(item) for item in other) - __lt__ = issubset - - def __le__(self, other): - return self.data <= set(ref(item) for item in other) - - def issuperset(self, other): - return self.data.issuperset(ref(item) for item in other) - __gt__ = issuperset - - def __ge__(self, other): - return self.data >= set(ref(item) for item in other) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.data == set(ref(item) for item in other) - - def symmetric_difference(self, other): - return self._apply(other, self.data.symmetric_difference) - __xor__ = symmetric_difference - - def symmetric_difference_update(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.symmetric_difference_update(ref(item) for item in other) - def __ixor__(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.symmetric_difference_update(ref(item) for item in other) - return self - - def union(self, other): - return self._apply(other, self.data.union) - __or__ = union - - def isdisjoint(self, other): - return len(self.intersection(other)) == 0 + return set_builder(_build, cls)(data) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -310,7 +310,7 @@ fo.close() test_support.unlink(test_support.TESTFN) - @unittest.skipIf(test_support.is_jython, "FIXME: Not yet sure how to fix this") + @unittest.skipIf(test_support.is_jython, "Not meaningful for Jython") def test_do_not_rehash_dict_keys(self): n = 10 d = dict.fromkeys(map(HashCountingInt, xrange(n))) @@ -917,6 +917,7 @@ set('abc') set(gooditer()) + @unittest.skipIf(test_support.is_jython, "Jython provides stronger support for concurrent updates") def test_changingSizeWhileIterating(self): s = set([1,2,3]) try: diff --git a/Lib/test/test_weakset.py b/Lib/test/test_weakset.py --- a/Lib/test/test_weakset.py +++ b/Lib/test/test_weakset.py @@ -1,5 +1,18 @@ +# Jython specific notes: +# +# 1. Deletion (del) or reliance on going out of scope must be followed +# by a gc.collect(); use extra_collect() if a callback will be +# tested as well. +# +# 2. Avoid eventual consistency issues in the length computation by +# computing the len of the list of the WeakSet +# +# 3. Jython can weakly refer to any object, unlike CPython, so don't +# test for nonexistent TypeErrors + import unittest from test import test_support +from test.test_weakref import extra_collect from weakref import proxy, ref, WeakSet import operator import copy @@ -30,6 +43,9 @@ def __hash__(self): return hash((SomeClass, self.value)) + def __repr__(self): + return "SC(%s,%s)" % (self.value, id(self.value)) + class RefCycle(object): def __init__(self): self.cycle = self @@ -63,14 +79,13 @@ def test_new_or_init(self): self.assertRaises(TypeError, WeakSet, [], 2) - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_len(self): self.assertEqual(len(self.s), len(self.d)) self.assertEqual(len(self.fs), 1) del self.obj + gc.collect() self.assertEqual(len(self.fs), 0) - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_contains(self): for c in self.letters: self.assertEqual(c in self.s, c in self.d) @@ -78,6 +93,7 @@ self.assertNotIn(1, self.s) self.assertIn(self.obj, self.fs) del self.obj + gc.collect() self.assertNotIn(SomeClass('F'), self.fs) def test_union(self): @@ -92,10 +108,11 @@ c = C(self.items2) self.assertEqual(self.s.union(c), x) del c - self.assertEqual(len(u), len(self.items) + len(self.items2)) + gc.collect() + self.assertEqual(len(list(u)), len(list(self.items)) + len(list(self.items2))) self.items2.pop() gc.collect() - self.assertEqual(len(u), len(self.items) + len(self.items2)) + self.assertEqual(len(list(u)), len(list(self.items)) + len(list(self.items2))) def test_or(self): i = self.s.union(self.items2) @@ -115,7 +132,7 @@ self.assertEqual(len(i), len(self.items2)) self.items2.pop() gc.collect() - self.assertEqual(len(i), len(self.items2)) + self.assertEqual(len(list(i)), len(list(self.items2))) def test_isdisjoint(self): self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) @@ -149,7 +166,7 @@ self.assertEqual(len(i), len(self.items) + len(self.items2)) self.items2.pop() gc.collect() - self.assertEqual(len(i), len(self.items) + len(self.items2)) + self.assertEqual(len(list(i)), len(list(self.items)) + len(list(self.items2))) def test_xor(self): i = self.s.symmetric_difference(self.items2) @@ -167,14 +184,12 @@ self.assertFalse(set('a').issubset('cbs')) self.assertFalse(set('cbs').issuperset('a')) - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_lt(self): self.assertTrue(self.ab_weakset < self.abcde_weakset) self.assertFalse(self.abcde_weakset < self.def_weakset) self.assertFalse(self.ab_weakset < self.ab_weakset) self.assertFalse(WeakSet() < WeakSet()) - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_gt(self): self.assertTrue(self.abcde_weakset > self.ab_weakset) self.assertFalse(self.abcde_weakset > self.def_weakset) @@ -229,7 +244,6 @@ self.assertEqual(self.s, dup) self.assertNotEqual(id(self.s), id(dup)) - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_add(self): x = SomeClass('Q') self.s.add(x) @@ -237,25 +251,29 @@ dup = self.s.copy() self.s.add(x) self.assertEqual(self.s, dup) - self.assertRaises(TypeError, self.s.add, []) + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects + self.assertRaises(TypeError, self.s.add, []) self.fs.add(Foo()) - self.assertTrue(len(self.fs) == 1) + gc.collect() # CPython assumes Foo() went out of scope and was collected, so ensure the same + self.assertEqual(len(list(self.fs)), 1) self.fs.add(self.obj) - self.assertTrue(len(self.fs) == 1) + self.assertEqual(len(list(self.fs)), 1) def test_remove(self): x = SomeClass('a') self.s.remove(x) self.assertNotIn(x, self.s) self.assertRaises(KeyError, self.s.remove, x) - self.assertRaises(TypeError, self.s.remove, []) + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects + self.assertRaises(TypeError, self.s.remove, []) def test_discard(self): a, q = SomeClass('a'), SomeClass('Q') self.s.discard(a) self.assertNotIn(a, self.s) self.s.discard(q) - self.assertRaises(TypeError, self.s.discard, []) + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects + self.assertRaises(TypeError, self.s.discard, []) def test_pop(self): for i in range(len(self.s)): @@ -306,8 +324,9 @@ self.assertIn(c, self.s) else: self.assertNotIn(c, self.s) - self.assertRaises(TypeError, self.s.difference_update, [[]]) - self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects + self.assertRaises(TypeError, self.s.difference_update, [[]]) + self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) def test_isub(self): self.s -= set(self.items2) @@ -348,15 +367,17 @@ self.assertEqual(t, WeakSet()) def test_eq(self): - # issue 5964 - self.assertTrue(self.s == self.s) - self.assertTrue(self.s == WeakSet(self.items)) - self.assertFalse(self.s == set(self.items)) - self.assertFalse(self.s == list(self.items)) - self.assertFalse(self.s == tuple(self.items)) - self.assertFalse(self.s == 1) + # issue 5964 (http://bugs.python.org/issue5964) + self.assertEqual(self.s, self.s) + self.assertEqual(self.s, WeakSet(self.items)) + # Jython diverges here in the next test because it constructs + # WeakSet as a subclass of set; this seems to be the proper + # thing to do given what is the typical comparison + self.assertEqual(self.s, set(self.items)) + self.assertNotEqual(self.s, list(self.items)) + self.assertNotEqual(self.s, tuple(self.items)) + self.assertNotEqual(self.s, 1) - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") def test_weak_destroy_while_iterating(self): # Issue #7105: iterators shouldn't crash when a key is implicitly removed # Create new items to be sure no-one else holds a reference @@ -370,6 +391,7 @@ # We have removed either the first consumed items, or another one self.assertIn(len(list(it)), [len(items), len(items) - 1]) del it + gc.collect() # The removal has been committed self.assertEqual(len(s), len(items)) @@ -410,6 +432,7 @@ items = [RefCycle() for i in range(N)] s = WeakSet(items) del items + gc.collect() it = iter(s) try: next(it) diff --git a/src/org/python/core/BaseSet.java b/src/org/python/core/BaseSet.java --- a/src/org/python/core/BaseSet.java +++ b/src/org/python/core/BaseSet.java @@ -231,9 +231,6 @@ @Override public PyObject __iternext__() { - if (size != size()) { - throw Py.RuntimeError("set changed size during iteration"); - } if (iterator.hasNext()) { return iterator.next(); } @@ -481,7 +478,7 @@ } /** - * Create a new backingMap, boolean useBackingMap) { - super(subtype, backingMap, useBackingMap); - slots=new PyObject[subtype.getNumSlots()]; - dict=subtype.instDict(); - if (subtype.needsFinalizer()) { - finalizeTrigger=FinalizeTrigger.makeTrigger(this); - } - } - public PyString __str__() { PyType self_type=getType(); PyObject impl=self_type.lookup("__str__"); @@ -1149,6 +1140,15 @@ return super.__coerce_ex__(o); } + public PyDictionaryDerived(PyType subtype,ConcurrentMap backingMap,boolean useBackingMap) { + super(subtype,backingMap,useBackingMap); + slots=new PyObject[subtype.getNumSlots()]; + dict=subtype.instDict(); + if (subtype.needsFinalizer()) { + finalizeTrigger=FinalizeTrigger.makeTrigger(this); + } + } + public String toString() { PyType self_type=getType(); PyObject impl=self_type.lookup("__repr__"); diff --git a/src/org/python/core/PySet.java b/src/org/python/core/PySet.java --- a/src/org/python/core/PySet.java +++ b/src/org/python/core/PySet.java @@ -2,6 +2,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Set; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; @@ -34,6 +35,14 @@ super(TYPE, _update(Generic.concurrentSet(), data)); } + public PySet(Set backing_set, PyObject data) { + super(TYPE, _update(backing_set, data)); + } + + public PySet(PyType type, Set backing_set, PyObject data) { + super(type, _update(backing_set, data)); + } + @ExposedNew @ExposedMethod(doc = BuiltinDocs.set___init___doc) final void set___init__(PyObject[] args, String[] kwds) { diff --git a/src/org/python/core/PySetDerived.java b/src/org/python/core/PySetDerived.java --- a/src/org/python/core/PySetDerived.java +++ b/src/org/python/core/PySetDerived.java @@ -1,6 +1,7 @@ /* Generated file, do not modify. See jython/src/templates/gderived.py. */ package org.python.core; +import java.util.Set; import java.io.Serializable; import org.python.core.finalization.FinalizeTrigger; import org.python.core.finalization.FinalizablePyObjectDerived; @@ -1139,6 +1140,15 @@ return super.__coerce_ex__(o); } + public PySetDerived(PyType subtype,Set backing_set,PyObject data) { + super(subtype,backing_set,data); + slots=new PyObject[subtype.getNumSlots()]; + dict=subtype.instDict(); + if (subtype.needsFinalizer()) { + finalizeTrigger=FinalizeTrigger.makeTrigger(this); + } + } + public String toString() { PyType self_type=getType(); PyObject impl=self_type.lookup("__repr__"); diff --git a/src/org/python/modules/_jythonlib/_jythonlib.java b/src/org/python/modules/_jythonlib/_jythonlib.java --- a/src/org/python/modules/_jythonlib/_jythonlib.java +++ b/src/org/python/modules/_jythonlib/_jythonlib.java @@ -15,6 +15,8 @@ dict.__setitem__("__doc__", __doc__); dict.__setitem__("__module__", new PyString("_jythonlib")); dict.__setitem__("dict_builder", dict_builder.TYPE); + dict.__setitem__("set_builder", set_builder.TYPE); + // Hide from Python dict.__setitem__("classDictInit", null); diff --git a/src/org/python/modules/_jythonlib/set_builder.java b/src/org/python/modules/_jythonlib/set_builder.java new file mode 100644 --- /dev/null +++ b/src/org/python/modules/_jythonlib/set_builder.java @@ -0,0 +1,48 @@ +/* Copyright (c) Jython Developers */ +package org.python.modules._jythonlib; + +import org.python.core.Py; +import org.python.core.PyObject; +import org.python.core.PySet; +import org.python.core.PySetDerived; +import org.python.core.PyType; + +import java.util.Set; + + +/* Support building PySet objects with arbitrary backing Set objects + * Uses a factory for efficiency. + * + * See the very similar dict_builder for more insight. But note that we do not + * impose the restriction that the set be concurrent, although this is generally + * what we would want. + */ + +public class set_builder extends PyObject { + + public static final PyType TYPE = PyType.fromClass(set_builder.class); + private final PyObject factory; + private final PyType set_type; + + public set_builder(PyObject factory) { + super(); + this.factory = factory; + this.set_type = null; + } + + public set_builder(PyObject factory, PyType set_type) { + super(); + this.factory = factory; + this.set_type = set_type; + } + + public PyObject __call__(PyObject iterable) { + Set backing_set = (Set) (factory.__call__().__tojava__(Set.class)); + if (set_type == null) { + return new PySet(backing_set, iterable == Py.None ? null : iterable); + } else { + return new PySetDerived(set_type, backing_set, iterable == Py.None ? null : iterable); + } + } + +} diff --git a/src/templates/dict.derived b/src/templates/dict.derived --- a/src/templates/dict.derived +++ b/src/templates/dict.derived @@ -2,3 +2,13 @@ want_dict: true ctr: incl: object +import: java.util.concurrent.ConcurrentMap +rest: + public PyDictionaryDerived(PyType subtype, ConcurrentMap backingMap, boolean useBackingMap) { + super(subtype, backingMap, useBackingMap); + slots=new PyObject[subtype.getNumSlots()]; + dict=subtype.instDict(); + if (subtype.needsFinalizer()) { + finalizeTrigger=FinalizeTrigger.makeTrigger(this); + } + } diff --git a/src/templates/gderived.py b/src/templates/gderived.py --- a/src/templates/gderived.py +++ b/src/templates/gderived.py @@ -25,6 +25,8 @@ modif_re = re.compile(r"(?:\((\w+)\))?(\w+)") +added_imports = [] + # os.path.samefile unavailable on Windows before Python v3.2 if hasattr(os.path, "samefile"): # Good: available on this platform @@ -43,6 +45,7 @@ 'unary1', 'binary', 'ibinary', 'rest', + 'import', 'no_toString' ] @@ -82,6 +85,10 @@ self.auxiliary = aux_gen.global_bindings return self.auxiliary[name] + def dire_import(self, name, parm, body): + global added_imports + added_imports = [x.strip() for x in parm.split(",")] + def dire_require(self, name, parm, body): if body is not None: self.invalid('require', 'non-empty body') @@ -217,6 +224,7 @@ directives.execute(directives.load(fn), gen) result = gen.generate() result = hack_derived_header(outfile, result) + result = add_imports(outfile, result) print >> open(outfile, 'w'), result #gen.debug() @@ -245,6 +253,23 @@ return '\n'.join(result) +def add_imports(fn, result): + if not added_imports: + return result + print 'Adding imports for: %s' % fn + result = result.splitlines() + + def f(): + added = False + for line in result: + if not added and line.startswith("import "): + added = True + for addition in added_imports: + yield "import %s;" % (addition,) + yield line + + return '\n'.join(f()) + if __name__ == '__main__': from gexpose import load_mappings, usage diff --git a/src/templates/set.derived b/src/templates/set.derived --- a/src/templates/set.derived +++ b/src/templates/set.derived @@ -2,3 +2,14 @@ want_dict: true ctr: incl: object +import: java.util.Set +rest: + public PySetDerived(PyType subtype, Set backing_set, PyObject data) { + super(subtype, backing_set, data); + slots=new PyObject[subtype.getNumSlots()]; + dict=subtype.instDict(); + if (subtype.needsFinalizer()) { + finalizeTrigger=FinalizeTrigger.makeTrigger(this); + } + } + -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sat Nov 29 00:31:17 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Fri, 28 Nov 2014 23:31:17 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixed_some_more_=5F=5Fdoc?= =?utf-8?b?X19zLg==?= Message-ID: <20141128233114.55103.82177@psf.io> https://hg.python.org/jython/rev/2b52d49a4c0f changeset: 7421:2b52d49a4c0f user: Stefan Richthofer date: Sat Nov 29 00:30:59 2014 +0100 summary: Fixed some more __doc__s. Fixed the doc of _csv.PyReader, added doc of _csv.PyWriter. Some cosmetics in docs of itertools. files: src/org/python/modules/_csv/PyReader.java | 6 +- src/org/python/modules/_csv/PyWriter.java | 14 +- src/org/python/modules/itertools/PyTeeIterator.java | 5 +- src/org/python/modules/itertools/itertools.java | 56 ++++----- src/org/python/modules/itertools/takewhile.java | 2 +- 5 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/org/python/modules/_csv/PyReader.java b/src/org/python/modules/_csv/PyReader.java --- a/src/org/python/modules/_csv/PyReader.java +++ b/src/org/python/modules/_csv/PyReader.java @@ -15,16 +15,16 @@ * * Analogous to CPython's _csv.c::ReaderObj struct. */ - at ExposedType(name = "_csv.reader") + at ExposedType(name = "_csv.reader", doc = PyReader.reader_doc) public class PyReader extends PyIterator { public static final PyType TYPE = PyType.fromClass(PyReader.class); - public PyString __doc__ = Py.newString( + public static final String reader_doc = "CSV reader\n" + "\n" + "Reader objects are responsible for reading and parsing tabular data\n" + - "in CSV format.\n"); + "in CSV format.\n"; /** Parsing Dialect. */ @ExposedGet diff --git a/src/org/python/modules/_csv/PyWriter.java b/src/org/python/modules/_csv/PyWriter.java --- a/src/org/python/modules/_csv/PyWriter.java +++ b/src/org/python/modules/_csv/PyWriter.java @@ -16,9 +16,15 @@ * * Analogous to CPython's _csv.c::WriterObj struct. */ - at ExposedType(name = "_csv.writer") + at ExposedType(name = "_csv.writer", doc = PyWriter.writer_doc) public class PyWriter extends PyObject { + public static final String writer_doc = + "CSV writer\n" + + "\n" + + "Writer objects are responsible for generating tabular data\n" + + "in CSV format from sequence input.\n"; + public static final PyType TYPE = PyType.fromClass(PyWriter.class); /** Parsing dialect. */ @@ -131,14 +137,14 @@ } else if (field == Py.None) { append_ok = join_append("", len == 1); } else { - + PyObject str; //XXX: in 3.x this check can go away and we can just always use // __str__ if (field.getClass() == PyFloat.class) { - str = field.__repr__(); + str = field.__repr__(); } else { - str = field.__str__(); + str = field.__str__(); } if (str == null) { diff --git a/src/org/python/modules/itertools/PyTeeIterator.java b/src/org/python/modules/itertools/PyTeeIterator.java --- a/src/org/python/modules/itertools/PyTeeIterator.java +++ b/src/org/python/modules/itertools/PyTeeIterator.java @@ -13,9 +13,12 @@ import org.python.expose.ExposedType; import org.python.util.Generic; - at ExposedType(name = "itertools.tee", base = PyObject.class, isBaseType = false) + at ExposedType(name = "itertools.tee", base = PyObject.class, + isBaseType = false, doc = PyTeeIterator.tee_doc) public class PyTeeIterator extends PyIterator { + public static final String tee_doc = "Iterator wrapped to make it copyable"; + private static class PyTeeData { private PyObject iterator; private int total; diff --git a/src/org/python/modules/itertools/itertools.java b/src/org/python/modules/itertools/itertools.java --- a/src/org/python/modules/itertools/itertools.java +++ b/src/org/python/modules/itertools/itertools.java @@ -19,31 +19,31 @@ public class itertools implements ClassDictInit { public static final PyString __doc__ = new PyString( - "Functional tools for creating and using iterators.\n\nInfinite iterators:\n" - + "count([n]) --> n, n+1, n+2, ...\n" - + "cycle(p) --> p0, p1, ... plast, p0, p1, ...\n" - + "repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times\n\n" - - + "Iterators terminating on the shortest input sequence:\n" - + "chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...\n" - + "compress(data, selectors) --> (d[0] if s[0]), (d[1] if s[1]), ...\n" - + "dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails\n" - + "groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)\n" - + "ifilter(pred, seq) --> elements of seq where pred(elem) is True\n" - + "ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False\n" - + "islice(seq, [start,] stop [, step]) --> elements from seq[start:stop:step]\n" - + "imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...\n" - + "starmap(fun, seq) --> fun(*seq[0]), fun(*seq[1]), ...\n" - + "tee(it, n=2) --> (it1, it2 , ... itn) splits one iterator into n\n" - + "takewhile(pred, seq) --> seq[0], seq[1], until pred fails\n" - + "izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...\n" - + "izip_longest(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...\n\n" + "Functional tools for creating and using iterators.\n\nInfinite iterators:\n" + + "count([n]) --> n, n+1, n+2, ...\n" + + "cycle(p) --> p0, p1, ... plast, p0, p1, ...\n" + + "repeat(elem [,n]) --> elem, elem, elem, ... endlessly or up to n times\n\n" + + + "Iterators terminating on the shortest input sequence:\n" + + "chain(p, q, ...) --> p0, p1, ... plast, q0, q1, ...\n" + + "compress(data, selectors) --> (d[0] if s[0]), (d[1] if s[1]), ...\n" + + "dropwhile(pred, seq) --> seq[n], seq[n+1], starting when pred fails\n" + + "groupby(iterable[, keyfunc]) --> sub-iterators grouped by value of keyfunc(v)\n" + + "ifilter(pred, seq) --> elements of seq where pred(elem) is True\n" + + "ifilterfalse(pred, seq) --> elements of seq where pred(elem) is False\n" + + "islice(seq, [start,] stop [, step]) --> elements from seq[start:stop:step]\n" + + "imap(fun, p, q, ...) --> fun(p0, q0), fun(p1, q1), ...\n" + + "starmap(fun, seq) --> fun(*seq[0]), fun(*seq[1]), ...\n" + + "tee(it, n=2) --> (it1, it2 , ... itn) splits one iterator into n\n" + + "takewhile(pred, seq) --> seq[0], seq[1], until pred fails\n" + + "izip(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...\n" + + "izip_longest(p, q, ...) --> (p[0], q[0]), (p[1], q[1]), ...\n\n" + - + "Combinatoric generators:\n" - + "product(p, q, ... [repeat=1]) --> cartesian product\n" - + "permutations(p[, r])\n" - + "combinations(p, r)\n" - + "combinations_with_replacement(p, r)"); + "Combinatoric generators:\n" + + "product(p, q, ... [repeat=1]) --> cartesian product\n" + + "permutations(p[, r])\n" + + "combinations(p, r)\n" + + "combinations_with_replacement(p, r)"); /** * Iterator base class used by most methods. @@ -98,14 +98,6 @@ dict.__setitem__("initClassExceptions", null); } - public static PyString __doc__islice = new PyString( - "islice(iterable, [start,] stop [, step]) --> islice object\n" - + "\nReturn an iterator whose next() method returns selected values from an\n" - + "iterable. If start is specified, will skip all preceding elements;\notherwise, start defaults to zero." - + "Step defaults to one. If\nspecified as another value, step determines how manyvalues are \n" - + "skipped between successive calls. Works like a slice() on a list\nbut returns an iterator."); - - static int py2int(PyObject obj, int defaultValue, String msg) { if (obj instanceof PyNone) { return defaultValue; diff --git a/src/org/python/modules/itertools/takewhile.java b/src/org/python/modules/itertools/takewhile.java --- a/src/org/python/modules/itertools/takewhile.java +++ b/src/org/python/modules/itertools/takewhile.java @@ -32,7 +32,7 @@ public static final String takewhile_doc = "takewhile(predicate, iterable) --> takewhile object\n\n" + - "Return successive entries from an iterable as long as the \n" + + "Return successive entries from an iterable as long as the\n" + "predicate evaluates to true for each entry."; /** -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sat Nov 29 01:59:10 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Sat, 29 Nov 2014 00:59:10 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Minor_comment_cosmetics=2E?= Message-ID: <20141129005910.84295.4683@psf.io> https://hg.python.org/jython/rev/724d4fa6ec19 changeset: 7422:724d4fa6ec19 user: Stefan Richthofer date: Sat Nov 29 01:58:59 2014 +0100 summary: Minor comment cosmetics. files: src/org/python/core/PyObject.java | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/src/org/python/core/PyObject.java b/src/org/python/core/PyObject.java --- a/src/org/python/core/PyObject.java +++ b/src/org/python/core/PyObject.java @@ -110,7 +110,8 @@ * Note that this empty finalizer implementation is optimized away by the JVM * (See {@link http://www.javaspecialists.eu/archive/Issue170.html}). * So {@code PyObject}s are not expensively treated as finalizable objects by the - * GC. Its only intention is to prevent subclasses from having Java-style finalizers. + * Java-GC. Its single intention is to prevent subclasses from having Java-style + * finalizers. *

*/ protected final void finalize() throws Throwable {} -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Sat Nov 29 03:16:10 2014 From: jython-checkins at python.org (stefan.richthofer) Date: Sat, 29 Nov 2014 02:16:10 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Cleaned_up_some_unused_impo?= =?utf-8?q?rts=2E?= Message-ID: <20141129021610.55103.75826@psf.io> https://hg.python.org/jython/rev/6aa434d5dc01 changeset: 7423:6aa434d5dc01 user: Stefan Richthofer date: Sat Nov 29 03:16:02 2014 +0100 summary: Cleaned up some unused imports. files: src/org/python/modules/_csv/PyReader.java | 1 - src/org/python/modules/itertools/chain.java | 4 ---- src/org/python/modules/itertools/combinations.java | 2 -- src/org/python/modules/itertools/combinationsWithReplacement.java | 2 -- src/org/python/modules/itertools/compress.java | 2 -- src/org/python/modules/itertools/count.java | 1 - src/org/python/modules/itertools/cycle.java | 2 -- src/org/python/modules/itertools/dropwhile.java | 2 -- src/org/python/modules/itertools/groupby.java | 2 -- src/org/python/modules/itertools/ifilter.java | 2 -- src/org/python/modules/itertools/ifilterfalse.java | 2 -- src/org/python/modules/itertools/imap.java | 2 -- src/org/python/modules/itertools/islice.java | 2 -- src/org/python/modules/itertools/izip.java | 2 -- src/org/python/modules/itertools/izipLongest.java | 2 -- src/org/python/modules/itertools/permutations.java | 2 -- src/org/python/modules/itertools/product.java | 2 -- src/org/python/modules/itertools/repeat.java | 1 - src/org/python/modules/itertools/starmap.java | 2 -- src/org/python/modules/itertools/takewhile.java | 2 -- 20 files changed, 0 insertions(+), 39 deletions(-) diff --git a/src/org/python/modules/_csv/PyReader.java b/src/org/python/modules/_csv/PyReader.java --- a/src/org/python/modules/_csv/PyReader.java +++ b/src/org/python/modules/_csv/PyReader.java @@ -1,7 +1,6 @@ /* Copyright (c) Jython Developers */ package org.python.modules._csv; -import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyList; import org.python.core.PyObject; diff --git a/src/org/python/modules/itertools/chain.java b/src/org/python/modules/itertools/chain.java --- a/src/org/python/modules/itertools/chain.java +++ b/src/org/python/modules/itertools/chain.java @@ -4,17 +4,13 @@ import org.python.core.ArgParser; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; import org.python.expose.ExposedClassMethod; -import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; -import java.util.ArrayList; - @ExposedType(name = "itertools.chain", base = PyObject.class, doc = chain.chain_doc) public class chain extends PyIterator { diff --git a/src/org/python/modules/itertools/combinations.java b/src/org/python/modules/itertools/combinations.java --- a/src/org/python/modules/itertools/combinations.java +++ b/src/org/python/modules/itertools/combinations.java @@ -4,10 +4,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/combinationsWithReplacement.java b/src/org/python/modules/itertools/combinationsWithReplacement.java --- a/src/org/python/modules/itertools/combinationsWithReplacement.java +++ b/src/org/python/modules/itertools/combinationsWithReplacement.java @@ -5,10 +5,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/compress.java b/src/org/python/modules/itertools/compress.java --- a/src/org/python/modules/itertools/compress.java +++ b/src/org/python/modules/itertools/compress.java @@ -5,9 +5,7 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/count.java b/src/org/python/modules/itertools/count.java --- a/src/org/python/modules/itertools/count.java +++ b/src/org/python/modules/itertools/count.java @@ -9,7 +9,6 @@ import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/cycle.java b/src/org/python/modules/itertools/cycle.java --- a/src/org/python/modules/itertools/cycle.java +++ b/src/org/python/modules/itertools/cycle.java @@ -3,9 +3,7 @@ import org.python.core.ArgParser; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/dropwhile.java b/src/org/python/modules/itertools/dropwhile.java --- a/src/org/python/modules/itertools/dropwhile.java +++ b/src/org/python/modules/itertools/dropwhile.java @@ -4,9 +4,7 @@ import org.python.core.ArgParser; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/groupby.java b/src/org/python/modules/itertools/groupby.java --- a/src/org/python/modules/itertools/groupby.java +++ b/src/org/python/modules/itertools/groupby.java @@ -5,11 +5,9 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; import org.python.core.PyXRange; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/ifilter.java b/src/org/python/modules/itertools/ifilter.java --- a/src/org/python/modules/itertools/ifilter.java +++ b/src/org/python/modules/itertools/ifilter.java @@ -4,9 +4,7 @@ import org.python.core.ArgParser; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/ifilterfalse.java b/src/org/python/modules/itertools/ifilterfalse.java --- a/src/org/python/modules/itertools/ifilterfalse.java +++ b/src/org/python/modules/itertools/ifilterfalse.java @@ -3,9 +3,7 @@ import org.python.core.ArgParser; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/imap.java b/src/org/python/modules/itertools/imap.java --- a/src/org/python/modules/itertools/imap.java +++ b/src/org/python/modules/itertools/imap.java @@ -4,10 +4,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/islice.java b/src/org/python/modules/itertools/islice.java --- a/src/org/python/modules/itertools/islice.java +++ b/src/org/python/modules/itertools/islice.java @@ -6,9 +6,7 @@ import org.python.core.PyIterator; import org.python.core.PyNone; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/izip.java b/src/org/python/modules/itertools/izip.java --- a/src/org/python/modules/itertools/izip.java +++ b/src/org/python/modules/itertools/izip.java @@ -4,11 +4,9 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; import org.python.core.PyXRange; -import org.python.expose.ExposedGet; import org.python.expose.ExposedNew; import org.python.expose.ExposedMethod; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/izipLongest.java b/src/org/python/modules/itertools/izipLongest.java --- a/src/org/python/modules/itertools/izipLongest.java +++ b/src/org/python/modules/itertools/izipLongest.java @@ -4,10 +4,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/permutations.java b/src/org/python/modules/itertools/permutations.java --- a/src/org/python/modules/itertools/permutations.java +++ b/src/org/python/modules/itertools/permutations.java @@ -5,10 +5,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/product.java b/src/org/python/modules/itertools/product.java --- a/src/org/python/modules/itertools/product.java +++ b/src/org/python/modules/itertools/product.java @@ -4,10 +4,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/repeat.java b/src/org/python/modules/itertools/repeat.java --- a/src/org/python/modules/itertools/repeat.java +++ b/src/org/python/modules/itertools/repeat.java @@ -8,7 +8,6 @@ import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/starmap.java b/src/org/python/modules/itertools/starmap.java --- a/src/org/python/modules/itertools/starmap.java +++ b/src/org/python/modules/itertools/starmap.java @@ -4,10 +4,8 @@ import org.python.core.Py; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; diff --git a/src/org/python/modules/itertools/takewhile.java b/src/org/python/modules/itertools/takewhile.java --- a/src/org/python/modules/itertools/takewhile.java +++ b/src/org/python/modules/itertools/takewhile.java @@ -4,9 +4,7 @@ import org.python.core.ArgParser; import org.python.core.PyIterator; import org.python.core.PyObject; -import org.python.core.PyString; import org.python.core.PyType; -import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; import org.python.expose.ExposedType; -- Repository URL: https://hg.python.org/jython From Stefan.Richthofer at gmx.de Sat Nov 29 05:49:00 2014 From: Stefan.Richthofer at gmx.de (Stefan Richthofer) Date: Sat, 29 Nov 2014 05:49:00 +0100 Subject: [Jython-checkins] jython: Now uses java.util.WeakHashMap to back weakref.WeakSet In-Reply-To: <20141121021824.7142.92011@psf.io> References: <20141121021824.7142.92011@psf.io> Message-ID: Hello Jim, I recently re-generated the *Derived classes and this results in the following build-error now (I suspect due to your changes): /modules/_collections/PyDefaultDictDerived.java:1144: error: invalid method declaration; return type required [javac] public PyDictionaryDerived(PyType subtype,ConcurrentMap backingMap,boolean useBackingMap) { [javac] ^ [javac] 1 error BUILD FAILED It is easy to repair modules._collections.PyDefaultDictDerived.java manually by changing the erroneous lines as follows: from public PyDictionaryDerived(PyType subtype,ConcurrentMap backingMap,boolean useBackingMap) { super(subtype,backingMap,useBackingMap); ... to public PyDefaultDictDerived(PyType subtype,ConcurrentMap backingMap) {//,boolean useBackingMap) { super(subtype,backingMap);//,useBackingMap); ... However, it is still a bug in the generator and should be fixed there, but I am spontaneously not sure how to do this. Do you see an easy fix, or should I create an issue for this? -Stefan > Gesendet: Freitag, 21. November 2014 um 03:18 Uhr > Von: "jim.baker" > An: jython-checkins at python.org > Betreff: [Jython-checkins] jython: Now uses java.util.WeakHashMap to back weakref.WeakSet > > https://hg.python.org/jython/rev/f51940218cf2 > changeset: 7420:f51940218cf2 > user: Jim Baker > date: Thu Nov 20 19:17:46 2014 -0700 > summary: > Now uses java.util.WeakHashMap to back weakref.WeakSet > > Adds jythonlib.set_builder so that Python sets/derived sets can be > built with an arbitrary backing java.util.Set. Updates test_weakset > to 2.7 and fixes all outstanding skips. Updates gderived.py so that it > can include arbitrary additional imports, based on a new import > directive. > > files: > Lib/_weakrefset.py | 213 +--------- > Lib/test/test_set.py | 3 +- > Lib/test/test_weakset.py | 71 ++- > src/org/python/core/BaseSet.java | 8 +- > src/org/python/core/PyDictionaryDerived.java | 18 +- > src/org/python/core/PySet.java | 9 + > src/org/python/core/PySetDerived.java | 10 + > src/org/python/modules/_jythonlib/_jythonlib.java | 2 + > src/org/python/modules/_jythonlib/set_builder.java | 48 ++ > src/templates/dict.derived | 10 + > src/templates/gderived.py | 25 + > src/templates/set.derived | 11 + > 12 files changed, 186 insertions(+), 242 deletions(-) > > > diff --git a/Lib/_weakrefset.py b/Lib/_weakrefset.py > --- a/Lib/_weakrefset.py > +++ b/Lib/_weakrefset.py > @@ -2,211 +2,20 @@ > # This code is separated-out because it is needed > # by abc.py to load everything else at startup. > > -from _weakref import ref > +from java.util import WeakHashMap > +from java.util.Collections import newSetFromMap, synchronizedMap > +from jythonlib import set_builder, MapMaker > > __all__ = ['WeakSet'] > > > -class _IterationGuard(object): > - # This context manager registers itself in the current iterators of the > - # weak container, such as to delay all removals until the context manager > - # exits. > - # This technique should be relatively thread-safe (since sets are). > +class WeakSet(set): > > - def __init__(self, weakcontainer): > - # Don't create cycles > - self.weakcontainer = ref(weakcontainer) > + def __new__(cls, data=None): > + def _build(): > + # Although not specified in the docs, WeakSet supports equality on > + # keys, as seen in test_weakset and its use of testing class > + # SomeClass. Therefore we cannot use MapMaker in this case. > + return newSetFromMap(synchronizedMap(WeakHashMap())) > > - def __enter__(self): > - w = self.weakcontainer() > - if w is not None: > - w._iterating.add(self) > - return self > - > - def __exit__(self, e, t, b): > - w = self.weakcontainer() > - if w is not None: > - s = w._iterating > - s.remove(self) > - if not s: > - w._commit_removals() > - > - > -class WeakSet(object): > - def __init__(self, data=None): > - self.data = set() > - def _remove(item, selfref=ref(self)): > - self = selfref() > - if self is not None: > - if self._iterating: > - self._pending_removals.append(item) > - else: > - self.data.discard(item) > - self._remove = _remove > - # A list of keys to be removed > - self._pending_removals = [] > - self._iterating = set() > - if data is not None: > - self.update(data) > - > - def _commit_removals(self): > - l = self._pending_removals > - discard = self.data.discard > - while l: > - discard(l.pop()) > - > - def __iter__(self): > - with _IterationGuard(self): > - for itemref in self.data: > - item = itemref() > - if item is not None: > - yield item > - > - def __len__(self): > - return sum(x() is not None for x in self.data) > - > - def __contains__(self, item): > - return ref(item) in self.data > - > - def __reduce__(self): > - return (self.__class__, (list(self),), > - getattr(self, '__dict__', None)) > - > - __hash__ = None > - > - def add(self, item): > - if self._pending_removals: > - self._commit_removals() > - self.data.add(ref(item, self._remove)) > - > - def clear(self): > - if self._pending_removals: > - self._commit_removals() > - self.data.clear() > - > - def copy(self): > - return self.__class__(self) > - > - def pop(self): > - if self._pending_removals: > - self._commit_removals() > - while True: > - try: > - itemref = self.data.pop() > - except KeyError: > - raise KeyError('pop from empty WeakSet') > - item = itemref() > - if item is not None: > - return item > - > - def remove(self, item): > - if self._pending_removals: > - self._commit_removals() > - self.data.remove(ref(item)) > - > - def discard(self, item): > - if self._pending_removals: > - self._commit_removals() > - self.data.discard(ref(item)) > - > - def update(self, other): > - if self._pending_removals: > - self._commit_removals() > - if isinstance(other, self.__class__): > - self.data.update(other.data) > - else: > - for element in other: > - self.add(element) > - > - def __ior__(self, other): > - self.update(other) > - return self > - > - # Helper functions for simple delegating methods. > - def _apply(self, other, method): > - if not isinstance(other, self.__class__): > - other = self.__class__(other) > - newdata = method(other.data) > - newset = self.__class__() > - newset.data = newdata > - return newset > - > - def difference(self, other): > - return self._apply(other, self.data.difference) > - __sub__ = difference > - > - def difference_update(self, other): > - if self._pending_removals: > - self._commit_removals() > - if self is other: > - self.data.clear() > - else: > - self.data.difference_update(ref(item) for item in other) > - def __isub__(self, other): > - if self._pending_removals: > - self._commit_removals() > - if self is other: > - self.data.clear() > - else: > - self.data.difference_update(ref(item) for item in other) > - return self > - > - def intersection(self, other): > - return self._apply(other, self.data.intersection) > - __and__ = intersection > - > - def intersection_update(self, other): > - if self._pending_removals: > - self._commit_removals() > - self.data.intersection_update(ref(item) for item in other) > - def __iand__(self, other): > - if self._pending_removals: > - self._commit_removals() > - self.data.intersection_update(ref(item) for item in other) > - return self > - > - def issubset(self, other): > - return self.data.issubset(ref(item) for item in other) > - __lt__ = issubset > - > - def __le__(self, other): > - return self.data <= set(ref(item) for item in other) > - > - def issuperset(self, other): > - return self.data.issuperset(ref(item) for item in other) > - __gt__ = issuperset > - > - def __ge__(self, other): > - return self.data >= set(ref(item) for item in other) > - > - def __eq__(self, other): > - if not isinstance(other, self.__class__): > - return NotImplemented > - return self.data == set(ref(item) for item in other) > - > - def symmetric_difference(self, other): > - return self._apply(other, self.data.symmetric_difference) > - __xor__ = symmetric_difference > - > - def symmetric_difference_update(self, other): > - if self._pending_removals: > - self._commit_removals() > - if self is other: > - self.data.clear() > - else: > - self.data.symmetric_difference_update(ref(item) for item in other) > - def __ixor__(self, other): > - if self._pending_removals: > - self._commit_removals() > - if self is other: > - self.data.clear() > - else: > - self.data.symmetric_difference_update(ref(item) for item in other) > - return self > - > - def union(self, other): > - return self._apply(other, self.data.union) > - __or__ = union > - > - def isdisjoint(self, other): > - return len(self.intersection(other)) == 0 > + return set_builder(_build, cls)(data) > diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py > --- a/Lib/test/test_set.py > +++ b/Lib/test/test_set.py > @@ -310,7 +310,7 @@ > fo.close() > test_support.unlink(test_support.TESTFN) > > - @unittest.skipIf(test_support.is_jython, "FIXME: Not yet sure how to fix this") > + @unittest.skipIf(test_support.is_jython, "Not meaningful for Jython") > def test_do_not_rehash_dict_keys(self): > n = 10 > d = dict.fromkeys(map(HashCountingInt, xrange(n))) > @@ -917,6 +917,7 @@ > set('abc') > set(gooditer()) > > + @unittest.skipIf(test_support.is_jython, "Jython provides stronger support for concurrent updates") > def test_changingSizeWhileIterating(self): > s = set([1,2,3]) > try: > diff --git a/Lib/test/test_weakset.py b/Lib/test/test_weakset.py > --- a/Lib/test/test_weakset.py > +++ b/Lib/test/test_weakset.py > @@ -1,5 +1,18 @@ > +# Jython specific notes: > +# > +# 1. Deletion (del) or reliance on going out of scope must be followed > +# by a gc.collect(); use extra_collect() if a callback will be > +# tested as well. > +# > +# 2. Avoid eventual consistency issues in the length computation by > +# computing the len of the list of the WeakSet > +# > +# 3. Jython can weakly refer to any object, unlike CPython, so don't > +# test for nonexistent TypeErrors > + > import unittest > from test import test_support > +from test.test_weakref import extra_collect > from weakref import proxy, ref, WeakSet > import operator > import copy > @@ -30,6 +43,9 @@ > def __hash__(self): > return hash((SomeClass, self.value)) > > + def __repr__(self): > + return "SC(%s,%s)" % (self.value, id(self.value)) > + > class RefCycle(object): > def __init__(self): > self.cycle = self > @@ -63,14 +79,13 @@ > def test_new_or_init(self): > self.assertRaises(TypeError, WeakSet, [], 2) > > - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") > def test_len(self): > self.assertEqual(len(self.s), len(self.d)) > self.assertEqual(len(self.fs), 1) > del self.obj > + gc.collect() > self.assertEqual(len(self.fs), 0) > > - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") > def test_contains(self): > for c in self.letters: > self.assertEqual(c in self.s, c in self.d) > @@ -78,6 +93,7 @@ > self.assertNotIn(1, self.s) > self.assertIn(self.obj, self.fs) > del self.obj > + gc.collect() > self.assertNotIn(SomeClass('F'), self.fs) > > def test_union(self): > @@ -92,10 +108,11 @@ > c = C(self.items2) > self.assertEqual(self.s.union(c), x) > del c > - self.assertEqual(len(u), len(self.items) + len(self.items2)) > + gc.collect() > + self.assertEqual(len(list(u)), len(list(self.items)) + len(list(self.items2))) > self.items2.pop() > gc.collect() > - self.assertEqual(len(u), len(self.items) + len(self.items2)) > + self.assertEqual(len(list(u)), len(list(self.items)) + len(list(self.items2))) > > def test_or(self): > i = self.s.union(self.items2) > @@ -115,7 +132,7 @@ > self.assertEqual(len(i), len(self.items2)) > self.items2.pop() > gc.collect() > - self.assertEqual(len(i), len(self.items2)) > + self.assertEqual(len(list(i)), len(list(self.items2))) > > def test_isdisjoint(self): > self.assertTrue(self.s.isdisjoint(WeakSet(self.items2))) > @@ -149,7 +166,7 @@ > self.assertEqual(len(i), len(self.items) + len(self.items2)) > self.items2.pop() > gc.collect() > - self.assertEqual(len(i), len(self.items) + len(self.items2)) > + self.assertEqual(len(list(i)), len(list(self.items)) + len(list(self.items2))) > > def test_xor(self): > i = self.s.symmetric_difference(self.items2) > @@ -167,14 +184,12 @@ > self.assertFalse(set('a').issubset('cbs')) > self.assertFalse(set('cbs').issuperset('a')) > > - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") > def test_lt(self): > self.assertTrue(self.ab_weakset < self.abcde_weakset) > self.assertFalse(self.abcde_weakset < self.def_weakset) > self.assertFalse(self.ab_weakset < self.ab_weakset) > self.assertFalse(WeakSet() < WeakSet()) > > - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") > def test_gt(self): > self.assertTrue(self.abcde_weakset > self.ab_weakset) > self.assertFalse(self.abcde_weakset > self.def_weakset) > @@ -229,7 +244,6 @@ > self.assertEqual(self.s, dup) > self.assertNotEqual(id(self.s), id(dup)) > > - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") > def test_add(self): > x = SomeClass('Q') > self.s.add(x) > @@ -237,25 +251,29 @@ > dup = self.s.copy() > self.s.add(x) > self.assertEqual(self.s, dup) > - self.assertRaises(TypeError, self.s.add, []) > + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects > + self.assertRaises(TypeError, self.s.add, []) > self.fs.add(Foo()) > - self.assertTrue(len(self.fs) == 1) > + gc.collect() # CPython assumes Foo() went out of scope and was collected, so ensure the same > + self.assertEqual(len(list(self.fs)), 1) > self.fs.add(self.obj) > - self.assertTrue(len(self.fs) == 1) > + self.assertEqual(len(list(self.fs)), 1) > > def test_remove(self): > x = SomeClass('a') > self.s.remove(x) > self.assertNotIn(x, self.s) > self.assertRaises(KeyError, self.s.remove, x) > - self.assertRaises(TypeError, self.s.remove, []) > + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects > + self.assertRaises(TypeError, self.s.remove, []) > > def test_discard(self): > a, q = SomeClass('a'), SomeClass('Q') > self.s.discard(a) > self.assertNotIn(a, self.s) > self.s.discard(q) > - self.assertRaises(TypeError, self.s.discard, []) > + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects > + self.assertRaises(TypeError, self.s.discard, []) > > def test_pop(self): > for i in range(len(self.s)): > @@ -306,8 +324,9 @@ > self.assertIn(c, self.s) > else: > self.assertNotIn(c, self.s) > - self.assertRaises(TypeError, self.s.difference_update, [[]]) > - self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) > + if not test_support.is_jython: # Jython/JVM can weakly reference list and other objects > + self.assertRaises(TypeError, self.s.difference_update, [[]]) > + self.assertRaises(TypeError, self.s.symmetric_difference_update, [[]]) > > def test_isub(self): > self.s -= set(self.items2) > @@ -348,15 +367,17 @@ > self.assertEqual(t, WeakSet()) > > def test_eq(self): > - # issue 5964 > - self.assertTrue(self.s == self.s) > - self.assertTrue(self.s == WeakSet(self.items)) > - self.assertFalse(self.s == set(self.items)) > - self.assertFalse(self.s == list(self.items)) > - self.assertFalse(self.s == tuple(self.items)) > - self.assertFalse(self.s == 1) > + # issue 5964 (http://bugs.python.org/issue5964) > + self.assertEqual(self.s, self.s) > + self.assertEqual(self.s, WeakSet(self.items)) > + # Jython diverges here in the next test because it constructs > + # WeakSet as a subclass of set; this seems to be the proper > + # thing to do given what is the typical comparison > + self.assertEqual(self.s, set(self.items)) > + self.assertNotEqual(self.s, list(self.items)) > + self.assertNotEqual(self.s, tuple(self.items)) > + self.assertNotEqual(self.s, 1) > > - #@unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython") > def test_weak_destroy_while_iterating(self): > # Issue #7105: iterators shouldn't crash when a key is implicitly removed > # Create new items to be sure no-one else holds a reference > @@ -370,6 +391,7 @@ > # We have removed either the first consumed items, or another one > self.assertIn(len(list(it)), [len(items), len(items) - 1]) > del it > + gc.collect() > # The removal has been committed > self.assertEqual(len(s), len(items)) > > @@ -410,6 +432,7 @@ > items = [RefCycle() for i in range(N)] > s = WeakSet(items) > del items > + gc.collect() > it = iter(s) > try: > next(it) > diff --git a/src/org/python/core/BaseSet.java b/src/org/python/core/BaseSet.java > --- a/src/org/python/core/BaseSet.java > +++ b/src/org/python/core/BaseSet.java > @@ -231,9 +231,6 @@ > > @Override > public PyObject __iternext__() { > - if (size != size()) { > - throw Py.RuntimeError("set changed size during iteration"); > - } > if (iterator.hasNext()) { > return iterator.next(); > } > @@ -481,7 +478,7 @@ > } > > /** > - * Create a new + * Create a new set of type from iterable. > * > * @param type a set type > * @param iterable an iterable or null > @@ -494,8 +491,7 @@ > } else if (type == PyFrozenSet.TYPE) { > so = new PyFrozenSet(iterable); > } else if (Py.isSubClass(type, PySet.TYPE)) { > - so = new PySetDerived(type); > - so._update(iterable); > + so = (BaseSet)(type.__call__(iterable == null ? Py.EmptyTuple : iterable)); > } else { > so = new PyFrozenSetDerived(type, iterable); > } > diff --git a/src/org/python/core/PyDictionaryDerived.java b/src/org/python/core/PyDictionaryDerived.java > --- a/src/org/python/core/PyDictionaryDerived.java > +++ b/src/org/python/core/PyDictionaryDerived.java > @@ -67,15 +67,6 @@ > } > } > > - public PyDictionaryDerived(PyType subtype, ConcurrentMap backingMap, boolean useBackingMap) { > - super(subtype, backingMap, useBackingMap); > - slots=new PyObject[subtype.getNumSlots()]; > - dict=subtype.instDict(); > - if (subtype.needsFinalizer()) { > - finalizeTrigger=FinalizeTrigger.makeTrigger(this); > - } > - } > - > public PyString __str__() { > PyType self_type=getType(); > PyObject impl=self_type.lookup("__str__"); > @@ -1149,6 +1140,15 @@ > return super.__coerce_ex__(o); > } > > + public PyDictionaryDerived(PyType subtype,ConcurrentMap backingMap,boolean useBackingMap) { > + super(subtype,backingMap,useBackingMap); > + slots=new PyObject[subtype.getNumSlots()]; > + dict=subtype.instDict(); > + if (subtype.needsFinalizer()) { > + finalizeTrigger=FinalizeTrigger.makeTrigger(this); > + } > + } > + > public String toString() { > PyType self_type=getType(); > PyObject impl=self_type.lookup("__repr__"); > diff --git a/src/org/python/core/PySet.java b/src/org/python/core/PySet.java > --- a/src/org/python/core/PySet.java > +++ b/src/org/python/core/PySet.java > @@ -2,6 +2,7 @@ > > import java.util.Iterator; > import java.util.NoSuchElementException; > +import java.util.Set; > > import org.python.expose.ExposedMethod; > import org.python.expose.ExposedNew; > @@ -34,6 +35,14 @@ > super(TYPE, _update(Generic.concurrentSet(), data)); > } > > + public PySet(Set backing_set, PyObject data) { > + super(TYPE, _update(backing_set, data)); > + } > + > + public PySet(PyType type, Set backing_set, PyObject data) { > + super(type, _update(backing_set, data)); > + } > + > @ExposedNew > @ExposedMethod(doc = BuiltinDocs.set___init___doc) > final void set___init__(PyObject[] args, String[] kwds) { > diff --git a/src/org/python/core/PySetDerived.java b/src/org/python/core/PySetDerived.java > --- a/src/org/python/core/PySetDerived.java > +++ b/src/org/python/core/PySetDerived.java > @@ -1,6 +1,7 @@ > /* Generated file, do not modify. See jython/src/templates/gderived.py. */ > package org.python.core; > > +import java.util.Set; > import java.io.Serializable; > import org.python.core.finalization.FinalizeTrigger; > import org.python.core.finalization.FinalizablePyObjectDerived; > @@ -1139,6 +1140,15 @@ > return super.__coerce_ex__(o); > } > > + public PySetDerived(PyType subtype,Set backing_set,PyObject data) { > + super(subtype,backing_set,data); > + slots=new PyObject[subtype.getNumSlots()]; > + dict=subtype.instDict(); > + if (subtype.needsFinalizer()) { > + finalizeTrigger=FinalizeTrigger.makeTrigger(this); > + } > + } > + > public String toString() { > PyType self_type=getType(); > PyObject impl=self_type.lookup("__repr__"); > diff --git a/src/org/python/modules/_jythonlib/_jythonlib.java b/src/org/python/modules/_jythonlib/_jythonlib.java > --- a/src/org/python/modules/_jythonlib/_jythonlib.java > +++ b/src/org/python/modules/_jythonlib/_jythonlib.java > @@ -15,6 +15,8 @@ > dict.__setitem__("__doc__", __doc__); > dict.__setitem__("__module__", new PyString("_jythonlib")); > dict.__setitem__("dict_builder", dict_builder.TYPE); > + dict.__setitem__("set_builder", set_builder.TYPE); > + > > // Hide from Python > dict.__setitem__("classDictInit", null); > diff --git a/src/org/python/modules/_jythonlib/set_builder.java b/src/org/python/modules/_jythonlib/set_builder.java > new file mode 100644 > --- /dev/null > +++ b/src/org/python/modules/_jythonlib/set_builder.java > @@ -0,0 +1,48 @@ > +/* Copyright (c) Jython Developers */ > +package org.python.modules._jythonlib; > + > +import org.python.core.Py; > +import org.python.core.PyObject; > +import org.python.core.PySet; > +import org.python.core.PySetDerived; > +import org.python.core.PyType; > + > +import java.util.Set; > + > + > +/* Support building PySet objects with arbitrary backing Set objects > + * Uses a factory for efficiency. > + * > + * See the very similar dict_builder for more insight. But note that we do not > + * impose the restriction that the set be concurrent, although this is generally > + * what we would want. > + */ > + > +public class set_builder extends PyObject { > + > + public static final PyType TYPE = PyType.fromClass(set_builder.class); > + private final PyObject factory; > + private final PyType set_type; > + > + public set_builder(PyObject factory) { > + super(); > + this.factory = factory; > + this.set_type = null; > + } > + > + public set_builder(PyObject factory, PyType set_type) { > + super(); > + this.factory = factory; > + this.set_type = set_type; > + } > + > + public PyObject __call__(PyObject iterable) { > + Set backing_set = (Set) (factory.__call__().__tojava__(Set.class)); > + if (set_type == null) { > + return new PySet(backing_set, iterable == Py.None ? null : iterable); > + } else { > + return new PySetDerived(set_type, backing_set, iterable == Py.None ? null : iterable); > + } > + } > + > +} > diff --git a/src/templates/dict.derived b/src/templates/dict.derived > --- a/src/templates/dict.derived > +++ b/src/templates/dict.derived > @@ -2,3 +2,13 @@ > want_dict: true > ctr: > incl: object > +import: java.util.concurrent.ConcurrentMap > +rest: > + public PyDictionaryDerived(PyType subtype, ConcurrentMap backingMap, boolean useBackingMap) { > + super(subtype, backingMap, useBackingMap); > + slots=new PyObject[subtype.getNumSlots()]; > + dict=subtype.instDict(); > + if (subtype.needsFinalizer()) { > + finalizeTrigger=FinalizeTrigger.makeTrigger(this); > + } > + } > diff --git a/src/templates/gderived.py b/src/templates/gderived.py > --- a/src/templates/gderived.py > +++ b/src/templates/gderived.py > @@ -25,6 +25,8 @@ > > modif_re = re.compile(r"(?:\((\w+)\))?(\w+)") > > +added_imports = [] > + > # os.path.samefile unavailable on Windows before Python v3.2 > if hasattr(os.path, "samefile"): > # Good: available on this platform > @@ -43,6 +45,7 @@ > 'unary1', > 'binary', 'ibinary', > 'rest', > + 'import', > 'no_toString' > ] > > @@ -82,6 +85,10 @@ > self.auxiliary = aux_gen.global_bindings > return self.auxiliary[name] > > + def dire_import(self, name, parm, body): > + global added_imports > + added_imports = [x.strip() for x in parm.split(",")] > + > def dire_require(self, name, parm, body): > if body is not None: > self.invalid('require', 'non-empty body') > @@ -217,6 +224,7 @@ > directives.execute(directives.load(fn), gen) > result = gen.generate() > result = hack_derived_header(outfile, result) > + result = add_imports(outfile, result) > print >> open(outfile, 'w'), result > #gen.debug() > > @@ -245,6 +253,23 @@ > > return '\n'.join(result) > > +def add_imports(fn, result): > + if not added_imports: > + return result > + print 'Adding imports for: %s' % fn > + result = result.splitlines() > + > + def f(): > + added = False > + for line in result: > + if not added and line.startswith("import "): > + added = True > + for addition in added_imports: > + yield "import %s;" % (addition,) > + yield line > + > + return '\n'.join(f()) > + > > if __name__ == '__main__': > from gexpose import load_mappings, usage > diff --git a/src/templates/set.derived b/src/templates/set.derived > --- a/src/templates/set.derived > +++ b/src/templates/set.derived > @@ -2,3 +2,14 @@ > want_dict: true > ctr: > incl: object > +import: java.util.Set > +rest: > + public PySetDerived(PyType subtype, Set backing_set, PyObject data) { > + super(subtype, backing_set, data); > + slots=new PyObject[subtype.getNumSlots()]; > + dict=subtype.instDict(); > + if (subtype.needsFinalizer()) { > + finalizeTrigger=FinalizeTrigger.makeTrigger(this); > + } > + } > + > > -- > Repository URL: https://hg.python.org/jython > _______________________________________________ > Jython-checkins mailing list > Jython-checkins at python.org > https://mail.python.org/mailman/listinfo/jython-checkins >