From jython-checkins at python.org Mon Feb 2 21:09:04 2015
From: jython-checkins at python.org (jim.baker)
Date: Mon, 02 Feb 2015 20:09:04 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Use_Java_7=27s_support_of_f?=
=?utf-8?q?ile_attributes_instead_of_JNR_stat=2C_lstat?=
Message-ID: <20150202200859.96092.87236@psf.io>
https://hg.python.org/jython/rev/e04fa277ce19
changeset: 7563:e04fa277ce19
user: Jim Baker
date: Mon Feb 02 13:08:53 2015 -0700
summary:
Use Java 7's support of file attributes instead of JNR stat, lstat
Fixes http://bugs.jython.org/issue2256, http://bugs.jython.org/issue2263
files:
Lib/test/test_mailbox.py | 2122 ----------
src/org/python/modules/posix/PosixModule.java | 246 +-
src/org/python/modules/posix/PyStatResult.java | 116 +-
3 files changed, 271 insertions(+), 2213 deletions(-)
diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py
deleted file mode 100644
--- a/Lib/test/test_mailbox.py
+++ /dev/null
@@ -1,2122 +0,0 @@
-import os
-import sys
-import time
-import stat
-import socket
-import email
-import email.message
-import re
-import shutil
-import StringIO
-import tempfile
-from test import test_support
-import unittest
-import mailbox
-import glob
-try:
- import fcntl
-except ImportError:
- pass
-
-# Silence Py3k warning
-rfc822 = test_support.import_module('rfc822', deprecated=True)
-
-class TestBase:
-
- def _check_sample(self, msg):
- # Inspect a mailbox.Message representation of the sample message
- self.assertIsInstance(msg, email.message.Message)
- self.assertIsInstance(msg, mailbox.Message)
- for key, value in _sample_headers.iteritems():
- self.assertIn(value, msg.get_all(key))
- self.assertTrue(msg.is_multipart())
- self.assertEqual(len(msg.get_payload()), len(_sample_payloads))
- for i, payload in enumerate(_sample_payloads):
- part = msg.get_payload(i)
- self.assertIsInstance(part, email.message.Message)
- self.assertNotIsInstance(part, mailbox.Message)
- self.assertEqual(part.get_payload(), payload)
-
- def _delete_recursively(self, target):
- # Delete a file or delete a directory recursively
- if os.path.isdir(target):
- test_support.rmtree(target)
- elif os.path.exists(target):
- test_support.unlink(target)
-
-
-class TestMailbox(TestBase):
-
- _factory = None # Overridden by subclasses to reuse tests
- _template = 'From: foo\n\n%s\n'
-
- def setUp(self):
- self._path = test_support.TESTFN
- self._delete_recursively(self._path)
- self._box = self._factory(self._path)
-
- def tearDown(self):
- self._box.close()
- self._delete_recursively(self._path)
-
- def test_add(self):
- # Add copies of a sample message
- keys = []
- keys.append(self._box.add(self._template % 0))
- self.assertEqual(len(self._box), 1)
- keys.append(self._box.add(mailbox.Message(_sample_message)))
- self.assertEqual(len(self._box), 2)
- keys.append(self._box.add(email.message_from_string(_sample_message)))
- self.assertEqual(len(self._box), 3)
- keys.append(self._box.add(StringIO.StringIO(_sample_message)))
- self.assertEqual(len(self._box), 4)
- keys.append(self._box.add(_sample_message))
- self.assertEqual(len(self._box), 5)
- self.assertEqual(self._box.get_string(keys[0]), self._template % 0)
- for i in (1, 2, 3, 4):
- self._check_sample(self._box[keys[i]])
-
- def test_add_file(self):
- with tempfile.TemporaryFile('w+') as f:
- f.write(_sample_message)
- f.seek(0)
- key = self._box.add(f)
- self.assertEqual(self._box.get_string(key).split('\n'),
- _sample_message.split('\n'))
-
- def test_add_StringIO(self):
- key = self._box.add(StringIO.StringIO(self._template % "0"))
- self.assertEqual(self._box.get_string(key), self._template % "0")
-
- def test_remove(self):
- # Remove messages using remove()
- self._test_remove_or_delitem(self._box.remove)
-
- def test_delitem(self):
- # Remove messages using __delitem__()
- self._test_remove_or_delitem(self._box.__delitem__)
-
- def _test_remove_or_delitem(self, method):
- # (Used by test_remove() and test_delitem().)
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(self._template % 1)
- self.assertEqual(len(self._box), 2)
- method(key0)
- l = len(self._box)
- self.assertEqual(l, 1)
- self.assertRaises(KeyError, lambda: self._box[key0])
- self.assertRaises(KeyError, lambda: method(key0))
- self.assertEqual(self._box.get_string(key1), self._template % 1)
- key2 = self._box.add(self._template % 2)
- self.assertEqual(len(self._box), 2)
- method(key2)
- l = len(self._box)
- self.assertEqual(l, 1)
- self.assertRaises(KeyError, lambda: self._box[key2])
- self.assertRaises(KeyError, lambda: method(key2))
- self.assertEqual(self._box.get_string(key1), self._template % 1)
- method(key1)
- self.assertEqual(len(self._box), 0)
- self.assertRaises(KeyError, lambda: self._box[key1])
- self.assertRaises(KeyError, lambda: method(key1))
-
- def test_discard(self, repetitions=10):
- # Discard messages
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(self._template % 1)
- self.assertEqual(len(self._box), 2)
- self._box.discard(key0)
- self.assertEqual(len(self._box), 1)
- self.assertRaises(KeyError, lambda: self._box[key0])
- self._box.discard(key0)
- self.assertEqual(len(self._box), 1)
- self.assertRaises(KeyError, lambda: self._box[key0])
-
- def test_get(self):
- # Retrieve messages using get()
- key0 = self._box.add(self._template % 0)
- msg = self._box.get(key0)
- self.assertEqual(msg['from'], 'foo')
- self.assertEqual(msg.get_payload(), '0\n')
- self.assertIs(self._box.get('foo'), None)
- self.assertFalse(self._box.get('foo', False))
- self._box.close()
- self._box = self._factory(self._path, factory=rfc822.Message)
- key1 = self._box.add(self._template % 1)
- msg = self._box.get(key1)
- self.assertEqual(msg['from'], 'foo')
- self.assertEqual(msg.fp.read(), '1' + os.linesep)
- msg.fp.close()
-
- def test_getitem(self):
- # Retrieve message using __getitem__()
- key0 = self._box.add(self._template % 0)
- msg = self._box[key0]
- self.assertEqual(msg['from'], 'foo')
- self.assertEqual(msg.get_payload(), '0\n')
- self.assertRaises(KeyError, lambda: self._box['foo'])
- self._box.discard(key0)
- self.assertRaises(KeyError, lambda: self._box[key0])
-
- def test_get_message(self):
- # Get Message representations of messages
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(_sample_message)
- msg0 = self._box.get_message(key0)
- self.assertIsInstance(msg0, mailbox.Message)
- self.assertEqual(msg0['from'], 'foo')
- self.assertEqual(msg0.get_payload(), '0\n')
- self._check_sample(self._box.get_message(key1))
-
- def test_get_string(self):
- # Get string representations of messages
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(_sample_message)
- self.assertEqual(self._box.get_string(key0), self._template % 0)
- self.assertEqual(self._box.get_string(key1), _sample_message)
-
- def test_get_file(self):
- # Get file representations of messages
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(_sample_message)
- msg0 = self._box.get_file(key0)
- self.assertEqual(msg0.read().replace(os.linesep, '\n'),
- self._template % 0)
- msg1 = self._box.get_file(key1)
- self.assertEqual(msg1.read().replace(os.linesep, '\n'),
- _sample_message)
- msg0.close()
- msg1.close()
-
- def test_get_file_can_be_closed_twice(self):
- # Issue 11700
- key = self._box.add(_sample_message)
- f = self._box.get_file(key)
- f.close()
- f.close()
-
- def test_iterkeys(self):
- # Get keys using iterkeys()
- self._check_iteration(self._box.iterkeys, do_keys=True, do_values=False)
-
- def test_keys(self):
- # Get keys using keys()
- self._check_iteration(self._box.keys, do_keys=True, do_values=False)
-
- def test_itervalues(self):
- # Get values using itervalues()
- self._check_iteration(self._box.itervalues, do_keys=False,
- do_values=True)
-
- def test_iter(self):
- # Get values using __iter__()
- self._check_iteration(self._box.__iter__, do_keys=False,
- do_values=True)
-
- def test_values(self):
- # Get values using values()
- self._check_iteration(self._box.values, do_keys=False, do_values=True)
-
- def test_iteritems(self):
- # Get keys and values using iteritems()
- self._check_iteration(self._box.iteritems, do_keys=True,
- do_values=True)
-
- def test_items(self):
- # Get keys and values using items()
- self._check_iteration(self._box.items, do_keys=True, do_values=True)
-
- def _check_iteration(self, method, do_keys, do_values, repetitions=10):
- for value in method():
- self.fail("Not empty")
- keys, values = [], []
- for i in xrange(repetitions):
- keys.append(self._box.add(self._template % i))
- values.append(self._template % i)
- if do_keys and not do_values:
- returned_keys = list(method())
- elif do_values and not do_keys:
- returned_values = list(method())
- else:
- returned_keys, returned_values = [], []
- for key, value in method():
- returned_keys.append(key)
- returned_values.append(value)
- if do_keys:
- self.assertEqual(len(keys), len(returned_keys))
- self.assertEqual(set(keys), set(returned_keys))
- if do_values:
- count = 0
- for value in returned_values:
- self.assertEqual(value['from'], 'foo')
- self.assertTrue(int(value.get_payload()) < repetitions,
- (value.get_payload(), repetitions))
- count += 1
- self.assertEqual(len(values), count)
-
- def test_has_key(self):
- # Check existence of keys using has_key()
- self._test_has_key_or_contains(self._box.has_key)
-
- def test_contains(self):
- # Check existence of keys using __contains__()
- self._test_has_key_or_contains(self._box.__contains__)
-
- def _test_has_key_or_contains(self, method):
- # (Used by test_has_key() and test_contains().)
- self.assertFalse(method('foo'))
- key0 = self._box.add(self._template % 0)
- self.assertTrue(method(key0))
- self.assertFalse(method('foo'))
- key1 = self._box.add(self._template % 1)
- self.assertTrue(method(key1))
- self.assertTrue(method(key0))
- self.assertFalse(method('foo'))
- self._box.remove(key0)
- self.assertFalse(method(key0))
- self.assertTrue(method(key1))
- self.assertFalse(method('foo'))
- self._box.remove(key1)
- self.assertFalse(method(key1))
- self.assertFalse(method(key0))
- self.assertFalse(method('foo'))
-
- def test_len(self, repetitions=10):
- # Get message count
- keys = []
- for i in xrange(repetitions):
- self.assertEqual(len(self._box), i)
- keys.append(self._box.add(self._template % i))
- self.assertEqual(len(self._box), i + 1)
- for i in xrange(repetitions):
- self.assertEqual(len(self._box), repetitions - i)
- self._box.remove(keys[i])
- self.assertEqual(len(self._box), repetitions - i - 1)
-
- def test_set_item(self):
- # Modify messages using __setitem__()
- key0 = self._box.add(self._template % 'original 0')
- self.assertEqual(self._box.get_string(key0),
- self._template % 'original 0')
- key1 = self._box.add(self._template % 'original 1')
- self.assertEqual(self._box.get_string(key1),
- self._template % 'original 1')
- self._box[key0] = self._template % 'changed 0'
- self.assertEqual(self._box.get_string(key0),
- self._template % 'changed 0')
- self._box[key1] = self._template % 'changed 1'
- self.assertEqual(self._box.get_string(key1),
- self._template % 'changed 1')
- self._box[key0] = _sample_message
- self._check_sample(self._box[key0])
- self._box[key1] = self._box[key0]
- self._check_sample(self._box[key1])
- self._box[key0] = self._template % 'original 0'
- self.assertEqual(self._box.get_string(key0),
- self._template % 'original 0')
- self._check_sample(self._box[key1])
- self.assertRaises(KeyError,
- lambda: self._box.__setitem__('foo', 'bar'))
- self.assertRaises(KeyError, lambda: self._box['foo'])
- self.assertEqual(len(self._box), 2)
-
- def test_clear(self, iterations=10):
- # Remove all messages using clear()
- keys = []
- for i in xrange(iterations):
- self._box.add(self._template % i)
- for i, key in enumerate(keys):
- self.assertEqual(self._box.get_string(key), self._template % i)
- self._box.clear()
- self.assertEqual(len(self._box), 0)
- for i, key in enumerate(keys):
- self.assertRaises(KeyError, lambda: self._box.get_string(key))
-
- def test_pop(self):
- # Get and remove a message using pop()
- key0 = self._box.add(self._template % 0)
- self.assertIn(key0, self._box)
- key1 = self._box.add(self._template % 1)
- self.assertIn(key1, self._box)
- self.assertEqual(self._box.pop(key0).get_payload(), '0\n')
- self.assertNotIn(key0, self._box)
- self.assertIn(key1, self._box)
- key2 = self._box.add(self._template % 2)
- self.assertIn(key2, self._box)
- self.assertEqual(self._box.pop(key2).get_payload(), '2\n')
- self.assertNotIn(key2, self._box)
- self.assertIn(key1, self._box)
- self.assertEqual(self._box.pop(key1).get_payload(), '1\n')
- self.assertNotIn(key1, self._box)
- self.assertEqual(len(self._box), 0)
-
- def test_popitem(self, iterations=10):
- # Get and remove an arbitrary (key, message) using popitem()
- keys = []
- for i in xrange(10):
- keys.append(self._box.add(self._template % i))
- seen = []
- for i in xrange(10):
- key, msg = self._box.popitem()
- self.assertIn(key, keys)
- self.assertNotIn(key, seen)
- seen.append(key)
- self.assertEqual(int(msg.get_payload()), keys.index(key))
- self.assertEqual(len(self._box), 0)
- for key in keys:
- self.assertRaises(KeyError, lambda: self._box[key])
-
- def test_update(self):
- # Modify multiple messages using update()
- key0 = self._box.add(self._template % 'original 0')
- key1 = self._box.add(self._template % 'original 1')
- key2 = self._box.add(self._template % 'original 2')
- self._box.update({key0: self._template % 'changed 0',
- key2: _sample_message})
- self.assertEqual(len(self._box), 3)
- self.assertEqual(self._box.get_string(key0),
- self._template % 'changed 0')
- self.assertEqual(self._box.get_string(key1),
- self._template % 'original 1')
- self._check_sample(self._box[key2])
- self._box.update([(key2, self._template % 'changed 2'),
- (key1, self._template % 'changed 1'),
- (key0, self._template % 'original 0')])
- self.assertEqual(len(self._box), 3)
- self.assertEqual(self._box.get_string(key0),
- self._template % 'original 0')
- self.assertEqual(self._box.get_string(key1),
- self._template % 'changed 1')
- self.assertEqual(self._box.get_string(key2),
- self._template % 'changed 2')
- self.assertRaises(KeyError,
- lambda: self._box.update({'foo': 'bar',
- key0: self._template % "changed 0"}))
- self.assertEqual(len(self._box), 3)
- self.assertEqual(self._box.get_string(key0),
- self._template % "changed 0")
- self.assertEqual(self._box.get_string(key1),
- self._template % "changed 1")
- self.assertEqual(self._box.get_string(key2),
- self._template % "changed 2")
-
- def test_flush(self):
- # Write changes to disk
- self._test_flush_or_close(self._box.flush, True)
-
- def test_popitem_and_flush_twice(self):
- # See #15036.
- self._box.add(self._template % 0)
- self._box.add(self._template % 1)
- self._box.flush()
-
- self._box.popitem()
- self._box.flush()
- self._box.popitem()
- self._box.flush()
-
- def test_lock_unlock(self):
- # Lock and unlock the mailbox
- self.assertFalse(os.path.exists(self._get_lock_path()))
- self._box.lock()
- self.assertTrue(os.path.exists(self._get_lock_path()))
- self._box.unlock()
- self.assertFalse(os.path.exists(self._get_lock_path()))
-
- def test_close(self):
- # Close mailbox and flush changes to disk
- self._test_flush_or_close(self._box.close, False)
-
- def _test_flush_or_close(self, method, should_call_close):
- contents = [self._template % i for i in xrange(3)]
- self._box.add(contents[0])
- self._box.add(contents[1])
- self._box.add(contents[2])
- oldbox = self._box
- method()
- if should_call_close:
- self._box.close()
- self._box = self._factory(self._path)
- keys = self._box.keys()
- self.assertEqual(len(keys), 3)
- for key in keys:
- self.assertIn(self._box.get_string(key), contents)
- oldbox.close()
-
- def test_dump_message(self):
- # Write message representations to disk
- for input in (email.message_from_string(_sample_message),
- _sample_message, StringIO.StringIO(_sample_message)):
- output = StringIO.StringIO()
- self._box._dump_message(input, output)
- self.assertEqual(output.getvalue(),
- _sample_message.replace('\n', os.linesep))
- output = StringIO.StringIO()
- self.assertRaises(TypeError,
- lambda: self._box._dump_message(None, output))
-
- def _get_lock_path(self):
- # Return the path of the dot lock file. May be overridden.
- return self._path + '.lock'
-
-
-class TestMailboxSuperclass(TestBase, unittest.TestCase):
-
- def test_notimplemented(self):
- # Test that all Mailbox methods raise NotImplementedException.
- box = mailbox.Mailbox('path')
- self.assertRaises(NotImplementedError, lambda: box.add(''))
- self.assertRaises(NotImplementedError, lambda: box.remove(''))
- self.assertRaises(NotImplementedError, lambda: box.__delitem__(''))
- self.assertRaises(NotImplementedError, lambda: box.discard(''))
- self.assertRaises(NotImplementedError, lambda: box.__setitem__('', ''))
- self.assertRaises(NotImplementedError, lambda: box.iterkeys())
- self.assertRaises(NotImplementedError, lambda: box.keys())
- self.assertRaises(NotImplementedError, lambda: box.itervalues().next())
- self.assertRaises(NotImplementedError, lambda: box.__iter__().next())
- self.assertRaises(NotImplementedError, lambda: box.values())
- self.assertRaises(NotImplementedError, lambda: box.iteritems().next())
- self.assertRaises(NotImplementedError, lambda: box.items())
- self.assertRaises(NotImplementedError, lambda: box.get(''))
- self.assertRaises(NotImplementedError, lambda: box.__getitem__(''))
- self.assertRaises(NotImplementedError, lambda: box.get_message(''))
- self.assertRaises(NotImplementedError, lambda: box.get_string(''))
- self.assertRaises(NotImplementedError, lambda: box.get_file(''))
- self.assertRaises(NotImplementedError, lambda: box.has_key(''))
- self.assertRaises(NotImplementedError, lambda: box.__contains__(''))
- self.assertRaises(NotImplementedError, lambda: box.__len__())
- self.assertRaises(NotImplementedError, lambda: box.clear())
- self.assertRaises(NotImplementedError, lambda: box.pop(''))
- self.assertRaises(NotImplementedError, lambda: box.popitem())
- self.assertRaises(NotImplementedError, lambda: box.update((('', ''),)))
- self.assertRaises(NotImplementedError, lambda: box.flush())
- self.assertRaises(NotImplementedError, lambda: box.lock())
- self.assertRaises(NotImplementedError, lambda: box.unlock())
- self.assertRaises(NotImplementedError, lambda: box.close())
-
-
-class TestMaildir(TestMailbox, unittest.TestCase):
-
- _factory = lambda self, path, factory=None: mailbox.Maildir(path, factory)
-
- def setUp(self):
- TestMailbox.setUp(self)
- if os.name in ('nt', 'os2') or sys.platform == 'cygwin':
- self._box.colon = '!'
-
- def test_add_MM(self):
- # Add a MaildirMessage instance
- msg = mailbox.MaildirMessage(self._template % 0)
- msg.set_subdir('cur')
- msg.set_info('foo')
- key = self._box.add(msg)
- self.assertTrue(os.path.exists(os.path.join(self._path, 'cur', '%s%sfoo' %
- (key, self._box.colon))))
-
- def test_get_MM(self):
- # Get a MaildirMessage instance
- msg = mailbox.MaildirMessage(self._template % 0)
- msg.set_subdir('cur')
- msg.set_flags('RF')
- key = self._box.add(msg)
- msg_returned = self._box.get_message(key)
- self.assertIsInstance(msg_returned, mailbox.MaildirMessage)
- self.assertEqual(msg_returned.get_subdir(), 'cur')
- self.assertEqual(msg_returned.get_flags(), 'FR')
-
- def test_set_MM(self):
- # Set with a MaildirMessage instance
- msg0 = mailbox.MaildirMessage(self._template % 0)
- msg0.set_flags('TP')
- key = self._box.add(msg0)
- msg_returned = self._box.get_message(key)
- self.assertEqual(msg_returned.get_subdir(), 'new')
- self.assertEqual(msg_returned.get_flags(), 'PT')
- msg1 = mailbox.MaildirMessage(self._template % 1)
- self._box[key] = msg1
- msg_returned = self._box.get_message(key)
- self.assertEqual(msg_returned.get_subdir(), 'new')
- self.assertEqual(msg_returned.get_flags(), '')
- self.assertEqual(msg_returned.get_payload(), '1\n')
- msg2 = mailbox.MaildirMessage(self._template % 2)
- msg2.set_info('2,S')
- self._box[key] = msg2
- self._box[key] = self._template % 3
- msg_returned = self._box.get_message(key)
- self.assertEqual(msg_returned.get_subdir(), 'new')
- self.assertEqual(msg_returned.get_flags(), 'S')
- self.assertEqual(msg_returned.get_payload(), '3\n')
-
- def test_consistent_factory(self):
- # Add a message.
- msg = mailbox.MaildirMessage(self._template % 0)
- msg.set_subdir('cur')
- msg.set_flags('RF')
- key = self._box.add(msg)
-
- # Create new mailbox with
- class FakeMessage(mailbox.MaildirMessage):
- pass
- box = mailbox.Maildir(self._path, factory=FakeMessage)
- box.colon = self._box.colon
- msg2 = box.get_message(key)
- self.assertIsInstance(msg2, FakeMessage)
-
- def test_initialize_new(self):
- # Initialize a non-existent mailbox
- self.tearDown()
- self._box = mailbox.Maildir(self._path)
- self._check_basics(factory=rfc822.Message)
- self._delete_recursively(self._path)
- self._box = self._factory(self._path, factory=None)
- self._check_basics()
-
- def test_initialize_existing(self):
- # Initialize an existing mailbox
- self.tearDown()
- for subdir in '', 'tmp', 'new', 'cur':
- os.mkdir(os.path.normpath(os.path.join(self._path, subdir)))
- self._box = mailbox.Maildir(self._path)
- self._check_basics(factory=rfc822.Message)
- self._box = mailbox.Maildir(self._path, factory=None)
- self._check_basics()
-
- def _check_basics(self, factory=None):
- # (Used by test_open_new() and test_open_existing().)
- self.assertEqual(self._box._path, os.path.abspath(self._path))
- self.assertEqual(self._box._factory, factory)
- for subdir in '', 'tmp', 'new', 'cur':
- path = os.path.join(self._path, subdir)
- mode = os.stat(path)[stat.ST_MODE]
- self.assertTrue(stat.S_ISDIR(mode), "Not a directory: '%s'" % path)
-
- def test_list_folders(self):
- # List folders
- self._box.add_folder('one')
- self._box.add_folder('two')
- self._box.add_folder('three')
- self.assertEqual(len(self._box.list_folders()), 3)
- self.assertEqual(set(self._box.list_folders()),
- set(('one', 'two', 'three')))
-
- def test_get_folder(self):
- # Open folders
- self._box.add_folder('foo.bar')
- folder0 = self._box.get_folder('foo.bar')
- folder0.add(self._template % 'bar')
- self.assertTrue(os.path.isdir(os.path.join(self._path, '.foo.bar')))
- folder1 = self._box.get_folder('foo.bar')
- self.assertEqual(folder1.get_string(folder1.keys()[0]),
- self._template % 'bar')
-
- def test_add_and_remove_folders(self):
- # Delete folders
- self._box.add_folder('one')
- self._box.add_folder('two')
- self.assertEqual(len(self._box.list_folders()), 2)
- self.assertEqual(set(self._box.list_folders()), set(('one', 'two')))
- self._box.remove_folder('one')
- self.assertEqual(len(self._box.list_folders()), 1)
- self.assertEqual(set(self._box.list_folders()), set(('two',)))
- self._box.add_folder('three')
- self.assertEqual(len(self._box.list_folders()), 2)
- self.assertEqual(set(self._box.list_folders()), set(('two', 'three')))
- self._box.remove_folder('three')
- self.assertEqual(len(self._box.list_folders()), 1)
- self.assertEqual(set(self._box.list_folders()), set(('two',)))
- self._box.remove_folder('two')
- self.assertEqual(len(self._box.list_folders()), 0)
- self.assertEqual(self._box.list_folders(), [])
-
- def test_clean(self):
- # Remove old files from 'tmp'
- foo_path = os.path.join(self._path, 'tmp', 'foo')
- bar_path = os.path.join(self._path, 'tmp', 'bar')
- with open(foo_path, 'w') as f:
- f.write("@")
- with open(bar_path, 'w') as f:
- f.write("@")
- self._box.clean()
- self.assertTrue(os.path.exists(foo_path))
- self.assertTrue(os.path.exists(bar_path))
- foo_stat = os.stat(foo_path)
- os.utime(foo_path, (time.time() - 129600 - 2,
- foo_stat.st_mtime))
- self._box.clean()
- self.assertFalse(os.path.exists(foo_path))
- self.assertTrue(os.path.exists(bar_path))
-
- def test_create_tmp(self, repetitions=10):
- # Create files in tmp directory
- hostname = socket.gethostname()
- if '/' in hostname:
- hostname = hostname.replace('/', r'\057')
- if ':' in hostname:
- hostname = hostname.replace(':', r'\072')
- pid = os.getpid()
- pattern = re.compile(r"(?P\d+)\.M(?P\d{1,6})P(?P\d+)"
- r"Q(?P\d+)\.(?P[^:/]+)")
- previous_groups = None
- for x in xrange(repetitions):
- tmp_file = self._box._create_tmp()
- head, tail = os.path.split(tmp_file.name)
- self.assertEqual(head, os.path.abspath(os.path.join(self._path,
- "tmp")),
- "File in wrong location: '%s'" % head)
- match = pattern.match(tail)
- self.assertTrue(match is not None, "Invalid file name: '%s'" % tail)
- groups = match.groups()
- if previous_groups is not None:
- self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]),
- "Non-monotonic seconds: '%s' before '%s'" %
- (previous_groups[0], groups[0]))
- if int(groups[0]) == int(previous_groups[0]):
- self.assertGreaterEqual(int(groups[1]), int(previous_groups[1]),
- "Non-monotonic milliseconds: '%s' before '%s'" %
- (previous_groups[1], groups[1]))
- self.assertTrue(int(groups[2]) == pid,
- "Process ID mismatch: '%s' should be '%s'" %
- (groups[2], pid))
- self.assertTrue(int(groups[3]) == int(previous_groups[3]) + 1,
- "Non-sequential counter: '%s' before '%s'" %
- (previous_groups[3], groups[3]))
- self.assertTrue(groups[4] == hostname,
- "Host name mismatch: '%s' should be '%s'" %
- (groups[4], hostname))
- previous_groups = groups
- tmp_file.write(_sample_message)
- tmp_file.seek(0)
- self.assertTrue(tmp_file.read() == _sample_message)
- tmp_file.close()
- file_count = len(os.listdir(os.path.join(self._path, "tmp")))
- self.assertTrue(file_count == repetitions,
- "Wrong file count: '%s' should be '%s'" %
- (file_count, repetitions))
-
- def test_refresh(self):
- # Update the table of contents
- self.assertEqual(self._box._toc, {})
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(self._template % 1)
- self.assertEqual(self._box._toc, {})
- self._box._refresh()
- self.assertEqual(self._box._toc, {key0: os.path.join('new', key0),
- key1: os.path.join('new', key1)})
- key2 = self._box.add(self._template % 2)
- self.assertEqual(self._box._toc, {key0: os.path.join('new', key0),
- key1: os.path.join('new', key1)})
- self._box._refresh()
- self.assertEqual(self._box._toc, {key0: os.path.join('new', key0),
- key1: os.path.join('new', key1),
- key2: os.path.join('new', key2)})
-
- def test_refresh_after_safety_period(self):
- # Issue #13254: Call _refresh after the "file system safety
- # period" of 2 seconds has passed; _toc should still be
- # updated because this is the first call to _refresh.
- key0 = self._box.add(self._template % 0)
- key1 = self._box.add(self._template % 1)
-
- self._box = self._factory(self._path)
- self.assertEqual(self._box._toc, {})
-
- # Emulate sleeping. Instead of sleeping for 2 seconds, use the
- # skew factor to make _refresh think that the filesystem
- # safety period has passed and re-reading the _toc is only
- # required if mtimes differ.
- self._box._skewfactor = -3
-
- self._box._refresh()
- self.assertEqual(sorted(self._box._toc.keys()), sorted([key0, key1]))
-
- def test_lookup(self):
- # Look up message subpaths in the TOC
- self.assertRaises(KeyError, lambda: self._box._lookup('foo'))
- key0 = self._box.add(self._template % 0)
- self.assertEqual(self._box._lookup(key0), os.path.join('new', key0))
- os.remove(os.path.join(self._path, 'new', key0))
- self.assertEqual(self._box._toc, {key0: os.path.join('new', key0)})
- # Be sure that the TOC is read back from disk (see issue #6896
- # about bad mtime behaviour on some systems).
- self._box.flush()
- self.assertRaises(KeyError, lambda: self._box._lookup(key0))
- self.assertEqual(self._box._toc, {})
-
- def test_lock_unlock(self):
- # Lock and unlock the mailbox. For Maildir, this does nothing.
- self._box.lock()
- self._box.unlock()
-
- def test_folder (self):
- # Test for bug #1569790: verify that folders returned by .get_folder()
- # use the same factory function.
- def dummy_factory (s):
- return None
- box = self._factory(self._path, factory=dummy_factory)
- folder = box.add_folder('folder1')
- self.assertIs(folder._factory, dummy_factory)
-
- folder1_alias = box.get_folder('folder1')
- self.assertIs(folder1_alias._factory, dummy_factory)
-
- def test_directory_in_folder (self):
- # Test that mailboxes still work if there's a stray extra directory
- # in a folder.
- for i in range(10):
- self._box.add(mailbox.Message(_sample_message))
-
- # Create a stray directory
- os.mkdir(os.path.join(self._path, 'cur', 'stray-dir'))
-
- # Check that looping still works with the directory present.
- for msg in self._box:
- pass
-
- def test_file_permissions(self):
- # Verify that message files are created without execute permissions
- if not hasattr(os, "stat") or not hasattr(os, "umask"):
- return
- msg = mailbox.MaildirMessage(self._template % 0)
- orig_umask = os.umask(0)
- try:
- key = self._box.add(msg)
- finally:
- os.umask(orig_umask)
- path = os.path.join(self._path, self._box._lookup(key))
- mode = os.stat(path).st_mode
- self.assertEqual(mode & 0111, 0)
-
- def test_folder_file_perms(self):
- # From bug #3228, we want to verify that the file created inside a Maildir
- # subfolder isn't marked as executable.
- if not hasattr(os, "stat") or not hasattr(os, "umask"):
- return
-
- orig_umask = os.umask(0)
- try:
- subfolder = self._box.add_folder('subfolder')
- finally:
- os.umask(orig_umask)
-
- path = os.path.join(subfolder._path, 'maildirfolder')
- st = os.stat(path)
- perms = st.st_mode
- self.assertFalse((perms & 0111)) # Execute bits should all be off.
-
- def test_reread(self):
- # Do an initial unconditional refresh
- self._box._refresh()
-
- # Put the last modified times more than two seconds into the past
- # (because mtime may have only a two second granularity).
- for subdir in ('cur', 'new'):
- os.utime(os.path.join(self._box._path, subdir),
- (time.time()-5,)*2)
-
- # Because mtime has a two second granularity in worst case (FAT), a
- # refresh is done unconditionally if called for within
- # two-second-plus-a-bit of the last one, just in case the mbox has
- # changed; so now we have to wait for that interval to expire.
- #
- # Because this is a test, emulate sleeping. Instead of
- # sleeping for 2 seconds, use the skew factor to make _refresh
- # think that 2 seconds have passed and re-reading the _toc is
- # only required if mtimes differ.
- self._box._skewfactor = -3
-
- # Re-reading causes the ._toc attribute to be assigned a new dictionary
- # object, so we'll check that the ._toc attribute isn't a different
- # object.
- orig_toc = self._box._toc
- def refreshed():
- return self._box._toc is not orig_toc
-
- self._box._refresh()
- self.assertFalse(refreshed())
-
- # Now, write something into cur and remove it. This changes
- # the mtime and should cause a re-read. Note that "sleep
- # emulation" is still in effect, as skewfactor is -3.
- filename = os.path.join(self._path, 'cur', 'stray-file')
- f = open(filename, 'w')
- f.close()
- os.unlink(filename)
- self._box._refresh()
- self.assertTrue(refreshed())
-
-
-class _TestSingleFile(TestMailbox):
- '''Common tests for single-file mailboxes'''
-
- def test_add_doesnt_rewrite(self):
- # When only adding messages, flush() should not rewrite the
- # mailbox file. See issue #9559.
-
- # Inode number changes if the contents are written to another
- # file which is then renamed over the original file. So we
- # must check that the inode number doesn't change.
- inode_before = os.stat(self._path).st_ino
-
- self._box.add(self._template % 0)
- self._box.flush()
-
- inode_after = os.stat(self._path).st_ino
- self.assertEqual(inode_before, inode_after)
-
- # Make sure the message was really added
- self._box.close()
- self._box = self._factory(self._path)
- self.assertEqual(len(self._box), 1)
-
- def test_permissions_after_flush(self):
- # See issue #5346
-
- # Make the mailbox world writable. It's unlikely that the new
- # mailbox file would have these permissions after flush(),
- # because umask usually prevents it.
- mode = os.stat(self._path).st_mode | 0o666
- os.chmod(self._path, mode)
-
- self._box.add(self._template % 0)
- i = self._box.add(self._template % 1)
- # Need to remove one message to make flush() create a new file
- self._box.remove(i)
- self._box.flush()
-
- self.assertEqual(os.stat(self._path).st_mode, mode)
-
-
-class _TestMboxMMDF(_TestSingleFile):
-
- def tearDown(self):
- self._box.close()
- self._delete_recursively(self._path)
- for lock_remnant in glob.glob(self._path + '.*'):
- test_support.unlink(lock_remnant)
-
- def test_add_from_string(self):
- # Add a string starting with 'From ' to the mailbox
- key = self._box.add('From foo at bar blah\nFrom: foo\n\n0\n')
- self.assertEqual(self._box[key].get_from(), 'foo at bar blah')
- self.assertEqual(self._box[key].get_payload(), '0\n')
-
- def test_add_mbox_or_mmdf_message(self):
- # Add an mboxMessage or MMDFMessage
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg = class_('From foo at bar blah\nFrom: foo\n\n0\n')
- key = self._box.add(msg)
-
- def test_open_close_open(self):
- # Open and inspect previously-created mailbox
- values = [self._template % i for i in xrange(3)]
- for value in values:
- self._box.add(value)
- self._box.close()
- mtime = os.path.getmtime(self._path)
- self._box = self._factory(self._path)
- self.assertEqual(len(self._box), 3)
- for key in self._box.iterkeys():
- self.assertIn(self._box.get_string(key), values)
- self._box.close()
- self.assertEqual(mtime, os.path.getmtime(self._path))
-
- def test_add_and_close(self):
- # Verifying that closing a mailbox doesn't change added items
- self._box.add(_sample_message)
- for i in xrange(3):
- self._box.add(self._template % i)
- self._box.add(_sample_message)
- self._box._file.flush()
- self._box._file.seek(0)
- contents = self._box._file.read()
- self._box.close()
- with open(self._path, 'rb') as f:
- self.assertEqual(contents, f.read())
- self._box = self._factory(self._path)
-
- @unittest.skipUnless(hasattr(os, 'fork'), "Test needs fork().")
- @unittest.skipUnless(hasattr(socket, 'socketpair'), "Test needs socketpair().")
- def test_lock_conflict(self):
- # Fork off a child process that will lock the mailbox temporarily,
- # unlock it and exit.
- c, p = socket.socketpair()
- self.addCleanup(c.close)
- self.addCleanup(p.close)
-
- pid = os.fork()
- if pid == 0:
- # child
- try:
- # lock the mailbox, and signal the parent it can proceed
- self._box.lock()
- c.send(b'c')
-
- # wait until the parent is done, and unlock the mailbox
- c.recv(1)
- self._box.unlock()
- finally:
- os._exit(0)
-
- # In the parent, wait until the child signals it locked the mailbox.
- p.recv(1)
- try:
- self.assertRaises(mailbox.ExternalClashError,
- self._box.lock)
- finally:
- # Signal the child it can now release the lock and exit.
- p.send(b'p')
- # Wait for child to exit. Locking should now succeed.
- exited_pid, status = os.waitpid(pid, 0)
-
- self._box.lock()
- self._box.unlock()
-
- def test_relock(self):
- # Test case for bug #1575506: the mailbox class was locking the
- # wrong file object in its flush() method.
- msg = "Subject: sub\n\nbody\n"
- key1 = self._box.add(msg)
- self._box.flush()
- self._box.close()
-
- self._box = self._factory(self._path)
- self._box.lock()
- key2 = self._box.add(msg)
- self._box.flush()
- self.assertTrue(self._box._locked)
- self._box.close()
-
-
-class TestMbox(_TestMboxMMDF, unittest.TestCase):
-
- _factory = lambda self, path, factory=None: mailbox.mbox(path, factory)
-
- def test_file_perms(self):
- # From bug #3228, we want to verify that the mailbox file isn't executable,
- # even if the umask is set to something that would leave executable bits set.
- # We only run this test on platforms that support umask.
- if hasattr(os, 'umask') and hasattr(os, 'stat'):
- try:
- old_umask = os.umask(0077)
- self._box.close()
- os.unlink(self._path)
- self._box = mailbox.mbox(self._path, create=True)
- self._box.add('')
- self._box.close()
- finally:
- os.umask(old_umask)
-
- st = os.stat(self._path)
- perms = st.st_mode
- self.assertFalse((perms & 0111)) # Execute bits should all be off.
-
- def test_terminating_newline(self):
- message = email.message.Message()
- message['From'] = 'john at example.com'
- message.set_payload('No newline at the end')
- i = self._box.add(message)
-
- # A newline should have been appended to the payload
- message = self._box.get(i)
- self.assertEqual(message.get_payload(), 'No newline at the end\n')
-
- def test_message_separator(self):
- # Check there's always a single blank line after each message
- self._box.add('From: foo\n\n0') # No newline at the end
- with open(self._path) as f:
- data = f.read()
- self.assertEqual(data[-3:], '0\n\n')
-
- self._box.add('From: foo\n\n0\n') # Newline at the end
- with open(self._path) as f:
- data = f.read()
- self.assertEqual(data[-3:], '0\n\n')
-
-
-class TestMMDF(_TestMboxMMDF, unittest.TestCase):
-
- _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory)
-
-
-class TestMH(TestMailbox, unittest.TestCase):
-
- _factory = lambda self, path, factory=None: mailbox.MH(path, factory)
-
- def test_list_folders(self):
- # List folders
- self._box.add_folder('one')
- self._box.add_folder('two')
- self._box.add_folder('three')
- self.assertEqual(len(self._box.list_folders()), 3)
- self.assertEqual(set(self._box.list_folders()),
- set(('one', 'two', 'three')))
-
- def test_get_folder(self):
- # Open folders
- def dummy_factory (s):
- return None
- self._box = self._factory(self._path, dummy_factory)
-
- new_folder = self._box.add_folder('foo.bar')
- folder0 = self._box.get_folder('foo.bar')
- folder0.add(self._template % 'bar')
- self.assertTrue(os.path.isdir(os.path.join(self._path, 'foo.bar')))
- folder1 = self._box.get_folder('foo.bar')
- self.assertEqual(folder1.get_string(folder1.keys()[0]),
- self._template % 'bar')
-
- # Test for bug #1569790: verify that folders returned by .get_folder()
- # use the same factory function.
- self.assertIs(new_folder._factory, self._box._factory)
- self.assertIs(folder0._factory, self._box._factory)
-
- def test_add_and_remove_folders(self):
- # Delete folders
- self._box.add_folder('one')
- self._box.add_folder('two')
- self.assertEqual(len(self._box.list_folders()), 2)
- self.assertEqual(set(self._box.list_folders()), set(('one', 'two')))
- self._box.remove_folder('one')
- self.assertEqual(len(self._box.list_folders()), 1)
- self.assertEqual(set(self._box.list_folders()), set(('two', )))
- self._box.add_folder('three')
- self.assertEqual(len(self._box.list_folders()), 2)
- self.assertEqual(set(self._box.list_folders()), set(('two', 'three')))
- self._box.remove_folder('three')
- self.assertEqual(len(self._box.list_folders()), 1)
- self.assertEqual(set(self._box.list_folders()), set(('two', )))
- self._box.remove_folder('two')
- self.assertEqual(len(self._box.list_folders()), 0)
- self.assertEqual(self._box.list_folders(), [])
-
- def test_sequences(self):
- # Get and set sequences
- self.assertEqual(self._box.get_sequences(), {})
- msg0 = mailbox.MHMessage(self._template % 0)
- msg0.add_sequence('foo')
- key0 = self._box.add(msg0)
- self.assertEqual(self._box.get_sequences(), {'foo':[key0]})
- msg1 = mailbox.MHMessage(self._template % 1)
- msg1.set_sequences(['bar', 'replied', 'foo'])
- key1 = self._box.add(msg1)
- self.assertEqual(self._box.get_sequences(),
- {'foo':[key0, key1], 'bar':[key1], 'replied':[key1]})
- msg0.set_sequences(['flagged'])
- self._box[key0] = msg0
- self.assertEqual(self._box.get_sequences(),
- {'foo':[key1], 'bar':[key1], 'replied':[key1],
- 'flagged':[key0]})
- self._box.remove(key1)
- self.assertEqual(self._box.get_sequences(), {'flagged':[key0]})
-
- def test_issue2625(self):
- msg0 = mailbox.MHMessage(self._template % 0)
- msg0.add_sequence('foo')
- key0 = self._box.add(msg0)
- refmsg0 = self._box.get_message(key0)
-
- def test_issue7627(self):
- msg0 = mailbox.MHMessage(self._template % 0)
- key0 = self._box.add(msg0)
- self._box.lock()
- self._box.remove(key0)
- self._box.unlock()
-
- def test_pack(self):
- # Pack the contents of the mailbox
- msg0 = mailbox.MHMessage(self._template % 0)
- msg1 = mailbox.MHMessage(self._template % 1)
- msg2 = mailbox.MHMessage(self._template % 2)
- msg3 = mailbox.MHMessage(self._template % 3)
- msg0.set_sequences(['foo', 'unseen'])
- msg1.set_sequences(['foo'])
- msg2.set_sequences(['foo', 'flagged'])
- msg3.set_sequences(['foo', 'bar', 'replied'])
- key0 = self._box.add(msg0)
- key1 = self._box.add(msg1)
- key2 = self._box.add(msg2)
- key3 = self._box.add(msg3)
- self.assertEqual(self._box.get_sequences(),
- {'foo':[key0,key1,key2,key3], 'unseen':[key0],
- 'flagged':[key2], 'bar':[key3], 'replied':[key3]})
- self._box.remove(key2)
- self.assertEqual(self._box.get_sequences(),
- {'foo':[key0,key1,key3], 'unseen':[key0], 'bar':[key3],
- 'replied':[key3]})
- self._box.pack()
- self.assertEqual(self._box.keys(), [1, 2, 3])
- key0 = key0
- key1 = key0 + 1
- key2 = key1 + 1
- self.assertEqual(self._box.get_sequences(),
- {'foo':[1, 2, 3], 'unseen':[1], 'bar':[3], 'replied':[3]})
-
- # Test case for packing while holding the mailbox locked.
- key0 = self._box.add(msg1)
- key1 = self._box.add(msg1)
- key2 = self._box.add(msg1)
- key3 = self._box.add(msg1)
-
- self._box.remove(key0)
- self._box.remove(key2)
- self._box.lock()
- self._box.pack()
- self._box.unlock()
- self.assertEqual(self._box.get_sequences(),
- {'foo':[1, 2, 3, 4, 5],
- 'unseen':[1], 'bar':[3], 'replied':[3]})
-
- def _get_lock_path(self):
- return os.path.join(self._path, '.mh_sequences.lock')
-
-
-class TestBabyl(_TestSingleFile, unittest.TestCase):
-
- _factory = lambda self, path, factory=None: mailbox.Babyl(path, factory)
-
- def tearDown(self):
- self._box.close()
- self._delete_recursively(self._path)
- for lock_remnant in glob.glob(self._path + '.*'):
- test_support.unlink(lock_remnant)
-
- def test_labels(self):
- # Get labels from the mailbox
- self.assertEqual(self._box.get_labels(), [])
- msg0 = mailbox.BabylMessage(self._template % 0)
- msg0.add_label('foo')
- key0 = self._box.add(msg0)
- self.assertEqual(self._box.get_labels(), ['foo'])
- msg1 = mailbox.BabylMessage(self._template % 1)
- msg1.set_labels(['bar', 'answered', 'foo'])
- key1 = self._box.add(msg1)
- self.assertEqual(set(self._box.get_labels()), set(['foo', 'bar']))
- msg0.set_labels(['blah', 'filed'])
- self._box[key0] = msg0
- self.assertEqual(set(self._box.get_labels()),
- set(['foo', 'bar', 'blah']))
- self._box.remove(key1)
- self.assertEqual(set(self._box.get_labels()), set(['blah']))
-
-
-class TestMessage(TestBase, unittest.TestCase):
-
- _factory = mailbox.Message # Overridden by subclasses to reuse tests
-
- def setUp(self):
- self._path = test_support.TESTFN
-
- def tearDown(self):
- self._delete_recursively(self._path)
-
- def test_initialize_with_eMM(self):
- # Initialize based on email.message.Message instance
- eMM = email.message_from_string(_sample_message)
- msg = self._factory(eMM)
- self._post_initialize_hook(msg)
- self._check_sample(msg)
-
- def test_initialize_with_string(self):
- # Initialize based on string
- msg = self._factory(_sample_message)
- self._post_initialize_hook(msg)
- self._check_sample(msg)
-
- def test_initialize_with_file(self):
- # Initialize based on contents of file
- with open(self._path, 'w+') as f:
- f.write(_sample_message)
- f.seek(0)
- msg = self._factory(f)
- self._post_initialize_hook(msg)
- self._check_sample(msg)
-
- def test_initialize_with_nothing(self):
- # Initialize without arguments
- msg = self._factory()
- self._post_initialize_hook(msg)
- self.assertIsInstance(msg, email.message.Message)
- self.assertIsInstance(msg, mailbox.Message)
- self.assertIsInstance(msg, self._factory)
- self.assertEqual(msg.keys(), [])
- self.assertFalse(msg.is_multipart())
- self.assertEqual(msg.get_payload(), None)
-
- def test_initialize_incorrectly(self):
- # Initialize with invalid argument
- self.assertRaises(TypeError, lambda: self._factory(object()))
-
- def test_become_message(self):
- # Take on the state of another message
- eMM = email.message_from_string(_sample_message)
- msg = self._factory()
- msg._become_message(eMM)
- self._check_sample(msg)
-
- def test_explain_to(self):
- # Copy self's format-specific data to other message formats.
- # This test is superficial; better ones are in TestMessageConversion.
- msg = self._factory()
- for class_ in (mailbox.Message, mailbox.MaildirMessage,
- mailbox.mboxMessage, mailbox.MHMessage,
- mailbox.BabylMessage, mailbox.MMDFMessage):
- other_msg = class_()
- msg._explain_to(other_msg)
- other_msg = email.message.Message()
- self.assertRaises(TypeError, lambda: msg._explain_to(other_msg))
-
- def _post_initialize_hook(self, msg):
- # Overridden by subclasses to check extra things after initialization
- pass
-
-
-class TestMaildirMessage(TestMessage, unittest.TestCase):
-
- _factory = mailbox.MaildirMessage
-
- def _post_initialize_hook(self, msg):
- self.assertEqual(msg._subdir, 'new')
- self.assertEqual(msg._info,'')
-
- def test_subdir(self):
- # Use get_subdir() and set_subdir()
- msg = mailbox.MaildirMessage(_sample_message)
- self.assertEqual(msg.get_subdir(), 'new')
- msg.set_subdir('cur')
- self.assertEqual(msg.get_subdir(), 'cur')
- msg.set_subdir('new')
- self.assertEqual(msg.get_subdir(), 'new')
- self.assertRaises(ValueError, lambda: msg.set_subdir('tmp'))
- self.assertEqual(msg.get_subdir(), 'new')
- msg.set_subdir('new')
- self.assertEqual(msg.get_subdir(), 'new')
- self._check_sample(msg)
-
- def test_flags(self):
- # Use get_flags(), set_flags(), add_flag(), remove_flag()
- msg = mailbox.MaildirMessage(_sample_message)
- self.assertEqual(msg.get_flags(), '')
- self.assertEqual(msg.get_subdir(), 'new')
- msg.set_flags('F')
- self.assertEqual(msg.get_subdir(), 'new')
- self.assertEqual(msg.get_flags(), 'F')
- msg.set_flags('SDTP')
- self.assertEqual(msg.get_flags(), 'DPST')
- msg.add_flag('FT')
- self.assertEqual(msg.get_flags(), 'DFPST')
- msg.remove_flag('TDRP')
- self.assertEqual(msg.get_flags(), 'FS')
- self.assertEqual(msg.get_subdir(), 'new')
- self._check_sample(msg)
-
- def test_date(self):
- # Use get_date() and set_date()
- msg = mailbox.MaildirMessage(_sample_message)
- diff = msg.get_date() - time.time()
- self.assertTrue(abs(diff) < 60, diff)
- msg.set_date(0.0)
- self.assertEqual(msg.get_date(), 0.0)
-
- def test_info(self):
- # Use get_info() and set_info()
- msg = mailbox.MaildirMessage(_sample_message)
- self.assertEqual(msg.get_info(), '')
- msg.set_info('1,foo=bar')
- self.assertEqual(msg.get_info(), '1,foo=bar')
- self.assertRaises(TypeError, lambda: msg.set_info(None))
- self._check_sample(msg)
-
- def test_info_and_flags(self):
- # Test interaction of info and flag methods
- msg = mailbox.MaildirMessage(_sample_message)
- self.assertEqual(msg.get_info(), '')
- msg.set_flags('SF')
- self.assertEqual(msg.get_flags(), 'FS')
- self.assertEqual(msg.get_info(), '2,FS')
- msg.set_info('1,')
- self.assertEqual(msg.get_flags(), '')
- self.assertEqual(msg.get_info(), '1,')
- msg.remove_flag('RPT')
- self.assertEqual(msg.get_flags(), '')
- self.assertEqual(msg.get_info(), '1,')
- msg.add_flag('D')
- self.assertEqual(msg.get_flags(), 'D')
- self.assertEqual(msg.get_info(), '2,D')
- self._check_sample(msg)
-
-
-class _TestMboxMMDFMessage:
-
- _factory = mailbox._mboxMMDFMessage
-
- def _post_initialize_hook(self, msg):
- self._check_from(msg)
-
- def test_initialize_with_unixfrom(self):
- # Initialize with a message that already has a _unixfrom attribute
- msg = mailbox.Message(_sample_message)
- msg.set_unixfrom('From foo at bar blah')
- msg = mailbox.mboxMessage(msg)
- self.assertEqual(msg.get_from(), 'foo at bar blah')
-
- def test_from(self):
- # Get and set "From " line
- msg = mailbox.mboxMessage(_sample_message)
- self._check_from(msg)
- msg.set_from('foo bar')
- self.assertEqual(msg.get_from(), 'foo bar')
- msg.set_from('foo at bar', True)
- self._check_from(msg, 'foo at bar')
- msg.set_from('blah at temp', time.localtime())
- self._check_from(msg, 'blah at temp')
-
- def test_flags(self):
- # Use get_flags(), set_flags(), add_flag(), remove_flag()
- msg = mailbox.mboxMessage(_sample_message)
- self.assertEqual(msg.get_flags(), '')
- msg.set_flags('F')
- self.assertEqual(msg.get_flags(), 'F')
- msg.set_flags('XODR')
- self.assertEqual(msg.get_flags(), 'RODX')
- msg.add_flag('FA')
- self.assertEqual(msg.get_flags(), 'RODFAX')
- msg.remove_flag('FDXA')
- self.assertEqual(msg.get_flags(), 'RO')
- self._check_sample(msg)
-
- def _check_from(self, msg, sender=None):
- # Check contents of "From " line
- if sender is None:
- sender = "MAILER-DAEMON"
- self.assertTrue(re.match(sender + r" \w{3} \w{3} [\d ]\d [\d ]\d:\d{2}:"
- r"\d{2} \d{4}", msg.get_from()))
-
-
-class TestMboxMessage(_TestMboxMMDFMessage, TestMessage):
-
- _factory = mailbox.mboxMessage
-
-
-class TestMHMessage(TestMessage, unittest.TestCase):
-
- _factory = mailbox.MHMessage
-
- def _post_initialize_hook(self, msg):
- self.assertEqual(msg._sequences, [])
-
- def test_sequences(self):
- # Get, set, join, and leave sequences
- msg = mailbox.MHMessage(_sample_message)
- self.assertEqual(msg.get_sequences(), [])
- msg.set_sequences(['foobar'])
- self.assertEqual(msg.get_sequences(), ['foobar'])
- msg.set_sequences([])
- self.assertEqual(msg.get_sequences(), [])
- msg.add_sequence('unseen')
- self.assertEqual(msg.get_sequences(), ['unseen'])
- msg.add_sequence('flagged')
- self.assertEqual(msg.get_sequences(), ['unseen', 'flagged'])
- msg.add_sequence('flagged')
- self.assertEqual(msg.get_sequences(), ['unseen', 'flagged'])
- msg.remove_sequence('unseen')
- self.assertEqual(msg.get_sequences(), ['flagged'])
- msg.add_sequence('foobar')
- self.assertEqual(msg.get_sequences(), ['flagged', 'foobar'])
- msg.remove_sequence('replied')
- self.assertEqual(msg.get_sequences(), ['flagged', 'foobar'])
- msg.set_sequences(['foobar', 'replied'])
- self.assertEqual(msg.get_sequences(), ['foobar', 'replied'])
-
-
-class TestBabylMessage(TestMessage, unittest.TestCase):
-
- _factory = mailbox.BabylMessage
-
- def _post_initialize_hook(self, msg):
- self.assertEqual(msg._labels, [])
-
- def test_labels(self):
- # Get, set, join, and leave labels
- msg = mailbox.BabylMessage(_sample_message)
- self.assertEqual(msg.get_labels(), [])
- msg.set_labels(['foobar'])
- self.assertEqual(msg.get_labels(), ['foobar'])
- msg.set_labels([])
- self.assertEqual(msg.get_labels(), [])
- msg.add_label('filed')
- self.assertEqual(msg.get_labels(), ['filed'])
- msg.add_label('resent')
- self.assertEqual(msg.get_labels(), ['filed', 'resent'])
- msg.add_label('resent')
- self.assertEqual(msg.get_labels(), ['filed', 'resent'])
- msg.remove_label('filed')
- self.assertEqual(msg.get_labels(), ['resent'])
- msg.add_label('foobar')
- self.assertEqual(msg.get_labels(), ['resent', 'foobar'])
- msg.remove_label('unseen')
- self.assertEqual(msg.get_labels(), ['resent', 'foobar'])
- msg.set_labels(['foobar', 'answered'])
- self.assertEqual(msg.get_labels(), ['foobar', 'answered'])
-
- def test_visible(self):
- # Get, set, and update visible headers
- msg = mailbox.BabylMessage(_sample_message)
- visible = msg.get_visible()
- self.assertEqual(visible.keys(), [])
- self.assertIs(visible.get_payload(), None)
- visible['User-Agent'] = 'FooBar 1.0'
- visible['X-Whatever'] = 'Blah'
- self.assertEqual(msg.get_visible().keys(), [])
- msg.set_visible(visible)
- visible = msg.get_visible()
- self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever'])
- self.assertEqual(visible['User-Agent'], 'FooBar 1.0')
- self.assertEqual(visible['X-Whatever'], 'Blah')
- self.assertIs(visible.get_payload(), None)
- msg.update_visible()
- self.assertEqual(visible.keys(), ['User-Agent', 'X-Whatever'])
- self.assertIs(visible.get_payload(), None)
- visible = msg.get_visible()
- self.assertEqual(visible.keys(), ['User-Agent', 'Date', 'From', 'To',
- 'Subject'])
- for header in ('User-Agent', 'Date', 'From', 'To', 'Subject'):
- self.assertEqual(visible[header], msg[header])
-
-
-class TestMMDFMessage(_TestMboxMMDFMessage, TestMessage):
-
- _factory = mailbox.MMDFMessage
-
-
-class TestMessageConversion(TestBase, unittest.TestCase):
-
- def test_plain_to_x(self):
- # Convert Message to all formats
- for class_ in (mailbox.Message, mailbox.MaildirMessage,
- mailbox.mboxMessage, mailbox.MHMessage,
- mailbox.BabylMessage, mailbox.MMDFMessage):
- msg_plain = mailbox.Message(_sample_message)
- msg = class_(msg_plain)
- self._check_sample(msg)
-
- def test_x_to_plain(self):
- # Convert all formats to Message
- for class_ in (mailbox.Message, mailbox.MaildirMessage,
- mailbox.mboxMessage, mailbox.MHMessage,
- mailbox.BabylMessage, mailbox.MMDFMessage):
- msg = class_(_sample_message)
- msg_plain = mailbox.Message(msg)
- self._check_sample(msg_plain)
-
- def test_x_to_invalid(self):
- # Convert all formats to an invalid format
- for class_ in (mailbox.Message, mailbox.MaildirMessage,
- mailbox.mboxMessage, mailbox.MHMessage,
- mailbox.BabylMessage, mailbox.MMDFMessage):
- self.assertRaises(TypeError, lambda: class_(False))
-
- def test_maildir_to_maildir(self):
- # Convert MaildirMessage to MaildirMessage
- msg_maildir = mailbox.MaildirMessage(_sample_message)
- msg_maildir.set_flags('DFPRST')
- msg_maildir.set_subdir('cur')
- date = msg_maildir.get_date()
- msg = mailbox.MaildirMessage(msg_maildir)
- self._check_sample(msg)
- self.assertEqual(msg.get_flags(), 'DFPRST')
- self.assertEqual(msg.get_subdir(), 'cur')
- self.assertEqual(msg.get_date(), date)
-
- def test_maildir_to_mboxmmdf(self):
- # Convert MaildirMessage to mboxmessage and MMDFMessage
- pairs = (('D', ''), ('F', 'F'), ('P', ''), ('R', 'A'), ('S', 'R'),
- ('T', 'D'), ('DFPRST', 'RDFA'))
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg_maildir = mailbox.MaildirMessage(_sample_message)
- msg_maildir.set_date(0.0)
- for setting, result in pairs:
- msg_maildir.set_flags(setting)
- msg = class_(msg_maildir)
- self.assertEqual(msg.get_flags(), result)
- self.assertEqual(msg.get_from(), 'MAILER-DAEMON %s' %
- time.asctime(time.gmtime(0.0)))
- msg_maildir.set_subdir('cur')
- self.assertEqual(class_(msg_maildir).get_flags(), 'RODFA')
-
- def test_maildir_to_mh(self):
- # Convert MaildirMessage to MHMessage
- msg_maildir = mailbox.MaildirMessage(_sample_message)
- pairs = (('D', ['unseen']), ('F', ['unseen', 'flagged']),
- ('P', ['unseen']), ('R', ['unseen', 'replied']), ('S', []),
- ('T', ['unseen']), ('DFPRST', ['replied', 'flagged']))
- for setting, result in pairs:
- msg_maildir.set_flags(setting)
- self.assertEqual(mailbox.MHMessage(msg_maildir).get_sequences(),
- result)
-
- def test_maildir_to_babyl(self):
- # Convert MaildirMessage to Babyl
- msg_maildir = mailbox.MaildirMessage(_sample_message)
- pairs = (('D', ['unseen']), ('F', ['unseen']),
- ('P', ['unseen', 'forwarded']), ('R', ['unseen', 'answered']),
- ('S', []), ('T', ['unseen', 'deleted']),
- ('DFPRST', ['deleted', 'answered', 'forwarded']))
- for setting, result in pairs:
- msg_maildir.set_flags(setting)
- self.assertEqual(mailbox.BabylMessage(msg_maildir).get_labels(),
- result)
-
- def test_mboxmmdf_to_maildir(self):
- # Convert mboxMessage and MMDFMessage to MaildirMessage
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg_mboxMMDF = class_(_sample_message)
- msg_mboxMMDF.set_from('foo at bar', time.gmtime(0.0))
- pairs = (('R', 'S'), ('O', ''), ('D', 'T'), ('F', 'F'), ('A', 'R'),
- ('RODFA', 'FRST'))
- for setting, result in pairs:
- msg_mboxMMDF.set_flags(setting)
- msg = mailbox.MaildirMessage(msg_mboxMMDF)
- self.assertEqual(msg.get_flags(), result)
- self.assertEqual(msg.get_date(), 0.0)
- msg_mboxMMDF.set_flags('O')
- self.assertEqual(mailbox.MaildirMessage(msg_mboxMMDF).get_subdir(),
- 'cur')
-
- def test_mboxmmdf_to_mboxmmdf(self):
- # Convert mboxMessage and MMDFMessage to mboxMessage and MMDFMessage
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg_mboxMMDF = class_(_sample_message)
- msg_mboxMMDF.set_flags('RODFA')
- msg_mboxMMDF.set_from('foo at bar')
- for class2_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg2 = class2_(msg_mboxMMDF)
- self.assertEqual(msg2.get_flags(), 'RODFA')
- self.assertEqual(msg2.get_from(), 'foo at bar')
-
- def test_mboxmmdf_to_mh(self):
- # Convert mboxMessage and MMDFMessage to MHMessage
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg_mboxMMDF = class_(_sample_message)
- pairs = (('R', []), ('O', ['unseen']), ('D', ['unseen']),
- ('F', ['unseen', 'flagged']),
- ('A', ['unseen', 'replied']),
- ('RODFA', ['replied', 'flagged']))
- for setting, result in pairs:
- msg_mboxMMDF.set_flags(setting)
- self.assertEqual(mailbox.MHMessage(msg_mboxMMDF).get_sequences(),
- result)
-
- def test_mboxmmdf_to_babyl(self):
- # Convert mboxMessage and MMDFMessage to BabylMessage
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg = class_(_sample_message)
- pairs = (('R', []), ('O', ['unseen']),
- ('D', ['unseen', 'deleted']), ('F', ['unseen']),
- ('A', ['unseen', 'answered']),
- ('RODFA', ['deleted', 'answered']))
- for setting, result in pairs:
- msg.set_flags(setting)
- self.assertEqual(mailbox.BabylMessage(msg).get_labels(), result)
-
- def test_mh_to_maildir(self):
- # Convert MHMessage to MaildirMessage
- pairs = (('unseen', ''), ('replied', 'RS'), ('flagged', 'FS'))
- for setting, result in pairs:
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence(setting)
- self.assertEqual(mailbox.MaildirMessage(msg).get_flags(), result)
- self.assertEqual(mailbox.MaildirMessage(msg).get_subdir(), 'cur')
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence('unseen')
- msg.add_sequence('replied')
- msg.add_sequence('flagged')
- self.assertEqual(mailbox.MaildirMessage(msg).get_flags(), 'FR')
- self.assertEqual(mailbox.MaildirMessage(msg).get_subdir(), 'cur')
-
- def test_mh_to_mboxmmdf(self):
- # Convert MHMessage to mboxMessage and MMDFMessage
- pairs = (('unseen', 'O'), ('replied', 'ROA'), ('flagged', 'ROF'))
- for setting, result in pairs:
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence(setting)
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- self.assertEqual(class_(msg).get_flags(), result)
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence('unseen')
- msg.add_sequence('replied')
- msg.add_sequence('flagged')
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- self.assertEqual(class_(msg).get_flags(), 'OFA')
-
- def test_mh_to_mh(self):
- # Convert MHMessage to MHMessage
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence('unseen')
- msg.add_sequence('replied')
- msg.add_sequence('flagged')
- self.assertEqual(mailbox.MHMessage(msg).get_sequences(),
- ['unseen', 'replied', 'flagged'])
-
- def test_mh_to_babyl(self):
- # Convert MHMessage to BabylMessage
- pairs = (('unseen', ['unseen']), ('replied', ['answered']),
- ('flagged', []))
- for setting, result in pairs:
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence(setting)
- self.assertEqual(mailbox.BabylMessage(msg).get_labels(), result)
- msg = mailbox.MHMessage(_sample_message)
- msg.add_sequence('unseen')
- msg.add_sequence('replied')
- msg.add_sequence('flagged')
- self.assertEqual(mailbox.BabylMessage(msg).get_labels(),
- ['unseen', 'answered'])
-
- def test_babyl_to_maildir(self):
- # Convert BabylMessage to MaildirMessage
- pairs = (('unseen', ''), ('deleted', 'ST'), ('filed', 'S'),
- ('answered', 'RS'), ('forwarded', 'PS'), ('edited', 'S'),
- ('resent', 'PS'))
- for setting, result in pairs:
- msg = mailbox.BabylMessage(_sample_message)
- msg.add_label(setting)
- self.assertEqual(mailbox.MaildirMessage(msg).get_flags(), result)
- self.assertEqual(mailbox.MaildirMessage(msg).get_subdir(), 'cur')
- msg = mailbox.BabylMessage(_sample_message)
- for label in ('unseen', 'deleted', 'filed', 'answered', 'forwarded',
- 'edited', 'resent'):
- msg.add_label(label)
- self.assertEqual(mailbox.MaildirMessage(msg).get_flags(), 'PRT')
- self.assertEqual(mailbox.MaildirMessage(msg).get_subdir(), 'cur')
-
- def test_babyl_to_mboxmmdf(self):
- # Convert BabylMessage to mboxMessage and MMDFMessage
- pairs = (('unseen', 'O'), ('deleted', 'ROD'), ('filed', 'RO'),
- ('answered', 'ROA'), ('forwarded', 'RO'), ('edited', 'RO'),
- ('resent', 'RO'))
- for setting, result in pairs:
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- msg = mailbox.BabylMessage(_sample_message)
- msg.add_label(setting)
- self.assertEqual(class_(msg).get_flags(), result)
- msg = mailbox.BabylMessage(_sample_message)
- for label in ('unseen', 'deleted', 'filed', 'answered', 'forwarded',
- 'edited', 'resent'):
- msg.add_label(label)
- for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
- self.assertEqual(class_(msg).get_flags(), 'ODA')
-
- def test_babyl_to_mh(self):
- # Convert BabylMessage to MHMessage
- pairs = (('unseen', ['unseen']), ('deleted', []), ('filed', []),
- ('answered', ['replied']), ('forwarded', []), ('edited', []),
- ('resent', []))
- for setting, result in pairs:
- msg = mailbox.BabylMessage(_sample_message)
- msg.add_label(setting)
- self.assertEqual(mailbox.MHMessage(msg).get_sequences(), result)
- msg = mailbox.BabylMessage(_sample_message)
- for label in ('unseen', 'deleted', 'filed', 'answered', 'forwarded',
- 'edited', 'resent'):
- msg.add_label(label)
- self.assertEqual(mailbox.MHMessage(msg).get_sequences(),
- ['unseen', 'replied'])
-
- def test_babyl_to_babyl(self):
- # Convert BabylMessage to BabylMessage
- msg = mailbox.BabylMessage(_sample_message)
- msg.update_visible()
- for label in ('unseen', 'deleted', 'filed', 'answered', 'forwarded',
- 'edited', 'resent'):
- msg.add_label(label)
- msg2 = mailbox.BabylMessage(msg)
- self.assertEqual(msg2.get_labels(), ['unseen', 'deleted', 'filed',
- 'answered', 'forwarded', 'edited',
- 'resent'])
- self.assertEqual(msg.get_visible().keys(), msg2.get_visible().keys())
- for key in msg.get_visible().keys():
- self.assertEqual(msg.get_visible()[key], msg2.get_visible()[key])
-
-
-class TestProxyFileBase(TestBase):
-
- def _test_read(self, proxy):
- # Read by byte
- proxy.seek(0)
- self.assertEqual(proxy.read(), 'bar')
- proxy.seek(1)
- self.assertEqual(proxy.read(), 'ar')
- proxy.seek(0)
- self.assertEqual(proxy.read(2), 'ba')
- proxy.seek(1)
- self.assertEqual(proxy.read(-1), 'ar')
- proxy.seek(2)
- self.assertEqual(proxy.read(1000), 'r')
-
- def _test_readline(self, proxy):
- # Read by line
- proxy.seek(0)
- self.assertEqual(proxy.readline(), 'foo' + os.linesep)
- self.assertEqual(proxy.readline(), 'bar' + os.linesep)
- self.assertEqual(proxy.readline(), 'fred' + os.linesep)
- self.assertEqual(proxy.readline(), 'bob')
- proxy.seek(2)
- self.assertEqual(proxy.readline(), 'o' + os.linesep)
- proxy.seek(6 + 2 * len(os.linesep))
- self.assertEqual(proxy.readline(), 'fred' + os.linesep)
- proxy.seek(6 + 2 * len(os.linesep))
- self.assertEqual(proxy.readline(2), 'fr')
- self.assertEqual(proxy.readline(-10), 'ed' + os.linesep)
-
- def _test_readlines(self, proxy):
- # Read multiple lines
- proxy.seek(0)
- self.assertEqual(proxy.readlines(), ['foo' + os.linesep,
- 'bar' + os.linesep,
- 'fred' + os.linesep, 'bob'])
- proxy.seek(0)
- self.assertEqual(proxy.readlines(2), ['foo' + os.linesep])
- proxy.seek(3 + len(os.linesep))
- self.assertEqual(proxy.readlines(4 + len(os.linesep)),
- ['bar' + os.linesep, 'fred' + os.linesep])
- proxy.seek(3)
- self.assertEqual(proxy.readlines(1000), [os.linesep, 'bar' + os.linesep,
- 'fred' + os.linesep, 'bob'])
-
- def _test_iteration(self, proxy):
- # Iterate by line
- proxy.seek(0)
- iterator = iter(proxy)
- self.assertEqual(list(iterator),
- ['foo' + os.linesep, 'bar' + os.linesep, 'fred' + os.linesep, 'bob'])
-
- def _test_seek_and_tell(self, proxy):
- # Seek and use tell to check position
- proxy.seek(3)
- self.assertEqual(proxy.tell(), 3)
- self.assertEqual(proxy.read(len(os.linesep)), os.linesep)
- proxy.seek(2, 1)
- self.assertEqual(proxy.read(1 + len(os.linesep)), 'r' + os.linesep)
- proxy.seek(-3 - len(os.linesep), 2)
- self.assertEqual(proxy.read(3), 'bar')
- proxy.seek(2, 0)
- self.assertEqual(proxy.read(), 'o' + os.linesep + 'bar' + os.linesep)
- proxy.seek(100)
- self.assertEqual(proxy.read(), '')
-
- def _test_close(self, proxy):
- # Close a file
- proxy.close()
- # Issue 11700 subsequent closes should be a no-op, not an error.
- proxy.close()
-
-
-class TestProxyFile(TestProxyFileBase, unittest.TestCase):
-
- def setUp(self):
- self._path = test_support.TESTFN
- self._file = open(self._path, 'wb+')
-
- def tearDown(self):
- self._file.close()
- self._delete_recursively(self._path)
-
- def test_initialize(self):
- # Initialize and check position
- self._file.write('foo')
- pos = self._file.tell()
- proxy0 = mailbox._ProxyFile(self._file)
- self.assertEqual(proxy0.tell(), pos)
- self.assertEqual(self._file.tell(), pos)
- proxy1 = mailbox._ProxyFile(self._file, 0)
- self.assertEqual(proxy1.tell(), 0)
- self.assertEqual(self._file.tell(), pos)
-
- def test_read(self):
- self._file.write('bar')
- self._test_read(mailbox._ProxyFile(self._file))
-
- def test_readline(self):
- self._file.write('foo%sbar%sfred%sbob' % (os.linesep, os.linesep,
- os.linesep))
- self._test_readline(mailbox._ProxyFile(self._file))
-
- def test_readlines(self):
- self._file.write('foo%sbar%sfred%sbob' % (os.linesep, os.linesep,
- os.linesep))
- self._test_readlines(mailbox._ProxyFile(self._file))
-
- def test_iteration(self):
- self._file.write('foo%sbar%sfred%sbob' % (os.linesep, os.linesep,
- os.linesep))
- self._test_iteration(mailbox._ProxyFile(self._file))
-
- def test_seek_and_tell(self):
- self._file.write('foo%sbar%s' % (os.linesep, os.linesep))
- self._test_seek_and_tell(mailbox._ProxyFile(self._file))
-
- def test_close(self):
- self._file.write('foo%sbar%s' % (os.linesep, os.linesep))
- self._test_close(mailbox._ProxyFile(self._file))
-
-
-class TestPartialFile(TestProxyFileBase, unittest.TestCase):
-
- def setUp(self):
- self._path = test_support.TESTFN
- self._file = open(self._path, 'wb+')
-
- def tearDown(self):
- self._file.close()
- self._delete_recursively(self._path)
-
- def test_initialize(self):
- # Initialize and check position
- self._file.write('foo' + os.linesep + 'bar')
- pos = self._file.tell()
- proxy = mailbox._PartialFile(self._file, 2, 5)
- self.assertEqual(proxy.tell(), 0)
- self.assertEqual(self._file.tell(), pos)
-
- def test_read(self):
- self._file.write('***bar***')
- self._test_read(mailbox._PartialFile(self._file, 3, 6))
-
- def test_readline(self):
- self._file.write('!!!!!foo%sbar%sfred%sbob!!!!!' %
- (os.linesep, os.linesep, os.linesep))
- self._test_readline(mailbox._PartialFile(self._file, 5,
- 18 + 3 * len(os.linesep)))
-
- def test_readlines(self):
- self._file.write('foo%sbar%sfred%sbob?????' %
- (os.linesep, os.linesep, os.linesep))
- self._test_readlines(mailbox._PartialFile(self._file, 0,
- 13 + 3 * len(os.linesep)))
-
- def test_iteration(self):
- self._file.write('____foo%sbar%sfred%sbob####' %
- (os.linesep, os.linesep, os.linesep))
- self._test_iteration(mailbox._PartialFile(self._file, 4,
- 17 + 3 * len(os.linesep)))
-
- def test_seek_and_tell(self):
- self._file.write('(((foo%sbar%s$$$' % (os.linesep, os.linesep))
- self._test_seek_and_tell(mailbox._PartialFile(self._file, 3,
- 9 + 2 * len(os.linesep)))
-
- def test_close(self):
- self._file.write('&foo%sbar%s^' % (os.linesep, os.linesep))
- self._test_close(mailbox._PartialFile(self._file, 1,
- 6 + 3 * len(os.linesep)))
-
-
-## Start: tests from the original module (for backward compatibility).
-
-FROM_ = "From some.body at dummy.domain Sat Jul 24 13:43:35 2004\n"
-DUMMY_MESSAGE = """\
-From: some.body at dummy.domain
-To: me at my.domain
-Subject: Simple Test
-
-This is a dummy message.
-"""
-
-class MaildirTestCase(unittest.TestCase):
-
- def setUp(self):
- # create a new maildir mailbox to work with:
- self._dir = test_support.TESTFN
- if os.path.isdir(self._dir):
- test_support.rmtree(self._dir)
- if os.path.isfile(self._dir):
- test_support.unlink(self._dir)
- os.mkdir(self._dir)
- os.mkdir(os.path.join(self._dir, "cur"))
- os.mkdir(os.path.join(self._dir, "tmp"))
- os.mkdir(os.path.join(self._dir, "new"))
- self._counter = 1
- self._msgfiles = []
-
- def tearDown(self):
- map(os.unlink, self._msgfiles)
- test_support.rmdir(os.path.join(self._dir, "cur"))
- test_support.rmdir(os.path.join(self._dir, "tmp"))
- test_support.rmdir(os.path.join(self._dir, "new"))
- test_support.rmdir(self._dir)
-
- def createMessage(self, dir, mbox=False):
- t = int(time.time() % 1000000)
- pid = self._counter
- self._counter += 1
- filename = os.extsep.join((str(t), str(pid), "myhostname", "mydomain"))
- tmpname = os.path.join(self._dir, "tmp", filename)
- newname = os.path.join(self._dir, dir, filename)
- with open(tmpname, "w") as fp:
- self._msgfiles.append(tmpname)
- if mbox:
- fp.write(FROM_)
- fp.write(DUMMY_MESSAGE)
- if hasattr(os, "link"):
- os.link(tmpname, newname)
- else:
- with open(newname, "w") as fp:
- fp.write(DUMMY_MESSAGE)
- self._msgfiles.append(newname)
- return tmpname
-
- def test_empty_maildir(self):
- """Test an empty maildir mailbox"""
- # Test for regression on bug #117490:
- # Make sure the boxes attribute actually gets set.
- self.mbox = mailbox.Maildir(test_support.TESTFN)
- #self.assertTrue(hasattr(self.mbox, "boxes"))
- #self.assertTrue(len(self.mbox.boxes) == 0)
- self.assertIs(self.mbox.next(), None)
- self.assertIs(self.mbox.next(), None)
-
- def test_nonempty_maildir_cur(self):
- self.createMessage("cur")
- self.mbox = mailbox.Maildir(test_support.TESTFN)
- #self.assertTrue(len(self.mbox.boxes) == 1)
- msg = self.mbox.next()
- self.assertIsNot(msg, None)
- msg.fp.close()
- self.assertIs(self.mbox.next(), None)
- self.assertIs(self.mbox.next(), None)
-
- def test_nonempty_maildir_new(self):
- self.createMessage("new")
- self.mbox = mailbox.Maildir(test_support.TESTFN)
- #self.assertTrue(len(self.mbox.boxes) == 1)
- msg = self.mbox.next()
- self.assertIsNot(msg, None)
- msg.fp.close()
- self.assertIs(self.mbox.next(), None)
- self.assertIs(self.mbox.next(), None)
-
- def test_nonempty_maildir_both(self):
- self.createMessage("cur")
- self.createMessage("new")
- self.mbox = mailbox.Maildir(test_support.TESTFN)
- #self.assertTrue(len(self.mbox.boxes) == 2)
- msg = self.mbox.next()
- self.assertIsNot(msg, None)
- msg.fp.close()
- msg = self.mbox.next()
- self.assertIsNot(msg, None)
- msg.fp.close()
- self.assertIs(self.mbox.next(), None)
- self.assertIs(self.mbox.next(), None)
-
- def test_unix_mbox(self):
- ### should be better!
- import email.parser
- fname = self.createMessage("cur", True)
- n = 0
- fid = open(fname)
- for msg in mailbox.PortableUnixMailbox(fid,
- email.parser.Parser().parse):
- n += 1
- self.assertEqual(msg["subject"], "Simple Test")
- self.assertEqual(len(str(msg)), len(FROM_)+len(DUMMY_MESSAGE))
- fid.close()
- self.assertEqual(n, 1)
-
-## End: classes from the original module (for backward compatibility).
-
-
-_sample_message = """\
-Return-Path:
-X-Original-To: gkj+person at localhost
-Delivered-To: gkj+person at localhost
-Received: from localhost (localhost [127.0.0.1])
- by andy.gregorykjohnson.com (Postfix) with ESMTP id 356ED9DD17
- for ; Wed, 13 Jul 2005 17:23:16 -0400 (EDT)
-Delivered-To: gkj at sundance.gregorykjohnson.com
-Received: from localhost [127.0.0.1]
- by localhost with POP3 (fetchmail-6.2.5)
- for gkj+person at localhost (single-drop); Wed, 13 Jul 2005 17:23:16 -0400 (EDT)
-Received: from andy.gregorykjohnson.com (andy.gregorykjohnson.com [64.32.235.228])
- by sundance.gregorykjohnson.com (Postfix) with ESMTP id 5B056316746
- for ; Wed, 13 Jul 2005 17:23:11 -0400 (EDT)
-Received: by andy.gregorykjohnson.com (Postfix, from userid 1000)
- id 490CD9DD17; Wed, 13 Jul 2005 17:23:11 -0400 (EDT)
-Date: Wed, 13 Jul 2005 17:23:11 -0400
-From: "Gregory K. Johnson"
-To: gkj at gregorykjohnson.com
-Subject: Sample message
-Message-ID: <20050713212311.GC4701 at andy.gregorykjohnson.com>
-Mime-Version: 1.0
-Content-Type: multipart/mixed; boundary="NMuMz9nt05w80d4+"
-Content-Disposition: inline
-User-Agent: Mutt/1.5.9i
-
-
---NMuMz9nt05w80d4+
-Content-Type: text/plain; charset=us-ascii
-Content-Disposition: inline
-
-This is a sample message.
-
---
-Gregory K. Johnson
-
---NMuMz9nt05w80d4+
-Content-Type: application/octet-stream
-Content-Disposition: attachment; filename="text.gz"
-Content-Transfer-Encoding: base64
-
-H4sICM2D1UIAA3RleHQAC8nILFYAokSFktSKEoW0zJxUPa7wzJIMhZLyfIWczLzUYj0uAHTs
-3FYlAAAA
-
---NMuMz9nt05w80d4+--
-"""
-
-_sample_headers = {
- "Return-Path":"",
- "X-Original-To":"gkj+person at localhost",
- "Delivered-To":"gkj+person at localhost",
- "Received":"""from localhost (localhost [127.0.0.1])
- by andy.gregorykjohnson.com (Postfix) with ESMTP id 356ED9DD17
- for ; Wed, 13 Jul 2005 17:23:16 -0400 (EDT)""",
- "Delivered-To":"gkj at sundance.gregorykjohnson.com",
- "Received":"""from localhost [127.0.0.1]
- by localhost with POP3 (fetchmail-6.2.5)
- for gkj+person at localhost (single-drop); Wed, 13 Jul 2005 17:23:16 -0400 (EDT)""",
- "Received":"""from andy.gregorykjohnson.com (andy.gregorykjohnson.com [64.32.235.228])
- by sundance.gregorykjohnson.com (Postfix) with ESMTP id 5B056316746
- for ; Wed, 13 Jul 2005 17:23:11 -0400 (EDT)""",
- "Received":"""by andy.gregorykjohnson.com (Postfix, from userid 1000)
- id 490CD9DD17; Wed, 13 Jul 2005 17:23:11 -0400 (EDT)""",
- "Date":"Wed, 13 Jul 2005 17:23:11 -0400",
- "From":""""Gregory K. Johnson" """,
- "To":"gkj at gregorykjohnson.com",
- "Subject":"Sample message",
- "Mime-Version":"1.0",
- "Content-Type":"""multipart/mixed; boundary="NMuMz9nt05w80d4+\"""",
- "Content-Disposition":"inline",
- "User-Agent": "Mutt/1.5.9i" }
-
-_sample_payloads = ("""This is a sample message.
-
---
-Gregory K. Johnson
-""",
-"""H4sICM2D1UIAA3RleHQAC8nILFYAokSFktSKEoW0zJxUPa7wzJIMhZLyfIWczLzUYj0uAHTs
-3FYlAAAA
-""")
-
-
-def test_main():
- tests = (TestMailboxSuperclass, TestMaildir, TestMbox, TestMMDF, TestMH,
- TestBabyl, TestMessage, TestMaildirMessage, TestMboxMessage,
- TestMHMessage, TestBabylMessage, TestMMDFMessage,
- TestMessageConversion, TestProxyFile, TestPartialFile,
- MaildirTestCase)
- test_support.run_unittest(*tests)
- test_support.reap_children()
-
-
-if __name__ == '__main__':
- test_main()
diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java
--- a/src/org/python/modules/posix/PosixModule.java
+++ b/src/org/python/modules/posix/PosixModule.java
@@ -15,7 +15,9 @@
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.nio.file.attribute.DosFileAttributes;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Map;
@@ -62,29 +64,29 @@
"corresponding Unix manual entries for more information on calls.");
/** Current OS information. */
- private static OS os = OS.getOS();
+ private static final OS os = OS.getOS();
/** Platform specific POSIX services. */
- private static POSIX posix = POSIXFactory.getPOSIX(new PythonPOSIXHandler(), true);
+ private static final POSIX posix = POSIXFactory.getPOSIX(new PythonPOSIXHandler(), true);
/** os.open flags. */
- private static int O_RDONLY = 0x0;
- private static int O_WRONLY = 0x1;
- private static int O_RDWR = 0x2;
- private static int O_APPEND = 0x8;
- private static int O_SYNC = 0x80;
- private static int O_CREAT = 0x200;
- private static int O_TRUNC = 0x400;
- private static int O_EXCL = 0x800;
+ private static final int O_RDONLY = 0x0;
+ private static final int O_WRONLY = 0x1;
+ private static final int O_RDWR = 0x2;
+ private static final int O_APPEND = 0x8;
+ private static final int O_SYNC = 0x80;
+ private static final int O_CREAT = 0x200;
+ private static final int O_TRUNC = 0x400;
+ private static final int O_EXCL = 0x800;
/** os.access constants. */
- private static int F_OK = 0;
- private static int X_OK = 1 << 0;
- private static int W_OK = 1 << 1;
- private static int R_OK = 1 << 2;
+ private static final int F_OK = 0;
+ private static final int X_OK = 1 << 0;
+ private static final int W_OK = 1 << 1;
+ private static final int R_OK = 1 << 2;
/** os.path.realpath function for use by chdir. Lazily loaded. */
- private static PyObject realpath;
+ private static volatile PyObject realpath;
/** Lazily initialzed singleton source for urandom. */
private static class UrandomSource {
@@ -119,8 +121,14 @@
// Faster call paths, because __call__ is defined
dict.__setitem__("fstat", new FstatFunction());
- dict.__setitem__("lstat", new LstatFunction());
- dict.__setitem__("stat", new StatFunction());
+ if (os == OS.NT) {
+ WindowsStatFunction stat = new WindowsStatFunction();
+ dict.__setitem__("lstat", stat);
+ dict.__setitem__("stat", stat);
+ } else {
+ dict.__setitem__("lstat", new LstatFunction());
+ dict.__setitem__("stat", new StatFunction());
+ }
// Hide from Python
Hider.hideFunctions(PosixModule.class, dict, os, nativePosix);
@@ -145,8 +153,8 @@
dict.__setitem__("__doc__", __doc__);
}
- // Combine Java FileDescriptor objects with
- // Posix int file descriptors in one representation
+ // Combine Java FileDescriptor objects with Posix int file descriptors in one representation.
+ // Unfortunate ugliness!
private static class FDUnion {
volatile int intFD;
final FileDescriptor javaFD;
@@ -164,7 +172,6 @@
return intFD != -1;
}
- // FIXME should this store this int fd -> FileDescriptor in a weak value map so it can be looked up later?
int getIntFD() {
return getIntFD(true);
}
@@ -180,10 +187,14 @@
} catch (SecurityException e) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
- }
+ } catch (NullPointerException e) {}
}
if (checkFD) {
- posix.fstat(intFD); // check if this a good FD or not
+ if (intFD == -1) {
+ throw Py.OSError(Errno.EBADF);
+ } else {
+ posix.fstat(intFD); // side effect of checking if this a good FD or not
+ }
}
return intFD;
}
@@ -239,8 +250,7 @@
"specified access to the path. The mode argument can be F_OK to test\n" +
"existence, or the inclusive-OR of R_OK, W_OK, and X_OK.");
public static boolean access(PyObject path, int mode) {
- String absolutePath = absolutePath(path);
- File file = new File(absolutePath);
+ File file = absolutePath(path).toFile();
boolean result = true;
if (!file.exists()) {
@@ -255,7 +265,7 @@
if ((mode & X_OK) != 0) {
// NOTE: always true without native jna-posix stat
try {
- result = posix.stat(absolutePath).isExecutable();
+ result = posix.stat(absolutePath(path).toString()).isExecutable();
} catch (PyException pye) {
if (!pye.match(Py.OSError)) {
throw pye;
@@ -272,7 +282,7 @@
"Change the current working directory to the specified path.");
public static void chdir(PyObject path) {
// stat raises ENOENT for us if path doesn't exist
- if (!posix.stat(absolutePath(path)).isDirectory()) {
+ if (!posix.stat(absolutePath(path).toString()).isDirectory()) {
throw Py.OSError(Errno.ENOTDIR, path);
}
@@ -286,7 +296,7 @@
"chmod(path, mode)\n\n" +
"Change the access permissions of a file.");
public static void chmod(PyObject path, int mode) {
- if (posix.chmod(absolutePath(path), mode) < 0) {
+ if (posix.chmod(absolutePath(path).toString(), mode) < 0) {
throw errorFromErrno(path);
}
}
@@ -296,7 +306,7 @@
"Change the owner and group id of path to the numeric uid and gid.");
@Hide(OS.NT)
public static void chown(PyObject path, int uid, int gid) {
- if (posix.chown(absolutePath(path), uid, gid) < 0) {
+ if (posix.chown(absolutePath(path).toString(), uid, gid) < 0) {
throw errorFromErrno(path);
}
}
@@ -560,7 +570,7 @@
"affects the link itself rather than the target.");
@Hide(OS.NT)
public static void lchmod(PyObject path, int mode) {
- if (posix.lchmod(absolutePath(path), mode) < 0) {
+ if (posix.lchmod(absolutePath(path).toString(), mode) < 0) {
throw errorFromErrno(path);
}
}
@@ -571,7 +581,7 @@
"This function will not follow symbolic links.");
@Hide(OS.NT)
public static void lchown(PyObject path, int uid, int gid) {
- if (posix.lchown(absolutePath(path), uid, gid) < 0) {
+ if (posix.lchown(absolutePath(path).toString(), uid, gid) < 0) {
throw errorFromErrno(path);
}
}
@@ -581,7 +591,7 @@
"Create a hard link to a file.");
@Hide(OS.NT)
public static void link(PyObject src, PyObject dst) {
- if (posix.link(absolutePath(src), absolutePath(dst)) < 0) {
+ if (posix.link(absolutePath(src).toString(), absolutePath(dst).toString()) < 0) {
throw errorFromErrno();
}
}
@@ -593,16 +603,11 @@
"The list is in arbitrary order. It does not include the special\n" +
"entries '.' and '..' even if they are present in the directory.");
public static PyList listdir(PyObject path) {
- String absolutePath = absolutePath(path);
- File file = new File(absolutePath);
+ File file = absolutePath(path).toFile();
String[] names = file.list();
if (names == null) {
- // Can't read the path for some reason. stat will throw an error if it can't
- // read it either
- FileStat stat = posix.stat(absolutePath);
- // It exists, maybe not a dir, or we don't have permission?
- if (!stat.isDirectory()) {
+ if (!file.isDirectory()) {
throw Py.OSError(Errno.ENOTDIR, path);
}
if (!file.canRead()) {
@@ -642,7 +647,7 @@
}
public static void mkdir(PyObject path, int mode) {
- if (posix.mkdir(absolutePath(path), mode) < 0) {
+ if (posix.mkdir(absolutePath(path).toString(), mode) < 0) {
throw errorFromErrno(path);
}
}
@@ -656,8 +661,7 @@
}
public static FileIO open(PyObject path, int flag, int mode) {
- String absolutePath = absolutePath(path);
- File file = new File(absolutePath);
+ File file = absolutePath(path).toFile();
boolean reading = (flag & O_RDONLY) != 0;
boolean writing = (flag & O_WRONLY) != 0;
boolean updating = (flag & O_RDWR) != 0;
@@ -753,7 +757,7 @@
@Hide(OS.NT)
public static String readlink(PyObject path) {
try {
- return posix.readlink(absolutePath(path));
+ return posix.readlink(absolutePath(path).toString());
} catch (IOException ioe) {
throw Py.OSError(ioe);
}
@@ -770,7 +774,7 @@
"rename(old, new)\n\n" +
"Rename a file or directory.");
public static void rename(PyObject oldpath, PyObject newpath) {
- if (!new File(absolutePath(oldpath)).renameTo(new File(absolutePath(newpath)))) {
+ if (!(absolutePath(oldpath).toFile().renameTo(absolutePath(newpath).toFile()))) {
PyObject args = new PyTuple(Py.Zero, new PyString("Couldn't rename file"));
throw new PyException(Py.OSError, args);
}
@@ -780,7 +784,7 @@
"rmdir(path)\n\n" +
"Remove a directory.");
public static void rmdir(PyObject path) {
- File file = new File(absolutePath(path));
+ File file = absolutePath(path).toFile();
if (!file.exists()) {
throw Py.OSError(Errno.ENOENT, path);
} else if (!file.isDirectory()) {
@@ -834,7 +838,7 @@
"Create a symbolic link pointing to src named dst.");
@Hide(OS.NT)
public static void symlink(PyObject src, PyObject dst) {
- if (posix.symlink(asPath(src), absolutePath(dst)) < 0) {
+ if (posix.symlink(asPath(src), absolutePath(dst).toString()) < 0) {
throw errorFromErrno();
}
}
@@ -871,22 +875,21 @@
+ "Remove a file (same as remove(path)).");
public static void unlink(PyObject path) {
- String absolutePath = absolutePath(path);
+ Path nioPath = absolutePath(path);
try {
- Path nioPath = new File(absolutePath).toPath();
if (Files.isDirectory(nioPath, LinkOption.NOFOLLOW_LINKS)) {
throw Py.OSError(Errno.EISDIR, path);
} else if (!Files.deleteIfExists(nioPath)) {
// Something went wrong, does stat raise an error?
- posix.stat(absolutePath);
+ posix.stat(nioPath.toString());
// It exists, do we not have permissions?
if (!Files.isWritable(nioPath)) {
throw Py.OSError(Errno.EPERM, path);
}
- throw Py.OSError("unlink(): an unknown error occurred: " + absolutePath);
+ throw Py.OSError("unlink(): an unknown error occurred: " + nioPath.toString());
}
} catch (IOException ex) {
- PyException pyError = Py.OSError("unlink(): an unknown error occurred: " + absolutePath);
+ PyException pyError = Py.OSError("unlink(): an unknown error occurred: " + nioPath.toString());
pyError.initCause(ex);
throw pyError;
}
@@ -909,7 +912,7 @@
} else {
throw Py.TypeError("utime() arg 2 must be a tuple (atime, mtime)");
}
- if (posix.utimes(absolutePath(path), atimeval, mtimeval) < 0) {
+ if (posix.utimes(absolutePath(path).toString(), atimeval, mtimeval) < 0) {
throw errorFromErrno(path);
}
}
@@ -925,7 +928,7 @@
long[] timeval = new long[] {Platform.IS_32_BIT ? seconds.asInt() : seconds.asLong(), 0L};
if (seconds instanceof PyFloat) {
// can't exceed 1000000
- long usec = (long)((seconds.asDouble() % 1.0) * 1e6);
+ long usec = (long)((seconds.asDouble() - timeval[0]) * 1e6);
if (usec < 0) {
// If rounding gave us a negative number, truncate
usec = 0;
@@ -1065,18 +1068,25 @@
* @param pathObj a PyObject, raising a TypeError if an invalid path type
* @return an absolute path String
*/
- private static String absolutePath(PyObject pathObj) {
+ private static Path absolutePath(PyObject pathObj) {
String pathStr = asPath(pathObj);
if (pathStr.equals("")) {
// Otherwise this path will get prefixed with the current working directory,
// which is both wrong and very surprising!
throw Py.OSError(Errno.ENOENT, pathObj);
}
- Path path = FileSystems.getDefault().getPath(pathStr);
- if (!path.isAbsolute()) {
- path = FileSystems.getDefault().getPath(Py.getSystemState().getCurrentWorkingDir(), pathStr);
+ try {
+ Path path = FileSystems.getDefault().getPath(pathStr);
+ if (!path.isAbsolute()) {
+ path = FileSystems.getDefault().getPath(Py.getSystemState().getCurrentWorkingDir(), pathStr);
+ }
+ return path.toAbsolutePath();
+ } catch (java.nio.file.InvalidPathException ex) {
+ // Thrown on Windows for paths like foo/bar/, where is the literal text, not a metavariable :)
+ // NOTE: In this case on CPython, Windows throws the Windows-specific internal error WindowsError [Error 123],
+ // but it seems excessive to duplicate this error hierarchy
+ throw Py.OSError(Errno.EINVAL, pathObj);
}
- return path.toAbsolutePath().normalize().toString();
}
private static PyException badFD() {
@@ -1099,6 +1109,16 @@
return os.getModuleName();
}
+ private static void checkTrailingSlash(PyObject path, Map attributes) {
+ Boolean isDirectory = (Boolean) attributes.get("isDirectory");
+ if (isDirectory != null && !isDirectory.booleanValue()) {
+ String pathStr = path.toString();
+ if (pathStr.endsWith(File.separator) || pathStr.endsWith("/.")) {
+ throw Py.OSError(Errno.ENOTDIR, path);
+ }
+ }
+ }
+
static class LstatFunction extends PyBuiltinFunctionNarrow {
LstatFunction() {
super("lstat", 1, 1,
@@ -1108,15 +1128,27 @@
@Override
public PyObject __call__(PyObject path) {
- String absolutePath = absolutePath(path);
+ Path absolutePath = absolutePath(path);
+ try {
+ Map attributes = Files.readAttributes(
+ absolutePath, "unix:*", LinkOption.NOFOLLOW_LINKS);
+ Boolean isSymbolicLink = (Boolean) attributes.get("isSymbolicLink");
+ if (isSymbolicLink != null && isSymbolicLink.booleanValue() && path.toString().endsWith("/")) {
+ // Chase the symbolic link, but do not follow further - this is a special case for lstat
+ Path symlink = Files.readSymbolicLink(absolutePath);
+ symlink = absolutePath.getParent().resolve(symlink);
+ attributes = Files.readAttributes(
+ symlink, "unix:*", LinkOption.NOFOLLOW_LINKS);
- // round tripping from a string to a file to a string loses
- // trailing slashes so add them back back in to get correct posix.lstat
- // behaviour if path is not a directory.
- if (asPath(path).endsWith(File.separator)) {
- absolutePath = absolutePath + File.separator;
+ } else {
+ checkTrailingSlash(path, attributes);
+ }
+ return PyStatResult.fromUnixFileAttributes(attributes);
+ } catch (NoSuchFileException ex) {
+ throw Py.OSError(Errno.ENOENT, path);
+ } catch (IOException ioe) {
+ throw Py.OSError(Errno.EBADF, path);
}
- return PyStatResult.fromFileStat(posix.lstat(absolutePath));
}
}
@@ -1131,14 +1163,67 @@
@Override
public PyObject __call__(PyObject path) {
- String absolutePath = absolutePath(path);
- // round tripping from a string to a file to a string loses
- // trailing slashes so add them back back in to get correct posix.stat
- // behaviour if path is not a directory.
- if (asPath(path).endsWith(File.separator)) {
- absolutePath = absolutePath + File.separator;
+ Path absolutePath = absolutePath(path);
+ try {
+ Map attributes = Files.readAttributes(absolutePath, "unix:*");
+ checkTrailingSlash(path, attributes);
+ return PyStatResult.fromUnixFileAttributes(attributes);
+ } catch (NoSuchFileException ex) {
+ throw Py.OSError(Errno.ENOENT, path);
+ } catch (IOException ioe) {
+ throw Py.OSError(Errno.EBADF, path);
}
- return PyStatResult.fromFileStat(posix.stat(absolutePath));
+ }
+ }
+
+ // Follows the approach taken by posixmodule.c for a Windows specific stat;
+ // in particular this is driven by the fact that Windows CRT does not properly handle
+ // daylight savings time in timestamps.
+ //
+ // Another advantage is setting the st_mode the same as CPython would return.
+ static class WindowsStatFunction extends PyBuiltinFunctionNarrow {
+ WindowsStatFunction() {
+ super("stat", 1, 1,
+ "stat(path) -> stat result\n\n" +
+ "Perform a stat system call on the given path.\n\n" +
+ "Note that some platforms may return only a small subset of the\n" +
+ "standard fields"); // like this one!
+ }
+
+ private final static int _S_IFDIR = 0x4000;
+ private final static int _S_IFREG = 0x8000;
+
+ static int attributes_to_mode(DosFileAttributes attr) {
+ int m = 0;
+ if (attr.isDirectory()) {
+ m |= _S_IFDIR | 0111; /* IFEXEC for user,group,other */
+ } else {
+ m |= _S_IFREG;
+ }
+ if (attr.isReadOnly()) {
+ m |= 0444;
+ } else {
+ m |= 0666;
+ }
+ return m;
+ }
+
+ @Override
+ public PyObject __call__(PyObject path) {
+ Path absolutePath = absolutePath(path);
+ try {
+ DosFileAttributes attributes = Files.readAttributes(absolutePath, DosFileAttributes.class);
+ int mode = attributes_to_mode(attributes);
+ String extension = com.google.common.io.Files.getFileExtension(absolutePath.toString());
+ if (extension.equals("bat") || extension.equals("cmd") || extension.equals("exe") || extension.equals("com")) {
+ mode |= 0111;
+ }
+ return PyStatResult.fromDosFileAttributes(mode, attributes);
+ } catch (NoSuchFileException ex) {
+ throw Py.OSError(Errno.ENOENT, path);
+ } catch (IOException ioe) {
+ throw Py.OSError(Errno.EBADF, path);
+ }
}
}
@@ -1150,14 +1235,19 @@
@Override
public PyObject __call__(PyObject fdObj) {
- FDUnion fd = getFD(fdObj);
- FileStat stat;
- if (fd.isIntFD()) {
- stat = posix.fstat(fd.intFD);
- } else {
- stat = posix.fstat(fd.javaFD);;
+ try {
+ FDUnion fd = getFD(fdObj);
+ FileStat stat;
+ if (fd.isIntFD()) {
+ stat = posix.fstat(fd.intFD);
+ } else {
+ stat = posix.fstat(fd.javaFD);
+ ;
+ }
+ return PyStatResult.fromFileStat(stat);
+ } catch (PyException ex) {
+ throw Py.OSError(Errno.EBADF);
}
- return PyStatResult.fromFileStat(stat);
}
}
}
diff --git a/src/org/python/modules/posix/PyStatResult.java b/src/org/python/modules/posix/PyStatResult.java
--- a/src/org/python/modules/posix/PyStatResult.java
+++ b/src/org/python/modules/posix/PyStatResult.java
@@ -18,6 +18,11 @@
import org.python.expose.ExposedType;
import org.python.expose.MethodType;
+import java.nio.file.attribute.DosFileAttributes;
+import java.nio.file.attribute.FileTime;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
@ExposedType(name = "stat_result", isBaseType = false)
public class PyStatResult extends PyTuple {
@@ -74,20 +79,105 @@
}
}
- /**
- * Return a Python stat result from a posix layer FileStat object.
- *
- * Ideally times would be returned as floats, like CPython, however, the underlying FileStat
- * object from JNR Posix only has integer resolution.
- *
- * We should be able to resolve by using java.nio.file.attribute.PosixFileAttributeView
- */
+ // Return a Python stat result from a posix layer FileStat object, a bulk Map retrieval of attributes, or AttributeView.
public static PyStatResult fromFileStat(FileStat stat) {
- return new PyStatResult(Py.newInteger(stat.mode()), Py.newInteger(stat.ino()),
- Py.newLong(stat.dev()), Py.newInteger(stat.nlink()),
- Py.newInteger(stat.uid()), Py.newInteger(stat.gid()),
- Py.newInteger(stat.st_size()), Py.newInteger(stat.atime()),
- Py.newInteger(stat.mtime()), Py.newInteger(stat.ctime()));
+ return new PyStatResult(
+ Py.newInteger(stat.mode()),
+ Py.newInteger(stat.ino()),
+ Py.newLong(stat.dev()),
+ Py.newInteger(stat.nlink()),
+ Py.newInteger(stat.uid()),
+ Py.newInteger(stat.gid()),
+ Py.newInteger(stat.st_size()),
+ Py.newFloat(stat.atime()),
+ Py.newFloat(stat.mtime()),
+ Py.newFloat(stat.ctime()));
+ }
+
+ private static double fromFileTime(FileTime fileTime) {
+ return fileTime.to(TimeUnit.NANOSECONDS) / 1e9;
+ }
+
+ private static Long zeroOrValue(Long value) {
+ if (value == null) {
+ return new Long(0L);
+ } else {
+ return value;
+ }
+ }
+
+ private static Integer zeroOrValue(Integer value) {
+ if (value == null) {
+ return new Integer(0);
+ } else {
+ return value;
+ }
+ }
+
+ public static PyStatResult fromUnixFileAttributes(Map stat) {
+
+ Integer mode = zeroOrValue((Integer)stat.get("mode"));
+ Long ino = zeroOrValue((Long)stat.get("ino"));
+ Long dev = zeroOrValue((Long)stat.get("dev"));
+ Integer nlink = zeroOrValue((Integer)stat.get("nlink"));
+ Integer uid = zeroOrValue((Integer)stat.get("uid"));
+ Integer gid = zeroOrValue((Integer)stat.get("gid"));
+ Long size = zeroOrValue((Long)stat.get("size"));
+
+ return new PyStatResult(
+ Py.newInteger(mode),
+ Py.newInteger(ino),
+ Py.newLong(dev),
+ Py.newInteger(nlink),
+ Py.newInteger(uid),
+ Py.newInteger(gid),
+ Py.newInteger(size),
+ Py.newFloat(fromFileTime((FileTime)stat.get("lastAccessTime"))),
+ Py.newFloat(fromFileTime((FileTime)stat.get("lastModifiedTime"))),
+ Py.newFloat(fromFileTime((FileTime) stat.get("ctime"))));
+ }
+
+ public static PyStatResult fromDosFileAttributes(int mode, DosFileAttributes stat) {
+ return new PyStatResult(
+ Py.newInteger(mode),
+ Py.newInteger(0),
+ Py.newLong(0),
+ Py.newInteger(0),
+ Py.newInteger(0),
+ Py.newInteger(0),
+ Py.newInteger(stat.size()),
+ Py.newFloat(fromFileTime(stat.lastAccessTime())),
+ Py.newFloat(fromFileTime(stat.lastModifiedTime())),
+ Py.newFloat(fromFileTime(stat.creationTime())));
+ }
+
+ // Override pyget, getslice to preserve backwards compatiblity that ints are returned for time elements
+ // if accessing by an index or slice
+
+ private final static int ST_ATIME = 7;
+ private final static int ST_MTIME = 8;
+ private final static int ST_CTIME = 9;
+
+ @Override
+ public PyObject pyget(int index) {
+ if (index == ST_ATIME || index == ST_MTIME || index == ST_CTIME) {
+ return super.pyget(index).__int__();
+ } else {
+ return super.pyget(index);
+ }
+ }
+
+ @Override
+ protected PyObject getslice(int start, int stop, int step) {
+ if (step > 0 && stop < start) {
+ stop = start;
+ }
+ int n = sliceLength(start, stop, step);
+ PyObject[] newArray = new PyObject[n];
+ for (int i = start, j = 0; j < n; i += step, j++) {
+ newArray[j] = pyget(i);
+ }
+ return new PyTuple(newArray, false);
}
@Override
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Mon Feb 2 23:21:39 2015
From: jython-checkins at python.org (jim.baker)
Date: Mon, 02 Feb 2015 22:21:39 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Silence_=5Fsocket_log_handl?=
=?utf-8?q?ing_when_not_debugging?=
Message-ID: <20150202222131.96098.7459@psf.io>
https://hg.python.org/jython/rev/e37122c32d8a
changeset: 7564:e37122c32d8a
user: Jim Baker
date: Mon Feb 02 15:21:27 2015 -0700
summary:
Silence _socket log handling when not debugging
Fixes http://bugs.jython.org/issue2253
files:
Lib/_socket.py | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/Lib/_socket.py b/Lib/_socket.py
--- a/Lib/_socket.py
+++ b/Lib/_socket.py
@@ -51,6 +51,7 @@
log = logging.getLogger("_socket")
+log.setLevel(level=logging.WARNING)
def _debug():
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 3 02:55:00 2015
From: jython-checkins at python.org (jim.baker)
Date: Tue, 03 Feb 2015 01:55:00 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Use_source_file_for_log_rec?=
=?utf-8?q?ord_attribute_filename?=
Message-ID: <20150203015500.96084.99209@psf.io>
https://hg.python.org/jython/rev/d5fdbc0a5b11
changeset: 7565:d5fdbc0a5b11
user: Jim Baker
date: Mon Feb 02 18:54:55 2015 -0700
summary:
Use source file for log record attribute filename
Fixes http://bugs.jython.org/issue1760
files:
Lib/logging/__init__.py | 1728 +++++++++++++++++++++++
Lib/test/test_logging.py | 3 +-
Lib/test/test_logging_jy.py | 28 +
3 files changed, 1758 insertions(+), 1 deletions(-)
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
new file mode 100644
--- /dev/null
+++ b/Lib/logging/__init__.py
@@ -0,0 +1,1728 @@
+# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted,
+# provided that the above copyright notice appear in all copies and that
+# both that copyright notice and this permission notice appear in
+# supporting documentation, and that the name of Vinay Sajip
+# not be used in advertising or publicity pertaining to distribution
+# of the software without specific, written prior permission.
+# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Logging package for Python. Based on PEP 282 and comments thereto in
+comp.lang.python.
+
+Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
+
+To use, simply 'import logging' and log away!
+"""
+
+import sys, os, time, cStringIO, traceback, warnings, weakref
+
+__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
+ 'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO',
+ 'LogRecord', 'Logger', 'LoggerAdapter', 'NOTSET', 'NullHandler',
+ 'StreamHandler', 'WARN', 'WARNING', 'addLevelName', 'basicConfig',
+ 'captureWarnings', 'critical', 'debug', 'disable', 'error',
+ 'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
+ 'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning']
+
+try:
+ import codecs
+except ImportError:
+ codecs = None
+
+try:
+ import thread
+ import threading
+except ImportError:
+ thread = None
+
+__author__ = "Vinay Sajip "
+__status__ = "production"
+__version__ = "0.5.1.2"
+__date__ = "07 February 2010"
+
+#---------------------------------------------------------------------------
+# Miscellaneous module data
+#---------------------------------------------------------------------------
+try:
+ unicode
+ _unicode = True
+except NameError:
+ _unicode = False
+
+#
+# _srcfile is used when walking the stack to check when we've got the first
+# caller stack frame.
+#
+if hasattr(sys, 'frozen'): #support for py2exe
+ _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
+elif __file__[-4:].lower() in ['.pyc', '.pyo']:
+ _srcfile = __file__[:-4] + '.py'
+elif __file__.endswith('$py.class'):
+ _srcfile = __file__[:-9] + '.py'
+else:
+ _srcfile = __file__
+_srcfile = os.path.normcase(_srcfile)
+
+# next bit filched from 1.5.2's inspect.py
+def currentframe():
+ """Return the frame object for the caller's stack frame."""
+ try:
+ raise Exception
+ except:
+ return sys.exc_info()[2].tb_frame.f_back
+
+if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
+# done filching
+
+# _srcfile is only used in conjunction with sys._getframe().
+# To provide compatibility with older versions of Python, set _srcfile
+# to None if _getframe() is not available; this value will prevent
+# findCaller() from being called.
+#if not hasattr(sys, "_getframe"):
+# _srcfile = None
+
+#
+#_startTime is used as the base when calculating the relative time of events
+#
+_startTime = time.time()
+
+#
+#raiseExceptions is used to see if exceptions during handling should be
+#propagated
+#
+raiseExceptions = 1
+
+#
+# If you don't want threading information in the log, set this to zero
+#
+logThreads = 1
+
+#
+# If you don't want multiprocessing information in the log, set this to zero
+#
+logMultiprocessing = 1
+
+#
+# If you don't want process information in the log, set this to zero
+#
+logProcesses = 1
+
+#---------------------------------------------------------------------------
+# Level related stuff
+#---------------------------------------------------------------------------
+#
+# Default levels and level names, these can be replaced with any positive set
+# of values having corresponding names. There is a pseudo-level, NOTSET, which
+# is only really there as a lower limit for user-defined levels. Handlers and
+# loggers are initialized with NOTSET so that they will log all messages, even
+# at user-defined levels.
+#
+
+CRITICAL = 50
+FATAL = CRITICAL
+ERROR = 40
+WARNING = 30
+WARN = WARNING
+INFO = 20
+DEBUG = 10
+NOTSET = 0
+
+_levelNames = {
+ CRITICAL : 'CRITICAL',
+ ERROR : 'ERROR',
+ WARNING : 'WARNING',
+ INFO : 'INFO',
+ DEBUG : 'DEBUG',
+ NOTSET : 'NOTSET',
+ 'CRITICAL' : CRITICAL,
+ 'ERROR' : ERROR,
+ 'WARN' : WARNING,
+ 'WARNING' : WARNING,
+ 'INFO' : INFO,
+ 'DEBUG' : DEBUG,
+ 'NOTSET' : NOTSET,
+}
+
+def getLevelName(level):
+ """
+ Return the textual representation of logging level 'level'.
+
+ If the level is one of the predefined levels (CRITICAL, ERROR, WARNING,
+ INFO, DEBUG) then you get the corresponding string. If you have
+ associated levels with names using addLevelName then the name you have
+ associated with 'level' is returned.
+
+ If a numeric value corresponding to one of the defined levels is passed
+ in, the corresponding string representation is returned.
+
+ Otherwise, the string "Level %s" % level is returned.
+ """
+ return _levelNames.get(level, ("Level %s" % level))
+
+def addLevelName(level, levelName):
+ """
+ Associate 'levelName' with 'level'.
+
+ This is used when converting levels to text during message formatting.
+ """
+ _acquireLock()
+ try: #unlikely to cause an exception, but you never know...
+ _levelNames[level] = levelName
+ _levelNames[levelName] = level
+ finally:
+ _releaseLock()
+
+def _checkLevel(level):
+ if isinstance(level, (int, long)):
+ rv = level
+ elif str(level) == level:
+ if level not in _levelNames:
+ raise ValueError("Unknown level: %r" % level)
+ rv = _levelNames[level]
+ else:
+ raise TypeError("Level not an integer or a valid string: %r" % level)
+ return rv
+
+#---------------------------------------------------------------------------
+# Thread-related stuff
+#---------------------------------------------------------------------------
+
+#
+#_lock is used to serialize access to shared data structures in this module.
+#This needs to be an RLock because fileConfig() creates and configures
+#Handlers, and so might arbitrary user threads. Since Handler code updates the
+#shared dictionary _handlers, it needs to acquire the lock. But if configuring,
+#the lock would already have been acquired - so we need an RLock.
+#The same argument applies to Loggers and Manager.loggerDict.
+#
+if thread:
+ _lock = threading.RLock()
+else:
+ _lock = None
+
+def _acquireLock():
+ """
+ Acquire the module-level lock for serializing access to shared data.
+
+ This should be released with _releaseLock().
+ """
+ if _lock:
+ _lock.acquire()
+
+def _releaseLock():
+ """
+ Release the module-level lock acquired by calling _acquireLock().
+ """
+ if _lock:
+ _lock.release()
+
+#---------------------------------------------------------------------------
+# The logging record
+#---------------------------------------------------------------------------
+
+class LogRecord(object):
+ """
+ A LogRecord instance represents an event being logged.
+
+ LogRecord instances are created every time something is logged. They
+ contain all the information pertinent to the event being logged. The
+ main information passed in is in msg and args, which are combined
+ using str(msg) % args to create the message field of the record. The
+ record also includes information such as when the record was created,
+ the source line where the logging call was made, and any exception
+ information to be logged.
+ """
+ def __init__(self, name, level, pathname, lineno,
+ msg, args, exc_info, func=None):
+ """
+ Initialize a logging record with interesting information.
+ """
+ ct = time.time()
+ self.name = name
+ self.msg = msg
+ #
+ # The following statement allows passing of a dictionary as a sole
+ # argument, so that you can do something like
+ # logging.debug("a %(a)d b %(b)s", {'a':1, 'b':2})
+ # Suggested by Stefan Behnel.
+ # Note that without the test for args[0], we get a problem because
+ # during formatting, we test to see if the arg is present using
+ # 'if self.args:'. If the event being logged is e.g. 'Value is %d'
+ # and if the passed arg fails 'if self.args:' then no formatting
+ # is done. For example, logger.warn('Value is %d', 0) would log
+ # 'Value is %d' instead of 'Value is 0'.
+ # For the use case of passing a dictionary, this should not be a
+ # problem.
+ if args and len(args) == 1 and isinstance(args[0], dict) and args[0]:
+ args = args[0]
+ self.args = args
+ self.levelname = getLevelName(level)
+ self.levelno = level
+ self.pathname = pathname
+ try:
+ self.filename = os.path.basename(pathname)
+ self.module = os.path.splitext(self.filename)[0]
+ except (TypeError, ValueError, AttributeError):
+ self.filename = pathname
+ self.module = "Unknown module"
+ self.exc_info = exc_info
+ self.exc_text = None # used to cache the traceback text
+ self.lineno = lineno
+ self.funcName = func
+ self.created = ct
+ self.msecs = (ct - long(ct)) * 1000
+ self.relativeCreated = (self.created - _startTime) * 1000
+ if logThreads and thread:
+ self.thread = thread.get_ident()
+ self.threadName = threading.current_thread().name
+ else:
+ self.thread = None
+ self.threadName = None
+ if not logMultiprocessing:
+ self.processName = None
+ else:
+ self.processName = 'MainProcess'
+ mp = sys.modules.get('multiprocessing')
+ if mp is not None:
+ # Errors may occur if multiprocessing has not finished loading
+ # yet - e.g. if a custom import hook causes third-party code
+ # to run when multiprocessing calls import. See issue 8200
+ # for an example
+ try:
+ self.processName = mp.current_process().name
+ except StandardError:
+ pass
+ if logProcesses and hasattr(os, 'getpid'):
+ self.process = os.getpid()
+ else:
+ self.process = None
+
+ def __str__(self):
+ return ''%(self.name, self.levelno,
+ self.pathname, self.lineno, self.msg)
+
+ def getMessage(self):
+ """
+ Return the message for this LogRecord.
+
+ Return the message for this LogRecord after merging any user-supplied
+ arguments with the message.
+ """
+ if not _unicode: #if no unicode support...
+ msg = str(self.msg)
+ else:
+ msg = self.msg
+ if not isinstance(msg, basestring):
+ try:
+ msg = str(self.msg)
+ except UnicodeError:
+ msg = self.msg #Defer encoding till later
+ if self.args:
+ msg = msg % self.args
+ return msg
+
+def makeLogRecord(dict):
+ """
+ Make a LogRecord whose attributes are defined by the specified dictionary,
+ This function is useful for converting a logging event received over
+ a socket connection (which is sent as a dictionary) into a LogRecord
+ instance.
+ """
+ rv = LogRecord(None, None, "", 0, "", (), None, None)
+ rv.__dict__.update(dict)
+ return rv
+
+#---------------------------------------------------------------------------
+# Formatter classes and functions
+#---------------------------------------------------------------------------
+
+class Formatter(object):
+ """
+ Formatter instances are used to convert a LogRecord to text.
+
+ Formatters need to know how a LogRecord is constructed. They are
+ responsible for converting a LogRecord to (usually) a string which can
+ be interpreted by either a human or an external system. The base Formatter
+ allows a formatting string to be specified. If none is supplied, the
+ default value of "%s(message)\\n" is used.
+
+ The Formatter can be initialized with a format string which makes use of
+ knowledge of the LogRecord attributes - e.g. the default value mentioned
+ above makes use of the fact that the user's message and arguments are pre-
+ formatted into a LogRecord's message attribute. Currently, the useful
+ attributes in a LogRecord are described by:
+
+ %(name)s Name of the logger (logging channel)
+ %(levelno)s Numeric logging level for the message (DEBUG, INFO,
+ WARNING, ERROR, CRITICAL)
+ %(levelname)s Text logging level for the message ("DEBUG", "INFO",
+ "WARNING", "ERROR", "CRITICAL")
+ %(pathname)s Full pathname of the source file where the logging
+ call was issued (if available)
+ %(filename)s Filename portion of pathname
+ %(module)s Module (name portion of filename)
+ %(lineno)d Source line number where the logging call was issued
+ (if available)
+ %(funcName)s Function name
+ %(created)f Time when the LogRecord was created (time.time()
+ return value)
+ %(asctime)s Textual time when the LogRecord was created
+ %(msecs)d Millisecond portion of the creation time
+ %(relativeCreated)d Time in milliseconds when the LogRecord was created,
+ relative to the time the logging module was loaded
+ (typically at application startup time)
+ %(thread)d Thread ID (if available)
+ %(threadName)s Thread name (if available)
+ %(process)d Process ID (if available)
+ %(message)s The result of record.getMessage(), computed just as
+ the record is emitted
+ """
+
+ converter = time.localtime
+
+ def __init__(self, fmt=None, datefmt=None):
+ """
+ Initialize the formatter with specified format strings.
+
+ Initialize the formatter either with the specified format string, or a
+ default as described above. Allow for specialized date formatting with
+ the optional datefmt argument (if omitted, you get the ISO8601 format).
+ """
+ if fmt:
+ self._fmt = fmt
+ else:
+ self._fmt = "%(message)s"
+ self.datefmt = datefmt
+
+ def formatTime(self, record, datefmt=None):
+ """
+ Return the creation time of the specified LogRecord as formatted text.
+
+ This method should be called from format() by a formatter which
+ wants to make use of a formatted time. This method can be overridden
+ in formatters to provide for any specific requirement, but the
+ basic behaviour is as follows: if datefmt (a string) is specified,
+ it is used with time.strftime() to format the creation time of the
+ record. Otherwise, the ISO8601 format is used. The resulting
+ string is returned. This function uses a user-configurable function
+ to convert the creation time to a tuple. By default, time.localtime()
+ is used; to change this for a particular formatter instance, set the
+ 'converter' attribute to a function with the same signature as
+ time.localtime() or time.gmtime(). To change it for all formatters,
+ for example if you want all logging times to be shown in GMT,
+ set the 'converter' attribute in the Formatter class.
+ """
+ ct = self.converter(record.created)
+ if datefmt:
+ s = time.strftime(datefmt, ct)
+ else:
+ t = time.strftime("%Y-%m-%d %H:%M:%S", ct)
+ s = "%s,%03d" % (t, record.msecs)
+ return s
+
+ def formatException(self, ei):
+ """
+ Format and return the specified exception information as a string.
+
+ This default implementation just uses
+ traceback.print_exception()
+ """
+ sio = cStringIO.StringIO()
+ traceback.print_exception(ei[0], ei[1], ei[2], None, sio)
+ s = sio.getvalue()
+ sio.close()
+ if s[-1:] == "\n":
+ s = s[:-1]
+ return s
+
+ def usesTime(self):
+ """
+ Check if the format uses the creation time of the record.
+ """
+ return self._fmt.find("%(asctime)") >= 0
+
+ def format(self, record):
+ """
+ Format the specified record as text.
+
+ The record's attribute dictionary is used as the operand to a
+ string formatting operation which yields the returned string.
+ Before formatting the dictionary, a couple of preparatory steps
+ are carried out. The message attribute of the record is computed
+ using LogRecord.getMessage(). If the formatting string uses the
+ time (as determined by a call to usesTime(), formatTime() is
+ called to format the event time. If there is exception information,
+ it is formatted using formatException() and appended to the message.
+ """
+ record.message = record.getMessage()
+ if self.usesTime():
+ record.asctime = self.formatTime(record, self.datefmt)
+ s = self._fmt % record.__dict__
+ if record.exc_info:
+ # Cache the traceback text to avoid converting it multiple times
+ # (it's constant anyway)
+ if not record.exc_text:
+ record.exc_text = self.formatException(record.exc_info)
+ if record.exc_text:
+ if s[-1:] != "\n":
+ s = s + "\n"
+ try:
+ s = s + record.exc_text
+ except UnicodeError:
+ # Sometimes filenames have non-ASCII chars, which can lead
+ # to errors when s is Unicode and record.exc_text is str
+ # See issue 8924.
+ # We also use replace for when there are multiple
+ # encodings, e.g. UTF-8 for the filesystem and latin-1
+ # for a script. See issue 13232.
+ s = s + record.exc_text.decode(sys.getfilesystemencoding(),
+ 'replace')
+ return s
+
+#
+# The default formatter to use when no other is specified
+#
+_defaultFormatter = Formatter()
+
+class BufferingFormatter(object):
+ """
+ A formatter suitable for formatting a number of records.
+ """
+ def __init__(self, linefmt=None):
+ """
+ Optionally specify a formatter which will be used to format each
+ individual record.
+ """
+ if linefmt:
+ self.linefmt = linefmt
+ else:
+ self.linefmt = _defaultFormatter
+
+ def formatHeader(self, records):
+ """
+ Return the header string for the specified records.
+ """
+ return ""
+
+ def formatFooter(self, records):
+ """
+ Return the footer string for the specified records.
+ """
+ return ""
+
+ def format(self, records):
+ """
+ Format the specified records and return the result as a string.
+ """
+ rv = ""
+ if len(records) > 0:
+ rv = rv + self.formatHeader(records)
+ for record in records:
+ rv = rv + self.linefmt.format(record)
+ rv = rv + self.formatFooter(records)
+ return rv
+
+#---------------------------------------------------------------------------
+# Filter classes and functions
+#---------------------------------------------------------------------------
+
+class Filter(object):
+ """
+ Filter instances are used to perform arbitrary filtering of LogRecords.
+
+ Loggers and Handlers can optionally use Filter instances to filter
+ records as desired. The base filter class only allows events which are
+ below a certain point in the logger hierarchy. For example, a filter
+ initialized with "A.B" will allow events logged by loggers "A.B",
+ "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If
+ initialized with the empty string, all events are passed.
+ """
+ def __init__(self, name=''):
+ """
+ Initialize a filter.
+
+ Initialize with the name of the logger which, together with its
+ children, will have its events allowed through the filter. If no
+ name is specified, allow every event.
+ """
+ self.name = name
+ self.nlen = len(name)
+
+ def filter(self, record):
+ """
+ Determine if the specified record is to be logged.
+
+ Is the specified record to be logged? Returns 0 for no, nonzero for
+ yes. If deemed appropriate, the record may be modified in-place.
+ """
+ if self.nlen == 0:
+ return 1
+ elif self.name == record.name:
+ return 1
+ elif record.name.find(self.name, 0, self.nlen) != 0:
+ return 0
+ return (record.name[self.nlen] == ".")
+
+class Filterer(object):
+ """
+ A base class for loggers and handlers which allows them to share
+ common code.
+ """
+ def __init__(self):
+ """
+ Initialize the list of filters to be an empty list.
+ """
+ self.filters = []
+
+ def addFilter(self, filter):
+ """
+ Add the specified filter to this handler.
+ """
+ if not (filter in self.filters):
+ self.filters.append(filter)
+
+ def removeFilter(self, filter):
+ """
+ Remove the specified filter from this handler.
+ """
+ if filter in self.filters:
+ self.filters.remove(filter)
+
+ def filter(self, record):
+ """
+ Determine if a record is loggable by consulting all the filters.
+
+ The default is to allow the record to be logged; any filter can veto
+ this and the record is then dropped. Returns a zero value if a record
+ is to be dropped, else non-zero.
+ """
+ rv = 1
+ for f in self.filters:
+ if not f.filter(record):
+ rv = 0
+ break
+ return rv
+
+#---------------------------------------------------------------------------
+# Handler classes and functions
+#---------------------------------------------------------------------------
+
+_handlers = weakref.WeakValueDictionary() #map of handler names to handlers
+_handlerList = [] # added to allow handlers to be removed in reverse of order initialized
+
+def _removeHandlerRef(wr):
+ """
+ Remove a handler reference from the internal cleanup list.
+ """
+ # This function can be called during module teardown, when globals are
+ # set to None. If _acquireLock is None, assume this is the case and do
+ # nothing.
+ if (_acquireLock is not None and _handlerList is not None and
+ _releaseLock is not None):
+ _acquireLock()
+ try:
+ if wr in _handlerList:
+ _handlerList.remove(wr)
+ finally:
+ _releaseLock()
+
+def _addHandlerRef(handler):
+ """
+ Add a handler to the internal cleanup list using a weak reference.
+ """
+ _acquireLock()
+ try:
+ _handlerList.append(weakref.ref(handler, _removeHandlerRef))
+ finally:
+ _releaseLock()
+
+class Handler(Filterer):
+ """
+ Handler instances dispatch logging events to specific destinations.
+
+ The base handler class. Acts as a placeholder which defines the Handler
+ interface. Handlers can optionally use Formatter instances to format
+ records as desired. By default, no formatter is specified; in this case,
+ the 'raw' message as determined by record.message is logged.
+ """
+ def __init__(self, level=NOTSET):
+ """
+ Initializes the instance - basically setting the formatter to None
+ and the filter list to empty.
+ """
+ Filterer.__init__(self)
+ self._name = None
+ self.level = _checkLevel(level)
+ self.formatter = None
+ # Add the handler to the global _handlerList (for cleanup on shutdown)
+ _addHandlerRef(self)
+ self.createLock()
+
+ def get_name(self):
+ return self._name
+
+ def set_name(self, name):
+ _acquireLock()
+ try:
+ if self._name in _handlers:
+ del _handlers[self._name]
+ self._name = name
+ if name:
+ _handlers[name] = self
+ finally:
+ _releaseLock()
+
+ name = property(get_name, set_name)
+
+ def createLock(self):
+ """
+ Acquire a thread lock for serializing access to the underlying I/O.
+ """
+ if thread:
+ self.lock = threading.RLock()
+ else:
+ self.lock = None
+
+ def acquire(self):
+ """
+ Acquire the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.acquire()
+
+ def release(self):
+ """
+ Release the I/O thread lock.
+ """
+ if self.lock:
+ self.lock.release()
+
+ def setLevel(self, level):
+ """
+ Set the logging level of this handler.
+ """
+ self.level = _checkLevel(level)
+
+ def format(self, record):
+ """
+ Format the specified record.
+
+ If a formatter is set, use it. Otherwise, use the default formatter
+ for the module.
+ """
+ if self.formatter:
+ fmt = self.formatter
+ else:
+ fmt = _defaultFormatter
+ return fmt.format(record)
+
+ def emit(self, record):
+ """
+ Do whatever it takes to actually log the specified logging record.
+
+ This version is intended to be implemented by subclasses and so
+ raises a NotImplementedError.
+ """
+ raise NotImplementedError('emit must be implemented '
+ 'by Handler subclasses')
+
+ def handle(self, record):
+ """
+ Conditionally emit the specified logging record.
+
+ Emission depends on filters which may have been added to the handler.
+ Wrap the actual emission of the record with acquisition/release of
+ the I/O thread lock. Returns whether the filter passed the record for
+ emission.
+ """
+ rv = self.filter(record)
+ if rv:
+ self.acquire()
+ try:
+ self.emit(record)
+ finally:
+ self.release()
+ return rv
+
+ def setFormatter(self, fmt):
+ """
+ Set the formatter for this handler.
+ """
+ self.formatter = fmt
+
+ def flush(self):
+ """
+ Ensure all logging output has been flushed.
+
+ This version does nothing and is intended to be implemented by
+ subclasses.
+ """
+ pass
+
+ def close(self):
+ """
+ Tidy up any resources used by the handler.
+
+ This version removes the handler from an internal map of handlers,
+ _handlers, which is used for handler lookup by name. Subclasses
+ should ensure that this gets called from overridden close()
+ methods.
+ """
+ #get the module data lock, as we're updating a shared structure.
+ _acquireLock()
+ try: #unlikely to raise an exception, but you never know...
+ if self._name and self._name in _handlers:
+ del _handlers[self._name]
+ finally:
+ _releaseLock()
+
+ def handleError(self, record):
+ """
+ Handle errors which occur during an emit() call.
+
+ This method should be called from handlers when an exception is
+ encountered during an emit() call. If raiseExceptions is false,
+ exceptions get silently ignored. This is what is mostly wanted
+ for a logging system - most users will not care about errors in
+ the logging system, they are more interested in application errors.
+ You could, however, replace this with a custom handler if you wish.
+ The record which was being processed is passed in to this method.
+ """
+ if raiseExceptions and sys.stderr: # see issue 13807
+ ei = sys.exc_info()
+ try:
+ traceback.print_exception(ei[0], ei[1], ei[2],
+ None, sys.stderr)
+ sys.stderr.write('Logged from file %s, line %s\n' % (
+ record.filename, record.lineno))
+ except IOError:
+ pass # see issue 5971
+ finally:
+ del ei
+
+class StreamHandler(Handler):
+ """
+ A handler class which writes logging records, appropriately formatted,
+ to a stream. Note that this class does not close the stream, as
+ sys.stdout or sys.stderr may be used.
+ """
+
+ def __init__(self, stream=None):
+ """
+ Initialize the handler.
+
+ If stream is not specified, sys.stderr is used.
+ """
+ Handler.__init__(self)
+ if stream is None:
+ stream = sys.stderr
+ self.stream = stream
+
+ def flush(self):
+ """
+ Flushes the stream.
+ """
+ self.acquire()
+ try:
+ if self.stream and hasattr(self.stream, "flush"):
+ self.stream.flush()
+ finally:
+ self.release()
+
+ def emit(self, record):
+ """
+ Emit a record.
+
+ If a formatter is specified, it is used to format the record.
+ The record is then written to the stream with a trailing newline. If
+ exception information is present, it is formatted using
+ traceback.print_exception and appended to the stream. If the stream
+ has an 'encoding' attribute, it is used to determine how to do the
+ output to the stream.
+ """
+ try:
+ msg = self.format(record)
+ stream = self.stream
+ fs = "%s\n"
+ if not _unicode: #if no unicode support...
+ stream.write(fs % msg)
+ else:
+ try:
+ if (isinstance(msg, unicode) and
+ getattr(stream, 'encoding', None)):
+ ufs = fs.decode(stream.encoding)
+ try:
+ stream.write(ufs % msg)
+ except UnicodeEncodeError:
+ #Printing to terminals sometimes fails. For example,
+ #with an encoding of 'cp1251', the above write will
+ #work if written to a stream opened or wrapped by
+ #the codecs module, but fail when writing to a
+ #terminal even when the codepage is set to cp1251.
+ #An extra encoding step seems to be needed.
+ stream.write((ufs % msg).encode(stream.encoding))
+ else:
+ stream.write(fs % msg)
+ except UnicodeError:
+ stream.write(fs % msg.encode("UTF-8"))
+ self.flush()
+ except (KeyboardInterrupt, SystemExit):
+ raise
+ except:
+ self.handleError(record)
+
+class FileHandler(StreamHandler):
+ """
+ A handler class which writes formatted logging records to disk files.
+ """
+ def __init__(self, filename, mode='a', encoding=None, delay=0):
+ """
+ Open the specified file and use it as the stream for logging.
+ """
+ #keep the absolute path, otherwise derived classes which use this
+ #may come a cropper when the current directory changes
+ if codecs is None:
+ encoding = None
+ self.baseFilename = os.path.abspath(filename)
+ self.mode = mode
+ self.encoding = encoding
+ if delay:
+ #We don't open the stream, but we still need to call the
+ #Handler constructor to set level, formatter, lock etc.
+ Handler.__init__(self)
+ self.stream = None
+ else:
+ StreamHandler.__init__(self, self._open())
+
+ def close(self):
+ """
+ Closes the stream.
+ """
+ self.acquire()
+ try:
+ if self.stream:
+ self.flush()
+ if hasattr(self.stream, "close"):
+ self.stream.close()
+ StreamHandler.close(self)
+ self.stream = None
+ finally:
+ self.release()
+
+ def _open(self):
+ """
+ Open the current base file with the (original) mode and encoding.
+ Return the resulting stream.
+ """
+ if self.encoding is None:
+ stream = open(self.baseFilename, self.mode)
+ else:
+ stream = codecs.open(self.baseFilename, self.mode, self.encoding)
+ return stream
+
+ def emit(self, record):
+ """
+ Emit a record.
+
+ If the stream was not opened because 'delay' was specified in the
+ constructor, open it before calling the superclass's emit.
+ """
+ if self.stream is None:
+ self.stream = self._open()
+ StreamHandler.emit(self, record)
+
+#---------------------------------------------------------------------------
+# Manager classes and functions
+#---------------------------------------------------------------------------
+
+class PlaceHolder(object):
+ """
+ PlaceHolder instances are used in the Manager logger hierarchy to take
+ the place of nodes for which no loggers have been defined. This class is
+ intended for internal use only and not as part of the public API.
+ """
+ def __init__(self, alogger):
+ """
+ Initialize with the specified logger being a child of this placeholder.
+ """
+ #self.loggers = [alogger]
+ self.loggerMap = { alogger : None }
+
+ def append(self, alogger):
+ """
+ Add the specified logger as a child of this placeholder.
+ """
+ #if alogger not in self.loggers:
+ if alogger not in self.loggerMap:
+ #self.loggers.append(alogger)
+ self.loggerMap[alogger] = None
+
+#
+# Determine which class to use when instantiating loggers.
+#
+_loggerClass = None
+
+def setLoggerClass(klass):
+ """
+ Set the class to be used when instantiating a logger. The class should
+ define __init__() such that only a name argument is required, and the
+ __init__() should call Logger.__init__()
+ """
+ if klass != Logger:
+ if not issubclass(klass, Logger):
+ raise TypeError("logger not derived from logging.Logger: "
+ + klass.__name__)
+ global _loggerClass
+ _loggerClass = klass
+
+def getLoggerClass():
+ """
+ Return the class to be used when instantiating a logger.
+ """
+
+ return _loggerClass
+
+class Manager(object):
+ """
+ There is [under normal circumstances] just one Manager instance, which
+ holds the hierarchy of loggers.
+ """
+ def __init__(self, rootnode):
+ """
+ Initialize the manager with the root node of the logger hierarchy.
+ """
+ self.root = rootnode
+ self.disable = 0
+ self.emittedNoHandlerWarning = 0
+ self.loggerDict = {}
+ self.loggerClass = None
+
+ def getLogger(self, name):
+ """
+ Get a logger with the specified name (channel name), creating it
+ if it doesn't yet exist. This name is a dot-separated hierarchical
+ name, such as "a", "a.b", "a.b.c" or similar.
+
+ If a PlaceHolder existed for the specified name [i.e. the logger
+ didn't exist but a child of it did], replace it with the created
+ logger and fix up the parent/child references which pointed to the
+ placeholder to now point to the logger.
+ """
+ rv = None
+ if not isinstance(name, basestring):
+ raise TypeError('A logger name must be string or Unicode')
+ if isinstance(name, unicode):
+ name = name.encode('utf-8')
+ _acquireLock()
+ try:
+ if name in self.loggerDict:
+ rv = self.loggerDict[name]
+ if isinstance(rv, PlaceHolder):
+ ph = rv
+ rv = (self.loggerClass or _loggerClass)(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupChildren(ph, rv)
+ self._fixupParents(rv)
+ else:
+ rv = (self.loggerClass or _loggerClass)(name)
+ rv.manager = self
+ self.loggerDict[name] = rv
+ self._fixupParents(rv)
+ finally:
+ _releaseLock()
+ return rv
+
+ def setLoggerClass(self, klass):
+ """
+ Set the class to be used when instantiating a logger with this Manager.
+ """
+ if klass != Logger:
+ if not issubclass(klass, Logger):
+ raise TypeError("logger not derived from logging.Logger: "
+ + klass.__name__)
+ self.loggerClass = klass
+
+ def _fixupParents(self, alogger):
+ """
+ Ensure that there are either loggers or placeholders all the way
+ from the specified logger to the root of the logger hierarchy.
+ """
+ name = alogger.name
+ i = name.rfind(".")
+ rv = None
+ while (i > 0) and not rv:
+ substr = name[:i]
+ if substr not in self.loggerDict:
+ self.loggerDict[substr] = PlaceHolder(alogger)
+ else:
+ obj = self.loggerDict[substr]
+ if isinstance(obj, Logger):
+ rv = obj
+ else:
+ assert isinstance(obj, PlaceHolder)
+ obj.append(alogger)
+ i = name.rfind(".", 0, i - 1)
+ if not rv:
+ rv = self.root
+ alogger.parent = rv
+
+ def _fixupChildren(self, ph, alogger):
+ """
+ Ensure that children of the placeholder ph are connected to the
+ specified logger.
+ """
+ name = alogger.name
+ namelen = len(name)
+ for c in ph.loggerMap.keys():
+ #The if means ... if not c.parent.name.startswith(nm)
+ if c.parent.name[:namelen] != name:
+ alogger.parent = c.parent
+ c.parent = alogger
+
+#---------------------------------------------------------------------------
+# Logger classes and functions
+#---------------------------------------------------------------------------
+
+class Logger(Filterer):
+ """
+ Instances of the Logger class represent a single logging channel. A
+ "logging channel" indicates an area of an application. Exactly how an
+ "area" is defined is up to the application developer. Since an
+ application can have any number of areas, logging channels are identified
+ by a unique string. Application areas can be nested (e.g. an area
+ of "input processing" might include sub-areas "read CSV files", "read
+ XLS files" and "read Gnumeric files"). To cater for this natural nesting,
+ channel names are organized into a namespace hierarchy where levels are
+ separated by periods, much like the Java or Python package namespace. So
+ in the instance given above, channel names might be "input" for the upper
+ level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
+ There is no arbitrary limit to the depth of nesting.
+ """
+ def __init__(self, name, level=NOTSET):
+ """
+ Initialize the logger with a name and an optional level.
+ """
+ Filterer.__init__(self)
+ self.name = name
+ self.level = _checkLevel(level)
+ self.parent = None
+ self.propagate = 1
+ self.handlers = []
+ self.disabled = 0
+
+ def setLevel(self, level):
+ """
+ Set the logging level of this logger.
+ """
+ self.level = _checkLevel(level)
+
+ def debug(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'DEBUG'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
+ """
+ if self.isEnabledFor(DEBUG):
+ self._log(DEBUG, msg, args, **kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'INFO'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
+ """
+ if self.isEnabledFor(INFO):
+ self._log(INFO, msg, args, **kwargs)
+
+ def warning(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'WARNING'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1)
+ """
+ if self.isEnabledFor(WARNING):
+ self._log(WARNING, msg, args, **kwargs)
+
+ warn = warning
+
+ def error(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'ERROR'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.error("Houston, we have a %s", "major problem", exc_info=1)
+ """
+ if self.isEnabledFor(ERROR):
+ self._log(ERROR, msg, args, **kwargs)
+
+ def exception(self, msg, *args, **kwargs):
+ """
+ Convenience method for logging an ERROR with exception information.
+ """
+ kwargs['exc_info'] = 1
+ self.error(msg, *args, **kwargs)
+
+ def critical(self, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with severity 'CRITICAL'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.critical("Houston, we have a %s", "major disaster", exc_info=1)
+ """
+ if self.isEnabledFor(CRITICAL):
+ self._log(CRITICAL, msg, args, **kwargs)
+
+ fatal = critical
+
+ def log(self, level, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with the integer severity 'level'.
+
+ To pass exception information, use the keyword argument exc_info with
+ a true value, e.g.
+
+ logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
+ """
+ if not isinstance(level, int):
+ if raiseExceptions:
+ raise TypeError("level must be an integer")
+ else:
+ return
+ if self.isEnabledFor(level):
+ self._log(level, msg, args, **kwargs)
+
+ def findCaller(self):
+ """
+ Find the stack frame of the caller so that we can note the source
+ file name, line number and function name.
+ """
+ f = currentframe()
+ #On some versions of IronPython, currentframe() returns None if
+ #IronPython isn't run with -X:Frames.
+ if f is not None:
+ f = f.f_back
+ rv = "(unknown file)", 0, "(unknown function)"
+ while hasattr(f, "f_code"):
+ co = f.f_code
+ filename = os.path.normcase(co.co_filename)
+ if filename == _srcfile:
+ f = f.f_back
+ continue
+ rv = (co.co_filename, f.f_lineno, co.co_name)
+ break
+ return rv
+
+ def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
+ """
+ A factory method which can be overridden in subclasses to create
+ specialized LogRecords.
+ """
+ rv = LogRecord(name, level, fn, lno, msg, args, exc_info, func)
+ if extra is not None:
+ for key in extra:
+ if (key in ["message", "asctime"]) or (key in rv.__dict__):
+ raise KeyError("Attempt to overwrite %r in LogRecord" % key)
+ rv.__dict__[key] = extra[key]
+ return rv
+
+ def _log(self, level, msg, args, exc_info=None, extra=None):
+ """
+ Low-level logging routine which creates a LogRecord and then calls
+ all the handlers of this logger to handle the record.
+ """
+ if _srcfile:
+ #IronPython doesn't track Python frames, so findCaller raises an
+ #exception on some versions of IronPython. We trap it here so that
+ #IronPython can use logging.
+ try:
+ fn, lno, func = self.findCaller()
+ except ValueError:
+ fn, lno, func = "(unknown file)", 0, "(unknown function)"
+ else:
+ fn, lno, func = "(unknown file)", 0, "(unknown function)"
+ if exc_info:
+ if not isinstance(exc_info, tuple):
+ exc_info = sys.exc_info()
+ record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
+ self.handle(record)
+
+ def handle(self, record):
+ """
+ Call the handlers for the specified record.
+
+ This method is used for unpickled records received from a socket, as
+ well as those created locally. Logger-level filtering is applied.
+ """
+ if (not self.disabled) and self.filter(record):
+ self.callHandlers(record)
+
+ def addHandler(self, hdlr):
+ """
+ Add the specified handler to this logger.
+ """
+ _acquireLock()
+ try:
+ if not (hdlr in self.handlers):
+ self.handlers.append(hdlr)
+ finally:
+ _releaseLock()
+
+ def removeHandler(self, hdlr):
+ """
+ Remove the specified handler from this logger.
+ """
+ _acquireLock()
+ try:
+ if hdlr in self.handlers:
+ self.handlers.remove(hdlr)
+ finally:
+ _releaseLock()
+
+ def callHandlers(self, record):
+ """
+ Pass a record to all relevant handlers.
+
+ Loop through all handlers for this logger and its parents in the
+ logger hierarchy. If no handler was found, output a one-off error
+ message to sys.stderr. Stop searching up the hierarchy whenever a
+ logger with the "propagate" attribute set to zero is found - that
+ will be the last logger whose handlers are called.
+ """
+ c = self
+ found = 0
+ while c:
+ for hdlr in c.handlers:
+ found = found + 1
+ if record.levelno >= hdlr.level:
+ hdlr.handle(record)
+ if not c.propagate:
+ c = None #break out
+ else:
+ c = c.parent
+ if (found == 0) and raiseExceptions and not self.manager.emittedNoHandlerWarning:
+ sys.stderr.write("No handlers could be found for logger"
+ " \"%s\"\n" % self.name)
+ self.manager.emittedNoHandlerWarning = 1
+
+ def getEffectiveLevel(self):
+ """
+ Get the effective level for this logger.
+
+ Loop through this logger and its parents in the logger hierarchy,
+ looking for a non-zero logging level. Return the first one found.
+ """
+ logger = self
+ while logger:
+ if logger.level:
+ return logger.level
+ logger = logger.parent
+ return NOTSET
+
+ def isEnabledFor(self, level):
+ """
+ Is this logger enabled for level 'level'?
+ """
+ if self.manager.disable >= level:
+ return 0
+ return level >= self.getEffectiveLevel()
+
+ def getChild(self, suffix):
+ """
+ Get a logger which is a descendant to this one.
+
+ This is a convenience method, such that
+
+ logging.getLogger('abc').getChild('def.ghi')
+
+ is the same as
+
+ logging.getLogger('abc.def.ghi')
+
+ It's useful, for example, when the parent logger is named using
+ __name__ rather than a literal string.
+ """
+ if self.root is not self:
+ suffix = '.'.join((self.name, suffix))
+ return self.manager.getLogger(suffix)
+
+class RootLogger(Logger):
+ """
+ A root logger is not that different to any other logger, except that
+ it must have a logging level and there is only one instance of it in
+ the hierarchy.
+ """
+ def __init__(self, level):
+ """
+ Initialize the logger with the name "root".
+ """
+ Logger.__init__(self, "root", level)
+
+_loggerClass = Logger
+
+class LoggerAdapter(object):
+ """
+ An adapter for loggers which makes it easier to specify contextual
+ information in logging output.
+ """
+
+ def __init__(self, logger, extra):
+ """
+ Initialize the adapter with a logger and a dict-like object which
+ provides contextual information. This constructor signature allows
+ easy stacking of LoggerAdapters, if so desired.
+
+ You can effectively pass keyword arguments as shown in the
+ following example:
+
+ adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
+ """
+ self.logger = logger
+ self.extra = extra
+
+ def process(self, msg, kwargs):
+ """
+ Process the logging message and keyword arguments passed in to
+ a logging call to insert contextual information. You can either
+ manipulate the message itself, the keyword args or both. Return
+ the message and kwargs modified (or not) to suit your needs.
+
+ Normally, you'll only need to override this one method in a
+ LoggerAdapter subclass for your specific needs.
+ """
+ kwargs["extra"] = self.extra
+ return msg, kwargs
+
+ def debug(self, msg, *args, **kwargs):
+ """
+ Delegate a debug call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.debug(msg, *args, **kwargs)
+
+ def info(self, msg, *args, **kwargs):
+ """
+ Delegate an info call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.info(msg, *args, **kwargs)
+
+ def warning(self, msg, *args, **kwargs):
+ """
+ Delegate a warning call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.warning(msg, *args, **kwargs)
+
+ def error(self, msg, *args, **kwargs):
+ """
+ Delegate an error call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.error(msg, *args, **kwargs)
+
+ def exception(self, msg, *args, **kwargs):
+ """
+ Delegate an exception call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ kwargs["exc_info"] = 1
+ self.logger.error(msg, *args, **kwargs)
+
+ def critical(self, msg, *args, **kwargs):
+ """
+ Delegate a critical call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.critical(msg, *args, **kwargs)
+
+ def log(self, level, msg, *args, **kwargs):
+ """
+ Delegate a log call to the underlying logger, after adding
+ contextual information from this adapter instance.
+ """
+ msg, kwargs = self.process(msg, kwargs)
+ self.logger.log(level, msg, *args, **kwargs)
+
+ def isEnabledFor(self, level):
+ """
+ See if the underlying logger is enabled for the specified level.
+ """
+ return self.logger.isEnabledFor(level)
+
+root = RootLogger(WARNING)
+Logger.root = root
+Logger.manager = Manager(Logger.root)
+
+#---------------------------------------------------------------------------
+# Configuration classes and functions
+#---------------------------------------------------------------------------
+
+BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s"
+
+def basicConfig(**kwargs):
+ """
+ Do basic configuration for the logging system.
+
+ This function does nothing if the root logger already has handlers
+ configured. It is a convenience method intended for use by simple scripts
+ to do one-shot configuration of the logging package.
+
+ The default behaviour is to create a StreamHandler which writes to
+ sys.stderr, set a formatter using the BASIC_FORMAT format string, and
+ add the handler to the root logger.
+
+ A number of optional keyword arguments may be specified, which can alter
+ the default behaviour.
+
+ filename Specifies that a FileHandler be created, using the specified
+ filename, rather than a StreamHandler.
+ filemode Specifies the mode to open the file, if filename is specified
+ (if filemode is unspecified, it defaults to 'a').
+ format Use the specified format string for the handler.
+ datefmt Use the specified date/time format.
+ level Set the root logger level to the specified level.
+ stream Use the specified stream to initialize the StreamHandler. Note
+ that this argument is incompatible with 'filename' - if both
+ are present, 'stream' is ignored.
+
+ Note that you could specify a stream created using open(filename, mode)
+ rather than passing the filename and mode in. However, it should be
+ remembered that StreamHandler does not close its stream (since it may be
+ using sys.stdout or sys.stderr), whereas FileHandler closes its stream
+ when the handler is closed.
+ """
+ # Add thread safety in case someone mistakenly calls
+ # basicConfig() from multiple threads
+ _acquireLock()
+ try:
+ if len(root.handlers) == 0:
+ filename = kwargs.get("filename")
+ if filename:
+ mode = kwargs.get("filemode", 'a')
+ hdlr = FileHandler(filename, mode)
+ else:
+ stream = kwargs.get("stream")
+ hdlr = StreamHandler(stream)
+ fs = kwargs.get("format", BASIC_FORMAT)
+ dfs = kwargs.get("datefmt", None)
+ fmt = Formatter(fs, dfs)
+ hdlr.setFormatter(fmt)
+ root.addHandler(hdlr)
+ level = kwargs.get("level")
+ if level is not None:
+ root.setLevel(level)
+ finally:
+ _releaseLock()
+
+#---------------------------------------------------------------------------
+# Utility functions at module level.
+# Basically delegate everything to the root logger.
+#---------------------------------------------------------------------------
+
+def getLogger(name=None):
+ """
+ Return a logger with the specified name, creating it if necessary.
+
+ If no name is specified, return the root logger.
+ """
+ if name:
+ return Logger.manager.getLogger(name)
+ else:
+ return root
+
+#def getRootLogger():
+# """
+# Return the root logger.
+#
+# Note that getLogger('') now does the same thing, so this function is
+# deprecated and may disappear in the future.
+# """
+# return root
+
+def critical(msg, *args, **kwargs):
+ """
+ Log a message with severity 'CRITICAL' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.critical(msg, *args, **kwargs)
+
+fatal = critical
+
+def error(msg, *args, **kwargs):
+ """
+ Log a message with severity 'ERROR' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.error(msg, *args, **kwargs)
+
+def exception(msg, *args, **kwargs):
+ """
+ Log a message with severity 'ERROR' on the root logger,
+ with exception information.
+ """
+ kwargs['exc_info'] = 1
+ error(msg, *args, **kwargs)
+
+def warning(msg, *args, **kwargs):
+ """
+ Log a message with severity 'WARNING' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.warning(msg, *args, **kwargs)
+
+warn = warning
+
+def info(msg, *args, **kwargs):
+ """
+ Log a message with severity 'INFO' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.info(msg, *args, **kwargs)
+
+def debug(msg, *args, **kwargs):
+ """
+ Log a message with severity 'DEBUG' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.debug(msg, *args, **kwargs)
+
+def log(level, msg, *args, **kwargs):
+ """
+ Log 'msg % args' with the integer severity 'level' on the root logger.
+ """
+ if len(root.handlers) == 0:
+ basicConfig()
+ root.log(level, msg, *args, **kwargs)
+
+def disable(level):
+ """
+ Disable all logging calls of severity 'level' and below.
+ """
+ root.manager.disable = level
+
+def shutdown(handlerList=_handlerList):
+ """
+ Perform any cleanup actions in the logging system (e.g. flushing
+ buffers).
+
+ Should be called at application exit.
+ """
+ for wr in reversed(handlerList[:]):
+ #errors might occur, for example, if files are locked
+ #we just ignore them if raiseExceptions is not set
+ try:
+ h = wr()
+ if h:
+ try:
+ h.acquire()
+ h.flush()
+ h.close()
+ except (IOError, ValueError):
+ # Ignore errors which might be caused
+ # because handlers have been closed but
+ # references to them are still around at
+ # application exit.
+ pass
+ finally:
+ h.release()
+ except:
+ if raiseExceptions:
+ raise
+ #else, swallow
+
+#Let's try and shutdown automatically on application exit...
+import atexit
+atexit.register(shutdown)
+
+# Null handler
+
+class NullHandler(Handler):
+ """
+ This handler does nothing. It's intended to be used to avoid the
+ "No handlers could be found for logger XXX" one-off warning. This is
+ important for library code, which may contain code to log events. If a user
+ of the library does not configure logging, the one-off warning might be
+ produced; to avoid this, the library developer simply needs to instantiate
+ a NullHandler and add it to the top-level logger of the library module or
+ package.
+ """
+ def handle(self, record):
+ pass
+
+ def emit(self, record):
+ pass
+
+ def createLock(self):
+ self.lock = None
+
+# Warnings integration
+
+_warnings_showwarning = None
+
+def _showwarning(message, category, filename, lineno, file=None, line=None):
+ """
+ Implementation of showwarnings which redirects to logging, which will first
+ check to see if the file parameter is None. If a file is specified, it will
+ delegate to the original warnings implementation of showwarning. Otherwise,
+ it will call warnings.formatwarning and will log the resulting string to a
+ warnings logger named "py.warnings" with level logging.WARNING.
+ """
+ if file is not None:
+ if _warnings_showwarning is not None:
+ _warnings_showwarning(message, category, filename, lineno, file, line)
+ else:
+ s = warnings.formatwarning(message, category, filename, lineno, line)
+ logger = getLogger("py.warnings")
+ if not logger.handlers:
+ logger.addHandler(NullHandler())
+ logger.warning("%s", s)
+
+def captureWarnings(capture):
+ """
+ If capture is true, redirect all warnings to the logging package.
+ If capture is False, ensure that warnings are not redirected to logging
+ but to their original destinations.
+ """
+ global _warnings_showwarning
+ if capture:
+ if _warnings_showwarning is None:
+ _warnings_showwarning = warnings.showwarning
+ warnings.showwarning = _showwarning
+ else:
+ if _warnings_showwarning is not None:
+ warnings.showwarning = _warnings_showwarning
+ _warnings_showwarning = None
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -1879,7 +1879,8 @@
class HandlerTest(BaseTest):
- @unittest.skipIf(os.name in ('java', 'nt'), 'WatchedFileHandler not appropriate for Jython or Windows.')
+ @unittest.skipIf(os.name == 'nt' or (os.name == 'java' and os._name == 'nt'),
+ 'WatchedFileHandler not appropriate for Windows.')
@unittest.skipUnless(threading, 'Threading required for this test.')
def test_race(self):
# Issue #14632 refers.
diff --git a/Lib/test/test_logging_jy.py b/Lib/test/test_logging_jy.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_logging_jy.py
@@ -0,0 +1,28 @@
+import logging
+import unittest
+from test.test_support import run_with_locale, run_unittest
+from test.test_logging import BaseTest
+
+
+class FileNameTest(BaseTest):
+
+ log_format = "%(filename)s %(funcName)s %(name)s -> %(levelname)s: %(message)s"
+ expected_log_pat = r"^([\w.]+) ([\w.]+) ([\w.]+) -> ([\w.]+): ([\d]+)$"
+ # test_logging_jy.py test_filename_is_set root -> ERROR: 47
+ message_num = 0
+
+ def test_filename_is_set(self):
+ # http://bugs.jython.org/issue1760
+ log = self.root_logger
+ log.error("47")
+ self.assert_log_lines([
+ ("test_logging_jy.py", "test_filename_is_set", "root", "ERROR", "47")])
+
+
+ at run_with_locale('LC_ALL', '')
+def test_main():
+ run_unittest(FileNameTest,)
+
+
+if __name__ == "__main__":
+ test_main()
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 3 03:05:58 2015
From: jython-checkins at python.org (jim.baker)
Date: Tue, 03 Feb 2015 02:05:58 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_repr_of_stat_result_for_os?=
=?utf-8?q?=2Estat=2C_etc=2E=2C_should_use_dynamic_type_name?=
Message-ID: <20150203020557.96072.84137@psf.io>
https://hg.python.org/jython/rev/7b89227f4846
changeset: 7566:7b89227f4846
user: Jim Baker
date: Mon Feb 02 19:05:53 2015 -0700
summary:
repr of stat result for os.stat, etc., should use dynamic type name
files:
src/org/python/modules/posix/PyStatResult.java | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/org/python/modules/posix/PyStatResult.java b/src/org/python/modules/posix/PyStatResult.java
--- a/src/org/python/modules/posix/PyStatResult.java
+++ b/src/org/python/modules/posix/PyStatResult.java
@@ -237,7 +237,7 @@
@Override
public PyString __repr__() {
return (PyString) Py.newString(
- "posix.stat_result(" +
+ TYPE.fastGetName() + "(" +
"st_mode=%r, st_ino=%r, st_dev=%r, st_nlink=%r, st_uid=%r, "+
"st_gid=%r, st_size=%r, st_atime=%r, st_mtime=%r, st_ctime=%r)").__mod__(this);
}
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 3 06:34:03 2015
From: jython-checkins at python.org (jim.baker)
Date: Tue, 03 Feb 2015 05:34:03 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Ignore_IOException_if_attem?=
=?utf-8?q?pting_to_truncate_file_for_write?=
Message-ID: <20150203053348.25871.60893@psf.io>
https://hg.python.org/jython/rev/31b98240dcf1
changeset: 7567:31b98240dcf1
user: Jim Baker
date: Mon Feb 02 22:33:45 2015 -0700
summary:
Ignore IOException if attempting to truncate file for write
Opening files for write will call FileChannel#truncate(0). For special
files like /dev/null, such calls raise IOException, which is not
specific. Attempting to distinguish with the exception message causes
internationalization issues (http://bugs.jython.org/issue1944), so
simply suppress. This should just work, since other more detailed
exceptions are raised for cases like attempting to write to say
/dev/cannot-write-here
files:
src/org/python/core/io/FileIO.java | 10 ++++------
1 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/org/python/core/io/FileIO.java b/src/org/python/core/io/FileIO.java
--- a/src/org/python/core/io/FileIO.java
+++ b/src/org/python/core/io/FileIO.java
@@ -216,12 +216,10 @@
// ERROR_INVALID_HANDLE on ttys. Identifying those by the IOException
// message is tedious as their messages are localized, so we suppress them
// all =[
- if (Platform.IS_WINDOWS ||
- ((Platform.IS_SOLARIS || Platform.IS_LINUX)
- && Errno.EINVAL.description().equals(ioe.getMessage()))) {
- return;
- }
- throw Py.IOError(ioe);
+ //
+ // Unfortunately attempting to distinguish by localized messages is too hard.
+ // Give up and swallow the exception.
+ // See http://bugs.jython.org/issue1944
}
}
}
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 3 06:57:24 2015
From: jython-checkins at python.org (jim.baker)
Date: Tue, 03 Feb 2015 05:57:24 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Verify_fix_for_http=3A//bug?=
=?utf-8?q?s=2Ejython=2Eorg/issue1929?=
Message-ID: <20150203055715.25855.60319@psf.io>
https://hg.python.org/jython/rev/bd4f7717a5d5
changeset: 7568:bd4f7717a5d5
user: Jim Baker
date: Mon Feb 02 22:55:14 2015 -0700
summary:
Verify fix for http://bugs.jython.org/issue1929
files:
Lib/test/test_class_jy.py | 45 ++++++++++++++++++++++++++-
1 files changed, 44 insertions(+), 1 deletions(-)
diff --git a/Lib/test/test_class_jy.py b/Lib/test/test_class_jy.py
--- a/Lib/test/test_class_jy.py
+++ b/Lib/test/test_class_jy.py
@@ -387,6 +387,48 @@
self.assertEqual(str(Bar), 'foo')
+class LenTestCase(unittest.TestCase):
+ """__len__ of new-style classes should raise OverflowError if length is too long"""
+
+ # Verifies fix for http://bugs.jython.org/issue1929
+
+ def test_len(self):
+ class C(object):
+ def __len__(self):
+ return 2 ** 70
+ with self.assertRaises(OverflowError) as cm:
+ len(C())
+ self.assertEqual(str(cm.exception), "long int too large to convert to int")
+
+ class D(object):
+ def __len__(self):
+ return "foo"
+ with self.assertRaises(TypeError) as cm:
+ len(D())
+ self.assertEqual(str(cm.exception), "an integer is required")
+
+ def test_len_faux_int(self):
+ class C(object):
+ def __len__(self):
+ class FauxInt(object):
+ def __int__(self):
+ return 2 ** 70
+ return FauxInt()
+ with self.assertRaises(OverflowError) as cm:
+ len(C())
+ self.assertEqual(str(cm.exception), "long int too large to convert to int")
+
+ def test_len_derived_int(self):
+ class C(object):
+ def __len__(self):
+ class MyInt(int):
+ pass
+ return MyInt(2 ** 70)
+ with self.assertRaises(OverflowError) as cm:
+ len(C())
+ self.assertEqual(str(cm.exception), "long int too large to convert to int")
+
+
def test_main():
test_support.run_unittest(
ClassGeneralTestCase,
@@ -396,7 +438,8 @@
IsDescendentTestCase,
JavaClassNamingTestCase,
ClassDefinesDunderModule,
- ClassMetaclassRepr)
+ ClassMetaclassRepr,
+ LenTestCase)
if __name__ == "__main__":
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 3 06:57:24 2015
From: jython-checkins at python.org (jim.baker)
Date: Tue, 03 Feb 2015 05:57:24 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Throw_OverflowError_if_=5F?=
=?utf-8?q?=5Flen=5F=5F_returns_a_value_that_is_too_large?=
Message-ID: <20150203055715.106357.24256@psf.io>
https://hg.python.org/jython/rev/41d5032ef380
changeset: 7569:41d5032ef380
user: Indra Talip
date: Mon Feb 02 22:57:01 2015 -0700
summary:
Throw OverflowError if __len__ returns a value that is too large
Fixes http://bugs.jython.org/issue1929
files:
src/org/python/antlr/ast/AssertDerived.java | 4 +---
src/org/python/antlr/ast/AssignDerived.java | 4 +---
src/org/python/antlr/ast/AttributeDerived.java | 4 +---
src/org/python/antlr/ast/AugAssignDerived.java | 4 +---
src/org/python/antlr/ast/BinOpDerived.java | 4 +---
src/org/python/antlr/ast/BoolOpDerived.java | 4 +---
src/org/python/antlr/ast/BreakDerived.java | 4 +---
src/org/python/antlr/ast/CallDerived.java | 4 +---
src/org/python/antlr/ast/ClassDefDerived.java | 4 +---
src/org/python/antlr/ast/CompareDerived.java | 4 +---
src/org/python/antlr/ast/ContinueDerived.java | 4 +---
src/org/python/antlr/ast/DeleteDerived.java | 4 +---
src/org/python/antlr/ast/DictDerived.java | 4 +---
src/org/python/antlr/ast/EllipsisDerived.java | 4 +---
src/org/python/antlr/ast/ExceptHandlerDerived.java | 4 +---
src/org/python/antlr/ast/ExecDerived.java | 4 +---
src/org/python/antlr/ast/ExprDerived.java | 4 +---
src/org/python/antlr/ast/ExpressionDerived.java | 4 +---
src/org/python/antlr/ast/ExtSliceDerived.java | 4 +---
src/org/python/antlr/ast/ForDerived.java | 4 +---
src/org/python/antlr/ast/FunctionDefDerived.java | 4 +---
src/org/python/antlr/ast/GeneratorExpDerived.java | 4 +---
src/org/python/antlr/ast/GlobalDerived.java | 4 +---
src/org/python/antlr/ast/IfDerived.java | 4 +---
src/org/python/antlr/ast/IfExpDerived.java | 4 +---
src/org/python/antlr/ast/ImportDerived.java | 4 +---
src/org/python/antlr/ast/ImportFromDerived.java | 4 +---
src/org/python/antlr/ast/IndexDerived.java | 4 +---
src/org/python/antlr/ast/InteractiveDerived.java | 4 +---
src/org/python/antlr/ast/LambdaDerived.java | 4 +---
src/org/python/antlr/ast/ListCompDerived.java | 4 +---
src/org/python/antlr/ast/ListDerived.java | 4 +---
src/org/python/antlr/ast/ModuleDerived.java | 4 +---
src/org/python/antlr/ast/NameDerived.java | 4 +---
src/org/python/antlr/ast/NumDerived.java | 4 +---
src/org/python/antlr/ast/PassDerived.java | 4 +---
src/org/python/antlr/ast/PrintDerived.java | 4 +---
src/org/python/antlr/ast/RaiseDerived.java | 4 +---
src/org/python/antlr/ast/ReprDerived.java | 4 +---
src/org/python/antlr/ast/ReturnDerived.java | 4 +---
src/org/python/antlr/ast/SliceDerived.java | 4 +---
src/org/python/antlr/ast/StrDerived.java | 4 +---
src/org/python/antlr/ast/SubscriptDerived.java | 4 +---
src/org/python/antlr/ast/SuiteDerived.java | 4 +---
src/org/python/antlr/ast/TryExceptDerived.java | 4 +---
src/org/python/antlr/ast/TryFinallyDerived.java | 4 +---
src/org/python/antlr/ast/TupleDerived.java | 4 +---
src/org/python/antlr/ast/UnaryOpDerived.java | 4 +---
src/org/python/antlr/ast/WhileDerived.java | 4 +---
src/org/python/antlr/ast/WithDerived.java | 4 +---
src/org/python/antlr/ast/YieldDerived.java | 4 +---
src/org/python/antlr/ast/aliasDerived.java | 4 +---
src/org/python/antlr/ast/argumentsDerived.java | 4 +---
src/org/python/antlr/ast/comprehensionDerived.java | 4 +---
src/org/python/antlr/ast/keywordDerived.java | 4 +---
src/org/python/antlr/op/AddDerived.java | 4 +---
src/org/python/antlr/op/AndDerived.java | 4 +---
src/org/python/antlr/op/AugLoadDerived.java | 4 +---
src/org/python/antlr/op/AugStoreDerived.java | 4 +---
src/org/python/antlr/op/BitAndDerived.java | 4 +---
src/org/python/antlr/op/BitOrDerived.java | 4 +---
src/org/python/antlr/op/BitXorDerived.java | 4 +---
src/org/python/antlr/op/DelDerived.java | 4 +---
src/org/python/antlr/op/DivDerived.java | 4 +---
src/org/python/antlr/op/EqDerived.java | 4 +---
src/org/python/antlr/op/FloorDivDerived.java | 4 +---
src/org/python/antlr/op/GtDerived.java | 4 +---
src/org/python/antlr/op/GtEDerived.java | 4 +---
src/org/python/antlr/op/InDerived.java | 4 +---
src/org/python/antlr/op/InvertDerived.java | 4 +---
src/org/python/antlr/op/IsDerived.java | 4 +---
src/org/python/antlr/op/IsNotDerived.java | 4 +---
src/org/python/antlr/op/LShiftDerived.java | 4 +---
src/org/python/antlr/op/LoadDerived.java | 4 +---
src/org/python/antlr/op/LtDerived.java | 4 +---
src/org/python/antlr/op/LtEDerived.java | 4 +---
src/org/python/antlr/op/ModDerived.java | 4 +---
src/org/python/antlr/op/MultDerived.java | 4 +---
src/org/python/antlr/op/NotDerived.java | 4 +---
src/org/python/antlr/op/NotEqDerived.java | 4 +---
src/org/python/antlr/op/NotInDerived.java | 4 +---
src/org/python/antlr/op/OrDerived.java | 4 +---
src/org/python/antlr/op/ParamDerived.java | 4 +---
src/org/python/antlr/op/PowDerived.java | 4 +---
src/org/python/antlr/op/RShiftDerived.java | 4 +---
src/org/python/antlr/op/StoreDerived.java | 4 +---
src/org/python/antlr/op/SubDerived.java | 4 +---
src/org/python/antlr/op/UAddDerived.java | 4 +---
src/org/python/antlr/op/USubDerived.java | 4 +---
src/org/python/core/ClasspathPyImporterDerived.java | 4 +---
src/org/python/core/PyArrayDerived.java | 4 +---
src/org/python/core/PyBaseExceptionDerived.java | 4 +---
src/org/python/core/PyByteArrayDerived.java | 4 +---
src/org/python/core/PyClassMethodDerived.java | 4 +---
src/org/python/core/PyComplexDerived.java | 4 +---
src/org/python/core/PyDictionaryDerived.java | 4 +---
src/org/python/core/PyEnumerateDerived.java | 4 +---
src/org/python/core/PyFileDerived.java | 4 +---
src/org/python/core/PyFloatDerived.java | 4 +---
src/org/python/core/PyFrozenSetDerived.java | 4 +---
src/org/python/core/PyIntegerDerived.java | 4 +---
src/org/python/core/PyListDerived.java | 4 +---
src/org/python/core/PyLongDerived.java | 4 +---
src/org/python/core/PyModuleDerived.java | 4 +---
src/org/python/core/PyObjectDerived.java | 4 +---
src/org/python/core/PyPropertyDerived.java | 4 +---
src/org/python/core/PySetDerived.java | 4 +---
src/org/python/core/PyStringDerived.java | 4 +---
src/org/python/core/PySuperDerived.java | 4 +---
src/org/python/core/PyTupleDerived.java | 4 +---
src/org/python/core/PyTypeDerived.java | 4 +---
src/org/python/core/PyUnicodeDerived.java | 4 +---
src/org/python/modules/PyStructDerived.java | 4 +---
src/org/python/modules/_collections/PyDefaultDictDerived.java | 4 +---
src/org/python/modules/_collections/PyDequeDerived.java | 4 +---
src/org/python/modules/_csv/PyDialectDerived.java | 4 +---
src/org/python/modules/_functools/PyPartialDerived.java | 4 +---
src/org/python/modules/_io/PyFileIODerived.java | 4 +---
src/org/python/modules/_io/PyIOBaseDerived.java | 4 +---
src/org/python/modules/_io/PyRawIOBaseDerived.java | 4 +---
src/org/python/modules/_weakref/ReferenceTypeDerived.java | 4 +---
src/org/python/modules/bz2/PyBZ2CompressorDerived.java | 4 +---
src/org/python/modules/bz2/PyBZ2DecompressorDerived.java | 4 +---
src/org/python/modules/bz2/PyBZ2FileDerived.java | 4 +---
src/org/python/modules/itertools/PyTeeIteratorDerived.java | 4 +---
src/org/python/modules/itertools/chainDerived.java | 4 +---
src/org/python/modules/itertools/combinationsDerived.java | 4 +---
src/org/python/modules/itertools/combinationsWithReplacementDerived.java | 4 +---
src/org/python/modules/itertools/compressDerived.java | 4 +---
src/org/python/modules/itertools/countDerived.java | 4 +---
src/org/python/modules/itertools/cycleDerived.java | 4 +---
src/org/python/modules/itertools/dropwhileDerived.java | 4 +---
src/org/python/modules/itertools/groupbyDerived.java | 4 +---
src/org/python/modules/itertools/ifilterDerived.java | 4 +---
src/org/python/modules/itertools/ifilterfalseDerived.java | 4 +---
src/org/python/modules/itertools/isliceDerived.java | 4 +---
src/org/python/modules/itertools/izipDerived.java | 4 +---
src/org/python/modules/itertools/izipLongestDerived.java | 4 +---
src/org/python/modules/itertools/permutationsDerived.java | 4 +---
src/org/python/modules/itertools/productDerived.java | 4 +---
src/org/python/modules/itertools/repeatDerived.java | 4 +---
src/org/python/modules/itertools/starmapDerived.java | 4 +---
src/org/python/modules/itertools/takewhileDerived.java | 4 +---
src/org/python/modules/random/PyRandomDerived.java | 4 +---
src/org/python/modules/thread/PyLocalDerived.java | 4 +---
src/org/python/modules/zipimport/zipimporterDerived.java | 4 +---
src/templates/object.derived | 4 +---
147 files changed, 147 insertions(+), 441 deletions(-)
diff --git a/src/org/python/antlr/ast/AssertDerived.java b/src/org/python/antlr/ast/AssertDerived.java
--- a/src/org/python/antlr/ast/AssertDerived.java
+++ b/src/org/python/antlr/ast/AssertDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/AssignDerived.java b/src/org/python/antlr/ast/AssignDerived.java
--- a/src/org/python/antlr/ast/AssignDerived.java
+++ b/src/org/python/antlr/ast/AssignDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/AttributeDerived.java b/src/org/python/antlr/ast/AttributeDerived.java
--- a/src/org/python/antlr/ast/AttributeDerived.java
+++ b/src/org/python/antlr/ast/AttributeDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/AugAssignDerived.java b/src/org/python/antlr/ast/AugAssignDerived.java
--- a/src/org/python/antlr/ast/AugAssignDerived.java
+++ b/src/org/python/antlr/ast/AugAssignDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/BinOpDerived.java b/src/org/python/antlr/ast/BinOpDerived.java
--- a/src/org/python/antlr/ast/BinOpDerived.java
+++ b/src/org/python/antlr/ast/BinOpDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/BoolOpDerived.java b/src/org/python/antlr/ast/BoolOpDerived.java
--- a/src/org/python/antlr/ast/BoolOpDerived.java
+++ b/src/org/python/antlr/ast/BoolOpDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/BreakDerived.java b/src/org/python/antlr/ast/BreakDerived.java
--- a/src/org/python/antlr/ast/BreakDerived.java
+++ b/src/org/python/antlr/ast/BreakDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/CallDerived.java b/src/org/python/antlr/ast/CallDerived.java
--- a/src/org/python/antlr/ast/CallDerived.java
+++ b/src/org/python/antlr/ast/CallDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ClassDefDerived.java b/src/org/python/antlr/ast/ClassDefDerived.java
--- a/src/org/python/antlr/ast/ClassDefDerived.java
+++ b/src/org/python/antlr/ast/ClassDefDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/CompareDerived.java b/src/org/python/antlr/ast/CompareDerived.java
--- a/src/org/python/antlr/ast/CompareDerived.java
+++ b/src/org/python/antlr/ast/CompareDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ContinueDerived.java b/src/org/python/antlr/ast/ContinueDerived.java
--- a/src/org/python/antlr/ast/ContinueDerived.java
+++ b/src/org/python/antlr/ast/ContinueDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/DeleteDerived.java b/src/org/python/antlr/ast/DeleteDerived.java
--- a/src/org/python/antlr/ast/DeleteDerived.java
+++ b/src/org/python/antlr/ast/DeleteDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/DictDerived.java b/src/org/python/antlr/ast/DictDerived.java
--- a/src/org/python/antlr/ast/DictDerived.java
+++ b/src/org/python/antlr/ast/DictDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/EllipsisDerived.java b/src/org/python/antlr/ast/EllipsisDerived.java
--- a/src/org/python/antlr/ast/EllipsisDerived.java
+++ b/src/org/python/antlr/ast/EllipsisDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ExceptHandlerDerived.java b/src/org/python/antlr/ast/ExceptHandlerDerived.java
--- a/src/org/python/antlr/ast/ExceptHandlerDerived.java
+++ b/src/org/python/antlr/ast/ExceptHandlerDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ExecDerived.java b/src/org/python/antlr/ast/ExecDerived.java
--- a/src/org/python/antlr/ast/ExecDerived.java
+++ b/src/org/python/antlr/ast/ExecDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ExprDerived.java b/src/org/python/antlr/ast/ExprDerived.java
--- a/src/org/python/antlr/ast/ExprDerived.java
+++ b/src/org/python/antlr/ast/ExprDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ExpressionDerived.java b/src/org/python/antlr/ast/ExpressionDerived.java
--- a/src/org/python/antlr/ast/ExpressionDerived.java
+++ b/src/org/python/antlr/ast/ExpressionDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ExtSliceDerived.java b/src/org/python/antlr/ast/ExtSliceDerived.java
--- a/src/org/python/antlr/ast/ExtSliceDerived.java
+++ b/src/org/python/antlr/ast/ExtSliceDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ForDerived.java b/src/org/python/antlr/ast/ForDerived.java
--- a/src/org/python/antlr/ast/ForDerived.java
+++ b/src/org/python/antlr/ast/ForDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/FunctionDefDerived.java b/src/org/python/antlr/ast/FunctionDefDerived.java
--- a/src/org/python/antlr/ast/FunctionDefDerived.java
+++ b/src/org/python/antlr/ast/FunctionDefDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/GeneratorExpDerived.java b/src/org/python/antlr/ast/GeneratorExpDerived.java
--- a/src/org/python/antlr/ast/GeneratorExpDerived.java
+++ b/src/org/python/antlr/ast/GeneratorExpDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/GlobalDerived.java b/src/org/python/antlr/ast/GlobalDerived.java
--- a/src/org/python/antlr/ast/GlobalDerived.java
+++ b/src/org/python/antlr/ast/GlobalDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/IfDerived.java b/src/org/python/antlr/ast/IfDerived.java
--- a/src/org/python/antlr/ast/IfDerived.java
+++ b/src/org/python/antlr/ast/IfDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/IfExpDerived.java b/src/org/python/antlr/ast/IfExpDerived.java
--- a/src/org/python/antlr/ast/IfExpDerived.java
+++ b/src/org/python/antlr/ast/IfExpDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ImportDerived.java b/src/org/python/antlr/ast/ImportDerived.java
--- a/src/org/python/antlr/ast/ImportDerived.java
+++ b/src/org/python/antlr/ast/ImportDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ImportFromDerived.java b/src/org/python/antlr/ast/ImportFromDerived.java
--- a/src/org/python/antlr/ast/ImportFromDerived.java
+++ b/src/org/python/antlr/ast/ImportFromDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/IndexDerived.java b/src/org/python/antlr/ast/IndexDerived.java
--- a/src/org/python/antlr/ast/IndexDerived.java
+++ b/src/org/python/antlr/ast/IndexDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/InteractiveDerived.java b/src/org/python/antlr/ast/InteractiveDerived.java
--- a/src/org/python/antlr/ast/InteractiveDerived.java
+++ b/src/org/python/antlr/ast/InteractiveDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/LambdaDerived.java b/src/org/python/antlr/ast/LambdaDerived.java
--- a/src/org/python/antlr/ast/LambdaDerived.java
+++ b/src/org/python/antlr/ast/LambdaDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ListCompDerived.java b/src/org/python/antlr/ast/ListCompDerived.java
--- a/src/org/python/antlr/ast/ListCompDerived.java
+++ b/src/org/python/antlr/ast/ListCompDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ListDerived.java b/src/org/python/antlr/ast/ListDerived.java
--- a/src/org/python/antlr/ast/ListDerived.java
+++ b/src/org/python/antlr/ast/ListDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ModuleDerived.java b/src/org/python/antlr/ast/ModuleDerived.java
--- a/src/org/python/antlr/ast/ModuleDerived.java
+++ b/src/org/python/antlr/ast/ModuleDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/NameDerived.java b/src/org/python/antlr/ast/NameDerived.java
--- a/src/org/python/antlr/ast/NameDerived.java
+++ b/src/org/python/antlr/ast/NameDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/NumDerived.java b/src/org/python/antlr/ast/NumDerived.java
--- a/src/org/python/antlr/ast/NumDerived.java
+++ b/src/org/python/antlr/ast/NumDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/PassDerived.java b/src/org/python/antlr/ast/PassDerived.java
--- a/src/org/python/antlr/ast/PassDerived.java
+++ b/src/org/python/antlr/ast/PassDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/PrintDerived.java b/src/org/python/antlr/ast/PrintDerived.java
--- a/src/org/python/antlr/ast/PrintDerived.java
+++ b/src/org/python/antlr/ast/PrintDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/RaiseDerived.java b/src/org/python/antlr/ast/RaiseDerived.java
--- a/src/org/python/antlr/ast/RaiseDerived.java
+++ b/src/org/python/antlr/ast/RaiseDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ReprDerived.java b/src/org/python/antlr/ast/ReprDerived.java
--- a/src/org/python/antlr/ast/ReprDerived.java
+++ b/src/org/python/antlr/ast/ReprDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/ReturnDerived.java b/src/org/python/antlr/ast/ReturnDerived.java
--- a/src/org/python/antlr/ast/ReturnDerived.java
+++ b/src/org/python/antlr/ast/ReturnDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/SliceDerived.java b/src/org/python/antlr/ast/SliceDerived.java
--- a/src/org/python/antlr/ast/SliceDerived.java
+++ b/src/org/python/antlr/ast/SliceDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/StrDerived.java b/src/org/python/antlr/ast/StrDerived.java
--- a/src/org/python/antlr/ast/StrDerived.java
+++ b/src/org/python/antlr/ast/StrDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/SubscriptDerived.java b/src/org/python/antlr/ast/SubscriptDerived.java
--- a/src/org/python/antlr/ast/SubscriptDerived.java
+++ b/src/org/python/antlr/ast/SubscriptDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/SuiteDerived.java b/src/org/python/antlr/ast/SuiteDerived.java
--- a/src/org/python/antlr/ast/SuiteDerived.java
+++ b/src/org/python/antlr/ast/SuiteDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/TryExceptDerived.java b/src/org/python/antlr/ast/TryExceptDerived.java
--- a/src/org/python/antlr/ast/TryExceptDerived.java
+++ b/src/org/python/antlr/ast/TryExceptDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/TryFinallyDerived.java b/src/org/python/antlr/ast/TryFinallyDerived.java
--- a/src/org/python/antlr/ast/TryFinallyDerived.java
+++ b/src/org/python/antlr/ast/TryFinallyDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/TupleDerived.java b/src/org/python/antlr/ast/TupleDerived.java
--- a/src/org/python/antlr/ast/TupleDerived.java
+++ b/src/org/python/antlr/ast/TupleDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/UnaryOpDerived.java b/src/org/python/antlr/ast/UnaryOpDerived.java
--- a/src/org/python/antlr/ast/UnaryOpDerived.java
+++ b/src/org/python/antlr/ast/UnaryOpDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/WhileDerived.java b/src/org/python/antlr/ast/WhileDerived.java
--- a/src/org/python/antlr/ast/WhileDerived.java
+++ b/src/org/python/antlr/ast/WhileDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/WithDerived.java b/src/org/python/antlr/ast/WithDerived.java
--- a/src/org/python/antlr/ast/WithDerived.java
+++ b/src/org/python/antlr/ast/WithDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/YieldDerived.java b/src/org/python/antlr/ast/YieldDerived.java
--- a/src/org/python/antlr/ast/YieldDerived.java
+++ b/src/org/python/antlr/ast/YieldDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/aliasDerived.java b/src/org/python/antlr/ast/aliasDerived.java
--- a/src/org/python/antlr/ast/aliasDerived.java
+++ b/src/org/python/antlr/ast/aliasDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/argumentsDerived.java b/src/org/python/antlr/ast/argumentsDerived.java
--- a/src/org/python/antlr/ast/argumentsDerived.java
+++ b/src/org/python/antlr/ast/argumentsDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/comprehensionDerived.java b/src/org/python/antlr/ast/comprehensionDerived.java
--- a/src/org/python/antlr/ast/comprehensionDerived.java
+++ b/src/org/python/antlr/ast/comprehensionDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/ast/keywordDerived.java b/src/org/python/antlr/ast/keywordDerived.java
--- a/src/org/python/antlr/ast/keywordDerived.java
+++ b/src/org/python/antlr/ast/keywordDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/AddDerived.java b/src/org/python/antlr/op/AddDerived.java
--- a/src/org/python/antlr/op/AddDerived.java
+++ b/src/org/python/antlr/op/AddDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/AndDerived.java b/src/org/python/antlr/op/AndDerived.java
--- a/src/org/python/antlr/op/AndDerived.java
+++ b/src/org/python/antlr/op/AndDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/AugLoadDerived.java b/src/org/python/antlr/op/AugLoadDerived.java
--- a/src/org/python/antlr/op/AugLoadDerived.java
+++ b/src/org/python/antlr/op/AugLoadDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/AugStoreDerived.java b/src/org/python/antlr/op/AugStoreDerived.java
--- a/src/org/python/antlr/op/AugStoreDerived.java
+++ b/src/org/python/antlr/op/AugStoreDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/BitAndDerived.java b/src/org/python/antlr/op/BitAndDerived.java
--- a/src/org/python/antlr/op/BitAndDerived.java
+++ b/src/org/python/antlr/op/BitAndDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/BitOrDerived.java b/src/org/python/antlr/op/BitOrDerived.java
--- a/src/org/python/antlr/op/BitOrDerived.java
+++ b/src/org/python/antlr/op/BitOrDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/BitXorDerived.java b/src/org/python/antlr/op/BitXorDerived.java
--- a/src/org/python/antlr/op/BitXorDerived.java
+++ b/src/org/python/antlr/op/BitXorDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/DelDerived.java b/src/org/python/antlr/op/DelDerived.java
--- a/src/org/python/antlr/op/DelDerived.java
+++ b/src/org/python/antlr/op/DelDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/DivDerived.java b/src/org/python/antlr/op/DivDerived.java
--- a/src/org/python/antlr/op/DivDerived.java
+++ b/src/org/python/antlr/op/DivDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/EqDerived.java b/src/org/python/antlr/op/EqDerived.java
--- a/src/org/python/antlr/op/EqDerived.java
+++ b/src/org/python/antlr/op/EqDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/FloorDivDerived.java b/src/org/python/antlr/op/FloorDivDerived.java
--- a/src/org/python/antlr/op/FloorDivDerived.java
+++ b/src/org/python/antlr/op/FloorDivDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/GtDerived.java b/src/org/python/antlr/op/GtDerived.java
--- a/src/org/python/antlr/op/GtDerived.java
+++ b/src/org/python/antlr/op/GtDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/GtEDerived.java b/src/org/python/antlr/op/GtEDerived.java
--- a/src/org/python/antlr/op/GtEDerived.java
+++ b/src/org/python/antlr/op/GtEDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/InDerived.java b/src/org/python/antlr/op/InDerived.java
--- a/src/org/python/antlr/op/InDerived.java
+++ b/src/org/python/antlr/op/InDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/InvertDerived.java b/src/org/python/antlr/op/InvertDerived.java
--- a/src/org/python/antlr/op/InvertDerived.java
+++ b/src/org/python/antlr/op/InvertDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/IsDerived.java b/src/org/python/antlr/op/IsDerived.java
--- a/src/org/python/antlr/op/IsDerived.java
+++ b/src/org/python/antlr/op/IsDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/IsNotDerived.java b/src/org/python/antlr/op/IsNotDerived.java
--- a/src/org/python/antlr/op/IsNotDerived.java
+++ b/src/org/python/antlr/op/IsNotDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/LShiftDerived.java b/src/org/python/antlr/op/LShiftDerived.java
--- a/src/org/python/antlr/op/LShiftDerived.java
+++ b/src/org/python/antlr/op/LShiftDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/LoadDerived.java b/src/org/python/antlr/op/LoadDerived.java
--- a/src/org/python/antlr/op/LoadDerived.java
+++ b/src/org/python/antlr/op/LoadDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/LtDerived.java b/src/org/python/antlr/op/LtDerived.java
--- a/src/org/python/antlr/op/LtDerived.java
+++ b/src/org/python/antlr/op/LtDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/LtEDerived.java b/src/org/python/antlr/op/LtEDerived.java
--- a/src/org/python/antlr/op/LtEDerived.java
+++ b/src/org/python/antlr/op/LtEDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/ModDerived.java b/src/org/python/antlr/op/ModDerived.java
--- a/src/org/python/antlr/op/ModDerived.java
+++ b/src/org/python/antlr/op/ModDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/MultDerived.java b/src/org/python/antlr/op/MultDerived.java
--- a/src/org/python/antlr/op/MultDerived.java
+++ b/src/org/python/antlr/op/MultDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/NotDerived.java b/src/org/python/antlr/op/NotDerived.java
--- a/src/org/python/antlr/op/NotDerived.java
+++ b/src/org/python/antlr/op/NotDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/NotEqDerived.java b/src/org/python/antlr/op/NotEqDerived.java
--- a/src/org/python/antlr/op/NotEqDerived.java
+++ b/src/org/python/antlr/op/NotEqDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/NotInDerived.java b/src/org/python/antlr/op/NotInDerived.java
--- a/src/org/python/antlr/op/NotInDerived.java
+++ b/src/org/python/antlr/op/NotInDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/OrDerived.java b/src/org/python/antlr/op/OrDerived.java
--- a/src/org/python/antlr/op/OrDerived.java
+++ b/src/org/python/antlr/op/OrDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/ParamDerived.java b/src/org/python/antlr/op/ParamDerived.java
--- a/src/org/python/antlr/op/ParamDerived.java
+++ b/src/org/python/antlr/op/ParamDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/PowDerived.java b/src/org/python/antlr/op/PowDerived.java
--- a/src/org/python/antlr/op/PowDerived.java
+++ b/src/org/python/antlr/op/PowDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/RShiftDerived.java b/src/org/python/antlr/op/RShiftDerived.java
--- a/src/org/python/antlr/op/RShiftDerived.java
+++ b/src/org/python/antlr/op/RShiftDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/StoreDerived.java b/src/org/python/antlr/op/StoreDerived.java
--- a/src/org/python/antlr/op/StoreDerived.java
+++ b/src/org/python/antlr/op/StoreDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/SubDerived.java b/src/org/python/antlr/op/SubDerived.java
--- a/src/org/python/antlr/op/SubDerived.java
+++ b/src/org/python/antlr/op/SubDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/UAddDerived.java b/src/org/python/antlr/op/UAddDerived.java
--- a/src/org/python/antlr/op/UAddDerived.java
+++ b/src/org/python/antlr/op/UAddDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/antlr/op/USubDerived.java b/src/org/python/antlr/op/USubDerived.java
--- a/src/org/python/antlr/op/USubDerived.java
+++ b/src/org/python/antlr/op/USubDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/ClasspathPyImporterDerived.java b/src/org/python/core/ClasspathPyImporterDerived.java
--- a/src/org/python/core/ClasspathPyImporterDerived.java
+++ b/src/org/python/core/ClasspathPyImporterDerived.java
@@ -829,9 +829,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyArrayDerived.java b/src/org/python/core/PyArrayDerived.java
--- a/src/org/python/core/PyArrayDerived.java
+++ b/src/org/python/core/PyArrayDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyBaseExceptionDerived.java b/src/org/python/core/PyBaseExceptionDerived.java
--- a/src/org/python/core/PyBaseExceptionDerived.java
+++ b/src/org/python/core/PyBaseExceptionDerived.java
@@ -829,9 +829,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyByteArrayDerived.java b/src/org/python/core/PyByteArrayDerived.java
--- a/src/org/python/core/PyByteArrayDerived.java
+++ b/src/org/python/core/PyByteArrayDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyClassMethodDerived.java b/src/org/python/core/PyClassMethodDerived.java
--- a/src/org/python/core/PyClassMethodDerived.java
+++ b/src/org/python/core/PyClassMethodDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyComplexDerived.java b/src/org/python/core/PyComplexDerived.java
--- a/src/org/python/core/PyComplexDerived.java
+++ b/src/org/python/core/PyComplexDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
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
@@ -866,9 +866,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyEnumerateDerived.java b/src/org/python/core/PyEnumerateDerived.java
--- a/src/org/python/core/PyEnumerateDerived.java
+++ b/src/org/python/core/PyEnumerateDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyFileDerived.java b/src/org/python/core/PyFileDerived.java
--- a/src/org/python/core/PyFileDerived.java
+++ b/src/org/python/core/PyFileDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyFloatDerived.java b/src/org/python/core/PyFloatDerived.java
--- a/src/org/python/core/PyFloatDerived.java
+++ b/src/org/python/core/PyFloatDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyFrozenSetDerived.java b/src/org/python/core/PyFrozenSetDerived.java
--- a/src/org/python/core/PyFrozenSetDerived.java
+++ b/src/org/python/core/PyFrozenSetDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyIntegerDerived.java b/src/org/python/core/PyIntegerDerived.java
--- a/src/org/python/core/PyIntegerDerived.java
+++ b/src/org/python/core/PyIntegerDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyListDerived.java b/src/org/python/core/PyListDerived.java
--- a/src/org/python/core/PyListDerived.java
+++ b/src/org/python/core/PyListDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyLongDerived.java b/src/org/python/core/PyLongDerived.java
--- a/src/org/python/core/PyLongDerived.java
+++ b/src/org/python/core/PyLongDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyModuleDerived.java b/src/org/python/core/PyModuleDerived.java
--- a/src/org/python/core/PyModuleDerived.java
+++ b/src/org/python/core/PyModuleDerived.java
@@ -829,9 +829,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyObjectDerived.java b/src/org/python/core/PyObjectDerived.java
--- a/src/org/python/core/PyObjectDerived.java
+++ b/src/org/python/core/PyObjectDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyPropertyDerived.java b/src/org/python/core/PyPropertyDerived.java
--- a/src/org/python/core/PyPropertyDerived.java
+++ b/src/org/python/core/PyPropertyDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
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
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyStringDerived.java b/src/org/python/core/PyStringDerived.java
--- a/src/org/python/core/PyStringDerived.java
+++ b/src/org/python/core/PyStringDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PySuperDerived.java b/src/org/python/core/PySuperDerived.java
--- a/src/org/python/core/PySuperDerived.java
+++ b/src/org/python/core/PySuperDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyTupleDerived.java b/src/org/python/core/PyTupleDerived.java
--- a/src/org/python/core/PyTupleDerived.java
+++ b/src/org/python/core/PyTupleDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyTypeDerived.java b/src/org/python/core/PyTypeDerived.java
--- a/src/org/python/core/PyTypeDerived.java
+++ b/src/org/python/core/PyTypeDerived.java
@@ -829,9 +829,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/core/PyUnicodeDerived.java b/src/org/python/core/PyUnicodeDerived.java
--- a/src/org/python/core/PyUnicodeDerived.java
+++ b/src/org/python/core/PyUnicodeDerived.java
@@ -856,9 +856,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/PyStructDerived.java b/src/org/python/modules/PyStructDerived.java
--- a/src/org/python/modules/PyStructDerived.java
+++ b/src/org/python/modules/PyStructDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_collections/PyDefaultDictDerived.java b/src/org/python/modules/_collections/PyDefaultDictDerived.java
--- a/src/org/python/modules/_collections/PyDefaultDictDerived.java
+++ b/src/org/python/modules/_collections/PyDefaultDictDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_collections/PyDequeDerived.java b/src/org/python/modules/_collections/PyDequeDerived.java
--- a/src/org/python/modules/_collections/PyDequeDerived.java
+++ b/src/org/python/modules/_collections/PyDequeDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_csv/PyDialectDerived.java b/src/org/python/modules/_csv/PyDialectDerived.java
--- a/src/org/python/modules/_csv/PyDialectDerived.java
+++ b/src/org/python/modules/_csv/PyDialectDerived.java
@@ -830,9 +830,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_functools/PyPartialDerived.java b/src/org/python/modules/_functools/PyPartialDerived.java
--- a/src/org/python/modules/_functools/PyPartialDerived.java
+++ b/src/org/python/modules/_functools/PyPartialDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_io/PyFileIODerived.java b/src/org/python/modules/_io/PyFileIODerived.java
--- a/src/org/python/modules/_io/PyFileIODerived.java
+++ b/src/org/python/modules/_io/PyFileIODerived.java
@@ -830,9 +830,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_io/PyIOBaseDerived.java b/src/org/python/modules/_io/PyIOBaseDerived.java
--- a/src/org/python/modules/_io/PyIOBaseDerived.java
+++ b/src/org/python/modules/_io/PyIOBaseDerived.java
@@ -830,9 +830,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_io/PyRawIOBaseDerived.java b/src/org/python/modules/_io/PyRawIOBaseDerived.java
--- a/src/org/python/modules/_io/PyRawIOBaseDerived.java
+++ b/src/org/python/modules/_io/PyRawIOBaseDerived.java
@@ -830,9 +830,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/_weakref/ReferenceTypeDerived.java b/src/org/python/modules/_weakref/ReferenceTypeDerived.java
--- a/src/org/python/modules/_weakref/ReferenceTypeDerived.java
+++ b/src/org/python/modules/_weakref/ReferenceTypeDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/bz2/PyBZ2CompressorDerived.java b/src/org/python/modules/bz2/PyBZ2CompressorDerived.java
--- a/src/org/python/modules/bz2/PyBZ2CompressorDerived.java
+++ b/src/org/python/modules/bz2/PyBZ2CompressorDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/bz2/PyBZ2DecompressorDerived.java b/src/org/python/modules/bz2/PyBZ2DecompressorDerived.java
--- a/src/org/python/modules/bz2/PyBZ2DecompressorDerived.java
+++ b/src/org/python/modules/bz2/PyBZ2DecompressorDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/bz2/PyBZ2FileDerived.java b/src/org/python/modules/bz2/PyBZ2FileDerived.java
--- a/src/org/python/modules/bz2/PyBZ2FileDerived.java
+++ b/src/org/python/modules/bz2/PyBZ2FileDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/PyTeeIteratorDerived.java b/src/org/python/modules/itertools/PyTeeIteratorDerived.java
--- a/src/org/python/modules/itertools/PyTeeIteratorDerived.java
+++ b/src/org/python/modules/itertools/PyTeeIteratorDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/chainDerived.java b/src/org/python/modules/itertools/chainDerived.java
--- a/src/org/python/modules/itertools/chainDerived.java
+++ b/src/org/python/modules/itertools/chainDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/combinationsDerived.java b/src/org/python/modules/itertools/combinationsDerived.java
--- a/src/org/python/modules/itertools/combinationsDerived.java
+++ b/src/org/python/modules/itertools/combinationsDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/combinationsWithReplacementDerived.java b/src/org/python/modules/itertools/combinationsWithReplacementDerived.java
--- a/src/org/python/modules/itertools/combinationsWithReplacementDerived.java
+++ b/src/org/python/modules/itertools/combinationsWithReplacementDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/compressDerived.java b/src/org/python/modules/itertools/compressDerived.java
--- a/src/org/python/modules/itertools/compressDerived.java
+++ b/src/org/python/modules/itertools/compressDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/countDerived.java b/src/org/python/modules/itertools/countDerived.java
--- a/src/org/python/modules/itertools/countDerived.java
+++ b/src/org/python/modules/itertools/countDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/cycleDerived.java b/src/org/python/modules/itertools/cycleDerived.java
--- a/src/org/python/modules/itertools/cycleDerived.java
+++ b/src/org/python/modules/itertools/cycleDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/dropwhileDerived.java b/src/org/python/modules/itertools/dropwhileDerived.java
--- a/src/org/python/modules/itertools/dropwhileDerived.java
+++ b/src/org/python/modules/itertools/dropwhileDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/groupbyDerived.java b/src/org/python/modules/itertools/groupbyDerived.java
--- a/src/org/python/modules/itertools/groupbyDerived.java
+++ b/src/org/python/modules/itertools/groupbyDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/ifilterDerived.java b/src/org/python/modules/itertools/ifilterDerived.java
--- a/src/org/python/modules/itertools/ifilterDerived.java
+++ b/src/org/python/modules/itertools/ifilterDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/ifilterfalseDerived.java b/src/org/python/modules/itertools/ifilterfalseDerived.java
--- a/src/org/python/modules/itertools/ifilterfalseDerived.java
+++ b/src/org/python/modules/itertools/ifilterfalseDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/isliceDerived.java b/src/org/python/modules/itertools/isliceDerived.java
--- a/src/org/python/modules/itertools/isliceDerived.java
+++ b/src/org/python/modules/itertools/isliceDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/izipDerived.java b/src/org/python/modules/itertools/izipDerived.java
--- a/src/org/python/modules/itertools/izipDerived.java
+++ b/src/org/python/modules/itertools/izipDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/izipLongestDerived.java b/src/org/python/modules/itertools/izipLongestDerived.java
--- a/src/org/python/modules/itertools/izipLongestDerived.java
+++ b/src/org/python/modules/itertools/izipLongestDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/permutationsDerived.java b/src/org/python/modules/itertools/permutationsDerived.java
--- a/src/org/python/modules/itertools/permutationsDerived.java
+++ b/src/org/python/modules/itertools/permutationsDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/productDerived.java b/src/org/python/modules/itertools/productDerived.java
--- a/src/org/python/modules/itertools/productDerived.java
+++ b/src/org/python/modules/itertools/productDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/repeatDerived.java b/src/org/python/modules/itertools/repeatDerived.java
--- a/src/org/python/modules/itertools/repeatDerived.java
+++ b/src/org/python/modules/itertools/repeatDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/starmapDerived.java b/src/org/python/modules/itertools/starmapDerived.java
--- a/src/org/python/modules/itertools/starmapDerived.java
+++ b/src/org/python/modules/itertools/starmapDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/itertools/takewhileDerived.java b/src/org/python/modules/itertools/takewhileDerived.java
--- a/src/org/python/modules/itertools/takewhileDerived.java
+++ b/src/org/python/modules/itertools/takewhileDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/random/PyRandomDerived.java b/src/org/python/modules/random/PyRandomDerived.java
--- a/src/org/python/modules/random/PyRandomDerived.java
+++ b/src/org/python/modules/random/PyRandomDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/thread/PyLocalDerived.java b/src/org/python/modules/thread/PyLocalDerived.java
--- a/src/org/python/modules/thread/PyLocalDerived.java
+++ b/src/org/python/modules/thread/PyLocalDerived.java
@@ -830,9 +830,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/org/python/modules/zipimport/zipimporterDerived.java b/src/org/python/modules/zipimport/zipimporterDerived.java
--- a/src/org/python/modules/zipimport/zipimporterDerived.java
+++ b/src/org/python/modules/zipimport/zipimporterDerived.java
@@ -857,9 +857,7 @@
PyObject impl=self_type.lookup("__len__");
if (impl!=null) {
PyObject res=impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
diff --git a/src/templates/object.derived b/src/templates/object.derived
--- a/src/templates/object.derived
+++ b/src/templates/object.derived
@@ -149,9 +149,7 @@
PyObject impl = self_type.lookup("__len__");
if (impl != null) {
PyObject res = impl.__get__(this,self_type).__call__();
- if (res instanceof PyInteger)
- return ((PyInteger)res).getValue();
- throw Py.TypeError("__len__ should return a int");
+ return res.asInt();
}
return super.__len__();
}
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Thu Feb 5 06:57:34 2015
From: jython-checkins at python.org (jim.baker)
Date: Thu, 05 Feb 2015 05:57:34 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Remove_os=2Edup=2C_dup2_unt?=
=?utf-8?q?il_returned_descriptors_are_supported_by_os=2Efdopen?=
Message-ID: <20150205055710.25867.56640@psf.io>
https://hg.python.org/jython/rev/836926c051b2
changeset: 7570:836926c051b2
user: Jim Baker
date: Wed Feb 04 22:57:05 2015 -0700
summary:
Remove os.dup, dup2 until returned descriptors are supported by os.fdopen
Such incomplete support is breaking pytest. But without fdopen,
these are not useful functions.
files:
src/org/python/modules/posix/PosixModule.java | 15 ++++++----
1 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java
--- a/src/org/python/modules/posix/PosixModule.java
+++ b/src/org/python/modules/posix/PosixModule.java
@@ -333,13 +333,16 @@
}
}
- public static PyObject dup(PyObject fd1) {
- return Py.newInteger(posix.dup(getFD(fd1).getIntFD()));
- }
+ // Disable dup support until it fully works with fdopen;
+ // this incomplete support currently breaks py.test
- public static PyObject dup2(PyObject fd1, PyObject fd2) {
- return Py.newInteger(posix.dup2(getFD(fd1).getIntFD(), getFD(fd2).getIntFD()));
- }
+// public static PyObject dup(PyObject fd1) {
+// return Py.newInteger(posix.dup(getFD(fd1).getIntFD()));
+// }
+//
+// public static PyObject dup2(PyObject fd1, PyObject fd2) {
+// return Py.newInteger(posix.dup2(getFD(fd1).getIntFD(), getFD(fd2).getIntFD()));
+// }
public static PyString __doc__fdopen = new PyString(
"fdopen(fd [, mode='r' [, bufsize]]) -> file_object\n\n" +
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Fri Feb 6 03:34:53 2015
From: jython-checkins at python.org (jim.baker)
Date: Fri, 06 Feb 2015 02:34:53 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Modified_os=2Esystem_to_def?=
=?utf-8?q?er_import_of_its_functionality_from_subprocess?=
Message-ID: <20150206023448.39280.69026@psf.io>
https://hg.python.org/jython/rev/b4aa09794592
changeset: 7571:b4aa09794592
user: Jim Baker
date: Thu Feb 05 19:34:42 2015 -0700
summary:
Modified os.system to defer import of its functionality from subprocess
os.system used a bad circular import of its functionality from
subprocess. However, this would be only observed when Jython was
started with -S or with site imports turned off, so it was not being
caught in testing. Modified accordingly, with a test to verify.
files:
Lib/os.py | 14 ++++++++++++--
Lib/test/test_os_jy.py | 21 +++++++++++++++++++++
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/Lib/os.py b/Lib/os.py
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -721,5 +721,15 @@
return iter(self._stream)
-# Recursive import! So need to put it at the end
-from subprocess import _os_system as system
+def system(command):
+ """system(command) -> exit_status
+
+ Execute the command (a string) in a subshell."""
+ # Because this is a circular import, we need to perform
+ # a late binding. Monkeypatch to avoid doing this import
+ # repeatedly.
+ global system # writable name of this function!
+
+ from subprocess import _os_system
+ system = _os_system
+ return _os_system(command)
diff --git a/Lib/test/test_os_jy.py b/Lib/test/test_os_jy.py
--- a/Lib/test/test_os_jy.py
+++ b/Lib/test/test_os_jy.py
@@ -257,6 +257,26 @@
"2015-01-22 00:00:00\n")
+class SystemTestCase(unittest.TestCase):
+
+ def test_system_no_site_import(self):
+ # If not importing site (-S), importing traceback previously
+ # would fail with an import error due to creating a circular
+ # import chain. This root cause is because the os module
+ # imports the subprocess module for the system function; but
+ # the subprocess module imports from os. Verrifies that this
+ # managed by making the import late; also verify the
+ # monkeypatching optimization is successful by calling
+ # os.system twice.
+ with test_support.temp_cwd() as temp_cwd:
+ self.assertEqual(
+ subprocess.check_output(
+ [sys.executable, "-S", "-c",
+ "import traceback; import os; os.system('echo 42'); os.system('echo 47')"])\
+ .replace("\r", ""), # in case of running on Windows
+ "42\n47\n")
+
+
def test_main():
test_support.run_unittest(
OSFileTestCase,
@@ -265,6 +285,7 @@
OSWriteTestCase,
UnicodeTestCase,
LocaleTestCase,
+ SystemTestCase
)
if __name__ == '__main__':
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Fri Feb 6 20:40:23 2015
From: jython-checkins at python.org (jim.baker)
Date: Fri, 06 Feb 2015 19:40:23 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Support_os=2Echdir_for_Unic?=
=?utf-8?q?ode_paths_on_Windows?=
Message-ID: <20150206194022.34396.81058@psf.io>
https://hg.python.org/jython/rev/8b2db200158f
changeset: 7572:8b2db200158f
user: Jim Baker
date: Fri Feb 06 12:40:17 2015 -0700
summary:
Support os.chdir for Unicode paths on Windows
files:
Lib/test/test_os_jy.py | 38 +++++++--
Lib/test/test_support.py | 3 +-
src/org/python/core/PyString.java | 4 +-
src/org/python/modules/posix/OS.java | 2 +-
src/org/python/modules/posix/PosixModule.java | 38 +++++++--
5 files changed, 62 insertions(+), 23 deletions(-)
diff --git a/Lib/test/test_os_jy.py b/Lib/test/test_os_jy.py
--- a/Lib/test/test_os_jy.py
+++ b/Lib/test/test_os_jy.py
@@ -173,14 +173,26 @@
# glob.glob builds on os.listdir; note that we don't use
# Unicode paths in the arg to glob
- self.assertEqual(glob.glob("unicode/*"), [u"unicode/??"])
- self.assertEqual(glob.glob("unicode/*/*"), [u"unicode/??/??"])
- self.assertEqual(glob.glob("unicode/*/*/*"), [u"unicode/??/??/test.txt"])
+ self.assertEqual(
+ glob.glob(os.path.join("unicode", "*")),
+ [os.path.join(u"unicode", u"??")])
+ self.assertEqual(
+ glob.glob(os.path.join("unicode", "*", "*")),
+ [os.path.join(u"unicode", u"??", u"??")])
+ self.assertEqual(
+ glob.glob(os.path.join("unicode", "*", "*", "*")),
+ [os.path.join(u"unicode", u"??", u"??", "test.txt")])
- # Now use a Unicode path as well as the glob arg
- self.assertEqual(glob.glob(u"unicode/*"), [u"unicode/??"])
- self.assertEqual(glob.glob(u"unicode/*/*"), [u"unicode/??/??"])
- self.assertEqual(glob.glob(u"unicode/*/*/*"), [u"unicode/??/??/test.txt"])
+ # Now use a Unicode path as well as in the glob arg
+ self.assertEqual(
+ glob.glob(os.path.join(u"unicode", "*")),
+ [os.path.join(u"unicode", u"??")])
+ self.assertEqual(
+ glob.glob(os.path.join(u"unicode", "*", "*")),
+ [os.path.join(u"unicode", u"??", u"??")])
+ self.assertEqual(
+ glob.glob(os.path.join(u"unicode", "*", "*", "*")),
+ [os.path.join(u"unicode", u"??", u"??", "test.txt")])
# Verify Java integration. But we will need to construct
# an absolute path since chdir doesn't work with Java
@@ -201,8 +213,8 @@
try:
installed_codes = dict(((normalize(code), code) for
code in subprocess.check_output(["locale", "-a"]).split()))
- except subprocess.CalledProcessError:
- unittest.skip("locale command not available, cannot test")
+ except (subprocess.CalledProcessError, OSError):
+ raise unittest.SkipTest("locale command not available, cannot test")
if msg is None:
msg = "One of %s tested locales is not installed" % (codes,)
@@ -231,7 +243,7 @@
self.get_installed_locales(["tr_TR.UTF-8"], "Turkish locale not installed, cannot test")
newenv = os.environ.copy()
newenv["LC_ALL"] = "tr_TR.UTF-8" # set to Turkish locale
- self.assertEqual(
+ self.assertIn(
subprocess.check_output(
[sys.executable, "-c",
'print repr(["I".lower(), u"I".lower(), "i".upper(), u"i".upper()])'],
@@ -239,7 +251,11 @@
# Should not convert str for 'i'/'I', but should convert
# unicode if in Turkish locale; this behavior intentionally is
# different than CPython; see also http://bugs.python.org/issue17252
- "['i', u'\\u0131', 'I', u'\\u0130']\n")
+ #
+ # Note that JVMs seem to have some latitude here however, so support
+ # either for now.
+ ["['i', u'\\u0131', 'I', u'\\u0130']\n",
+ "['i', u'i', 'I', u'I']\n"])
def test_strptime_locale(self):
# Verifies fix of http://bugs.jython.org/issue2261
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -189,7 +189,7 @@
except KeyError:
pass
-if sys.platform.startswith("win") or os.name == "java" and os._name == "nt":
+if sys.platform.startswith("win") or (os.name == "java" and os._name == "nt"):
def _waitfor(func, pathname, waitall=False):
# Peform the operation
func(pathname)
@@ -220,6 +220,7 @@
# Increase the timeout and try again
time.sleep(timeout)
timeout *= 2
+ print "Still cannot delete", pathname
warnings.warn('tests may fail, delete still pending for ' + pathname,
RuntimeWarning, stacklevel=4)
diff --git a/src/org/python/core/PyString.java b/src/org/python/core/PyString.java
--- a/src/org/python/core/PyString.java
+++ b/src/org/python/core/PyString.java
@@ -1049,7 +1049,7 @@
@ExposedMethod(doc = BuiltinDocs.str_lower_doc)
final String str_lower() {
- return getString().toLowerCase(Locale.ENGLISH);
+ return getString().toLowerCase(Locale.ROOT);
}
public String upper() {
@@ -1058,7 +1058,7 @@
@ExposedMethod(doc = BuiltinDocs.str_upper_doc)
final String str_upper() {
- return getString().toUpperCase(Locale.ENGLISH);
+ return getString().toUpperCase(Locale.ROOT);
}
public String title() {
diff --git a/src/org/python/modules/posix/OS.java b/src/org/python/modules/posix/OS.java
--- a/src/org/python/modules/posix/OS.java
+++ b/src/org/python/modules/posix/OS.java
@@ -33,7 +33,7 @@
}
String getModuleName() {
- return name().toLowerCase(Locale.ENGLISH);
+ return name().toLowerCase(Locale.ROOT);
}
String[][] getShellCommands() {
diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java
--- a/src/org/python/modules/posix/PosixModule.java
+++ b/src/org/python/modules/posix/PosixModule.java
@@ -17,6 +17,7 @@
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributes;
import java.security.SecureRandom;
import java.util.Iterator;
@@ -85,9 +86,6 @@
private static final int W_OK = 1 << 1;
private static final int R_OK = 1 << 2;
- /** os.path.realpath function for use by chdir. Lazily loaded. */
- private static volatile PyObject realpath;
-
/** Lazily initialzed singleton source for urandom. */
private static class UrandomSource {
static final SecureRandom INSTANCE = new SecureRandom();
@@ -282,14 +280,15 @@
"Change the current working directory to the specified path.");
public static void chdir(PyObject path) {
// stat raises ENOENT for us if path doesn't exist
- if (!posix.stat(absolutePath(path).toString()).isDirectory()) {
+ Path absolutePath = absolutePath(path);
+ if (!basicstat(path, absolutePath).isDirectory()) {
throw Py.OSError(Errno.ENOTDIR, path);
}
-
- if (realpath == null) {
- realpath = imp.load("os.path").__getattr__("realpath");
+ try {
+ Py.getSystemState().setCurrentWorkingDir(absolutePath.toRealPath().toString());
+ } catch (IOException ioe) {
+ throw Py.OSError(ioe);
}
- Py.getSystemState().setCurrentWorkingDir(realpath.__call__(path).asString());
}
public static PyString __doc__chmod = new PyString(
@@ -1122,6 +1121,23 @@
}
}
+ private static BasicFileAttributes basicstat(PyObject path, Path absolutePath) {
+ try {
+ BasicFileAttributes attributes = Files.readAttributes(absolutePath, BasicFileAttributes.class);
+ if (!attributes.isDirectory()) {
+ String pathStr = path.toString();
+ if (pathStr.endsWith(File.separator) || pathStr.endsWith("/")) {
+ throw Py.OSError(Errno.ENOTDIR, path);
+ }
+ }
+ return attributes;
+ } catch (NoSuchFileException ex) {
+ throw Py.OSError(Errno.ENOENT, path);
+ } catch (IOException ioe) {
+ throw Py.OSError(Errno.EBADF, path);
+ }
+ }
+
static class LstatFunction extends PyBuiltinFunctionNarrow {
LstatFunction() {
super("lstat", 1, 1,
@@ -1216,6 +1232,12 @@
Path absolutePath = absolutePath(path);
try {
DosFileAttributes attributes = Files.readAttributes(absolutePath, DosFileAttributes.class);
+ if (!attributes.isDirectory()) {
+ String pathStr = path.toString();
+ if (pathStr.endsWith(File.separator) || pathStr.endsWith("/")) {
+ throw Py.OSError(Errno.ENOTDIR, path);
+ }
+ }
int mode = attributes_to_mode(attributes);
String extension = com.google.common.io.Files.getFileExtension(absolutePath.toString());
if (extension.equals("bat") || extension.equals("cmd") || extension.equals("exe") || extension.equals("com")) {
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sat Feb 7 02:12:12 2015
From: jython-checkins at python.org (jim.baker)
Date: Sat, 07 Feb 2015 01:12:12 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Use_latest_version_of_datet?=
=?utf-8?q?ime_module_from_PyPy?=
Message-ID: <20150207011210.50317.66334@psf.io>
https://hg.python.org/jython/rev/daa6bf9a14d5
changeset: 7573:daa6bf9a14d5
user: Jim Baker
date: Fri Feb 06 18:11:56 2015 -0700
summary:
Use latest version of datetime module from PyPy
Jython shares with PyPy its datetime module, so update to latest version
https://bitbucket.org/pypy/pypy/src/302f635e170f/lib_pypy/datetime.py
Fixes http://bugs.jython.org/issue2010
files:
ACKNOWLEDGMENTS | 1 +
Lib/datetime.py | 1003 +++---
Lib/test/test_datetime.py | 3372 -------------------------
3 files changed, 534 insertions(+), 3842 deletions(-)
diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS
--- a/ACKNOWLEDGMENTS
+++ b/ACKNOWLEDGMENTS
@@ -33,6 +33,7 @@
* JUnit, licenseed under Eclipse Public License 1.0 from the JUnit project
* Mock Runner, licensed under the Apache 1.1 license
* Netty 4, licensed under the Apache 2.0 license from the Netty project
+* PyPy datetime module, licensed under the MIT License from the PyPy project
Jython follows closely the Python language and its reference
implementation CPython, as created by Guido van Rossum.
diff --git a/Lib/datetime.py b/Lib/datetime.py
--- a/Lib/datetime.py
+++ b/Lib/datetime.py
@@ -16,19 +16,20 @@
Thanks to Tim Peters for suggesting using it.
"""
+from __future__ import division
import time as _time
import math as _math
-import sys as _sys
+import struct as _struct
-if _sys.platform.startswith('java'):
- from java.lang import Object
- from java.sql import Date, Timestamp, Time
- from java.util import Calendar
- from org.python.core import Py
+def _cmp(x, y):
+ return 0 if x == y else 1 if x > y else -1
+def _round(x):
+ return int(_math.floor(x + 0.5) if x >= 0.0 else _math.ceil(x - 0.5))
MINYEAR = 1
MAXYEAR = 9999
+_MINYEARFMT = 1900
# Utility functions, adapted from Python's Demo/classes/Dates.py, which
# also assumes the current Gregorian calendar indefinitely extended in
@@ -39,9 +40,9 @@
# for all computations. See the book for algorithms for converting between
# proleptic Gregorian ordinals and many other calendar systems.
-_DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
+_DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-_DAYS_BEFORE_MONTH = [None]
+_DAYS_BEFORE_MONTH = [-1]
dbm = 0
for dim in _DAYS_IN_MONTH[1:]:
_DAYS_BEFORE_MONTH.append(dbm)
@@ -52,10 +53,6 @@
"year -> 1 if leap year, else 0."
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
-def _days_in_year(year):
- "year -> number of days in year (366 if a leap year, else 365)."
- return 365 + _is_leap(year)
-
def _days_before_year(year):
"year -> number of days before January 1st of year."
y = year - 1
@@ -69,18 +66,15 @@
return _DAYS_IN_MONTH[month]
def _days_before_month(year, month):
- "year, month -> number of days in year preceeding first day of month."
- if not 1 <= month <= 12:
- raise ValueError('month must be in 1..12', month)
+ "year, month -> number of days in year preceding first day of month."
+ assert 1 <= month <= 12, 'month must be in 1..12'
return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
def _ymd2ord(year, month, day):
"year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
- if not 1 <= month <= 12:
- raise ValueError('month must be in 1..12', month)
+ assert 1 <= month <= 12, 'month must be in 1..12'
dim = _days_in_month(year, month)
- if not 1 <= day <= dim:
- raise ValueError('day must be in 1..%d' % dim, day)
+ assert 1 <= day <= dim, ('day must be in 1..%d' % dim)
return (_days_before_year(year) +
_days_before_month(year, month) +
day)
@@ -182,14 +176,16 @@
return result
# Correctly substitute for %z and %Z escapes in strftime formats.
-def _wrap_strftime(object, format, timetuple, microsecond=0):
+def _wrap_strftime(object, format, timetuple):
year = timetuple[0]
- if year < 1900:
- raise ValueError("year=%d is before 1900; the datetime strftime() "
- "methods require year >= 1900" % year)
- # Don't call _utcoffset() or tzname() unless actually needed.
- zreplace = None # the string to use for %z
- Zreplace = None # the string to use for %Z
+ if year < _MINYEARFMT:
+ raise ValueError("year=%d is before %d; the datetime strftime() "
+ "methods require year >= %d" %
+ (year, _MINYEARFMT, _MINYEARFMT))
+ # Don't call utcoffset() or tzname() unless actually needed.
+ freplace = None # the string to use for %f
+ zreplace = None # the string to use for %z
+ Zreplace = None # the string to use for %Z
# Scan format for %z and %Z escapes, replacing as needed.
newformat = []
@@ -202,7 +198,12 @@
if i < n:
ch = format[i]
i += 1
- if ch == 'z':
+ if ch == 'f':
+ if freplace is None:
+ freplace = '%06d' % getattr(object,
+ 'microsecond', 0)
+ newformat.append(freplace)
+ elif ch == 'z':
if zreplace is None:
zreplace = ""
if hasattr(object, "_utcoffset"):
@@ -225,9 +226,6 @@
# strftime is going to have at this: escape %
Zreplace = s.replace('%', '%%')
newformat.append(Zreplace)
- elif ch == 'f':
- us_string = '%.06d' % microsecond
- newformat.append(us_string)
else:
push('%')
push(ch)
@@ -238,11 +236,6 @@
newformat = "".join(newformat)
return _time.strftime(newformat, timetuple)
-def _call_tzinfo_method(tzinfo, methname, tzinfoarg):
- if tzinfo is None:
- return None
- return getattr(tzinfo, methname)(tzinfoarg)
-
# Just raise TypeError if the arg isn't None or a string.
def _check_tzname(name):
if name is not None and not isinstance(name, str):
@@ -258,7 +251,7 @@
def _check_utc_offset(name, offset):
assert name in ("utcoffset", "dst")
if offset is None:
- return None
+ return
if not isinstance(offset, timedelta):
raise TypeError("tzinfo.%s() must return None "
"or timedelta, not '%s'" % (name, type(offset)))
@@ -272,11 +265,29 @@
raise ValueError("tzinfo.%s() must return a whole number "
"of minutes" % name)
offset = minutes
- if -1440 < offset < 1440:
- return offset
- raise ValueError("%s()=%d, must be in -1439..1439" % (name, offset))
+ if not -1440 < offset < 1440:
+ raise ValueError("%s()=%d, must be in -1439..1439" % (name, offset))
+ return offset
+
+def _check_int_field(value):
+ if isinstance(value, int):
+ return value
+ if not isinstance(value, float):
+ try:
+ value = value.__int__()
+ except AttributeError:
+ pass
+ else:
+ if isinstance(value, (int, long)):
+ return value
+ raise TypeError('__int__ method should return an integer')
+ raise TypeError('an integer is required')
+ raise TypeError('integer argument expected, got float')
def _check_date_fields(year, month, day):
+ year = _check_int_field(year)
+ month = _check_int_field(month)
+ day = _check_int_field(day)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
if not 1 <= month <= 12:
@@ -284,8 +295,13 @@
dim = _days_in_month(year, month)
if not 1 <= day <= dim:
raise ValueError('day must be in 1..%d' % dim, day)
+ return year, month, day
def _check_time_fields(hour, minute, second, microsecond):
+ hour = _check_int_field(hour)
+ minute = _check_int_field(minute)
+ second = _check_int_field(second)
+ microsecond = _check_int_field(microsecond)
if not 0 <= hour <= 23:
raise ValueError('hour must be in 0..23', hour)
if not 0 <= minute <= 59:
@@ -294,6 +310,7 @@
raise ValueError('second must be in 0..59', second)
if not 0 <= microsecond <= 999999:
raise ValueError('microsecond must be in 0..999999', microsecond)
+ return hour, minute, second, microsecond
def _check_tzinfo_arg(tz):
if tz is not None and not isinstance(tz, tzinfo):
@@ -336,9 +353,7 @@
# second-guess timezones or DST. Instead fold whatever adjustments you want
# into the minutes argument (and the constructor will normalize).
-_ORD1970 = _ymd2ord(1970, 1, 1) # base ordinal for UNIX epoch
-
-class tmxxx:
+class _tmxxx:
ordinal = None
@@ -399,32 +414,6 @@
self.hour, self.minute, self.second = hour, minute, second
self.microsecond = microsecond
- def toordinal(self):
- """Return proleptic Gregorian ordinal for the year, month and day.
-
- January 1 of year 1 is day 1. Only the year, month and day values
- contribute to the result.
- """
- if self.ordinal is None:
- self.ordinal = _ymd2ord(self.year, self.month, self.day)
- return self.ordinal
-
- def time(self):
- "Return Unixish timestamp, as a float (assuming UTC)."
- days = self.toordinal() - _ORD1970 # convert to UNIX epoch
- seconds = ((days * 24. + self.hour)*60. + self.minute)*60.
- return seconds + self.second + self.microsecond / 1e6
-
- def ctime(self):
- "Return ctime() style string."
- weekday = self.toordinal() % 7 or 7
- return "%s %s %2d %02d:%02d:%02d %04d" % (
- _DAYNAMES[weekday],
- _MONTHNAMES[self.month],
- self.day,
- self.hour, self.minute, self.second,
- self.year)
-
class timedelta(object):
"""Represent the difference between two datetime objects.
@@ -442,9 +431,9 @@
Representation: (days, seconds, microseconds). Why? Because I
felt like it.
"""
+ __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'
def __new__(cls, days=0, seconds=0, microseconds=0,
- # XXX The following should only be used as keyword args:
milliseconds=0, minutes=0, hours=0, weeks=0):
# Doing this efficiently and accurately in C is going to be difficult
# and error-prone, due to ubiquitous overflow possibilities, and that
@@ -472,8 +461,8 @@
daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.))
assert daysecondswhole == int(daysecondswhole) # can't overflow
s = int(daysecondswhole)
- assert days == long(days)
- d = long(days)
+ assert days == int(days)
+ d = int(days)
else:
daysecondsfrac = 0.0
d = days
@@ -485,8 +474,8 @@
if isinstance(seconds, float):
secondsfrac, seconds = _math.modf(seconds)
- assert seconds == long(seconds)
- seconds = long(seconds)
+ assert seconds == int(seconds)
+ seconds = int(seconds)
secondsfrac += daysecondsfrac
assert abs(secondsfrac) <= 2.0
else:
@@ -508,38 +497,27 @@
# secondsfrac isn't referenced again
if isinstance(microseconds, float):
- microseconds += usdouble
- microseconds = round(microseconds)
- seconds, microseconds = divmod(microseconds, 1e6)
- assert microseconds == int(microseconds)
- assert seconds == long(seconds)
- days, seconds = divmod(seconds, 24.*3600.)
- assert days == long(days)
- assert seconds == int(seconds)
- d += long(days)
- s += int(seconds) # can't overflow
- assert isinstance(s, int)
- assert abs(s) <= 3 * 24 * 3600
- else:
+ microseconds = _round(microseconds + usdouble)
seconds, microseconds = divmod(microseconds, 1000000)
days, seconds = divmod(seconds, 24*3600)
d += days
- s += int(seconds) # can't overflow
- assert isinstance(s, int)
- assert abs(s) <= 3 * 24 * 3600
- microseconds = float(microseconds)
- microseconds += usdouble
- microseconds = round(microseconds)
+ s += int(seconds)
+ microseconds = int(microseconds)
+ else:
+ microseconds = int(microseconds)
+ seconds, microseconds = divmod(microseconds, 1000000)
+ days, seconds = divmod(seconds, 24*3600)
+ d += days
+ s += int(seconds)
+ microseconds = _round(microseconds + usdouble)
+ assert isinstance(s, int)
+ assert isinstance(microseconds, int)
assert abs(s) <= 3 * 24 * 3600
assert abs(microseconds) < 3.1e6
# Just a little bit of carrying possible for microseconds and seconds.
- assert isinstance(microseconds, float)
- assert int(microseconds) == microseconds
- us = int(microseconds)
- seconds, us = divmod(us, 1000000)
- s += seconds # cant't overflow
- assert isinstance(s, int)
+ seconds, us = divmod(microseconds, 1000000)
+ s += seconds
days, s = divmod(s, 24*3600)
d += days
@@ -547,59 +525,79 @@
assert isinstance(s, int) and 0 <= s < 24*3600
assert isinstance(us, int) and 0 <= us < 1000000
- self = object.__new__(cls)
-
- self.__days = d
- self.__seconds = s
- self.__microseconds = us
if abs(d) > 999999999:
raise OverflowError("timedelta # of days is too large: %d" % d)
+ self = object.__new__(cls)
+ self._days = d
+ self._seconds = s
+ self._microseconds = us
+ self._hashcode = -1
return self
def __repr__(self):
- if self.__microseconds:
+ if self._microseconds:
return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
- self.__days,
- self.__seconds,
- self.__microseconds)
- if self.__seconds:
+ self._days,
+ self._seconds,
+ self._microseconds)
+ if self._seconds:
return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__,
- self.__days,
- self.__seconds)
- return "%s(%d)" % ('datetime.' + self.__class__.__name__, self.__days)
+ self._days,
+ self._seconds)
+ return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days)
def __str__(self):
- mm, ss = divmod(self.__seconds, 60)
+ mm, ss = divmod(self._seconds, 60)
hh, mm = divmod(mm, 60)
s = "%d:%02d:%02d" % (hh, mm, ss)
- if self.__days:
+ if self._days:
def plural(n):
return n, abs(n) != 1 and "s" or ""
- s = ("%d day%s, " % plural(self.__days)) + s
- if self.__microseconds:
- s = s + ".%06d" % self.__microseconds
+ s = ("%d day%s, " % plural(self._days)) + s
+ if self._microseconds:
+ s = s + ".%06d" % self._microseconds
return s
- days = property(lambda self: self.__days, doc="days")
- seconds = property(lambda self: self.__seconds, doc="seconds")
- microseconds = property(lambda self: self.__microseconds,
- doc="microseconds")
+ def total_seconds(self):
+ """Total seconds in the duration."""
+ return ((self.days * 86400 + self.seconds) * 10**6 +
+ self.microseconds) / 10**6
+
+ # Read-only field accessors
+ @property
+ def days(self):
+ """days"""
+ return self._days
+
+ @property
+ def seconds(self):
+ """seconds"""
+ return self._seconds
+
+ @property
+ def microseconds(self):
+ """microseconds"""
+ return self._microseconds
def __add__(self, other):
if isinstance(other, timedelta):
# for CPython compatibility, we cannot use
# our __class__ here, but need a real timedelta
- return timedelta(self.__days + other.__days,
- self.__seconds + other.__seconds,
- self.__microseconds + other.__microseconds)
+ return timedelta(self._days + other._days,
+ self._seconds + other._seconds,
+ self._microseconds + other._microseconds)
return NotImplemented
__radd__ = __add__
def __sub__(self, other):
if isinstance(other, timedelta):
- return self + -other
+ # for CPython compatibility, we cannot use
+ # our __class__ here, but need a real timedelta
+ return timedelta(self._days - other._days,
+ self._seconds - other._seconds,
+ self._microseconds - other._microseconds)
return NotImplemented
def __rsub__(self, other):
@@ -608,17 +606,17 @@
return NotImplemented
def __neg__(self):
- # for CPython compatibility, we cannot use
- # our __class__ here, but need a real timedelta
- return timedelta(-self.__days,
- -self.__seconds,
- -self.__microseconds)
+ # for CPython compatibility, we cannot use
+ # our __class__ here, but need a real timedelta
+ return timedelta(-self._days,
+ -self._seconds,
+ -self._microseconds)
def __pos__(self):
return self
def __abs__(self):
- if self.__days < 0:
+ if self._days < 0:
return -self
else:
return self
@@ -627,81 +625,84 @@
if isinstance(other, (int, long)):
# for CPython compatibility, we cannot use
# our __class__ here, but need a real timedelta
- return timedelta(self.__days * other,
- self.__seconds * other,
- self.__microseconds * other)
+ return timedelta(self._days * other,
+ self._seconds * other,
+ self._microseconds * other)
return NotImplemented
__rmul__ = __mul__
+ def _to_microseconds(self):
+ return ((self._days * (24*3600) + self._seconds) * 1000000 +
+ self._microseconds)
+
def __div__(self, other):
- if isinstance(other, (int, long)):
- usec = ((self.__days * (24*3600L) + self.__seconds) * 1000000 +
- self.__microseconds)
- return timedelta(0, 0, usec // other)
- return NotImplemented
+ if not isinstance(other, (int, long)):
+ return NotImplemented
+ usec = self._to_microseconds()
+ return timedelta(0, 0, usec // other)
__floordiv__ = __div__
- # Comparisons.
+ # Comparisons of timedelta objects with other.
def __eq__(self, other):
if isinstance(other, timedelta):
- return self.__cmp(other) == 0
+ return self._cmp(other) == 0
else:
return False
def __ne__(self, other):
if isinstance(other, timedelta):
- return self.__cmp(other) != 0
+ return self._cmp(other) != 0
else:
return True
def __le__(self, other):
if isinstance(other, timedelta):
- return self.__cmp(other) <= 0
+ return self._cmp(other) <= 0
else:
_cmperror(self, other)
def __lt__(self, other):
if isinstance(other, timedelta):
- return self.__cmp(other) < 0
+ return self._cmp(other) < 0
else:
_cmperror(self, other)
def __ge__(self, other):
if isinstance(other, timedelta):
- return self.__cmp(other) >= 0
+ return self._cmp(other) >= 0
else:
_cmperror(self, other)
def __gt__(self, other):
if isinstance(other, timedelta):
- return self.__cmp(other) > 0
+ return self._cmp(other) > 0
else:
_cmperror(self, other)
- def __cmp(self, other):
+ def _cmp(self, other):
assert isinstance(other, timedelta)
- return cmp(self.__getstate(), other.__getstate())
+ return _cmp(self._getstate(), other._getstate())
def __hash__(self):
- return hash(self.__getstate())
+ if self._hashcode == -1:
+ self._hashcode = hash(self._getstate())
+ return self._hashcode
def __nonzero__(self):
- return (self.__days != 0 or
- self.__seconds != 0 or
- self.__microseconds != 0)
+ return (self._days != 0 or
+ self._seconds != 0 or
+ self._microseconds != 0)
# Pickle support.
- __safe_for_unpickling__ = True # For Python 2.2
-
- def __getstate(self):
- return (self.__days, self.__seconds, self.__microseconds)
+ def _getstate(self):
+ return (self._days, self._seconds, self._microseconds)
def __reduce__(self):
- return (self.__class__, self.__getstate())
+ return (self.__class__, self._getstate())
timedelta.min = timedelta(-999999999)
timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59,
@@ -736,6 +737,7 @@
Properties (readonly):
year, month, day
"""
+ __slots__ = '_year', '_month', '_day', '_hashcode'
def __new__(cls, year, month=None, day=None):
"""Constructor.
@@ -744,32 +746,36 @@
year, month, day (required, base 1)
"""
- if isinstance(year, str):
+ if month is None and isinstance(year, bytes) and len(year) == 4 and \
+ 1 <= ord(year[2]) <= 12:
# Pickle support
self = object.__new__(cls)
self.__setstate(year)
+ self._hashcode = -1
return self
- _check_date_fields(year, month, day)
+ year, month, day = _check_date_fields(year, month, day)
self = object.__new__(cls)
- self.__year = year
- self.__month = month
- self.__day = day
+ self._year = year
+ self._month = month
+ self._day = day
+ self._hashcode = -1
return self
# Additional constructors
+ @classmethod
def fromtimestamp(cls, t):
"Construct a date from a POSIX timestamp (like time.time())."
y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
return cls(y, m, d)
- fromtimestamp = classmethod(fromtimestamp)
+ @classmethod
def today(cls):
"Construct a date from time.time()."
t = _time.time()
return cls.fromtimestamp(t)
- today = classmethod(today)
+ @classmethod
def fromordinal(cls, n):
"""Contruct a date from a proleptic Gregorian ordinal.
@@ -778,29 +784,50 @@
"""
y, m, d = _ord2ymd(n)
return cls(y, m, d)
- fromordinal = classmethod(fromordinal)
# Conversions to string
def __repr__(self):
- "Convert to formal string, for repr()."
+ """Convert to formal string, for repr().
+
+ >>> dt = datetime(2010, 1, 1)
+ >>> repr(dt)
+ 'datetime.datetime(2010, 1, 1, 0, 0)'
+
+ >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
+ >>> repr(dt)
+ 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'
+ """
return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__,
- self.__year,
- self.__month,
- self.__day)
+ self._year,
+ self._month,
+ self._day)
+
# XXX These shouldn't depend on time.localtime(), because that
# clips the usable dates to [1970 .. 2038). At least ctime() is
# easily done without using strftime() -- that's better too because
# strftime("%c", ...) is locale specific.
def ctime(self):
- "Format a la ctime()."
- return tmxxx(self.__year, self.__month, self.__day).ctime()
+ "Return ctime() style string."
+ weekday = self.toordinal() % 7 or 7
+ return "%s %s %2d 00:00:00 %04d" % (
+ _DAYNAMES[weekday],
+ _MONTHNAMES[self._month],
+ self._day, self._year)
def strftime(self, fmt):
"Format using strftime()."
return _wrap_strftime(self, fmt, self.timetuple())
+ def __format__(self, fmt):
+ if not isinstance(fmt, (str, unicode)):
+ raise ValueError("__format__ expects str or unicode, not %s" %
+ fmt.__class__.__name__)
+ if len(fmt) != 0:
+ return self.strftime(fmt)
+ return str(self)
+
def isoformat(self):
"""Return the date formatted according to ISO.
@@ -810,21 +837,31 @@
- http://www.w3.org/TR/NOTE-datetime
- http://www.cl.cam.ac.uk/~mgk25/iso-time.html
"""
- return "%04d-%02d-%02d" % (self.__year, self.__month, self.__day)
+ return "%04d-%02d-%02d" % (self._year, self._month, self._day)
__str__ = isoformat
# Read-only field accessors
- year = property(lambda self: self.__year,
- doc="year (%d-%d)" % (MINYEAR, MAXYEAR))
- month = property(lambda self: self.__month, doc="month (1-12)")
- day = property(lambda self: self.__day, doc="day (1-31)")
+ @property
+ def year(self):
+ """year (1-9999)"""
+ return self._year
+
+ @property
+ def month(self):
+ """month (1-12)"""
+ return self._month
+
+ @property
+ def day(self):
+ """day (1-31)"""
+ return self._day
# Standard conversions, __cmp__, __hash__ (and helpers)
def timetuple(self):
"Return local time tuple compatible with time.localtime()."
- return _build_struct_time(self.__year, self.__month, self.__day,
+ return _build_struct_time(self._year, self._month, self._day,
0, 0, 0, -1)
def toordinal(self):
@@ -833,24 +870,23 @@
January 1 of year 1 is day 1. Only the year, month and day values
contribute to the result.
"""
- return _ymd2ord(self.__year, self.__month, self.__day)
+ return _ymd2ord(self._year, self._month, self._day)
def replace(self, year=None, month=None, day=None):
"""Return a new date with new values for the specified fields."""
if year is None:
- year = self.__year
+ year = self._year
if month is None:
- month = self.__month
+ month = self._month
if day is None:
- day = self.__day
- _check_date_fields(year, month, day)
+ day = self._day
return date(year, month, day)
- # Comparisons.
+ # Comparisons of date objects with other.
def __eq__(self, other):
if isinstance(other, date):
- return self.__cmp(other) == 0
+ return self._cmp(other) == 0
elif hasattr(other, "timetuple"):
return NotImplemented
else:
@@ -858,7 +894,7 @@
def __ne__(self, other):
if isinstance(other, date):
- return self.__cmp(other) != 0
+ return self._cmp(other) != 0
elif hasattr(other, "timetuple"):
return NotImplemented
else:
@@ -866,7 +902,7 @@
def __le__(self, other):
if isinstance(other, date):
- return self.__cmp(other) <= 0
+ return self._cmp(other) <= 0
elif hasattr(other, "timetuple"):
return NotImplemented
else:
@@ -874,7 +910,7 @@
def __lt__(self, other):
if isinstance(other, date):
- return self.__cmp(other) < 0
+ return self._cmp(other) < 0
elif hasattr(other, "timetuple"):
return NotImplemented
else:
@@ -882,7 +918,7 @@
def __ge__(self, other):
if isinstance(other, date):
- return self.__cmp(other) >= 0
+ return self._cmp(other) >= 0
elif hasattr(other, "timetuple"):
return NotImplemented
else:
@@ -890,21 +926,23 @@
def __gt__(self, other):
if isinstance(other, date):
- return self.__cmp(other) > 0
+ return self._cmp(other) > 0
elif hasattr(other, "timetuple"):
return NotImplemented
else:
_cmperror(self, other)
- def __cmp(self, other):
+ def _cmp(self, other):
assert isinstance(other, date)
- y, m, d = self.__year, self.__month, self.__day
- y2, m2, d2 = other.__year, other.__month, other.__day
- return cmp((y, m, d), (y2, m2, d2))
+ y, m, d = self._year, self._month, self._day
+ y2, m2, d2 = other._year, other._month, other._day
+ return _cmp((y, m, d), (y2, m2, d2))
def __hash__(self):
"Hash."
- return hash(self.__getstate())
+ if self._hashcode == -1:
+ self._hashcode = hash(self._getstate())
+ return self._hashcode
# Computations
@@ -916,14 +954,13 @@
def __add__(self, other):
"Add a date to a timedelta."
if isinstance(other, timedelta):
- t = tmxxx(self.__year,
- self.__month,
- self.__day + other.days)
+ t = _tmxxx(self._year,
+ self._month,
+ self._day + other.days)
self._checkOverflow(t.year)
result = date(t.year, t.month, t.day)
return result
- raise TypeError
- # XXX Should be 'return NotImplemented', but there's a bug in 2.2...
+ return NotImplemented
__radd__ = __add__
@@ -960,9 +997,9 @@
ISO calendar algorithm taken from
http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
"""
- year = self.__year
+ year = self._year
week1monday = _isoweek1monday(year)
- today = _ymd2ord(self.__year, self.__month, self.__day)
+ today = _ymd2ord(self._year, self._month, self._day)
# Internally, week and day have origin 0
week, day = divmod(today - week1monday, 7)
if week < 0:
@@ -977,34 +1014,17 @@
# Pickle support.
- __safe_for_unpickling__ = True # For Python 2.2
-
- def __getstate(self):
- yhi, ylo = divmod(self.__year, 256)
- return ("%c%c%c%c" % (yhi, ylo, self.__month, self.__day), )
+ def _getstate(self):
+ yhi, ylo = divmod(self._year, 256)
+ return (_struct.pack('4B', yhi, ylo, self._month, self._day),)
def __setstate(self, string):
- if len(string) != 4 or not (1 <= ord(string[2]) <= 12):
- raise TypeError("not enough arguments")
- yhi, ylo, self.__month, self.__day = map(ord, string)
- self.__year = yhi * 256 + ylo
+ yhi, ylo, self._month, self._day = (ord(string[0]), ord(string[1]),
+ ord(string[2]), ord(string[3]))
+ self._year = yhi * 256 + ylo
def __reduce__(self):
- return (self.__class__, self.__getstate())
-
- if _sys.platform.startswith('java'):
- def __tojava__(self, java_class):
- if java_class not in (Calendar, Date, Object):
- return Py.NoConversion
-
- calendar = Calendar.getInstance()
- calendar.clear()
- calendar.set(self.year, self.month - 1, self.day)
- if java_class == Calendar:
- return calendar
- else:
- return Date(calendar.getTimeInMillis())
-
+ return (self.__class__, self._getstate())
_date_class = date # so functions w/ args named "date" can get at the class
@@ -1017,6 +1037,7 @@
Subclasses must override the name(), utcoffset() and dst() methods.
"""
+ __slots__ = ()
def tzname(self, dt):
"datetime -> string name of time zone."
@@ -1066,8 +1087,6 @@
# Pickle support.
- __safe_for_unpickling__ = True # For Python 2.2
-
def __reduce__(self):
getinitargs = getattr(self, "__getinitargs__", None)
if getinitargs:
@@ -1084,7 +1103,7 @@
else:
return (self.__class__, args, state)
-_tzinfo_class = tzinfo # so functions w/ args named "tinfo" can get at it
+_tzinfo_class = tzinfo
class time(object):
"""Time with time zone.
@@ -1109,6 +1128,7 @@
Properties (readonly):
hour, minute, second, microsecond, tzinfo
"""
+ __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode'
def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None):
"""Constructor.
@@ -1119,69 +1139,91 @@
second, microsecond (default to zero)
tzinfo (default to None)
"""
+ if isinstance(hour, bytes) and len(hour) == 6 and ord(hour[0]) < 24:
+ # Pickle support
+ self = object.__new__(cls)
+ self.__setstate(hour, minute or None)
+ self._hashcode = -1
+ return self
+ hour, minute, second, microsecond = _check_time_fields(
+ hour, minute, second, microsecond)
+ _check_tzinfo_arg(tzinfo)
self = object.__new__(cls)
- if isinstance(hour, str):
- # Pickle support
- self.__setstate(hour, minute or None)
- return self
- _check_tzinfo_arg(tzinfo)
- _check_time_fields(hour, minute, second, microsecond)
- self.__hour = hour
- self.__minute = minute
- self.__second = second
- self.__microsecond = microsecond
+ self._hour = hour
+ self._minute = minute
+ self._second = second
+ self._microsecond = microsecond
self._tzinfo = tzinfo
+ self._hashcode = -1
return self
# Read-only field accessors
- hour = property(lambda self: self.__hour, doc="hour (0-23)")
- minute = property(lambda self: self.__minute, doc="minute (0-59)")
- second = property(lambda self: self.__second, doc="second (0-59)")
- microsecond = property(lambda self: self.__microsecond,
- doc="microsecond (0-999999)")
- tzinfo = property(lambda self: self._tzinfo, doc="timezone info object")
+ @property
+ def hour(self):
+ """hour (0-23)"""
+ return self._hour
+
+ @property
+ def minute(self):
+ """minute (0-59)"""
+ return self._minute
+
+ @property
+ def second(self):
+ """second (0-59)"""
+ return self._second
+
+ @property
+ def microsecond(self):
+ """microsecond (0-999999)"""
+ return self._microsecond
+
+ @property
+ def tzinfo(self):
+ """timezone info object"""
+ return self._tzinfo
# Standard conversions, __hash__ (and helpers)
- # Comparisons.
+ # Comparisons of time objects with other.
def __eq__(self, other):
if isinstance(other, time):
- return self.__cmp(other) == 0
+ return self._cmp(other) == 0
else:
return False
def __ne__(self, other):
if isinstance(other, time):
- return self.__cmp(other) != 0
+ return self._cmp(other) != 0
else:
return True
def __le__(self, other):
if isinstance(other, time):
- return self.__cmp(other) <= 0
+ return self._cmp(other) <= 0
else:
_cmperror(self, other)
def __lt__(self, other):
if isinstance(other, time):
- return self.__cmp(other) < 0
+ return self._cmp(other) < 0
else:
_cmperror(self, other)
def __ge__(self, other):
if isinstance(other, time):
- return self.__cmp(other) >= 0
+ return self._cmp(other) >= 0
else:
_cmperror(self, other)
def __gt__(self, other):
if isinstance(other, time):
- return self.__cmp(other) > 0
+ return self._cmp(other) > 0
else:
_cmperror(self, other)
- def __cmp(self, other):
+ def _cmp(self, other):
assert isinstance(other, time)
mytz = self._tzinfo
ottz = other._tzinfo
@@ -1195,27 +1237,30 @@
base_compare = myoff == otoff
if base_compare:
- return cmp((self.__hour, self.__minute, self.__second,
- self.__microsecond),
- (other.__hour, other.__minute, other.__second,
- other.__microsecond))
+ return _cmp((self._hour, self._minute, self._second,
+ self._microsecond),
+ (other._hour, other._minute, other._second,
+ other._microsecond))
if myoff is None or otoff is None:
- # XXX Buggy in 2.2.2.
- raise TypeError("cannot compare naive and aware times")
- myhhmm = self.__hour * 60 + self.__minute - myoff
- othhmm = other.__hour * 60 + other.__minute - otoff
- return cmp((myhhmm, self.__second, self.__microsecond),
- (othhmm, other.__second, other.__microsecond))
+ raise TypeError("can't compare offset-naive and offset-aware times")
+ myhhmm = self._hour * 60 + self._minute - myoff
+ othhmm = other._hour * 60 + other._minute - otoff
+ return _cmp((myhhmm, self._second, self._microsecond),
+ (othhmm, other._second, other._microsecond))
def __hash__(self):
"""Hash."""
- tzoff = self._utcoffset()
- if not tzoff: # zero or None
- return hash(self.__getstate()[0])
- h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
- if 0 <= h < 24:
- return hash(time(h, m, self.second, self.microsecond))
- return hash((h, m, self.second, self.microsecond))
+ if self._hashcode == -1:
+ tzoff = self._utcoffset()
+ if not tzoff: # zero or None
+ self._hashcode = hash(self._getstate()[0])
+ else:
+ h, m = divmod(self.hour * 60 + self.minute - tzoff, 60)
+ if 0 <= h < 24:
+ self._hashcode = hash(time(h, m, self.second, self.microsecond))
+ else:
+ self._hashcode = hash((h, m, self.second, self.microsecond))
+ return self._hashcode
# Conversion to string
@@ -1235,14 +1280,14 @@
def __repr__(self):
"""Convert to formal string, for repr()."""
- if self.__microsecond != 0:
- s = ", %d, %d" % (self.__second, self.__microsecond)
- elif self.__second != 0:
- s = ", %d" % self.__second
+ if self._microsecond != 0:
+ s = ", %d, %d" % (self._second, self._microsecond)
+ elif self._second != 0:
+ s = ", %d" % self._second
else:
s = ""
s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__,
- self.__hour, self.__minute, s)
+ self._hour, self._minute, s)
if self._tzinfo is not None:
assert s[-1:] == ")"
s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
@@ -1254,8 +1299,8 @@
This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if
self.microsecond == 0.
"""
- s = _format_time(self.__hour, self.__minute, self.__second,
- self.__microsecond)
+ s = _format_time(self._hour, self._minute, self._second,
+ self._microsecond)
tz = self._tzstr()
if tz:
s += tz
@@ -1267,19 +1312,29 @@
"""Format using strftime(). The date part of the timestamp passed
to underlying strftime should not be used.
"""
- # The year must be >= 1900 else Python's strftime implementation
+ # The year must be >= _MINYEARFMT else Python's strftime implementation
# can raise a bogus exception.
timetuple = (1900, 1, 1,
- self.__hour, self.__minute, self.__second,
+ self._hour, self._minute, self._second,
0, 1, -1)
- return _wrap_strftime(self, fmt, timetuple, self.microsecond)
+ return _wrap_strftime(self, fmt, timetuple)
+
+ def __format__(self, fmt):
+ if not isinstance(fmt, (str, unicode)):
+ raise ValueError("__format__ expects str or unicode, not %s" %
+ fmt.__class__.__name__)
+ if len(fmt) != 0:
+ return self.strftime(fmt)
+ return str(self)
# Timezone functions
def utcoffset(self):
"""Return the timezone offset in minutes east of UTC (negative west of
UTC)."""
- offset = _call_tzinfo_method(self._tzinfo, "utcoffset", None)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.utcoffset(None)
offset = _check_utc_offset("utcoffset", offset)
if offset is not None:
offset = timedelta(minutes=offset)
@@ -1287,7 +1342,9 @@
# Return an integer (or None) instead of a timedelta (or None).
def _utcoffset(self):
- offset = _call_tzinfo_method(self._tzinfo, "utcoffset", None)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.utcoffset(None)
offset = _check_utc_offset("utcoffset", offset)
return offset
@@ -1298,7 +1355,9 @@
it mean anything in particular. For example, "GMT", "UTC", "-500",
"-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
"""
- name = _call_tzinfo_method(self._tzinfo, "tzname", None)
+ if self._tzinfo is None:
+ return None
+ name = self._tzinfo.tzname(None)
_check_tzname(name)
return name
@@ -1311,12 +1370,22 @@
need to consult dst() unless you're interested in displaying the DST
info.
"""
- offset = _call_tzinfo_method(self._tzinfo, "dst", None)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.dst(None)
offset = _check_utc_offset("dst", offset)
if offset is not None:
offset = timedelta(minutes=offset)
return offset
+ # Return an integer (or None) instead of a timedelta (or None).
+ def _dst(self):
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.dst(None)
+ offset = _check_utc_offset("dst", offset)
+ return offset
+
def replace(self, hour=None, minute=None, second=None, microsecond=None,
tzinfo=True):
"""Return a new time with new values for the specified fields."""
@@ -1330,64 +1399,37 @@
microsecond = self.microsecond
if tzinfo is True:
tzinfo = self.tzinfo
- _check_time_fields(hour, minute, second, microsecond)
- _check_tzinfo_arg(tzinfo)
return time(hour, minute, second, microsecond, tzinfo)
- # Return an integer (or None) instead of a timedelta (or None).
- def _dst(self):
- offset = _call_tzinfo_method(self._tzinfo, "dst", None)
- offset = _check_utc_offset("dst", offset)
- return offset
-
def __nonzero__(self):
if self.second or self.microsecond:
- return 1
+ return True
offset = self._utcoffset() or 0
- return self.hour * 60 + self.minute - offset != 0
+ return self.hour * 60 + self.minute != offset
# Pickle support.
- __safe_for_unpickling__ = True # For Python 2.2
-
- def __getstate(self):
- us2, us3 = divmod(self.__microsecond, 256)
+ def _getstate(self):
+ us2, us3 = divmod(self._microsecond, 256)
us1, us2 = divmod(us2, 256)
- basestate = ("%c" * 6) % (self.__hour, self.__minute, self.__second,
- us1, us2, us3)
+ basestate = _struct.pack('6B', self._hour, self._minute, self._second,
+ us1, us2, us3)
if self._tzinfo is None:
return (basestate,)
else:
return (basestate, self._tzinfo)
def __setstate(self, string, tzinfo):
- if len(string) != 6 or ord(string[0]) >= 24:
- raise TypeError("an integer is required")
- self.__hour, self.__minute, self.__second, us1, us2, us3 = \
- map(ord, string)
- self.__microsecond = (((us1 << 8) | us2) << 8) | us3
+ if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
+ raise TypeError("bad tzinfo state arg")
+ self._hour, self._minute, self._second, us1, us2, us3 = (
+ ord(string[0]), ord(string[1]), ord(string[2]),
+ ord(string[3]), ord(string[4]), ord(string[5]))
+ self._microsecond = (((us1 << 8) | us2) << 8) | us3
self._tzinfo = tzinfo
def __reduce__(self):
- return (time, self.__getstate())
-
- if _sys.platform.startswith('java'):
- def __tojava__(self, java_class):
- # TODO, if self.tzinfo is not None, convert time to UTC
- if java_class not in (Calendar, Time, Object):
- return Py.NoConversion
-
- calendar = Calendar.getInstance()
- calendar.clear()
- calendar.set(Calendar.HOUR_OF_DAY, self.hour)
- calendar.set(Calendar.MINUTE, self.minute)
- calendar.set(Calendar.SECOND, self.second)
- calendar.set(Calendar.MILLISECOND, self.microsecond // 1000)
- if java_class == Calendar:
- return calendar
- else:
- return Time(calendar.getTimeInMillis())
-
+ return (time, self._getstate())
_time_class = time # so functions w/ args named "time" can get at the class
@@ -1396,39 +1438,65 @@
time.resolution = timedelta(microseconds=1)
class datetime(date):
+ """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
- # XXX needs docstrings
- # See http://www.zope.org/Members/fdrake/DateTimeWiki/TimeZoneInfo
+ The year, month and day arguments are required. tzinfo may be None, or an
+ instance of a tzinfo subclass. The remaining arguments may be ints or longs.
+ """
+ __slots__ = date.__slots__ + time.__slots__
def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
microsecond=0, tzinfo=None):
- if isinstance(year, str):
+ if isinstance(year, bytes) and len(year) == 10 and \
+ 1 <= ord(year[2]) <= 12:
# Pickle support
- self = date.__new__(cls, year[:4])
+ self = object.__new__(cls)
self.__setstate(year, month)
+ self._hashcode = -1
return self
+ year, month, day = _check_date_fields(year, month, day)
+ hour, minute, second, microsecond = _check_time_fields(
+ hour, minute, second, microsecond)
_check_tzinfo_arg(tzinfo)
- _check_time_fields(hour, minute, second, microsecond)
- self = date.__new__(cls, year, month, day)
- # XXX This duplicates __year, __month, __day for convenience :-(
- self.__year = year
- self.__month = month
- self.__day = day
- self.__hour = hour
- self.__minute = minute
- self.__second = second
- self.__microsecond = microsecond
+ self = object.__new__(cls)
+ self._year = year
+ self._month = month
+ self._day = day
+ self._hour = hour
+ self._minute = minute
+ self._second = second
+ self._microsecond = microsecond
self._tzinfo = tzinfo
+ self._hashcode = -1
return self
# Read-only field accessors
- hour = property(lambda self: self.__hour, doc="hour (0-23)")
- minute = property(lambda self: self.__minute, doc="minute (0-59)")
- second = property(lambda self: self.__second, doc="second (0-59)")
- microsecond = property(lambda self: self.__microsecond,
- doc="microsecond (0-999999)")
- tzinfo = property(lambda self: self._tzinfo, doc="timezone info object")
+ @property
+ def hour(self):
+ """hour (0-23)"""
+ return self._hour
+ @property
+ def minute(self):
+ """minute (0-59)"""
+ return self._minute
+
+ @property
+ def second(self):
+ """second (0-59)"""
+ return self._second
+
+ @property
+ def microsecond(self):
+ """microsecond (0-999999)"""
+ return self._microsecond
+
+ @property
+ def tzinfo(self):
+ """timezone info object"""
+ return self._tzinfo
+
+ @classmethod
def fromtimestamp(cls, t, tz=None):
"""Construct a datetime from a POSIX timestamp (like time.time()).
@@ -1436,53 +1504,56 @@
"""
_check_tzinfo_arg(tz)
- if tz is None:
- converter = _time.localtime
- else:
- converter = _time.gmtime
+
+ converter = _time.localtime if tz is None else _time.gmtime
+
+ t, frac = divmod(t, 1.0)
+ us = _round(frac * 1e6)
+
+ # If timestamp is less than one microsecond smaller than a
+ # full second, us can be rounded up to 1000000. In this case,
+ # roll over to seconds, otherwise, ValueError is raised
+ # by the constructor.
+ if us == 1000000:
+ t += 1
+ us = 0
y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
- us = int((t % 1.0) * 1000000)
-
- if us == 1000001 or us == 999999:
- us = 0
- rounded = True
- else:
- rounded = False
-
ss = min(ss, 59) # clamp out leap seconds if the platform has them
result = cls(y, m, d, hh, mm, ss, us, tz)
- if rounded:
- result += timedelta(seconds=1)
if tz is not None:
result = tz.fromutc(result)
return result
- fromtimestamp = classmethod(fromtimestamp)
+ @classmethod
def utcfromtimestamp(cls, t):
"Construct a UTC datetime from a POSIX timestamp (like time.time())."
+ t, frac = divmod(t, 1.0)
+ us = _round(frac * 1e6)
+
+ # If timestamp is less than one microsecond smaller than a
+ # full second, us can be rounded up to 1000000. In this case,
+ # roll over to seconds, otherwise, ValueError is raised
+ # by the constructor.
+ if us == 1000000:
+ t += 1
+ us = 0
y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
- us = int((t % 1.0) * 1000000)
ss = min(ss, 59) # clamp out leap seconds if the platform has them
return cls(y, m, d, hh, mm, ss, us)
- utcfromtimestamp = classmethod(utcfromtimestamp)
- # XXX This is supposed to do better than we *can* do by using time.time(),
- # XXX if the platform supports a more accurate way. The C implementation
- # XXX uses gettimeofday on platforms that have it, but that isn't
- # XXX available from Python. So now() may return different results
- # XXX across the implementations.
+ @classmethod
def now(cls, tz=None):
"Construct a datetime from time.time() and optional time zone info."
t = _time.time()
return cls.fromtimestamp(t, tz)
- now = classmethod(now)
+ @classmethod
def utcnow(cls):
"Construct a UTC datetime from time.time()."
t = _time.time()
return cls.utcfromtimestamp(t)
- utcnow = classmethod(utcnow)
+ @classmethod
def combine(cls, date, time):
"Construct a datetime from a given date and a given time."
if not isinstance(date, _date_class):
@@ -1492,16 +1563,6 @@
return cls(date.year, date.month, date.day,
time.hour, time.minute, time.second, time.microsecond,
time.tzinfo)
- combine = classmethod(combine)
-
- def strptime(cls, date_string, format):
- """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
-
- The year, month and day arguments are required. tzinfo may be None, or an
- instance of a tzinfo subclass. The remaining arguments may be ints or longs."""
- return cls(*(_time.strptime(date_string, format))[0:6])
-
- strptime = classmethod(strptime)
def timetuple(self):
"Return local time tuple compatible with time.localtime()."
@@ -1520,14 +1581,14 @@
hh, mm, ss = self.hour, self.minute, self.second
offset = self._utcoffset()
if offset: # neither None nor 0
- tm = tmxxx(y, m, d, hh, mm - offset)
+ tm = _tmxxx(y, m, d, hh, mm - offset)
y, m, d = tm.year, tm.month, tm.day
hh, mm = tm.hour, tm.minute
return _build_struct_time(y, m, d, hh, mm, ss, 0)
def date(self):
"Return the date part."
- return date(self.__year, self.__month, self.__day)
+ return date(self._year, self._month, self._day)
def time(self):
"Return the time part, with tzinfo None."
@@ -1557,11 +1618,8 @@
microsecond = self.microsecond
if tzinfo is True:
tzinfo = self.tzinfo
- _check_date_fields(year, month, day)
- _check_time_fields(hour, minute, second, microsecond)
- _check_tzinfo_arg(tzinfo)
- return datetime(year, month, day, hour, minute, second,
- microsecond, tzinfo)
+ return datetime(year, month, day, hour, minute, second, microsecond,
+ tzinfo)
def astimezone(self, tz):
if not isinstance(tz, tzinfo):
@@ -1586,10 +1644,14 @@
# Ways to produce a string.
def ctime(self):
- "Format a la ctime()."
- t = tmxxx(self.__year, self.__month, self.__day, self.__hour,
- self.__minute, self.__second)
- return t.ctime()
+ "Return ctime() style string."
+ weekday = self.toordinal() % 7 or 7
+ return "%s %s %2d %02d:%02d:%02d %04d" % (
+ _DAYNAMES[weekday],
+ _MONTHNAMES[self._month],
+ self._day,
+ self._hour, self._minute, self._second,
+ self._year)
def isoformat(self, sep='T'):
"""Return the time formatted according to ISO.
@@ -1603,10 +1665,9 @@
Optional argument sep specifies the separator between date and
time, default 'T'.
"""
- s = ("%04d-%02d-%02d%c" % (self.__year, self.__month, self.__day,
- sep) +
- _format_time(self.__hour, self.__minute, self.__second,
- self.__microsecond))
+ s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) +
+ _format_time(self._hour, self._minute, self._second,
+ self._microsecond))
off = self._utcoffset()
if off is not None:
if off < 0:
@@ -1619,9 +1680,9 @@
return s
def __repr__(self):
- "Convert to formal string, for repr()."
- L = [self.__year, self.__month, self.__day, # These are never zero
- self.__hour, self.__minute, self.__second, self.__microsecond]
+ """Convert to formal string, for repr()."""
+ L = [self._year, self._month, self._day, # These are never zero
+ self._hour, self._minute, self._second, self._microsecond]
if L[-1] == 0:
del L[-1]
if L[-1] == 0:
@@ -1637,14 +1698,22 @@
"Convert to string, for str()."
return self.isoformat(sep=' ')
- def strftime(self, fmt):
- "Format using strftime()."
- return _wrap_strftime(self, fmt, self.timetuple(), self.microsecond)
+ @classmethod
+ def strptime(cls, date_string, format):
+ 'string, format -> new datetime parsed from a string (like time.strptime()).'
+ from _strptime import _strptime
+ # _strptime._strptime returns a two-element tuple. The first
+ # element is a time.struct_time object. The second is the
+ # microseconds (which are not defined for time.struct_time).
+ struct, micros = _strptime(date_string, format)
+ return cls(*(struct[0:6] + (micros,)))
def utcoffset(self):
"""Return the timezone offset in minutes east of UTC (negative west of
UTC)."""
- offset = _call_tzinfo_method(self._tzinfo, "utcoffset", self)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.utcoffset(self)
offset = _check_utc_offset("utcoffset", offset)
if offset is not None:
offset = timedelta(minutes=offset)
@@ -1652,7 +1721,9 @@
# Return an integer (or None) instead of a timedelta (or None).
def _utcoffset(self):
- offset = _call_tzinfo_method(self._tzinfo, "utcoffset", self)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.utcoffset(self)
offset = _check_utc_offset("utcoffset", offset)
return offset
@@ -1663,7 +1734,9 @@
it mean anything in particular. For example, "GMT", "UTC", "-500",
"-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
"""
- name = _call_tzinfo_method(self._tzinfo, "tzname", self)
+ if self._tzinfo is None:
+ return None
+ name = self._tzinfo.tzname(self)
_check_tzname(name)
return name
@@ -1676,23 +1749,27 @@
need to consult dst() unless you're interested in displaying the DST
info.
"""
- offset = _call_tzinfo_method(self._tzinfo, "dst", self)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.dst(self)
offset = _check_utc_offset("dst", offset)
if offset is not None:
offset = timedelta(minutes=offset)
return offset
- # Return an integer (or None) instead of a timedelta (or None).1573
+ # Return an integer (or None) instead of a timedelta (or None).
def _dst(self):
- offset = _call_tzinfo_method(self._tzinfo, "dst", self)
+ if self._tzinfo is None:
+ return None
+ offset = self._tzinfo.dst(self)
offset = _check_utc_offset("dst", offset)
return offset
- # Comparisons.
+ # Comparisons of datetime objects with other.
def __eq__(self, other):
if isinstance(other, datetime):
- return self.__cmp(other) == 0
+ return self._cmp(other) == 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
@@ -1700,7 +1777,7 @@
def __ne__(self, other):
if isinstance(other, datetime):
- return self.__cmp(other) != 0
+ return self._cmp(other) != 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
@@ -1708,7 +1785,7 @@
def __le__(self, other):
if isinstance(other, datetime):
- return self.__cmp(other) <= 0
+ return self._cmp(other) <= 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
@@ -1716,7 +1793,7 @@
def __lt__(self, other):
if isinstance(other, datetime):
- return self.__cmp(other) < 0
+ return self._cmp(other) < 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
@@ -1724,7 +1801,7 @@
def __ge__(self, other):
if isinstance(other, datetime):
- return self.__cmp(other) >= 0
+ return self._cmp(other) >= 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
@@ -1732,13 +1809,13 @@
def __gt__(self, other):
if isinstance(other, datetime):
- return self.__cmp(other) > 0
+ return self._cmp(other) > 0
elif hasattr(other, "timetuple") and not isinstance(other, date):
return NotImplemented
else:
_cmperror(self, other)
- def __cmp(self, other):
+ def _cmp(self, other):
assert isinstance(other, datetime)
mytz = self._tzinfo
ottz = other._tzinfo
@@ -1754,15 +1831,14 @@
base_compare = myoff == otoff
if base_compare:
- return cmp((self.__year, self.__month, self.__day,
- self.__hour, self.__minute, self.__second,
- self.__microsecond),
- (other.__year, other.__month, other.__day,
- other.__hour, other.__minute, other.__second,
- other.__microsecond))
+ return _cmp((self._year, self._month, self._day,
+ self._hour, self._minute, self._second,
+ self._microsecond),
+ (other._year, other._month, other._day,
+ other._hour, other._minute, other._second,
+ other._microsecond))
if myoff is None or otoff is None:
- # XXX Buggy in 2.2.2.
- raise TypeError("cannot compare naive and aware datetimes")
+ raise TypeError("can't compare offset-naive and offset-aware datetimes")
# XXX What follows could be done more efficiently...
diff = self - other # this will take offsets into account
if diff.days < 0:
@@ -1773,13 +1849,13 @@
"Add a datetime and a timedelta."
if not isinstance(other, timedelta):
return NotImplemented
- t = tmxxx(self.__year,
- self.__month,
- self.__day + other.days,
- self.__hour,
- self.__minute,
- self.__second + other.seconds,
- self.__microsecond + other.microseconds)
+ t = _tmxxx(self._year,
+ self._month,
+ self._day + other.days,
+ self._hour,
+ self._minute,
+ self._second + other.seconds,
+ self._microsecond + other.microseconds)
self._checkOverflow(t.year)
result = datetime(t.year, t.month, t.day,
t.hour, t.minute, t.second,
@@ -1797,11 +1873,11 @@
days1 = self.toordinal()
days2 = other.toordinal()
- secs1 = self.__second + self.__minute * 60 + self.__hour * 3600
- secs2 = other.__second + other.__minute * 60 + other.__hour * 3600
+ secs1 = self._second + self._minute * 60 + self._hour * 3600
+ secs2 = other._second + other._minute * 60 + other._hour * 3600
base = timedelta(days1 - days2,
secs1 - secs2,
- self.__microsecond - other.__microsecond)
+ self._microsecond - other._microsecond)
if self._tzinfo is other._tzinfo:
return base
myoff = self._utcoffset()
@@ -1809,61 +1885,48 @@
if myoff == otoff:
return base
if myoff is None or otoff is None:
- raise TypeError, "cannot mix naive and timezone-aware time"
+ raise TypeError("can't subtract offset-naive and offset-aware datetimes")
return base + timedelta(minutes = otoff-myoff)
def __hash__(self):
- tzoff = self._utcoffset()
- if tzoff is None:
- return hash(self.__getstate()[0])
- days = _ymd2ord(self.year, self.month, self.day)
- seconds = self.hour * 3600 + (self.minute - tzoff) * 60 + self.second
- return hash(timedelta(days, seconds, self.microsecond))
+ if self._hashcode == -1:
+ tzoff = self._utcoffset()
+ if tzoff is None:
+ self._hashcode = hash(self._getstate()[0])
+ else:
+ days = _ymd2ord(self.year, self.month, self.day)
+ seconds = self.hour * 3600 + (self.minute - tzoff) * 60 + self.second
+ self._hashcode = hash(timedelta(days, seconds, self.microsecond))
+ return self._hashcode
# Pickle support.
- __safe_for_unpickling__ = True # For Python 2.2
-
- def __getstate(self):
- yhi, ylo = divmod(self.__year, 256)
- us2, us3 = divmod(self.__microsecond, 256)
+ def _getstate(self):
+ yhi, ylo = divmod(self._year, 256)
+ us2, us3 = divmod(self._microsecond, 256)
us1, us2 = divmod(us2, 256)
- basestate = ("%c" * 10) % (yhi, ylo, self.__month, self.__day,
- self.__hour, self.__minute, self.__second,
- us1, us2, us3)
+ basestate = _struct.pack('10B', yhi, ylo, self._month, self._day,
+ self._hour, self._minute, self._second,
+ us1, us2, us3)
if self._tzinfo is None:
return (basestate,)
else:
return (basestate, self._tzinfo)
def __setstate(self, string, tzinfo):
- (yhi, ylo, self.__month, self.__day, self.__hour,
- self.__minute, self.__second, us1, us2, us3) = map(ord, string)
- self.__year = yhi * 256 + ylo
- self.__microsecond = (((us1 << 8) | us2) << 8) | us3
+ if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
+ raise TypeError("bad tzinfo state arg")
+ (yhi, ylo, self._month, self._day, self._hour,
+ self._minute, self._second, us1, us2, us3) = (ord(string[0]),
+ ord(string[1]), ord(string[2]), ord(string[3]),
+ ord(string[4]), ord(string[5]), ord(string[6]),
+ ord(string[7]), ord(string[8]), ord(string[9]))
+ self._year = yhi * 256 + ylo
+ self._microsecond = (((us1 << 8) | us2) << 8) | us3
self._tzinfo = tzinfo
def __reduce__(self):
- return (self.__class__, self.__getstate())
-
- if _sys.platform.startswith('java'):
- def __tojava__(self, java_class):
- # TODO, if self.tzinfo is not None, convert time to UTC
- if java_class not in (Calendar, Timestamp, Object):
- return Py.NoConversion
-
- calendar = Calendar.getInstance()
- calendar.clear()
- calendar.set(self.year, self.month - 1, self.day,
- self.hour, self.minute, self.second)
-
- if java_class == Calendar:
- calendar.set(Calendar.MILLISECOND, self.microsecond // 1000)
- return calendar
- else:
- timestamp = Timestamp(calendar.getTimeInMillis())
- timestamp.setNanos(self.microsecond * 1000)
- return timestamp
+ return (self.__class__, self._getstate())
datetime.min = datetime(1, 1, 1)
@@ -1876,7 +1939,7 @@
# XXX This could be done more efficiently
THURSDAY = 3
firstday = _ymd2ord(year, 1, 1)
- firstweekday = (firstday + 6) % 7 # See weekday() above
+ firstweekday = (firstday + 6) % 7 # See weekday() above
week1monday = firstday - firstweekday
if firstweekday > THURSDAY:
week1monday += 7
@@ -2045,7 +2108,7 @@
Because we know z.d said z was in daylight time (else [5] would have held and
we would have stopped then), and we know z.d != z'.d (else [8] would have held
-and we we have stopped then), and there are only 2 possible values dst() can
+and we have stopped then), and there are only 2 possible values dst() can
return in Eastern, it follows that z'.d must be 0 (which it is in the example,
but the reasoning doesn't depend on the example -- it depends on there being
two possible dst() outcomes, one zero and the other non-zero). Therefore
diff --git a/Lib/test/test_datetime.py b/Lib/test/test_datetime.py
deleted file mode 100644
--- a/Lib/test/test_datetime.py
+++ /dev/null
@@ -1,3372 +0,0 @@
-"""Test date/time type.
-
-See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
-"""
-from __future__ import division
-import sys
-import pickle
-import cPickle
-import unittest
-
-from test import test_support
-
-from datetime import MINYEAR, MAXYEAR
-from datetime import timedelta
-from datetime import tzinfo
-from datetime import time
-from datetime import date, datetime
-
-pickle_choices = [(pickler, unpickler, proto)
- for pickler in pickle, cPickle
- for unpickler in pickle, cPickle
- for proto in range(3)]
-assert len(pickle_choices) == 2*2*3
-
-# An arbitrary collection of objects of non-datetime types, for testing
-# mixed-type comparisons.
-OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
-
-
-#############################################################################
-# module tests
-
-class TestModule(unittest.TestCase):
-
- def test_constants(self):
- import datetime
- self.assertEqual(datetime.MINYEAR, 1)
- self.assertEqual(datetime.MAXYEAR, 9999)
-
-#############################################################################
-# tzinfo tests
-
-class FixedOffset(tzinfo):
- def __init__(self, offset, name, dstoffset=42):
- if isinstance(offset, int):
- offset = timedelta(minutes=offset)
- if isinstance(dstoffset, int):
- dstoffset = timedelta(minutes=dstoffset)
- self.__offset = offset
- self.__name = name
- self.__dstoffset = dstoffset
- def __repr__(self):
- return self.__name.lower()
- def utcoffset(self, dt):
- return self.__offset
- def tzname(self, dt):
- return self.__name
- def dst(self, dt):
- return self.__dstoffset
-
-class PicklableFixedOffset(FixedOffset):
- def __init__(self, offset=None, name=None, dstoffset=None):
- FixedOffset.__init__(self, offset, name, dstoffset)
-
-class TestTZInfo(unittest.TestCase):
-
- def test_non_abstractness(self):
- # In order to allow subclasses to get pickled, the C implementation
- # wasn't able to get away with having __init__ raise
- # NotImplementedError.
- useless = tzinfo()
- dt = datetime.max
- self.assertRaises(NotImplementedError, useless.tzname, dt)
- self.assertRaises(NotImplementedError, useless.utcoffset, dt)
- self.assertRaises(NotImplementedError, useless.dst, dt)
-
- def test_subclass_must_override(self):
- class NotEnough(tzinfo):
- def __init__(self, offset, name):
- self.__offset = offset
- self.__name = name
- self.assertTrue(issubclass(NotEnough, tzinfo))
- ne = NotEnough(3, "NotByALongShot")
- self.assertIsInstance(ne, tzinfo)
-
- dt = datetime.now()
- self.assertRaises(NotImplementedError, ne.tzname, dt)
- self.assertRaises(NotImplementedError, ne.utcoffset, dt)
- self.assertRaises(NotImplementedError, ne.dst, dt)
-
- def test_normal(self):
- fo = FixedOffset(3, "Three")
- self.assertIsInstance(fo, tzinfo)
- for dt in datetime.now(), None:
- self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
- self.assertEqual(fo.tzname(dt), "Three")
- self.assertEqual(fo.dst(dt), timedelta(minutes=42))
-
- def test_pickling_base(self):
- # There's no point to pickling tzinfo objects on their own (they
- # carry no data), but they need to be picklable anyway else
- # concrete subclasses can't be pickled.
- orig = tzinfo.__new__(tzinfo)
- self.assertTrue(type(orig) is tzinfo)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertTrue(type(derived) is tzinfo)
-
- def test_pickling_subclass(self):
- # Make sure we can pickle/unpickle an instance of a subclass.
- offset = timedelta(minutes=-300)
- orig = PicklableFixedOffset(offset, 'cookie')
- self.assertIsInstance(orig, tzinfo)
- self.assertTrue(type(orig) is PicklableFixedOffset)
- self.assertEqual(orig.utcoffset(None), offset)
- self.assertEqual(orig.tzname(None), 'cookie')
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertIsInstance(derived, tzinfo)
- self.assertTrue(type(derived) is PicklableFixedOffset)
- self.assertEqual(derived.utcoffset(None), offset)
- self.assertEqual(derived.tzname(None), 'cookie')
-
-#############################################################################
-# Base clase for testing a particular aspect of timedelta, time, date and
-# datetime comparisons.
-
-class HarmlessMixedComparison:
- # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
-
- # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
- # legit constructor.
-
- def test_harmless_mixed_comparison(self):
- me = self.theclass(1, 1, 1)
-
- self.assertFalse(me == ())
- self.assertTrue(me != ())
- self.assertFalse(() == me)
- self.assertTrue(() != me)
-
- self.assertIn(me, [1, 20L, [], me])
- self.assertIn([], [me, 1, 20L, []])
-
- def test_harmful_mixed_comparison(self):
- me = self.theclass(1, 1, 1)
-
- self.assertRaises(TypeError, lambda: me < ())
- self.assertRaises(TypeError, lambda: me <= ())
- self.assertRaises(TypeError, lambda: me > ())
- self.assertRaises(TypeError, lambda: me >= ())
-
- self.assertRaises(TypeError, lambda: () < me)
- self.assertRaises(TypeError, lambda: () <= me)
- self.assertRaises(TypeError, lambda: () > me)
- self.assertRaises(TypeError, lambda: () >= me)
-
- self.assertRaises(TypeError, cmp, (), me)
- self.assertRaises(TypeError, cmp, me, ())
-
-#############################################################################
-# timedelta tests
-
-class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
-
- theclass = timedelta
-
- def test_constructor(self):
- eq = self.assertEqual
- td = timedelta
-
- # Check keyword args to constructor
- eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
- milliseconds=0, microseconds=0))
- eq(td(1), td(days=1))
- eq(td(0, 1), td(seconds=1))
- eq(td(0, 0, 1), td(microseconds=1))
- eq(td(weeks=1), td(days=7))
- eq(td(days=1), td(hours=24))
- eq(td(hours=1), td(minutes=60))
- eq(td(minutes=1), td(seconds=60))
- eq(td(seconds=1), td(milliseconds=1000))
- eq(td(milliseconds=1), td(microseconds=1000))
-
- # Check float args to constructor
- eq(td(weeks=1.0/7), td(days=1))
- eq(td(days=1.0/24), td(hours=1))
- eq(td(hours=1.0/60), td(minutes=1))
- eq(td(minutes=1.0/60), td(seconds=1))
- eq(td(seconds=0.001), td(milliseconds=1))
- eq(td(milliseconds=0.001), td(microseconds=1))
-
- @unittest.skipIf(test_support.is_jython, "FIXME: overflow error on Jython")
- def test_computations(self):
- eq = self.assertEqual
- td = timedelta
-
- a = td(7) # One week
- b = td(0, 60) # One minute
- c = td(0, 0, 1000) # One millisecond
- eq(a+b+c, td(7, 60, 1000))
- eq(a-b, td(6, 24*3600 - 60))
- eq(-a, td(-7))
- eq(+a, td(7))
- eq(-b, td(-1, 24*3600 - 60))
- eq(-c, td(-1, 24*3600 - 1, 999000))
- eq(abs(a), a)
- eq(abs(-a), a)
- eq(td(6, 24*3600), a)
- eq(td(0, 0, 60*1000000), b)
- eq(a*10, td(70))
- eq(a*10, 10*a)
- eq(a*10L, 10*a)
- eq(b*10, td(0, 600))
- eq(10*b, td(0, 600))
- eq(b*10L, td(0, 600))
- eq(c*10, td(0, 0, 10000))
- eq(10*c, td(0, 0, 10000))
- eq(c*10L, td(0, 0, 10000))
- eq(a*-1, -a)
- eq(b*-2, -b-b)
- eq(c*-2, -c+-c)
- eq(b*(60*24), (b*60)*24)
- eq(b*(60*24), (60*b)*24)
- eq(c*1000, td(0, 1))
- eq(1000*c, td(0, 1))
- eq(a//7, td(1))
- eq(b//10, td(0, 6))
- eq(c//1000, td(0, 0, 1))
- eq(a//10, td(0, 7*24*360))
- eq(a//3600000, td(0, 0, 7*24*1000))
-
- # Issue #11576
- eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
- td(0, 0, 1))
- eq(td(999999999, 1, 1) - td(999999999, 1, 0),
- td(0, 0, 1))
-
-
- def test_disallowed_computations(self):
- a = timedelta(42)
-
- # Add/sub ints, longs, floats should be illegal
- for i in 1, 1L, 1.0:
- self.assertRaises(TypeError, lambda: a+i)
- self.assertRaises(TypeError, lambda: a-i)
- self.assertRaises(TypeError, lambda: i+a)
- self.assertRaises(TypeError, lambda: i-a)
-
- # Mul/div by float isn't supported.
- x = 2.3
- self.assertRaises(TypeError, lambda: a*x)
- self.assertRaises(TypeError, lambda: x*a)
- self.assertRaises(TypeError, lambda: a/x)
- self.assertRaises(TypeError, lambda: x/a)
- self.assertRaises(TypeError, lambda: a // x)
- self.assertRaises(TypeError, lambda: x // a)
-
- # Division of int by timedelta doesn't make sense.
- # Division by zero doesn't make sense.
- for zero in 0, 0L:
- self.assertRaises(TypeError, lambda: zero // a)
- self.assertRaises(ZeroDivisionError, lambda: a // zero)
-
- def test_basic_attributes(self):
- days, seconds, us = 1, 7, 31
- td = timedelta(days, seconds, us)
- self.assertEqual(td.days, days)
- self.assertEqual(td.seconds, seconds)
- self.assertEqual(td.microseconds, us)
-
- @unittest.skipIf(test_support.is_jython, "FIXME: total_seconds() not supported")
- def test_total_seconds(self):
- td = timedelta(days=365)
- self.assertEqual(td.total_seconds(), 31536000.0)
- for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
- td = timedelta(seconds=total_seconds)
- self.assertEqual(td.total_seconds(), total_seconds)
- # Issue8644: Test that td.total_seconds() has the same
- # accuracy as td / timedelta(seconds=1).
- for ms in [-1, -2, -123]:
- td = timedelta(microseconds=ms)
- self.assertEqual(td.total_seconds(),
- ((24*3600*td.days + td.seconds)*10**6
- + td.microseconds)/10**6)
-
- def test_carries(self):
- t1 = timedelta(days=100,
- weeks=-7,
- hours=-24*(100-49),
- minutes=-3,
- seconds=12,
- microseconds=(3*60 - 12) * 1e6 + 1)
- t2 = timedelta(microseconds=1)
- self.assertEqual(t1, t2)
-
- def test_hash_equality(self):
- t1 = timedelta(days=100,
- weeks=-7,
- hours=-24*(100-49),
- minutes=-3,
- seconds=12,
- microseconds=(3*60 - 12) * 1000000)
- t2 = timedelta()
- self.assertEqual(hash(t1), hash(t2))
-
- t1 += timedelta(weeks=7)
- t2 += timedelta(days=7*7)
- self.assertEqual(t1, t2)
- self.assertEqual(hash(t1), hash(t2))
-
- d = {t1: 1}
- d[t2] = 2
- self.assertEqual(len(d), 1)
- self.assertEqual(d[t1], 2)
-
- def test_pickling(self):
- args = 12, 34, 56
- orig = timedelta(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_compare(self):
- t1 = timedelta(2, 3, 4)
- t2 = timedelta(2, 3, 4)
- self.assertTrue(t1 == t2)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t1 >= t2)
- self.assertTrue(not t1 != t2)
- self.assertTrue(not t1 < t2)
- self.assertTrue(not t1 > t2)
- self.assertEqual(cmp(t1, t2), 0)
- self.assertEqual(cmp(t2, t1), 0)
-
- for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
- t2 = timedelta(*args) # this is larger than t1
- self.assertTrue(t1 < t2)
- self.assertTrue(t2 > t1)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t2 >= t1)
- self.assertTrue(t1 != t2)
- self.assertTrue(t2 != t1)
- self.assertTrue(not t1 == t2)
- self.assertTrue(not t2 == t1)
- self.assertTrue(not t1 > t2)
- self.assertTrue(not t2 < t1)
- self.assertTrue(not t1 >= t2)
- self.assertTrue(not t2 <= t1)
- self.assertEqual(cmp(t1, t2), -1)
- self.assertEqual(cmp(t2, t1), 1)
-
- for badarg in OTHERSTUFF:
- self.assertEqual(t1 == badarg, False)
- self.assertEqual(t1 != badarg, True)
- self.assertEqual(badarg == t1, False)
- self.assertEqual(badarg != t1, True)
-
- self.assertRaises(TypeError, lambda: t1 <= badarg)
- self.assertRaises(TypeError, lambda: t1 < badarg)
- self.assertRaises(TypeError, lambda: t1 > badarg)
- self.assertRaises(TypeError, lambda: t1 >= badarg)
- self.assertRaises(TypeError, lambda: badarg <= t1)
- self.assertRaises(TypeError, lambda: badarg < t1)
- self.assertRaises(TypeError, lambda: badarg > t1)
- self.assertRaises(TypeError, lambda: badarg >= t1)
-
- def test_str(self):
- td = timedelta
- eq = self.assertEqual
-
- eq(str(td(1)), "1 day, 0:00:00")
- eq(str(td(-1)), "-1 day, 0:00:00")
- eq(str(td(2)), "2 days, 0:00:00")
- eq(str(td(-2)), "-2 days, 0:00:00")
-
- eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
- eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
- eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
- "-210 days, 23:12:34")
-
- eq(str(td(milliseconds=1)), "0:00:00.001000")
- eq(str(td(microseconds=3)), "0:00:00.000003")
-
- eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
- microseconds=999999)),
- "999999999 days, 23:59:59.999999")
-
- def test_roundtrip(self):
- for td in (timedelta(days=999999999, hours=23, minutes=59,
- seconds=59, microseconds=999999),
- timedelta(days=-999999999),
- timedelta(days=1, seconds=2, microseconds=3)):
-
- # Verify td -> string -> td identity.
- s = repr(td)
- self.assertTrue(s.startswith('datetime.'))
- s = s[9:]
- td2 = eval(s)
- self.assertEqual(td, td2)
-
- # Verify identity via reconstructing from pieces.
- td2 = timedelta(td.days, td.seconds, td.microseconds)
- self.assertEqual(td, td2)
-
- def test_resolution_info(self):
- self.assertIsInstance(timedelta.min, timedelta)
- self.assertIsInstance(timedelta.max, timedelta)
- self.assertIsInstance(timedelta.resolution, timedelta)
- self.assertTrue(timedelta.max > timedelta.min)
- self.assertEqual(timedelta.min, timedelta(-999999999))
- self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
- self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
-
- def test_overflow(self):
- tiny = timedelta.resolution
-
- td = timedelta.min + tiny
- td -= tiny # no problem
- self.assertRaises(OverflowError, td.__sub__, tiny)
- self.assertRaises(OverflowError, td.__add__, -tiny)
-
- td = timedelta.max - tiny
- td += tiny # no problem
- self.assertRaises(OverflowError, td.__add__, tiny)
- self.assertRaises(OverflowError, td.__sub__, -tiny)
-
- self.assertRaises(OverflowError, lambda: -timedelta.max)
-
- def test_microsecond_rounding(self):
- td = timedelta
- eq = self.assertEqual
-
- # Single-field rounding.
- eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
- eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
- eq(td(milliseconds=0.6/1000), td(microseconds=1))
- eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
-
- # Rounding due to contributions from more than one field.
- us_per_hour = 3600e6
- us_per_day = us_per_hour * 24
- eq(td(days=.4/us_per_day), td(0))
- eq(td(hours=.2/us_per_hour), td(0))
- eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
-
- eq(td(days=-.4/us_per_day), td(0))
- eq(td(hours=-.2/us_per_hour), td(0))
- eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
-
- def test_massive_normalization(self):
- td = timedelta(microseconds=-1)
- self.assertEqual((td.days, td.seconds, td.microseconds),
- (-1, 24*3600-1, 999999))
-
- def test_bool(self):
- self.assertTrue(timedelta(1))
- self.assertTrue(timedelta(0, 1))
- self.assertTrue(timedelta(0, 0, 1))
- self.assertTrue(timedelta(microseconds=1))
- self.assertTrue(not timedelta(0))
-
- def test_subclass_timedelta(self):
-
- class T(timedelta):
- @staticmethod
- def from_td(td):
- return T(td.days, td.seconds, td.microseconds)
-
- def as_hours(self):
- sum = (self.days * 24 +
- self.seconds / 3600.0 +
- self.microseconds / 3600e6)
- return round(sum)
-
- t1 = T(days=1)
- self.assertTrue(type(t1) is T)
- self.assertEqual(t1.as_hours(), 24)
-
- t2 = T(days=-1, seconds=-3600)
- self.assertTrue(type(t2) is T)
- self.assertEqual(t2.as_hours(), -25)
-
- t3 = t1 + t2
- self.assertTrue(type(t3) is timedelta)
- t4 = T.from_td(t3)
- self.assertTrue(type(t4) is T)
- self.assertEqual(t3.days, t4.days)
- self.assertEqual(t3.seconds, t4.seconds)
- self.assertEqual(t3.microseconds, t4.microseconds)
- self.assertEqual(str(t3), str(t4))
- self.assertEqual(t4.as_hours(), -1)
-
-#############################################################################
-# date tests
-
-class TestDateOnly(unittest.TestCase):
- # Tests here won't pass if also run on datetime objects, so don't
- # subclass this to test datetimes too.
-
- def test_delta_non_days_ignored(self):
- dt = date(2000, 1, 2)
- delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
- microseconds=5)
- days = timedelta(delta.days)
- self.assertEqual(days, timedelta(1))
-
- dt2 = dt + delta
- self.assertEqual(dt2, dt + days)
-
- dt2 = delta + dt
- self.assertEqual(dt2, dt + days)
-
- dt2 = dt - delta
- self.assertEqual(dt2, dt - days)
-
- delta = -delta
- days = timedelta(delta.days)
- self.assertEqual(days, timedelta(-2))
-
- dt2 = dt + delta
- self.assertEqual(dt2, dt + days)
-
- dt2 = delta + dt
- self.assertEqual(dt2, dt + days)
-
- dt2 = dt - delta
- self.assertEqual(dt2, dt - days)
-
-class SubclassDate(date):
- sub_var = 1
-
-class TestDate(HarmlessMixedComparison, unittest.TestCase):
- # Tests here should pass for both dates and datetimes, except for a
- # few tests that TestDateTime overrides.
-
- theclass = date
-
- def test_basic_attributes(self):
- dt = self.theclass(2002, 3, 1)
- self.assertEqual(dt.year, 2002)
- self.assertEqual(dt.month, 3)
- self.assertEqual(dt.day, 1)
-
- def test_roundtrip(self):
- for dt in (self.theclass(1, 2, 3),
- self.theclass.today()):
- # Verify dt -> string -> date identity.
- s = repr(dt)
- self.assertTrue(s.startswith('datetime.'))
- s = s[9:]
- dt2 = eval(s)
- self.assertEqual(dt, dt2)
-
- # Verify identity via reconstructing from pieces.
- dt2 = self.theclass(dt.year, dt.month, dt.day)
- self.assertEqual(dt, dt2)
-
- def test_ordinal_conversions(self):
- # Check some fixed values.
- for y, m, d, n in [(1, 1, 1, 1), # calendar origin
- (1, 12, 31, 365),
- (2, 1, 1, 366),
- # first example from "Calendrical Calculations"
- (1945, 11, 12, 710347)]:
- d = self.theclass(y, m, d)
- self.assertEqual(n, d.toordinal())
- fromord = self.theclass.fromordinal(n)
- self.assertEqual(d, fromord)
- if hasattr(fromord, "hour"):
- # if we're checking something fancier than a date, verify
- # the extra fields have been zeroed out
- self.assertEqual(fromord.hour, 0)
- self.assertEqual(fromord.minute, 0)
- self.assertEqual(fromord.second, 0)
- self.assertEqual(fromord.microsecond, 0)
-
- # Check first and last days of year spottily across the whole
- # range of years supported.
- for year in xrange(MINYEAR, MAXYEAR+1, 7):
- # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
- d = self.theclass(year, 1, 1)
- n = d.toordinal()
- d2 = self.theclass.fromordinal(n)
- self.assertEqual(d, d2)
- # Verify that moving back a day gets to the end of year-1.
- if year > 1:
- d = self.theclass.fromordinal(n-1)
- d2 = self.theclass(year-1, 12, 31)
- self.assertEqual(d, d2)
- self.assertEqual(d2.toordinal(), n-1)
-
- # Test every day in a leap-year and a non-leap year.
- dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
- for year, isleap in (2000, True), (2002, False):
- n = self.theclass(year, 1, 1).toordinal()
- for month, maxday in zip(range(1, 13), dim):
- if month == 2 and isleap:
- maxday += 1
- for day in range(1, maxday+1):
- d = self.theclass(year, month, day)
- self.assertEqual(d.toordinal(), n)
- self.assertEqual(d, self.theclass.fromordinal(n))
- n += 1
-
- def test_extreme_ordinals(self):
- a = self.theclass.min
- a = self.theclass(a.year, a.month, a.day) # get rid of time parts
- aord = a.toordinal()
- b = a.fromordinal(aord)
- self.assertEqual(a, b)
-
- self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
-
- b = a + timedelta(days=1)
- self.assertEqual(b.toordinal(), aord + 1)
- self.assertEqual(b, self.theclass.fromordinal(aord + 1))
-
- a = self.theclass.max
- a = self.theclass(a.year, a.month, a.day) # get rid of time parts
- aord = a.toordinal()
- b = a.fromordinal(aord)
- self.assertEqual(a, b)
-
- self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
-
- b = a - timedelta(days=1)
- self.assertEqual(b.toordinal(), aord - 1)
- self.assertEqual(b, self.theclass.fromordinal(aord - 1))
-
- def test_bad_constructor_arguments(self):
- # bad years
- self.theclass(MINYEAR, 1, 1) # no exception
- self.theclass(MAXYEAR, 1, 1) # no exception
- self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
- self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
- # bad months
- self.theclass(2000, 1, 1) # no exception
- self.theclass(2000, 12, 1) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
- self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
- # bad days
- self.theclass(2000, 2, 29) # no exception
- self.theclass(2004, 2, 29) # no exception
- self.theclass(2400, 2, 29) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
- self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
- self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
- self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
-
- def test_hash_equality(self):
- d = self.theclass(2000, 12, 31)
- # same thing
- e = self.theclass(2000, 12, 31)
- self.assertEqual(d, e)
- self.assertEqual(hash(d), hash(e))
-
- dic = {d: 1}
- dic[e] = 2
- self.assertEqual(len(dic), 1)
- self.assertEqual(dic[d], 2)
- self.assertEqual(dic[e], 2)
-
- d = self.theclass(2001, 1, 1)
- # same thing
- e = self.theclass(2001, 1, 1)
- self.assertEqual(d, e)
- self.assertEqual(hash(d), hash(e))
-
- dic = {d: 1}
- dic[e] = 2
- self.assertEqual(len(dic), 1)
- self.assertEqual(dic[d], 2)
- self.assertEqual(dic[e], 2)
-
- def test_computations(self):
- a = self.theclass(2002, 1, 31)
- b = self.theclass(1956, 1, 31)
-
- diff = a-b
- self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
- self.assertEqual(diff.seconds, 0)
- self.assertEqual(diff.microseconds, 0)
-
- day = timedelta(1)
- week = timedelta(7)
- a = self.theclass(2002, 3, 2)
- self.assertEqual(a + day, self.theclass(2002, 3, 3))
- self.assertEqual(day + a, self.theclass(2002, 3, 3))
- self.assertEqual(a - day, self.theclass(2002, 3, 1))
- self.assertEqual(-day + a, self.theclass(2002, 3, 1))
- self.assertEqual(a + week, self.theclass(2002, 3, 9))
- self.assertEqual(a - week, self.theclass(2002, 2, 23))
- self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
- self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
- self.assertEqual((a + week) - a, week)
- self.assertEqual((a + day) - a, day)
- self.assertEqual((a - week) - a, -week)
- self.assertEqual((a - day) - a, -day)
- self.assertEqual(a - (a + week), -week)
- self.assertEqual(a - (a + day), -day)
- self.assertEqual(a - (a - week), week)
- self.assertEqual(a - (a - day), day)
-
- # Add/sub ints, longs, floats should be illegal
- for i in 1, 1L, 1.0:
- self.assertRaises(TypeError, lambda: a+i)
- self.assertRaises(TypeError, lambda: a-i)
- self.assertRaises(TypeError, lambda: i+a)
- self.assertRaises(TypeError, lambda: i-a)
-
- # delta - date is senseless.
- self.assertRaises(TypeError, lambda: day - a)
- # mixing date and (delta or date) via * or // is senseless
- self.assertRaises(TypeError, lambda: day * a)
- self.assertRaises(TypeError, lambda: a * day)
- self.assertRaises(TypeError, lambda: day // a)
- self.assertRaises(TypeError, lambda: a // day)
- self.assertRaises(TypeError, lambda: a * a)
- self.assertRaises(TypeError, lambda: a // a)
- # date + date is senseless
- self.assertRaises(TypeError, lambda: a + a)
-
- def test_overflow(self):
- tiny = self.theclass.resolution
-
- for delta in [tiny, timedelta(1), timedelta(2)]:
- dt = self.theclass.min + delta
- dt -= delta # no problem
- self.assertRaises(OverflowError, dt.__sub__, delta)
- self.assertRaises(OverflowError, dt.__add__, -delta)
-
- dt = self.theclass.max - delta
- dt += delta # no problem
- self.assertRaises(OverflowError, dt.__add__, delta)
- self.assertRaises(OverflowError, dt.__sub__, -delta)
-
- def test_fromtimestamp(self):
- import time
-
- # Try an arbitrary fixed value.
- year, month, day = 1999, 9, 19
- ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
- d = self.theclass.fromtimestamp(ts)
- self.assertEqual(d.year, year)
- self.assertEqual(d.month, month)
- self.assertEqual(d.day, day)
-
- def test_insane_fromtimestamp(self):
- # It's possible that some platform maps time_t to double,
- # and that this test will fail there. This test should
- # exempt such platforms (provided they return reasonable
- # results!).
- for insane in -1e200, 1e200:
- self.assertRaises(ValueError, self.theclass.fromtimestamp,
- insane)
-
- def test_today(self):
- import time
-
- # We claim that today() is like fromtimestamp(time.time()), so
- # prove it.
- for dummy in range(3):
- today = self.theclass.today()
- ts = time.time()
- todayagain = self.theclass.fromtimestamp(ts)
- if today == todayagain:
- break
- # There are several legit reasons that could fail:
- # 1. It recently became midnight, between the today() and the
- # time() calls.
- # 2. The platform time() has such fine resolution that we'll
- # never get the same value twice.
- # 3. The platform time() has poor resolution, and we just
- # happened to call today() right before a resolution quantum
- # boundary.
- # 4. The system clock got fiddled between calls.
- # In any case, wait a little while and try again.
- time.sleep(0.1)
-
- # It worked or it didn't. If it didn't, assume it's reason #2, and
- # let the test pass if they're within half a second of each other.
- self.assertTrue(today == todayagain or
- abs(todayagain - today) < timedelta(seconds=0.5))
-
- def test_weekday(self):
- for i in range(7):
- # March 4, 2002 is a Monday
- self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
- self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
- # January 2, 1956 is a Monday
- self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
- self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
-
- def test_isocalendar(self):
- # Check examples from
- # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
- for i in range(7):
- d = self.theclass(2003, 12, 22+i)
- self.assertEqual(d.isocalendar(), (2003, 52, i+1))
- d = self.theclass(2003, 12, 29) + timedelta(i)
- self.assertEqual(d.isocalendar(), (2004, 1, i+1))
- d = self.theclass(2004, 1, 5+i)
- self.assertEqual(d.isocalendar(), (2004, 2, i+1))
- d = self.theclass(2009, 12, 21+i)
- self.assertEqual(d.isocalendar(), (2009, 52, i+1))
- d = self.theclass(2009, 12, 28) + timedelta(i)
- self.assertEqual(d.isocalendar(), (2009, 53, i+1))
- d = self.theclass(2010, 1, 4+i)
- self.assertEqual(d.isocalendar(), (2010, 1, i+1))
-
- def test_iso_long_years(self):
- # Calculate long ISO years and compare to table from
- # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
- ISO_LONG_YEARS_TABLE = """
- 4 32 60 88
- 9 37 65 93
- 15 43 71 99
- 20 48 76
- 26 54 82
-
- 105 133 161 189
- 111 139 167 195
- 116 144 172
- 122 150 178
- 128 156 184
-
- 201 229 257 285
- 207 235 263 291
- 212 240 268 296
- 218 246 274
- 224 252 280
-
- 303 331 359 387
- 308 336 364 392
- 314 342 370 398
- 320 348 376
- 325 353 381
- """
- iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
- iso_long_years.sort()
- L = []
- for i in range(400):
- d = self.theclass(2000+i, 12, 31)
- d1 = self.theclass(1600+i, 12, 31)
- self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
- if d.isocalendar()[1] == 53:
- L.append(i)
- self.assertEqual(L, iso_long_years)
-
- def test_isoformat(self):
- t = self.theclass(2, 3, 2)
- self.assertEqual(t.isoformat(), "0002-03-02")
-
- def test_ctime(self):
- t = self.theclass(2002, 3, 2)
- self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
-
- def test_strftime(self):
- t = self.theclass(2005, 3, 2)
- self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
- self.assertEqual(t.strftime(""), "") # SF bug #761337
- self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
-
- self.assertRaises(TypeError, t.strftime) # needs an arg
- self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
- self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
-
- # test that unicode input is allowed (issue 2782)
- self.assertEqual(t.strftime(u"%m"), "03")
-
- # A naive object replaces %z and %Z w/ empty strings.
- self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
-
- #make sure that invalid format specifiers are handled correctly
- #self.assertRaises(ValueError, t.strftime, "%e")
- #self.assertRaises(ValueError, t.strftime, "%")
- #self.assertRaises(ValueError, t.strftime, "%#")
-
- #oh well, some systems just ignore those invalid ones.
- #at least, excercise them to make sure that no crashes
- #are generated
- for f in ["%e", "%", "%#"]:
- try:
- t.strftime(f)
- except ValueError:
- pass
-
- #check that this standard extension works
- t.strftime("%f")
-
- @unittest.skipIf(test_support.is_jython, "FIXME: some formats not accepted by Jython")
- def test_format(self):
- dt = self.theclass(2007, 9, 10)
- self.assertEqual(dt.__format__(''), str(dt))
-
- # check that a derived class's __str__() gets called
- class A(self.theclass):
- def __str__(self):
- return 'A'
- a = A(2007, 9, 10)
- self.assertEqual(a.__format__(''), 'A')
-
- # check that a derived class's strftime gets called
- class B(self.theclass):
- def strftime(self, format_spec):
- return 'B'
- b = B(2007, 9, 10)
- self.assertEqual(b.__format__(''), str(dt))
-
- for fmt in ["m:%m d:%d y:%y",
- "m:%m d:%d y:%y H:%H M:%M S:%S",
- "%z %Z",
- ]:
- self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
- self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
- self.assertEqual(b.__format__(fmt), 'B')
-
- def test_resolution_info(self):
- self.assertIsInstance(self.theclass.min, self.theclass)
- self.assertIsInstance(self.theclass.max, self.theclass)
- self.assertIsInstance(self.theclass.resolution, timedelta)
- self.assertTrue(self.theclass.max > self.theclass.min)
-
- def test_extreme_timedelta(self):
- big = self.theclass.max - self.theclass.min
- # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
- n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
- # n == 315537897599999999 ~= 2**58.13
- justasbig = timedelta(0, 0, n)
- self.assertEqual(big, justasbig)
- self.assertEqual(self.theclass.min + big, self.theclass.max)
- self.assertEqual(self.theclass.max - big, self.theclass.min)
-
- def test_timetuple(self):
- for i in range(7):
- # January 2, 1956 is a Monday (0)
- d = self.theclass(1956, 1, 2+i)
- t = d.timetuple()
- self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
- # February 1, 1956 is a Wednesday (2)
- d = self.theclass(1956, 2, 1+i)
- t = d.timetuple()
- self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
- # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
- # of the year.
- d = self.theclass(1956, 3, 1+i)
- t = d.timetuple()
- self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
- self.assertEqual(t.tm_year, 1956)
- self.assertEqual(t.tm_mon, 3)
- self.assertEqual(t.tm_mday, 1+i)
- self.assertEqual(t.tm_hour, 0)
- self.assertEqual(t.tm_min, 0)
- self.assertEqual(t.tm_sec, 0)
- self.assertEqual(t.tm_wday, (3+i)%7)
- self.assertEqual(t.tm_yday, 61+i)
- self.assertEqual(t.tm_isdst, -1)
-
- def test_pickling(self):
- args = 6, 7, 23
- orig = self.theclass(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_compare(self):
- t1 = self.theclass(2, 3, 4)
- t2 = self.theclass(2, 3, 4)
- self.assertTrue(t1 == t2)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t1 >= t2)
- self.assertTrue(not t1 != t2)
- self.assertTrue(not t1 < t2)
- self.assertTrue(not t1 > t2)
- self.assertEqual(cmp(t1, t2), 0)
- self.assertEqual(cmp(t2, t1), 0)
-
- for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
- t2 = self.theclass(*args) # this is larger than t1
- self.assertTrue(t1 < t2)
- self.assertTrue(t2 > t1)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t2 >= t1)
- self.assertTrue(t1 != t2)
- self.assertTrue(t2 != t1)
- self.assertTrue(not t1 == t2)
- self.assertTrue(not t2 == t1)
- self.assertTrue(not t1 > t2)
- self.assertTrue(not t2 < t1)
- self.assertTrue(not t1 >= t2)
- self.assertTrue(not t2 <= t1)
- self.assertEqual(cmp(t1, t2), -1)
- self.assertEqual(cmp(t2, t1), 1)
-
- for badarg in OTHERSTUFF:
- self.assertEqual(t1 == badarg, False)
- self.assertEqual(t1 != badarg, True)
- self.assertEqual(badarg == t1, False)
- self.assertEqual(badarg != t1, True)
-
- self.assertRaises(TypeError, lambda: t1 < badarg)
- self.assertRaises(TypeError, lambda: t1 > badarg)
- self.assertRaises(TypeError, lambda: t1 >= badarg)
- self.assertRaises(TypeError, lambda: badarg <= t1)
- self.assertRaises(TypeError, lambda: badarg < t1)
- self.assertRaises(TypeError, lambda: badarg > t1)
- self.assertRaises(TypeError, lambda: badarg >= t1)
-
- def test_mixed_compare(self):
- our = self.theclass(2000, 4, 5)
- self.assertRaises(TypeError, cmp, our, 1)
- self.assertRaises(TypeError, cmp, 1, our)
-
- class AnotherDateTimeClass(object):
- def __cmp__(self, other):
- # Return "equal" so calling this can't be confused with
- # compare-by-address (which never says "equal" for distinct
- # objects).
- return 0
- __hash__ = None # Silence Py3k warning
-
- # This still errors, because date and datetime comparison raise
- # TypeError instead of NotImplemented when they don't know what to
- # do, in order to stop comparison from falling back to the default
- # compare-by-address.
- their = AnotherDateTimeClass()
- self.assertRaises(TypeError, cmp, our, their)
- # Oops: The next stab raises TypeError in the C implementation,
- # but not in the Python implementation of datetime. The difference
- # is due to that the Python implementation defines __cmp__ but
- # the C implementation defines tp_richcompare. This is more pain
- # to fix than it's worth, so commenting out the test.
- # self.assertEqual(cmp(their, our), 0)
-
- # But date and datetime comparison return NotImplemented instead if the
- # other object has a timetuple attr. This gives the other object a
- # chance to do the comparison.
- class Comparable(AnotherDateTimeClass):
- def timetuple(self):
- return ()
-
- their = Comparable()
- self.assertEqual(cmp(our, their), 0)
- self.assertEqual(cmp(their, our), 0)
- self.assertTrue(our == their)
- self.assertTrue(their == our)
-
- def test_bool(self):
- # All dates are considered true.
- self.assertTrue(self.theclass.min)
- self.assertTrue(self.theclass.max)
-
- def test_strftime_out_of_range(self):
- # For nasty technical reasons, we can't handle years before 1900.
- cls = self.theclass
- self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
- for y in 1, 49, 51, 99, 100, 1000, 1899:
- self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
-
- def test_replace(self):
- cls = self.theclass
- args = [1, 2, 3]
- base = cls(*args)
- self.assertEqual(base, base.replace())
-
- i = 0
- for name, newval in (("year", 2),
- ("month", 3),
- ("day", 4)):
- newargs = args[:]
- newargs[i] = newval
- expected = cls(*newargs)
- got = base.replace(**{name: newval})
- self.assertEqual(expected, got)
- i += 1
-
- # Out of bounds.
- base = cls(2000, 2, 29)
- self.assertRaises(ValueError, base.replace, year=2001)
-
- def test_subclass_date(self):
-
- class C(self.theclass):
- theAnswer = 42
-
- def __new__(cls, *args, **kws):
- temp = kws.copy()
- extra = temp.pop('extra')
- result = self.theclass.__new__(cls, *args, **temp)
- result.extra = extra
- return result
-
- def newmeth(self, start):
- return start + self.year + self.month
-
- args = 2003, 4, 14
-
- dt1 = self.theclass(*args)
- dt2 = C(*args, **{'extra': 7})
-
- self.assertEqual(dt2.__class__, C)
- self.assertEqual(dt2.theAnswer, 42)
- self.assertEqual(dt2.extra, 7)
- self.assertEqual(dt1.toordinal(), dt2.toordinal())
- self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
-
- def test_pickling_subclass_date(self):
-
- args = 6, 7, 23
- orig = SubclassDate(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_backdoor_resistance(self):
- # For fast unpickling, the constructor accepts a pickle string.
- # This is a low-overhead backdoor. A user can (by intent or
- # mistake) pass a string directly, which (if it's the right length)
- # will get treated like a pickle, and bypass the normal sanity
- # checks in the constructor. This can create insane objects.
- # The constructor doesn't want to burn the time to validate all
- # fields, but does check the month field. This stops, e.g.,
- # datetime.datetime('1995-03-25') from yielding an insane object.
- base = '1995-03-25'
- if not issubclass(self.theclass, datetime):
- base = base[:4]
- for month_byte in '9', chr(0), chr(13), '\xff':
- self.assertRaises(TypeError, self.theclass,
- base[:2] + month_byte + base[3:])
- for ord_byte in range(1, 13):
- # This shouldn't blow up because of the month byte alone. If
- # the implementation changes to do more-careful checking, it may
- # blow up because other fields are insane.
- self.theclass(base[:2] + chr(ord_byte) + base[3:])
-
-#############################################################################
-# datetime tests
-
-class SubclassDatetime(datetime):
- sub_var = 1
-
-class TestDateTime(TestDate):
-
- theclass = datetime
-
- def test_basic_attributes(self):
- dt = self.theclass(2002, 3, 1, 12, 0)
- self.assertEqual(dt.year, 2002)
- self.assertEqual(dt.month, 3)
- self.assertEqual(dt.day, 1)
- self.assertEqual(dt.hour, 12)
- self.assertEqual(dt.minute, 0)
- self.assertEqual(dt.second, 0)
- self.assertEqual(dt.microsecond, 0)
-
- def test_basic_attributes_nonzero(self):
- # Make sure all attributes are non-zero so bugs in
- # bit-shifting access show up.
- dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
- self.assertEqual(dt.year, 2002)
- self.assertEqual(dt.month, 3)
- self.assertEqual(dt.day, 1)
- self.assertEqual(dt.hour, 12)
- self.assertEqual(dt.minute, 59)
- self.assertEqual(dt.second, 59)
- self.assertEqual(dt.microsecond, 8000)
-
- def test_roundtrip(self):
- for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
- self.theclass.now()):
- # Verify dt -> string -> datetime identity.
- s = repr(dt)
- self.assertTrue(s.startswith('datetime.'))
- s = s[9:]
- dt2 = eval(s)
- self.assertEqual(dt, dt2)
-
- # Verify identity via reconstructing from pieces.
- dt2 = self.theclass(dt.year, dt.month, dt.day,
- dt.hour, dt.minute, dt.second,
- dt.microsecond)
- self.assertEqual(dt, dt2)
-
- def test_isoformat(self):
- t = self.theclass(2, 3, 2, 4, 5, 1, 123)
- self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
- self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
- self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
- self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
- # str is ISO format with the separator forced to a blank.
- self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
-
- t = self.theclass(2, 3, 2)
- self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
- self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
- self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
- # str is ISO format with the separator forced to a blank.
- self.assertEqual(str(t), "0002-03-02 00:00:00")
-
- @unittest.skipIf(test_support.is_jython, "FIXME: some formats not accepted by Jython")
- def test_format(self):
- dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
- self.assertEqual(dt.__format__(''), str(dt))
-
- # check that a derived class's __str__() gets called
- class A(self.theclass):
- def __str__(self):
- return 'A'
- a = A(2007, 9, 10, 4, 5, 1, 123)
- self.assertEqual(a.__format__(''), 'A')
-
- # check that a derived class's strftime gets called
- class B(self.theclass):
- def strftime(self, format_spec):
- return 'B'
- b = B(2007, 9, 10, 4, 5, 1, 123)
- self.assertEqual(b.__format__(''), str(dt))
-
- for fmt in ["m:%m d:%d y:%y",
- "m:%m d:%d y:%y H:%H M:%M S:%S",
- "%z %Z",
- ]:
- self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
- self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
- self.assertEqual(b.__format__(fmt), 'B')
-
- def test_more_ctime(self):
- # Test fields that TestDate doesn't touch.
- import time
-
- t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
- self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
- # Oops! The next line fails on Win2K under MSVC 6, so it's commented
- # out. The difference is that t.ctime() produces " 2" for the day,
- # but platform ctime() produces "02" for the day. According to
- # C99, t.ctime() is correct here.
- # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
-
- # So test a case where that difference doesn't matter.
- t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
- self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
-
- def test_tz_independent_comparing(self):
- dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
- dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
- dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
- self.assertEqual(dt1, dt3)
- self.assertTrue(dt2 > dt3)
-
- # Make sure comparison doesn't forget microseconds, and isn't done
- # via comparing a float timestamp (an IEEE double doesn't have enough
- # precision to span microsecond resolution across years 1 thru 9999,
- # so comparing via timestamp necessarily calls some distinct values
- # equal).
- dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
- us = timedelta(microseconds=1)
- dt2 = dt1 + us
- self.assertEqual(dt2 - dt1, us)
- self.assertTrue(dt1 < dt2)
-
- def test_strftime_with_bad_tzname_replace(self):
- # verify ok if tzinfo.tzname().replace() returns a non-string
- class MyTzInfo(FixedOffset):
- def tzname(self, dt):
- class MyStr(str):
- def replace(self, *args):
- return None
- return MyStr('name')
- t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
- self.assertRaises(TypeError, t.strftime, '%Z')
-
- def test_bad_constructor_arguments(self):
- # bad years
- self.theclass(MINYEAR, 1, 1) # no exception
- self.theclass(MAXYEAR, 1, 1) # no exception
- self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
- self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
- # bad months
- self.theclass(2000, 1, 1) # no exception
- self.theclass(2000, 12, 1) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
- self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
- # bad days
- self.theclass(2000, 2, 29) # no exception
- self.theclass(2004, 2, 29) # no exception
- self.theclass(2400, 2, 29) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
- self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
- self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
- self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
- # bad hours
- self.theclass(2000, 1, 31, 0) # no exception
- self.theclass(2000, 1, 31, 23) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
- # bad minutes
- self.theclass(2000, 1, 31, 23, 0) # no exception
- self.theclass(2000, 1, 31, 23, 59) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
- # bad seconds
- self.theclass(2000, 1, 31, 23, 59, 0) # no exception
- self.theclass(2000, 1, 31, 23, 59, 59) # no exception
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
- self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
- # bad microseconds
- self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
- self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
- self.assertRaises(ValueError, self.theclass,
- 2000, 1, 31, 23, 59, 59, -1)
- self.assertRaises(ValueError, self.theclass,
- 2000, 1, 31, 23, 59, 59,
- 1000000)
-
- def test_hash_equality(self):
- d = self.theclass(2000, 12, 31, 23, 30, 17)
- e = self.theclass(2000, 12, 31, 23, 30, 17)
- self.assertEqual(d, e)
- self.assertEqual(hash(d), hash(e))
-
- dic = {d: 1}
- dic[e] = 2
- self.assertEqual(len(dic), 1)
- self.assertEqual(dic[d], 2)
- self.assertEqual(dic[e], 2)
-
- d = self.theclass(2001, 1, 1, 0, 5, 17)
- e = self.theclass(2001, 1, 1, 0, 5, 17)
- self.assertEqual(d, e)
- self.assertEqual(hash(d), hash(e))
-
- dic = {d: 1}
- dic[e] = 2
- self.assertEqual(len(dic), 1)
- self.assertEqual(dic[d], 2)
- self.assertEqual(dic[e], 2)
-
- def test_computations(self):
- a = self.theclass(2002, 1, 31)
- b = self.theclass(1956, 1, 31)
- diff = a-b
- self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
- self.assertEqual(diff.seconds, 0)
- self.assertEqual(diff.microseconds, 0)
- a = self.theclass(2002, 3, 2, 17, 6)
- millisec = timedelta(0, 0, 1000)
- hour = timedelta(0, 3600)
- day = timedelta(1)
- week = timedelta(7)
- self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
- self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
- self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
- self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
- self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
- self.assertEqual(a - hour, a + -hour)
- self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
- self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
- self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
- self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
- self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
- self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
- self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
- self.assertEqual((a + week) - a, week)
- self.assertEqual((a + day) - a, day)
- self.assertEqual((a + hour) - a, hour)
- self.assertEqual((a + millisec) - a, millisec)
- self.assertEqual((a - week) - a, -week)
- self.assertEqual((a - day) - a, -day)
- self.assertEqual((a - hour) - a, -hour)
- self.assertEqual((a - millisec) - a, -millisec)
- self.assertEqual(a - (a + week), -week)
- self.assertEqual(a - (a + day), -day)
- self.assertEqual(a - (a + hour), -hour)
- self.assertEqual(a - (a + millisec), -millisec)
- self.assertEqual(a - (a - week), week)
- self.assertEqual(a - (a - day), day)
- self.assertEqual(a - (a - hour), hour)
- self.assertEqual(a - (a - millisec), millisec)
- self.assertEqual(a + (week + day + hour + millisec),
- self.theclass(2002, 3, 10, 18, 6, 0, 1000))
- self.assertEqual(a + (week + day + hour + millisec),
- (((a + week) + day) + hour) + millisec)
- self.assertEqual(a - (week + day + hour + millisec),
- self.theclass(2002, 2, 22, 16, 5, 59, 999000))
- self.assertEqual(a - (week + day + hour + millisec),
- (((a - week) - day) - hour) - millisec)
- # Add/sub ints, longs, floats should be illegal
- for i in 1, 1L, 1.0:
- self.assertRaises(TypeError, lambda: a+i)
- self.assertRaises(TypeError, lambda: a-i)
- self.assertRaises(TypeError, lambda: i+a)
- self.assertRaises(TypeError, lambda: i-a)
-
- # delta - datetime is senseless.
- self.assertRaises(TypeError, lambda: day - a)
- # mixing datetime and (delta or datetime) via * or // is senseless
- self.assertRaises(TypeError, lambda: day * a)
- self.assertRaises(TypeError, lambda: a * day)
- self.assertRaises(TypeError, lambda: day // a)
- self.assertRaises(TypeError, lambda: a // day)
- self.assertRaises(TypeError, lambda: a * a)
- self.assertRaises(TypeError, lambda: a // a)
- # datetime + datetime is senseless
- self.assertRaises(TypeError, lambda: a + a)
-
- def test_pickling(self):
- args = 6, 7, 23, 20, 59, 1, 64**2
- orig = self.theclass(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_more_pickling(self):
- a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
- s = pickle.dumps(a)
- b = pickle.loads(s)
- self.assertEqual(b.year, 2003)
- self.assertEqual(b.month, 2)
- self.assertEqual(b.day, 7)
-
- def test_pickling_subclass_datetime(self):
- args = 6, 7, 23, 20, 59, 1, 64**2
- orig = SubclassDatetime(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_more_compare(self):
- # The test_compare() inherited from TestDate covers the error cases.
- # We just want to test lexicographic ordering on the members datetime
- # has that date lacks.
- args = [2000, 11, 29, 20, 58, 16, 999998]
- t1 = self.theclass(*args)
- t2 = self.theclass(*args)
- self.assertTrue(t1 == t2)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t1 >= t2)
- self.assertTrue(not t1 != t2)
- self.assertTrue(not t1 < t2)
- self.assertTrue(not t1 > t2)
- self.assertEqual(cmp(t1, t2), 0)
- self.assertEqual(cmp(t2, t1), 0)
-
- for i in range(len(args)):
- newargs = args[:]
- newargs[i] = args[i] + 1
- t2 = self.theclass(*newargs) # this is larger than t1
- self.assertTrue(t1 < t2)
- self.assertTrue(t2 > t1)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t2 >= t1)
- self.assertTrue(t1 != t2)
- self.assertTrue(t2 != t1)
- self.assertTrue(not t1 == t2)
- self.assertTrue(not t2 == t1)
- self.assertTrue(not t1 > t2)
- self.assertTrue(not t2 < t1)
- self.assertTrue(not t1 >= t2)
- self.assertTrue(not t2 <= t1)
- self.assertEqual(cmp(t1, t2), -1)
- self.assertEqual(cmp(t2, t1), 1)
-
-
- # A helper for timestamp constructor tests.
- def verify_field_equality(self, expected, got):
- self.assertEqual(expected.tm_year, got.year)
- self.assertEqual(expected.tm_mon, got.month)
- self.assertEqual(expected.tm_mday, got.day)
- self.assertEqual(expected.tm_hour, got.hour)
- self.assertEqual(expected.tm_min, got.minute)
- self.assertEqual(expected.tm_sec, got.second)
-
- def test_fromtimestamp(self):
- import time
-
- ts = time.time()
- expected = time.localtime(ts)
- got = self.theclass.fromtimestamp(ts)
- self.verify_field_equality(expected, got)
-
- def test_utcfromtimestamp(self):
- import time
-
- ts = time.time()
- expected = time.gmtime(ts)
- got = self.theclass.utcfromtimestamp(ts)
- self.verify_field_equality(expected, got)
-
- def test_microsecond_rounding(self):
- # Test whether fromtimestamp "rounds up" floats that are less
- # than one microsecond smaller than an integer.
- self.assertEqual(self.theclass.fromtimestamp(0.9999999),
- self.theclass.fromtimestamp(1))
-
- def test_insane_fromtimestamp(self):
- # It's possible that some platform maps time_t to double,
- # and that this test will fail there. This test should
- # exempt such platforms (provided they return reasonable
- # results!).
- for insane in -1e200, 1e200:
- self.assertRaises(ValueError, self.theclass.fromtimestamp,
- insane)
-
- def test_insane_utcfromtimestamp(self):
- # It's possible that some platform maps time_t to double,
- # and that this test will fail there. This test should
- # exempt such platforms (provided they return reasonable
- # results!).
- for insane in -1e200, 1e200:
- self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
- insane)
- @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
- def test_negative_float_fromtimestamp(self):
- # The result is tz-dependent; at least test that this doesn't
- # fail (like it did before bug 1646728 was fixed).
- self.theclass.fromtimestamp(-1.05)
-
- @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
- def test_negative_float_utcfromtimestamp(self):
- d = self.theclass.utcfromtimestamp(-1.05)
- self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
-
- def test_utcnow(self):
- import time
-
- # Call it a success if utcnow() and utcfromtimestamp() are within
- # a second of each other.
- tolerance = timedelta(seconds=1)
- for dummy in range(3):
- from_now = self.theclass.utcnow()
- from_timestamp = self.theclass.utcfromtimestamp(time.time())
- if abs(from_timestamp - from_now) <= tolerance:
- break
- # Else try again a few times.
- self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
-
- @unittest.skipIf(test_support.is_jython, "FIXME: %f not accepted")
- def test_strptime(self):
- import _strptime
-
- string = '2004-12-01 13:02:47.197'
- format = '%Y-%m-%d %H:%M:%S.%f'
- result, frac = _strptime._strptime(string, format)
- expected = self.theclass(*(result[0:6]+(frac,)))
- got = self.theclass.strptime(string, format)
- self.assertEqual(expected, got)
-
- def test_more_timetuple(self):
- # This tests fields beyond those tested by the TestDate.test_timetuple.
- t = self.theclass(2004, 12, 31, 6, 22, 33)
- self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
- self.assertEqual(t.timetuple(),
- (t.year, t.month, t.day,
- t.hour, t.minute, t.second,
- t.weekday(),
- t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
- -1))
- tt = t.timetuple()
- self.assertEqual(tt.tm_year, t.year)
- self.assertEqual(tt.tm_mon, t.month)
- self.assertEqual(tt.tm_mday, t.day)
- self.assertEqual(tt.tm_hour, t.hour)
- self.assertEqual(tt.tm_min, t.minute)
- self.assertEqual(tt.tm_sec, t.second)
- self.assertEqual(tt.tm_wday, t.weekday())
- self.assertEqual(tt.tm_yday, t.toordinal() -
- date(t.year, 1, 1).toordinal() + 1)
- self.assertEqual(tt.tm_isdst, -1)
-
- def test_more_strftime(self):
- # This tests fields beyond those tested by the TestDate.test_strftime.
- t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
- self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
- "12 31 04 000047 33 22 06 366")
-
- def test_extract(self):
- dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
- self.assertEqual(dt.date(), date(2002, 3, 4))
- self.assertEqual(dt.time(), time(18, 45, 3, 1234))
-
- def test_combine(self):
- d = date(2002, 3, 4)
- t = time(18, 45, 3, 1234)
- expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
- combine = self.theclass.combine
- dt = combine(d, t)
- self.assertEqual(dt, expected)
-
- dt = combine(time=t, date=d)
- self.assertEqual(dt, expected)
-
- self.assertEqual(d, dt.date())
- self.assertEqual(t, dt.time())
- self.assertEqual(dt, combine(dt.date(), dt.time()))
-
- self.assertRaises(TypeError, combine) # need an arg
- self.assertRaises(TypeError, combine, d) # need two args
- self.assertRaises(TypeError, combine, t, d) # args reversed
- self.assertRaises(TypeError, combine, d, t, 1) # too many args
- self.assertRaises(TypeError, combine, "date", "time") # wrong types
-
- def test_replace(self):
- cls = self.theclass
- args = [1, 2, 3, 4, 5, 6, 7]
- base = cls(*args)
- self.assertEqual(base, base.replace())
-
- i = 0
- for name, newval in (("year", 2),
- ("month", 3),
- ("day", 4),
- ("hour", 5),
- ("minute", 6),
- ("second", 7),
- ("microsecond", 8)):
- newargs = args[:]
- newargs[i] = newval
- expected = cls(*newargs)
- got = base.replace(**{name: newval})
- self.assertEqual(expected, got)
- i += 1
-
- # Out of bounds.
- base = cls(2000, 2, 29)
- self.assertRaises(ValueError, base.replace, year=2001)
-
- def test_astimezone(self):
- # Pretty boring! The TZ test is more interesting here. astimezone()
- # simply can't be applied to a naive object.
- dt = self.theclass.now()
- f = FixedOffset(44, "")
- self.assertRaises(TypeError, dt.astimezone) # not enough args
- self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
- self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
- self.assertRaises(ValueError, dt.astimezone, f) # naive
- self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
-
- class Bogus(tzinfo):
- def utcoffset(self, dt): return None
- def dst(self, dt): return timedelta(0)
- bog = Bogus()
- self.assertRaises(ValueError, dt.astimezone, bog) # naive
-
- class AlsoBogus(tzinfo):
- def utcoffset(self, dt): return timedelta(0)
- def dst(self, dt): return None
- alsobog = AlsoBogus()
- self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
-
- def test_subclass_datetime(self):
-
- class C(self.theclass):
- theAnswer = 42
-
- def __new__(cls, *args, **kws):
- temp = kws.copy()
- extra = temp.pop('extra')
- result = self.theclass.__new__(cls, *args, **temp)
- result.extra = extra
- return result
-
- def newmeth(self, start):
- return start + self.year + self.month + self.second
-
- args = 2003, 4, 14, 12, 13, 41
-
- dt1 = self.theclass(*args)
- dt2 = C(*args, **{'extra': 7})
-
- self.assertEqual(dt2.__class__, C)
- self.assertEqual(dt2.theAnswer, 42)
- self.assertEqual(dt2.extra, 7)
- self.assertEqual(dt1.toordinal(), dt2.toordinal())
- self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
- dt1.second - 7)
-
-class SubclassTime(time):
- sub_var = 1
-
-class TestTime(HarmlessMixedComparison, unittest.TestCase):
-
- theclass = time
-
- def test_basic_attributes(self):
- t = self.theclass(12, 0)
- self.assertEqual(t.hour, 12)
- self.assertEqual(t.minute, 0)
- self.assertEqual(t.second, 0)
- self.assertEqual(t.microsecond, 0)
-
- def test_basic_attributes_nonzero(self):
- # Make sure all attributes are non-zero so bugs in
- # bit-shifting access show up.
- t = self.theclass(12, 59, 59, 8000)
- self.assertEqual(t.hour, 12)
- self.assertEqual(t.minute, 59)
- self.assertEqual(t.second, 59)
- self.assertEqual(t.microsecond, 8000)
-
- def test_roundtrip(self):
- t = self.theclass(1, 2, 3, 4)
-
- # Verify t -> string -> time identity.
- s = repr(t)
- self.assertTrue(s.startswith('datetime.'))
- s = s[9:]
- t2 = eval(s)
- self.assertEqual(t, t2)
-
- # Verify identity via reconstructing from pieces.
- t2 = self.theclass(t.hour, t.minute, t.second,
- t.microsecond)
- self.assertEqual(t, t2)
-
- def test_comparing(self):
- args = [1, 2, 3, 4]
- t1 = self.theclass(*args)
- t2 = self.theclass(*args)
- self.assertTrue(t1 == t2)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t1 >= t2)
- self.assertTrue(not t1 != t2)
- self.assertTrue(not t1 < t2)
- self.assertTrue(not t1 > t2)
- self.assertEqual(cmp(t1, t2), 0)
- self.assertEqual(cmp(t2, t1), 0)
-
- for i in range(len(args)):
- newargs = args[:]
- newargs[i] = args[i] + 1
- t2 = self.theclass(*newargs) # this is larger than t1
- self.assertTrue(t1 < t2)
- self.assertTrue(t2 > t1)
- self.assertTrue(t1 <= t2)
- self.assertTrue(t2 >= t1)
- self.assertTrue(t1 != t2)
- self.assertTrue(t2 != t1)
- self.assertTrue(not t1 == t2)
- self.assertTrue(not t2 == t1)
- self.assertTrue(not t1 > t2)
- self.assertTrue(not t2 < t1)
- self.assertTrue(not t1 >= t2)
- self.assertTrue(not t2 <= t1)
- self.assertEqual(cmp(t1, t2), -1)
- self.assertEqual(cmp(t2, t1), 1)
-
- for badarg in OTHERSTUFF:
- self.assertEqual(t1 == badarg, False)
- self.assertEqual(t1 != badarg, True)
- self.assertEqual(badarg == t1, False)
- self.assertEqual(badarg != t1, True)
-
- self.assertRaises(TypeError, lambda: t1 <= badarg)
- self.assertRaises(TypeError, lambda: t1 < badarg)
- self.assertRaises(TypeError, lambda: t1 > badarg)
- self.assertRaises(TypeError, lambda: t1 >= badarg)
- self.assertRaises(TypeError, lambda: badarg <= t1)
- self.assertRaises(TypeError, lambda: badarg < t1)
- self.assertRaises(TypeError, lambda: badarg > t1)
- self.assertRaises(TypeError, lambda: badarg >= t1)
-
- def test_bad_constructor_arguments(self):
- # bad hours
- self.theclass(0, 0) # no exception
- self.theclass(23, 0) # no exception
- self.assertRaises(ValueError, self.theclass, -1, 0)
- self.assertRaises(ValueError, self.theclass, 24, 0)
- # bad minutes
- self.theclass(23, 0) # no exception
- self.theclass(23, 59) # no exception
- self.assertRaises(ValueError, self.theclass, 23, -1)
- self.assertRaises(ValueError, self.theclass, 23, 60)
- # bad seconds
- self.theclass(23, 59, 0) # no exception
- self.theclass(23, 59, 59) # no exception
- self.assertRaises(ValueError, self.theclass, 23, 59, -1)
- self.assertRaises(ValueError, self.theclass, 23, 59, 60)
- # bad microseconds
- self.theclass(23, 59, 59, 0) # no exception
- self.theclass(23, 59, 59, 999999) # no exception
- self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
- self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
-
- def test_hash_equality(self):
- d = self.theclass(23, 30, 17)
- e = self.theclass(23, 30, 17)
- self.assertEqual(d, e)
- self.assertEqual(hash(d), hash(e))
-
- dic = {d: 1}
- dic[e] = 2
- self.assertEqual(len(dic), 1)
- self.assertEqual(dic[d], 2)
- self.assertEqual(dic[e], 2)
-
- d = self.theclass(0, 5, 17)
- e = self.theclass(0, 5, 17)
- self.assertEqual(d, e)
- self.assertEqual(hash(d), hash(e))
-
- dic = {d: 1}
- dic[e] = 2
- self.assertEqual(len(dic), 1)
- self.assertEqual(dic[d], 2)
- self.assertEqual(dic[e], 2)
-
- def test_isoformat(self):
- t = self.theclass(4, 5, 1, 123)
- self.assertEqual(t.isoformat(), "04:05:01.000123")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass()
- self.assertEqual(t.isoformat(), "00:00:00")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass(microsecond=1)
- self.assertEqual(t.isoformat(), "00:00:00.000001")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass(microsecond=10)
- self.assertEqual(t.isoformat(), "00:00:00.000010")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass(microsecond=100)
- self.assertEqual(t.isoformat(), "00:00:00.000100")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass(microsecond=1000)
- self.assertEqual(t.isoformat(), "00:00:00.001000")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass(microsecond=10000)
- self.assertEqual(t.isoformat(), "00:00:00.010000")
- self.assertEqual(t.isoformat(), str(t))
-
- t = self.theclass(microsecond=100000)
- self.assertEqual(t.isoformat(), "00:00:00.100000")
- self.assertEqual(t.isoformat(), str(t))
-
- def test_1653736(self):
- # verify it doesn't accept extra keyword arguments
- t = self.theclass(second=1)
- self.assertRaises(TypeError, t.isoformat, foo=3)
-
- def test_strftime(self):
- t = self.theclass(1, 2, 3, 4)
- self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
- # A naive object replaces %z and %Z with empty strings.
- self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
-
- @unittest.skipIf(test_support.is_jython, "FIXME: some formats not accepted")
- def test_format(self):
- t = self.theclass(1, 2, 3, 4)
- self.assertEqual(t.__format__(''), str(t))
-
- # check that a derived class's __str__() gets called
- class A(self.theclass):
- def __str__(self):
- return 'A'
- a = A(1, 2, 3, 4)
- self.assertEqual(a.__format__(''), 'A')
-
- # check that a derived class's strftime gets called
- class B(self.theclass):
- def strftime(self, format_spec):
- return 'B'
- b = B(1, 2, 3, 4)
- self.assertEqual(b.__format__(''), str(t))
-
- for fmt in ['%H %M %S',
- ]:
- self.assertEqual(t.__format__(fmt), t.strftime(fmt))
- self.assertEqual(a.__format__(fmt), t.strftime(fmt))
- self.assertEqual(b.__format__(fmt), 'B')
-
- def test_str(self):
- self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
- self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
- self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
- self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
- self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
-
- def test_repr(self):
- name = 'datetime.' + self.theclass.__name__
- self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
- "%s(1, 2, 3, 4)" % name)
- self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
- "%s(10, 2, 3, 4000)" % name)
- self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
- "%s(0, 2, 3, 400000)" % name)
- self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
- "%s(12, 2, 3)" % name)
- self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
- "%s(23, 15)" % name)
-
- def test_resolution_info(self):
- self.assertIsInstance(self.theclass.min, self.theclass)
- self.assertIsInstance(self.theclass.max, self.theclass)
- self.assertIsInstance(self.theclass.resolution, timedelta)
- self.assertTrue(self.theclass.max > self.theclass.min)
-
- def test_pickling(self):
- args = 20, 59, 16, 64**2
- orig = self.theclass(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_pickling_subclass_time(self):
- args = 20, 59, 16, 64**2
- orig = SubclassTime(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- def test_bool(self):
- cls = self.theclass
- self.assertTrue(cls(1))
- self.assertTrue(cls(0, 1))
- self.assertTrue(cls(0, 0, 1))
- self.assertTrue(cls(0, 0, 0, 1))
- self.assertTrue(not cls(0))
- self.assertTrue(not cls())
-
- def test_replace(self):
- cls = self.theclass
- args = [1, 2, 3, 4]
- base = cls(*args)
- self.assertEqual(base, base.replace())
-
- i = 0
- for name, newval in (("hour", 5),
- ("minute", 6),
- ("second", 7),
- ("microsecond", 8)):
- newargs = args[:]
- newargs[i] = newval
- expected = cls(*newargs)
- got = base.replace(**{name: newval})
- self.assertEqual(expected, got)
- i += 1
-
- # Out of bounds.
- base = cls(1)
- self.assertRaises(ValueError, base.replace, hour=24)
- self.assertRaises(ValueError, base.replace, minute=-1)
- self.assertRaises(ValueError, base.replace, second=100)
- self.assertRaises(ValueError, base.replace, microsecond=1000000)
-
- def test_subclass_time(self):
-
- class C(self.theclass):
- theAnswer = 42
-
- def __new__(cls, *args, **kws):
- temp = kws.copy()
- extra = temp.pop('extra')
- result = self.theclass.__new__(cls, *args, **temp)
- result.extra = extra
- return result
-
- def newmeth(self, start):
- return start + self.hour + self.second
-
- args = 4, 5, 6
-
- dt1 = self.theclass(*args)
- dt2 = C(*args, **{'extra': 7})
-
- self.assertEqual(dt2.__class__, C)
- self.assertEqual(dt2.theAnswer, 42)
- self.assertEqual(dt2.extra, 7)
- self.assertEqual(dt1.isoformat(), dt2.isoformat())
- self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
-
- def test_backdoor_resistance(self):
- # see TestDate.test_backdoor_resistance().
- base = '2:59.0'
- for hour_byte in ' ', '9', chr(24), '\xff':
- self.assertRaises(TypeError, self.theclass,
- hour_byte + base[1:])
-
-# A mixin for classes with a tzinfo= argument. Subclasses must define
-# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
-# must be legit (which is true for time and datetime).
-class TZInfoBase:
-
- def test_argument_passing(self):
- cls = self.theclass
- # A datetime passes itself on, a time passes None.
- class introspective(tzinfo):
- def tzname(self, dt): return dt and "real" or "none"
- def utcoffset(self, dt):
- return timedelta(minutes = dt and 42 or -42)
- dst = utcoffset
-
- obj = cls(1, 2, 3, tzinfo=introspective())
-
- expected = cls is time and "none" or "real"
- self.assertEqual(obj.tzname(), expected)
-
- expected = timedelta(minutes=(cls is time and -42 or 42))
- self.assertEqual(obj.utcoffset(), expected)
- self.assertEqual(obj.dst(), expected)
-
- def test_bad_tzinfo_classes(self):
- cls = self.theclass
- self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
-
- class NiceTry(object):
- def __init__(self): pass
- def utcoffset(self, dt): pass
- self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
-
- class BetterTry(tzinfo):
- def __init__(self): pass
- def utcoffset(self, dt): pass
- b = BetterTry()
- t = cls(1, 1, 1, tzinfo=b)
- self.assertTrue(t.tzinfo is b)
-
- def test_utc_offset_out_of_bounds(self):
- class Edgy(tzinfo):
- def __init__(self, offset):
- self.offset = timedelta(minutes=offset)
- def utcoffset(self, dt):
- return self.offset
-
- cls = self.theclass
- for offset, legit in ((-1440, False),
- (-1439, True),
- (1439, True),
- (1440, False)):
- if cls is time:
- t = cls(1, 2, 3, tzinfo=Edgy(offset))
- elif cls is datetime:
- t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
- else:
- assert 0, "impossible"
- if legit:
- aofs = abs(offset)
- h, m = divmod(aofs, 60)
- tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
- if isinstance(t, datetime):
- t = t.timetz()
- self.assertEqual(str(t), "01:02:03" + tag)
- else:
- self.assertRaises(ValueError, str, t)
-
- def test_tzinfo_classes(self):
- cls = self.theclass
- class C1(tzinfo):
- def utcoffset(self, dt): return None
- def dst(self, dt): return None
- def tzname(self, dt): return None
- for t in (cls(1, 1, 1),
- cls(1, 1, 1, tzinfo=None),
- cls(1, 1, 1, tzinfo=C1())):
- self.assertTrue(t.utcoffset() is None)
- self.assertTrue(t.dst() is None)
- self.assertTrue(t.tzname() is None)
-
- class C3(tzinfo):
- def utcoffset(self, dt): return timedelta(minutes=-1439)
- def dst(self, dt): return timedelta(minutes=1439)
- def tzname(self, dt): return "aname"
- t = cls(1, 1, 1, tzinfo=C3())
- self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
- self.assertEqual(t.dst(), timedelta(minutes=1439))
- self.assertEqual(t.tzname(), "aname")
-
- # Wrong types.
- class C4(tzinfo):
- def utcoffset(self, dt): return "aname"
- def dst(self, dt): return 7
- def tzname(self, dt): return 0
- t = cls(1, 1, 1, tzinfo=C4())
- self.assertRaises(TypeError, t.utcoffset)
- self.assertRaises(TypeError, t.dst)
- self.assertRaises(TypeError, t.tzname)
-
- # Offset out of range.
- class C6(tzinfo):
- def utcoffset(self, dt): return timedelta(hours=-24)
- def dst(self, dt): return timedelta(hours=24)
- t = cls(1, 1, 1, tzinfo=C6())
- self.assertRaises(ValueError, t.utcoffset)
- self.assertRaises(ValueError, t.dst)
-
- # Not a whole number of minutes.
- class C7(tzinfo):
- def utcoffset(self, dt): return timedelta(seconds=61)
- def dst(self, dt): return timedelta(microseconds=-81)
- t = cls(1, 1, 1, tzinfo=C7())
- self.assertRaises(ValueError, t.utcoffset)
- self.assertRaises(ValueError, t.dst)
-
- def test_aware_compare(self):
- cls = self.theclass
-
- # Ensure that utcoffset() gets ignored if the comparands have
- # the same tzinfo member.
- class OperandDependentOffset(tzinfo):
- def utcoffset(self, t):
- if t.minute < 10:
- # d0 and d1 equal after adjustment
- return timedelta(minutes=t.minute)
- else:
- # d2 off in the weeds
- return timedelta(minutes=59)
-
- base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
- d0 = base.replace(minute=3)
- d1 = base.replace(minute=9)
- d2 = base.replace(minute=11)
- for x in d0, d1, d2:
- for y in d0, d1, d2:
- got = cmp(x, y)
- expected = cmp(x.minute, y.minute)
- self.assertEqual(got, expected)
-
- # However, if they're different members, uctoffset is not ignored.
- # Note that a time can't actually have an operand-depedent offset,
- # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
- # so skip this test for time.
- if cls is not time:
- d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
- d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
- d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
- for x in d0, d1, d2:
- for y in d0, d1, d2:
- got = cmp(x, y)
- if (x is d0 or x is d1) and (y is d0 or y is d1):
- expected = 0
- elif x is y is d2:
- expected = 0
- elif x is d2:
- expected = -1
- else:
- assert y is d2
- expected = 1
- self.assertEqual(got, expected)
-
-
-# Testing time objects with a non-None tzinfo.
-class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
- theclass = time
-
- def test_empty(self):
- t = self.theclass()
- self.assertEqual(t.hour, 0)
- self.assertEqual(t.minute, 0)
- self.assertEqual(t.second, 0)
- self.assertEqual(t.microsecond, 0)
- self.assertTrue(t.tzinfo is None)
-
- def test_zones(self):
- est = FixedOffset(-300, "EST", 1)
- utc = FixedOffset(0, "UTC", -2)
- met = FixedOffset(60, "MET", 3)
- t1 = time( 7, 47, tzinfo=est)
- t2 = time(12, 47, tzinfo=utc)
- t3 = time(13, 47, tzinfo=met)
- t4 = time(microsecond=40)
- t5 = time(microsecond=40, tzinfo=utc)
-
- self.assertEqual(t1.tzinfo, est)
- self.assertEqual(t2.tzinfo, utc)
- self.assertEqual(t3.tzinfo, met)
- self.assertTrue(t4.tzinfo is None)
- self.assertEqual(t5.tzinfo, utc)
-
- self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
- self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
- self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
- self.assertTrue(t4.utcoffset() is None)
- self.assertRaises(TypeError, t1.utcoffset, "no args")
-
- self.assertEqual(t1.tzname(), "EST")
- self.assertEqual(t2.tzname(), "UTC")
- self.assertEqual(t3.tzname(), "MET")
- self.assertTrue(t4.tzname() is None)
- self.assertRaises(TypeError, t1.tzname, "no args")
-
- self.assertEqual(t1.dst(), timedelta(minutes=1))
- self.assertEqual(t2.dst(), timedelta(minutes=-2))
- self.assertEqual(t3.dst(), timedelta(minutes=3))
- self.assertTrue(t4.dst() is None)
- self.assertRaises(TypeError, t1.dst, "no args")
-
- self.assertEqual(hash(t1), hash(t2))
- self.assertEqual(hash(t1), hash(t3))
- self.assertEqual(hash(t2), hash(t3))
-
- self.assertEqual(t1, t2)
- self.assertEqual(t1, t3)
- self.assertEqual(t2, t3)
- self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
- self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
- self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
-
- self.assertEqual(str(t1), "07:47:00-05:00")
- self.assertEqual(str(t2), "12:47:00+00:00")
- self.assertEqual(str(t3), "13:47:00+01:00")
- self.assertEqual(str(t4), "00:00:00.000040")
- self.assertEqual(str(t5), "00:00:00.000040+00:00")
-
- self.assertEqual(t1.isoformat(), "07:47:00-05:00")
- self.assertEqual(t2.isoformat(), "12:47:00+00:00")
- self.assertEqual(t3.isoformat(), "13:47:00+01:00")
- self.assertEqual(t4.isoformat(), "00:00:00.000040")
- self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
-
- d = 'datetime.time'
- self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
- self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
- self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
- self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
- self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
-
- self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
- "07:47:00 %Z=EST %z=-0500")
- self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
- self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
-
- yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
- t1 = time(23, 59, tzinfo=yuck)
- self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
- "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
-
- # Check that an invalid tzname result raises an exception.
- class Badtzname(tzinfo):
- def tzname(self, dt): return 42
- t = time(2, 3, 4, tzinfo=Badtzname())
- self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
- self.assertRaises(TypeError, t.strftime, "%Z")
-
- def test_hash_edge_cases(self):
- # Offsets that overflow a basic time.
- t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
- t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
- self.assertEqual(hash(t1), hash(t2))
-
- t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
- t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
- self.assertEqual(hash(t1), hash(t2))
-
- def test_pickling(self):
- # Try one without a tzinfo.
- args = 20, 59, 16, 64**2
- orig = self.theclass(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- # Try one with a tzinfo.
- tinfo = PicklableFixedOffset(-300, 'cookie')
- orig = self.theclass(5, 6, 7, tzinfo=tinfo)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
- self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
- self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
- self.assertEqual(derived.tzname(), 'cookie')
-
- def test_more_bool(self):
- # Test cases with non-None tzinfo.
- cls = self.theclass
-
- t = cls(0, tzinfo=FixedOffset(-300, ""))
- self.assertTrue(t)
-
- t = cls(5, tzinfo=FixedOffset(-300, ""))
- self.assertTrue(t)
-
- t = cls(5, tzinfo=FixedOffset(300, ""))
- self.assertTrue(not t)
-
- t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
- self.assertTrue(not t)
-
- # Mostly ensuring this doesn't overflow internally.
- t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
- self.assertTrue(t)
-
- # But this should yield a value error -- the utcoffset is bogus.
- t = cls(0, tzinfo=FixedOffset(24*60, ""))
- self.assertRaises(ValueError, lambda: bool(t))
-
- # Likewise.
- t = cls(0, tzinfo=FixedOffset(-24*60, ""))
- self.assertRaises(ValueError, lambda: bool(t))
-
- def test_replace(self):
- cls = self.theclass
- z100 = FixedOffset(100, "+100")
- zm200 = FixedOffset(timedelta(minutes=-200), "-200")
- args = [1, 2, 3, 4, z100]
- base = cls(*args)
- self.assertEqual(base, base.replace())
-
- i = 0
- for name, newval in (("hour", 5),
- ("minute", 6),
- ("second", 7),
- ("microsecond", 8),
- ("tzinfo", zm200)):
- newargs = args[:]
- newargs[i] = newval
- expected = cls(*newargs)
- got = base.replace(**{name: newval})
- self.assertEqual(expected, got)
- i += 1
-
- # Ensure we can get rid of a tzinfo.
- self.assertEqual(base.tzname(), "+100")
- base2 = base.replace(tzinfo=None)
- self.assertTrue(base2.tzinfo is None)
- self.assertTrue(base2.tzname() is None)
-
- # Ensure we can add one.
- base3 = base2.replace(tzinfo=z100)
- self.assertEqual(base, base3)
- self.assertTrue(base.tzinfo is base3.tzinfo)
-
- # Out of bounds.
- base = cls(1)
- self.assertRaises(ValueError, base.replace, hour=24)
- self.assertRaises(ValueError, base.replace, minute=-1)
- self.assertRaises(ValueError, base.replace, second=100)
- self.assertRaises(ValueError, base.replace, microsecond=1000000)
-
- def test_mixed_compare(self):
- t1 = time(1, 2, 3)
- t2 = time(1, 2, 3)
- self.assertEqual(t1, t2)
- t2 = t2.replace(tzinfo=None)
- self.assertEqual(t1, t2)
- t2 = t2.replace(tzinfo=FixedOffset(None, ""))
- self.assertEqual(t1, t2)
- t2 = t2.replace(tzinfo=FixedOffset(0, ""))
- self.assertRaises(TypeError, lambda: t1 == t2)
-
- # In time w/ identical tzinfo objects, utcoffset is ignored.
- class Varies(tzinfo):
- def __init__(self):
- self.offset = timedelta(minutes=22)
- def utcoffset(self, t):
- self.offset += timedelta(minutes=1)
- return self.offset
-
- v = Varies()
- t1 = t2.replace(tzinfo=v)
- t2 = t2.replace(tzinfo=v)
- self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
- self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
- self.assertEqual(t1, t2)
-
- # But if they're not identical, it isn't ignored.
- t2 = t2.replace(tzinfo=Varies())
- self.assertTrue(t1 < t2) # t1's offset counter still going up
-
- def test_subclass_timetz(self):
-
- class C(self.theclass):
- theAnswer = 42
-
- def __new__(cls, *args, **kws):
- temp = kws.copy()
- extra = temp.pop('extra')
- result = self.theclass.__new__(cls, *args, **temp)
- result.extra = extra
- return result
-
- def newmeth(self, start):
- return start + self.hour + self.second
-
- args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
-
- dt1 = self.theclass(*args)
- dt2 = C(*args, **{'extra': 7})
-
- self.assertEqual(dt2.__class__, C)
- self.assertEqual(dt2.theAnswer, 42)
- self.assertEqual(dt2.extra, 7)
- self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
- self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
-
-
-# Testing datetime objects with a non-None tzinfo.
-
-class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
- theclass = datetime
-
- def test_trivial(self):
- dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
- self.assertEqual(dt.year, 1)
- self.assertEqual(dt.month, 2)
- self.assertEqual(dt.day, 3)
- self.assertEqual(dt.hour, 4)
- self.assertEqual(dt.minute, 5)
- self.assertEqual(dt.second, 6)
- self.assertEqual(dt.microsecond, 7)
- self.assertEqual(dt.tzinfo, None)
-
- def test_even_more_compare(self):
- # The test_compare() and test_more_compare() inherited from TestDate
- # and TestDateTime covered non-tzinfo cases.
-
- # Smallest possible after UTC adjustment.
- t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
- # Largest possible after UTC adjustment.
- t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
- tzinfo=FixedOffset(-1439, ""))
-
- # Make sure those compare correctly, and w/o overflow.
- self.assertTrue(t1 < t2)
- self.assertTrue(t1 != t2)
- self.assertTrue(t2 > t1)
-
- self.assertTrue(t1 == t1)
- self.assertTrue(t2 == t2)
-
- # Equal afer adjustment.
- t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
- t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
- self.assertEqual(t1, t2)
-
- # Change t1 not to subtract a minute, and t1 should be larger.
- t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
- self.assertTrue(t1 > t2)
-
- # Change t1 to subtract 2 minutes, and t1 should be smaller.
- t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
- self.assertTrue(t1 < t2)
-
- # Back to the original t1, but make seconds resolve it.
- t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
- second=1)
- self.assertTrue(t1 > t2)
-
- # Likewise, but make microseconds resolve it.
- t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
- microsecond=1)
- self.assertTrue(t1 > t2)
-
- # Make t2 naive and it should fail.
- t2 = self.theclass.min
- self.assertRaises(TypeError, lambda: t1 == t2)
- self.assertEqual(t2, t2)
-
- # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
- class Naive(tzinfo):
- def utcoffset(self, dt): return None
- t2 = self.theclass(5, 6, 7, tzinfo=Naive())
- self.assertRaises(TypeError, lambda: t1 == t2)
- self.assertEqual(t2, t2)
-
- # OTOH, it's OK to compare two of these mixing the two ways of being
- # naive.
- t1 = self.theclass(5, 6, 7)
- self.assertEqual(t1, t2)
-
- # Try a bogus uctoffset.
- class Bogus(tzinfo):
- def utcoffset(self, dt):
- return timedelta(minutes=1440) # out of bounds
- t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
- t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
- self.assertRaises(ValueError, lambda: t1 == t2)
-
- def test_pickling(self):
- # Try one without a tzinfo.
- args = 6, 7, 23, 20, 59, 1, 64**2
- orig = self.theclass(*args)
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
-
- # Try one with a tzinfo.
- tinfo = PicklableFixedOffset(-300, 'cookie')
- orig = self.theclass(*args, **{'tzinfo': tinfo})
- derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
- for pickler, unpickler, proto in pickle_choices:
- green = pickler.dumps(orig, proto)
- derived = unpickler.loads(green)
- self.assertEqual(orig, derived)
- self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
- self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
- self.assertEqual(derived.tzname(), 'cookie')
-
- def test_extreme_hashes(self):
- # If an attempt is made to hash these via subtracting the offset
- # then hashing a datetime object, OverflowError results. The
- # Python implementation used to blow up here.
- t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
- hash(t)
- t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
- tzinfo=FixedOffset(-1439, ""))
- hash(t)
-
- # OTOH, an OOB offset should blow up.
- t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
- self.assertRaises(ValueError, hash, t)
-
- def test_zones(self):
- est = FixedOffset(-300, "EST")
- utc = FixedOffset(0, "UTC")
- met = FixedOffset(60, "MET")
- t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
- t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
- t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
- self.assertEqual(t1.tzinfo, est)
- self.assertEqual(t2.tzinfo, utc)
- self.assertEqual(t3.tzinfo, met)
- self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
- self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
- self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
- self.assertEqual(t1.tzname(), "EST")
- self.assertEqual(t2.tzname(), "UTC")
- self.assertEqual(t3.tzname(), "MET")
- self.assertEqual(hash(t1), hash(t2))
- self.assertEqual(hash(t1), hash(t3))
- self.assertEqual(hash(t2), hash(t3))
- self.assertEqual(t1, t2)
- self.assertEqual(t1, t3)
- self.assertEqual(t2, t3)
- self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
- self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
- self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
- d = 'datetime.datetime(2002, 3, 19, '
- self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
- self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
- self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
-
- def test_combine(self):
- met = FixedOffset(60, "MET")
- d = date(2002, 3, 4)
- tz = time(18, 45, 3, 1234, tzinfo=met)
- dt = datetime.combine(d, tz)
- self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
- tzinfo=met))
-
- def test_extract(self):
- met = FixedOffset(60, "MET")
- dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
- self.assertEqual(dt.date(), date(2002, 3, 4))
- self.assertEqual(dt.time(), time(18, 45, 3, 1234))
- self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
-
- def test_tz_aware_arithmetic(self):
- import random
-
- now = self.theclass.now()
- tz55 = FixedOffset(-330, "west 5:30")
- timeaware = now.time().replace(tzinfo=tz55)
- nowaware = self.theclass.combine(now.date(), timeaware)
- self.assertTrue(nowaware.tzinfo is tz55)
- self.assertEqual(nowaware.timetz(), timeaware)
-
- # Can't mix aware and non-aware.
- self.assertRaises(TypeError, lambda: now - nowaware)
- self.assertRaises(TypeError, lambda: nowaware - now)
-
- # And adding datetime's doesn't make sense, aware or not.
- self.assertRaises(TypeError, lambda: now + nowaware)
- self.assertRaises(TypeError, lambda: nowaware + now)
- self.assertRaises(TypeError, lambda: nowaware + nowaware)
-
- # Subtracting should yield 0.
- self.assertEqual(now - now, timedelta(0))
- self.assertEqual(nowaware - nowaware, timedelta(0))
-
- # Adding a delta should preserve tzinfo.
- delta = timedelta(weeks=1, minutes=12, microseconds=5678)
- nowawareplus = nowaware + delta
- self.assertTrue(nowaware.tzinfo is tz55)
- nowawareplus2 = delta + nowaware
- self.assertTrue(nowawareplus2.tzinfo is tz55)
- self.assertEqual(nowawareplus, nowawareplus2)
-
- # that - delta should be what we started with, and that - what we
- # started with should be delta.
- diff = nowawareplus - delta
- self.assertTrue(diff.tzinfo is tz55)
- self.assertEqual(nowaware, diff)
- self.assertRaises(TypeError, lambda: delta - nowawareplus)
- self.assertEqual(nowawareplus - nowaware, delta)
-
- # Make up a random timezone.
- tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
- # Attach it to nowawareplus.
- nowawareplus = nowawareplus.replace(tzinfo=tzr)
- self.assertTrue(nowawareplus.tzinfo is tzr)
- # Make sure the difference takes the timezone adjustments into account.
- got = nowaware - nowawareplus
- # Expected: (nowaware base - nowaware offset) -
- # (nowawareplus base - nowawareplus offset) =
- # (nowaware base - nowawareplus base) +
- # (nowawareplus offset - nowaware offset) =
- # -delta + nowawareplus offset - nowaware offset
- expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
- self.assertEqual(got, expected)
-
- # Try max possible difference.
- min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
- max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
- tzinfo=FixedOffset(-1439, "max"))
- maxdiff = max - min
- self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
- timedelta(minutes=2*1439))
-
- def test_tzinfo_now(self):
- meth = self.theclass.now
- # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
- base = meth()
- # Try with and without naming the keyword.
- off42 = FixedOffset(42, "42")
- another = meth(off42)
- again = meth(tz=off42)
- self.assertTrue(another.tzinfo is again.tzinfo)
- self.assertEqual(another.utcoffset(), timedelta(minutes=42))
- # Bad argument with and w/o naming the keyword.
- self.assertRaises(TypeError, meth, 16)
- self.assertRaises(TypeError, meth, tzinfo=16)
- # Bad keyword name.
- self.assertRaises(TypeError, meth, tinfo=off42)
- # Too many args.
- self.assertRaises(TypeError, meth, off42, off42)
-
- # We don't know which time zone we're in, and don't have a tzinfo
- # class to represent it, so seeing whether a tz argument actually
- # does a conversion is tricky.
- weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
- utc = FixedOffset(0, "utc", 0)
- for dummy in range(3):
- now = datetime.now(weirdtz)
- self.assertTrue(now.tzinfo is weirdtz)
- utcnow = datetime.utcnow().replace(tzinfo=utc)
- now2 = utcnow.astimezone(weirdtz)
- if abs(now - now2) < timedelta(seconds=30):
- break
- # Else the code is broken, or more than 30 seconds passed between
- # calls; assuming the latter, just try again.
- else:
- # Three strikes and we're out.
- self.fail("utcnow(), now(tz), or astimezone() may be broken")
-
- def test_tzinfo_fromtimestamp(self):
- import time
- meth = self.theclass.fromtimestamp
- ts = time.time()
- # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
- base = meth(ts)
- # Try with and without naming the keyword.
- off42 = FixedOffset(42, "42")
- another = meth(ts, off42)
- again = meth(ts, tz=off42)
- self.assertTrue(another.tzinfo is again.tzinfo)
- self.assertEqual(another.utcoffset(), timedelta(minutes=42))
- # Bad argument with and w/o naming the keyword.
- self.assertRaises(TypeError, meth, ts, 16)
- self.assertRaises(TypeError, meth, ts, tzinfo=16)
- # Bad keyword name.
- self.assertRaises(TypeError, meth, ts, tinfo=off42)
- # Too many args.
- self.assertRaises(TypeError, meth, ts, off42, off42)
- # Too few args.
- self.assertRaises(TypeError, meth)
-
- # Try to make sure tz= actually does some conversion.
- timestamp = 1000000000
- utcdatetime = datetime.utcfromtimestamp(timestamp)
- # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
- # But on some flavor of Mac, it's nowhere near that. So we can't have
- # any idea here what time that actually is, we can only test that
- # relative changes match.
- utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
- tz = FixedOffset(utcoffset, "tz", 0)
- expected = utcdatetime + utcoffset
- got = datetime.fromtimestamp(timestamp, tz)
- self.assertEqual(expected, got.replace(tzinfo=None))
-
- def test_tzinfo_utcnow(self):
- meth = self.theclass.utcnow
- # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
- base = meth()
- # Try with and without naming the keyword; for whatever reason,
- # utcnow() doesn't accept a tzinfo argument.
- off42 = FixedOffset(42, "42")
- self.assertRaises(TypeError, meth, off42)
- self.assertRaises(TypeError, meth, tzinfo=off42)
-
- def test_tzinfo_utcfromtimestamp(self):
- import time
- meth = self.theclass.utcfromtimestamp
- ts = time.time()
- # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
- base = meth(ts)
- # Try with and without naming the keyword; for whatever reason,
- # utcfromtimestamp() doesn't accept a tzinfo argument.
- off42 = FixedOffset(42, "42")
- self.assertRaises(TypeError, meth, ts, off42)
- self.assertRaises(TypeError, meth, ts, tzinfo=off42)
-
- def test_tzinfo_timetuple(self):
- # TestDateTime tested most of this. datetime adds a twist to the
- # DST flag.
- class DST(tzinfo):
- def __init__(self, dstvalue):
- if isinstance(dstvalue, int):
- dstvalue = timedelta(minutes=dstvalue)
- self.dstvalue = dstvalue
- def dst(self, dt):
- return self.dstvalue
-
- cls = self.theclass
- for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
- d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
- t = d.timetuple()
- self.assertEqual(1, t.tm_year)
- self.assertEqual(1, t.tm_mon)
- self.assertEqual(1, t.tm_mday)
- self.assertEqual(10, t.tm_hour)
- self.assertEqual(20, t.tm_min)
- self.assertEqual(30, t.tm_sec)
- self.assertEqual(0, t.tm_wday)
- self.assertEqual(1, t.tm_yday)
- self.assertEqual(flag, t.tm_isdst)
-
- # dst() returns wrong type.
- self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
-
- # dst() at the edge.
- self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
- self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
-
- # dst() out of range.
- self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
- self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
-
- def test_utctimetuple(self):
- class DST(tzinfo):
- def __init__(self, dstvalue):
- if isinstance(dstvalue, int):
- dstvalue = timedelta(minutes=dstvalue)
- self.dstvalue = dstvalue
- def dst(self, dt):
- return self.dstvalue
-
- cls = self.theclass
- # This can't work: DST didn't implement utcoffset.
- self.assertRaises(NotImplementedError,
- cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
-
- class UOFS(DST):
- def __init__(self, uofs, dofs=None):
- DST.__init__(self, dofs)
- self.uofs = timedelta(minutes=uofs)
- def utcoffset(self, dt):
- return self.uofs
-
- # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
- # in effect for a UTC time.
- for dstvalue in -33, 33, 0, None:
- d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
- t = d.utctimetuple()
- self.assertEqual(d.year, t.tm_year)
- self.assertEqual(d.month, t.tm_mon)
- self.assertEqual(d.day, t.tm_mday)
- self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
- self.assertEqual(13, t.tm_min)
- self.assertEqual(d.second, t.tm_sec)
- self.assertEqual(d.weekday(), t.tm_wday)
- self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
- t.tm_yday)
- self.assertEqual(0, t.tm_isdst)
-
- # At the edges, UTC adjustment can normalize into years out-of-range
- # for a datetime object. Ensure that a correct timetuple is
- # created anyway.
- tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
- # That goes back 1 minute less than a full day.
- t = tiny.utctimetuple()
- self.assertEqual(t.tm_year, MINYEAR-1)
- self.assertEqual(t.tm_mon, 12)
- self.assertEqual(t.tm_mday, 31)
- self.assertEqual(t.tm_hour, 0)
- self.assertEqual(t.tm_min, 1)
- self.assertEqual(t.tm_sec, 37)
- self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
- self.assertEqual(t.tm_isdst, 0)
-
- huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
- # That goes forward 1 minute less than a full day.
- t = huge.utctimetuple()
- self.assertEqual(t.tm_year, MAXYEAR+1)
- self.assertEqual(t.tm_mon, 1)
- self.assertEqual(t.tm_mday, 1)
- self.assertEqual(t.tm_hour, 23)
- self.assertEqual(t.tm_min, 58)
- self.assertEqual(t.tm_sec, 37)
- self.assertEqual(t.tm_yday, 1)
- self.assertEqual(t.tm_isdst, 0)
-
- def test_tzinfo_isoformat(self):
- zero = FixedOffset(0, "+00:00")
- plus = FixedOffset(220, "+03:40")
- minus = FixedOffset(-231, "-03:51")
- unknown = FixedOffset(None, "")
-
- cls = self.theclass
- datestr = '0001-02-03'
- for ofs in None, zero, plus, minus, unknown:
- for us in 0, 987001:
- d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
- timestr = '04:05:59' + (us and '.987001' or '')
- ofsstr = ofs is not None and d.tzname() or ''
- tailstr = timestr + ofsstr
- iso = d.isoformat()
- self.assertEqual(iso, datestr + 'T' + tailstr)
- self.assertEqual(iso, d.isoformat('T'))
- self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
- self.assertEqual(str(d), datestr + ' ' + tailstr)
-
- def test_replace(self):
- cls = self.theclass
- z100 = FixedOffset(100, "+100")
- zm200 = FixedOffset(timedelta(minutes=-200), "-200")
- args = [1, 2, 3, 4, 5, 6, 7, z100]
- base = cls(*args)
- self.assertEqual(base, base.replace())
-
- i = 0
- for name, newval in (("year", 2),
- ("month", 3),
- ("day", 4),
- ("hour", 5),
- ("minute", 6),
- ("second", 7),
- ("microsecond", 8),
- ("tzinfo", zm200)):
- newargs = args[:]
- newargs[i] = newval
- expected = cls(*newargs)
- got = base.replace(**{name: newval})
- self.assertEqual(expected, got)
- i += 1
-
- # Ensure we can get rid of a tzinfo.
- self.assertEqual(base.tzname(), "+100")
- base2 = base.replace(tzinfo=None)
- self.assertTrue(base2.tzinfo is None)
- self.assertTrue(base2.tzname() is None)
-
- # Ensure we can add one.
- base3 = base2.replace(tzinfo=z100)
- self.assertEqual(base, base3)
- self.assertTrue(base.tzinfo is base3.tzinfo)
-
- # Out of bounds.
- base = cls(2000, 2, 29)
- self.assertRaises(ValueError, base.replace, year=2001)
-
- def test_more_astimezone(self):
- # The inherited test_astimezone covered some trivial and error cases.
- fnone = FixedOffset(None, "None")
- f44m = FixedOffset(44, "44")
- fm5h = FixedOffset(-timedelta(hours=5), "m300")
-
- dt = self.theclass.now(tz=f44m)
- self.assertTrue(dt.tzinfo is f44m)
- # Replacing with degenerate tzinfo raises an exception.
- self.assertRaises(ValueError, dt.astimezone, fnone)
- # Ditto with None tz.
- self.assertRaises(TypeError, dt.astimezone, None)
- # Replacing with same tzinfo makes no change.
- x = dt.astimezone(dt.tzinfo)
- self.assertTrue(x.tzinfo is f44m)
- self.assertEqual(x.date(), dt.date())
- self.assertEqual(x.time(), dt.time())
-
- # Replacing with different tzinfo does adjust.
- got = dt.astimezone(fm5h)
- self.assertTrue(got.tzinfo is fm5h)
- self.assertEqual(got.utcoffset(), timedelta(hours=-5))
- expected = dt - dt.utcoffset() # in effect, convert to UTC
- expected += fm5h.utcoffset(dt) # and from there to local time
- expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
- self.assertEqual(got.date(), expected.date())
- self.assertEqual(got.time(), expected.time())
- self.assertEqual(got.timetz(), expected.timetz())
- self.assertTrue(got.tzinfo is expected.tzinfo)
- self.assertEqual(got, expected)
-
- def test_aware_subtract(self):
- cls = self.theclass
-
- # Ensure that utcoffset() is ignored when the operands have the
- # same tzinfo member.
- class OperandDependentOffset(tzinfo):
- def utcoffset(self, t):
- if t.minute < 10:
- # d0 and d1 equal after adjustment
- return timedelta(minutes=t.minute)
- else:
- # d2 off in the weeds
- return timedelta(minutes=59)
-
- base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
- d0 = base.replace(minute=3)
- d1 = base.replace(minute=9)
- d2 = base.replace(minute=11)
- for x in d0, d1, d2:
- for y in d0, d1, d2:
- got = x - y
- expected = timedelta(minutes=x.minute - y.minute)
- self.assertEqual(got, expected)
-
- # OTOH, if the tzinfo members are distinct, utcoffsets aren't
- # ignored.
- base = cls(8, 9, 10, 11, 12, 13, 14)
- d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
- d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
- d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
- for x in d0, d1, d2:
- for y in d0, d1, d2:
- got = x - y
- if (x is d0 or x is d1) and (y is d0 or y is d1):
- expected = timedelta(0)
- elif x is y is d2:
- expected = timedelta(0)
- elif x is d2:
- expected = timedelta(minutes=(11-59)-0)
- else:
- assert y is d2
- expected = timedelta(minutes=0-(11-59))
- self.assertEqual(got, expected)
-
- def test_mixed_compare(self):
- t1 = datetime(1, 2, 3, 4, 5, 6, 7)
- t2 = datetime(1, 2, 3, 4, 5, 6, 7)
- self.assertEqual(t1, t2)
- t2 = t2.replace(tzinfo=None)
- self.assertEqual(t1, t2)
- t2 = t2.replace(tzinfo=FixedOffset(None, ""))
- self.assertEqual(t1, t2)
- t2 = t2.replace(tzinfo=FixedOffset(0, ""))
- self.assertRaises(TypeError, lambda: t1 == t2)
-
- # In datetime w/ identical tzinfo objects, utcoffset is ignored.
- class Varies(tzinfo):
- def __init__(self):
- self.offset = timedelta(minutes=22)
- def utcoffset(self, t):
- self.offset += timedelta(minutes=1)
- return self.offset
-
- v = Varies()
- t1 = t2.replace(tzinfo=v)
- t2 = t2.replace(tzinfo=v)
- self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
- self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
- self.assertEqual(t1, t2)
-
- # But if they're not identical, it isn't ignored.
- t2 = t2.replace(tzinfo=Varies())
- self.assertTrue(t1 < t2) # t1's offset counter still going up
-
- def test_subclass_datetimetz(self):
-
- class C(self.theclass):
- theAnswer = 42
-
- def __new__(cls, *args, **kws):
- temp = kws.copy()
- extra = temp.pop('extra')
- result = self.theclass.__new__(cls, *args, **temp)
- result.extra = extra
- return result
-
- def newmeth(self, start):
- return start + self.hour + self.year
-
- args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
-
- dt1 = self.theclass(*args)
- dt2 = C(*args, **{'extra': 7})
-
- self.assertEqual(dt2.__class__, C)
- self.assertEqual(dt2.theAnswer, 42)
- self.assertEqual(dt2.extra, 7)
- self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
- self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
-
-# Pain to set up DST-aware tzinfo classes.
-
-def first_sunday_on_or_after(dt):
- days_to_go = 6 - dt.weekday()
- if days_to_go:
- dt += timedelta(days_to_go)
- return dt
-
-ZERO = timedelta(0)
-HOUR = timedelta(hours=1)
-DAY = timedelta(days=1)
-# In the US, DST starts at 2am (standard time) on the first Sunday in April.
-DSTSTART = datetime(1, 4, 1, 2)
-# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
-# which is the first Sunday on or after Oct 25. Because we view 1:MM as
-# being standard time on that day, there is no spelling in local time of
-# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
-DSTEND = datetime(1, 10, 25, 1)
-
-class USTimeZone(tzinfo):
-
- def __init__(self, hours, reprname, stdname, dstname):
- self.stdoffset = timedelta(hours=hours)
- self.reprname = reprname
- self.stdname = stdname
- self.dstname = dstname
-
- def __repr__(self):
- return self.reprname
-
- def tzname(self, dt):
- if self.dst(dt):
- return self.dstname
- else:
- return self.stdname
-
- def utcoffset(self, dt):
- return self.stdoffset + self.dst(dt)
-
- def dst(self, dt):
- if dt is None or dt.tzinfo is None:
- # An exception instead may be sensible here, in one or more of
- # the cases.
- return ZERO
- assert dt.tzinfo is self
-
- # Find first Sunday in April.
- start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
- assert start.weekday() == 6 and start.month == 4 and start.day <= 7
-
- # Find last Sunday in October.
- end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
- assert end.weekday() == 6 and end.month == 10 and end.day >= 25
-
- # Can't compare naive to aware objects, so strip the timezone from
- # dt first.
- if start <= dt.replace(tzinfo=None) < end:
- return HOUR
- else:
- return ZERO
-
-Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
-Central = USTimeZone(-6, "Central", "CST", "CDT")
-Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
-Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
-utc_real = FixedOffset(0, "UTC", 0)
-# For better test coverage, we want another flavor of UTC that's west of
-# the Eastern and Pacific timezones.
-utc_fake = FixedOffset(-12*60, "UTCfake", 0)
-
-class TestTimezoneConversions(unittest.TestCase):
- # The DST switch times for 2002, in std time.
- dston = datetime(2002, 4, 7, 2)
- dstoff = datetime(2002, 10, 27, 1)
-
- theclass = datetime
-
- # Check a time that's inside DST.
- def checkinside(self, dt, tz, utc, dston, dstoff):
- self.assertEqual(dt.dst(), HOUR)
-
- # Conversion to our own timezone is always an identity.
- self.assertEqual(dt.astimezone(tz), dt)
-
- asutc = dt.astimezone(utc)
- there_and_back = asutc.astimezone(tz)
-
- # Conversion to UTC and back isn't always an identity here,
- # because there are redundant spellings (in local time) of
- # UTC time when DST begins: the clock jumps from 1:59:59
- # to 3:00:00, and a local time of 2:MM:SS doesn't really
- # make sense then. The classes above treat 2:MM:SS as
- # daylight time then (it's "after 2am"), really an alias
- # for 1:MM:SS standard time. The latter form is what
- # conversion back from UTC produces.
- if dt.date() == dston.date() and dt.hour == 2:
- # We're in the redundant hour, and coming back from
- # UTC gives the 1:MM:SS standard-time spelling.
- self.assertEqual(there_and_back + HOUR, dt)
- # Although during was considered to be in daylight
- # time, there_and_back is not.
- self.assertEqual(there_and_back.dst(), ZERO)
- # They're the same times in UTC.
- self.assertEqual(there_and_back.astimezone(utc),
- dt.astimezone(utc))
- else:
- # We're not in the redundant hour.
- self.assertEqual(dt, there_and_back)
-
- # Because we have a redundant spelling when DST begins, there is
- # (unfortunately) an hour when DST ends that can't be spelled at all in
- # local time. When DST ends, the clock jumps from 1:59 back to 1:00
- # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
- # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
- # daylight time. The hour 1:MM daylight == 0:MM standard can't be
- # expressed in local time. Nevertheless, we want conversion back
- # from UTC to mimic the local clock's "repeat an hour" behavior.
- nexthour_utc = asutc + HOUR
- nexthour_tz = nexthour_utc.astimezone(tz)
- if dt.date() == dstoff.date() and dt.hour == 0:
- # We're in the hour before the last DST hour. The last DST hour
- # is ineffable. We want the conversion back to repeat 1:MM.
- self.assertEqual(nexthour_tz, dt.replace(hour=1))
- nexthour_utc += HOUR
- nexthour_tz = nexthour_utc.astimezone(tz)
- self.assertEqual(nexthour_tz, dt.replace(hour=1))
- else:
- self.assertEqual(nexthour_tz - dt, HOUR)
-
- # Check a time that's outside DST.
- def checkoutside(self, dt, tz, utc):
- self.assertEqual(dt.dst(), ZERO)
-
- # Conversion to our own timezone is always an identity.
- self.assertEqual(dt.astimezone(tz), dt)
-
- # Converting to UTC and back is an identity too.
- asutc = dt.astimezone(utc)
- there_and_back = asutc.astimezone(tz)
- self.assertEqual(dt, there_and_back)
-
- def convert_between_tz_and_utc(self, tz, utc):
- dston = self.dston.replace(tzinfo=tz)
- # Because 1:MM on the day DST ends is taken as being standard time,
- # there is no spelling in tz for the last hour of daylight time.
- # For purposes of the test, the last hour of DST is 0:MM, which is
- # taken as being daylight time (and 1:MM is taken as being standard
- # time).
- dstoff = self.dstoff.replace(tzinfo=tz)
- for delta in (timedelta(weeks=13),
- DAY,
- HOUR,
- timedelta(minutes=1),
- timedelta(microseconds=1)):
-
- self.checkinside(dston, tz, utc, dston, dstoff)
- for during in dston + delta, dstoff - delta:
- self.checkinside(during, tz, utc, dston, dstoff)
-
- self.checkoutside(dstoff, tz, utc)
- for outside in dston - delta, dstoff + delta:
- self.checkoutside(outside, tz, utc)
-
- def test_easy(self):
- # Despite the name of this test, the endcases are excruciating.
- self.convert_between_tz_and_utc(Eastern, utc_real)
- self.convert_between_tz_and_utc(Pacific, utc_real)
- self.convert_between_tz_and_utc(Eastern, utc_fake)
- self.convert_between_tz_and_utc(Pacific, utc_fake)
- # The next is really dancing near the edge. It works because
- # Pacific and Eastern are far enough apart that their "problem
- # hours" don't overlap.
- self.convert_between_tz_and_utc(Eastern, Pacific)
- self.convert_between_tz_and_utc(Pacific, Eastern)
- # OTOH, these fail! Don't enable them. The difficulty is that
- # the edge case tests assume that every hour is representable in
- # the "utc" class. This is always true for a fixed-offset tzinfo
- # class (lke utc_real and utc_fake), but not for Eastern or Central.
- # For these adjacent DST-aware time zones, the range of time offsets
- # tested ends up creating hours in the one that aren't representable
- # in the other. For the same reason, we would see failures in the
- # Eastern vs Pacific tests too if we added 3*HOUR to the list of
- # offset deltas in convert_between_tz_and_utc().
- #
- # self.convert_between_tz_and_utc(Eastern, Central) # can't work
- # self.convert_between_tz_and_utc(Central, Eastern) # can't work
-
- def test_tricky(self):
- # 22:00 on day before daylight starts.
- fourback = self.dston - timedelta(hours=4)
- ninewest = FixedOffset(-9*60, "-0900", 0)
- fourback = fourback.replace(tzinfo=ninewest)
- # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
- # 2", we should get the 3 spelling.
- # If we plug 22:00 the day before into Eastern, it "looks like std
- # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
- # to 22:00 lands on 2:00, which makes no sense in local time (the
- # local clock jumps from 1 to 3). The point here is to make sure we
- # get the 3 spelling.
- expected = self.dston.replace(hour=3)
- got = fourback.astimezone(Eastern).replace(tzinfo=None)
- self.assertEqual(expected, got)
-
- # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
- # case we want the 1:00 spelling.
- sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
- # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
- # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
- # spelling.
- expected = self.dston.replace(hour=1)
- got = sixutc.astimezone(Eastern).replace(tzinfo=None)
- self.assertEqual(expected, got)
-
- # Now on the day DST ends, we want "repeat an hour" behavior.
- # UTC 4:MM 5:MM 6:MM 7:MM checking these
- # EST 23:MM 0:MM 1:MM 2:MM
- # EDT 0:MM 1:MM 2:MM 3:MM
- # wall 0:MM 1:MM 1:MM 2:MM against these
- for utc in utc_real, utc_fake:
- for tz in Eastern, Pacific:
- first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
- # Convert that to UTC.
- first_std_hour -= tz.utcoffset(None)
- # Adjust for possibly fake UTC.
- asutc = first_std_hour + utc.utcoffset(None)
- # First UTC hour to convert; this is 4:00 when utc=utc_real &
- # tz=Eastern.
- asutcbase = asutc.replace(tzinfo=utc)
- for tzhour in (0, 1, 1, 2):
- expectedbase = self.dstoff.replace(hour=tzhour)
- for minute in 0, 30, 59:
- expected = expectedbase.replace(minute=minute)
- asutc = asutcbase.replace(minute=minute)
- astz = asutc.astimezone(tz)
- self.assertEqual(astz.replace(tzinfo=None), expected)
- asutcbase += HOUR
-
-
- def test_bogus_dst(self):
- class ok(tzinfo):
- def utcoffset(self, dt): return HOUR
- def dst(self, dt): return HOUR
-
- now = self.theclass.now().replace(tzinfo=utc_real)
- # Doesn't blow up.
- now.astimezone(ok())
-
- # Does blow up.
- class notok(ok):
- def dst(self, dt): return None
- self.assertRaises(ValueError, now.astimezone, notok())
-
- def test_fromutc(self):
- self.assertRaises(TypeError, Eastern.fromutc) # not enough args
- now = datetime.utcnow().replace(tzinfo=utc_real)
- self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
- now = now.replace(tzinfo=Eastern) # insert correct tzinfo
- enow = Eastern.fromutc(now) # doesn't blow up
- self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
- self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
- self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
-
- # Always converts UTC to standard time.
- class FauxUSTimeZone(USTimeZone):
- def fromutc(self, dt):
- return dt + self.stdoffset
- FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
-
- # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
- # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
- # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
-
- # Check around DST start.
- start = self.dston.replace(hour=4, tzinfo=Eastern)
- fstart = start.replace(tzinfo=FEastern)
- for wall in 23, 0, 1, 3, 4, 5:
- expected = start.replace(hour=wall)
- if wall == 23:
- expected -= timedelta(days=1)
- got = Eastern.fromutc(start)
- self.assertEqual(expected, got)
-
- expected = fstart + FEastern.stdoffset
- got = FEastern.fromutc(fstart)
- self.assertEqual(expected, got)
-
- # Ensure astimezone() calls fromutc() too.
- got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
- self.assertEqual(expected, got)
-
- start += HOUR
- fstart += HOUR
-
- # Check around DST end.
- start = self.dstoff.replace(hour=4, tzinfo=Eastern)
- fstart = start.replace(tzinfo=FEastern)
- for wall in 0, 1, 1, 2, 3, 4:
- expected = start.replace(hour=wall)
- got = Eastern.fromutc(start)
- self.assertEqual(expected, got)
-
- expected = fstart + FEastern.stdoffset
- got = FEastern.fromutc(fstart)
- self.assertEqual(expected, got)
-
- # Ensure astimezone() calls fromutc() too.
- got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
- self.assertEqual(expected, got)
-
- start += HOUR
- fstart += HOUR
-
-
-#############################################################################
-# oddballs
-
-class Oddballs(unittest.TestCase):
-
- def test_bug_1028306(self):
- # Trying to compare a date to a datetime should act like a mixed-
- # type comparison, despite that datetime is a subclass of date.
- as_date = date.today()
- as_datetime = datetime.combine(as_date, time())
- self.assertTrue(as_date != as_datetime)
- self.assertTrue(as_datetime != as_date)
- self.assertTrue(not as_date == as_datetime)
- self.assertTrue(not as_datetime == as_date)
- self.assertRaises(TypeError, lambda: as_date < as_datetime)
- self.assertRaises(TypeError, lambda: as_datetime < as_date)
- self.assertRaises(TypeError, lambda: as_date <= as_datetime)
- self.assertRaises(TypeError, lambda: as_datetime <= as_date)
- self.assertRaises(TypeError, lambda: as_date > as_datetime)
- self.assertRaises(TypeError, lambda: as_datetime > as_date)
- self.assertRaises(TypeError, lambda: as_date >= as_datetime)
- self.assertRaises(TypeError, lambda: as_datetime >= as_date)
-
- # Neverthelss, comparison should work with the base-class (date)
- # projection if use of a date method is forced.
- self.assertTrue(as_date.__eq__(as_datetime))
- different_day = (as_date.day + 1) % 20 + 1
- self.assertTrue(not as_date.__eq__(as_datetime.replace(day=
- different_day)))
-
- # And date should compare with other subclasses of date. If a
- # subclass wants to stop this, it's up to the subclass to do so.
- date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
- self.assertEqual(as_date, date_sc)
- self.assertEqual(date_sc, as_date)
-
- # Ditto for datetimes.
- datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
- as_date.day, 0, 0, 0)
- self.assertEqual(as_datetime, datetime_sc)
- self.assertEqual(datetime_sc, as_datetime)
-
-def test_main():
- test_support.run_unittest(__name__)
-
-if __name__ == "__main__":
- test_main()
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sat Feb 7 03:38:21 2015
From: jython-checkins at python.org (jim.baker)
Date: Sat, 07 Feb 2015 02:38:21 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Support_readline=2Eset=5Fst?=
=?utf-8?q?artup=5Fhook_to_be_set_to_None?=
Message-ID: <20150207023819.50315.84438@psf.io>
https://hg.python.org/jython/rev/0a0bf07d7e7d
changeset: 7574:0a0bf07d7e7d
user: Geoffrey French
date: Fri Feb 06 19:31:13 2015 -0700
summary:
Support readline.set_startup_hook to be set to None
files:
src/org/python/util/JLineConsole.java | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/src/org/python/util/JLineConsole.java b/src/org/python/util/JLineConsole.java
--- a/src/org/python/util/JLineConsole.java
+++ b/src/org/python/util/JLineConsole.java
@@ -19,6 +19,7 @@
import org.python.core.PlainConsole;
import org.python.core.PyObject;
+import org.python.core.Py;
/**
* This class uses JLine to provide readline like
@@ -259,6 +260,10 @@
* Sets the startup hook (called prior to each readline)
*/
public void setStartupHook(PyObject hook) {
+ // Convert None to null here, so that readerReadLine can use only a null check
+ if (hook == Py.None) {
+ hook = null;
+ }
startup_hook = hook;
}
}
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sat Feb 7 04:21:19 2015
From: jython-checkins at python.org (jim.baker)
Date: Sat, 07 Feb 2015 03:21:19 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Restore_readline=2Eget=5Fli?=
=?utf-8?q?ne=5Fbuffer_function?=
Message-ID: <20150207032119.52854.12918@psf.io>
https://hg.python.org/jython/rev/e778f6a4c55e
changeset: 7575:e778f6a4c55e
user: Jim Baker
date: Fri Feb 06 19:59:54 2015 -0700
summary:
Restore readline.get_line_buffer function
Thanks, Dieter Vandenbussche!
files:
ACKNOWLEDGMENTS | 1 +
Lib/readline.py | 3 +++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS
--- a/ACKNOWLEDGMENTS
+++ b/ACKNOWLEDGMENTS
@@ -148,6 +148,7 @@
Pekka Kl?rck
Nathaniel Kenmir
Jiwon Seo
+ Dieter Vandenbussche
Local Variables:
mode: indented-text
diff --git a/Lib/readline.py b/Lib/readline.py
--- a/Lib/readline.py
+++ b/Lib/readline.py
@@ -58,6 +58,9 @@
def parse_and_bind(string):
pass
+def get_line_buffer():
+ return str(_reader.cursorBuffer.buffer)
+
def insert_text(string):
_reader.putString(string)
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sat Feb 7 04:21:20 2015
From: jython-checkins at python.org (jim.baker)
Date: Sat, 07 Feb 2015 03:21:20 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_readline=2Eread=5Fhisto?=
=?utf-8?q?ry=5Ffile=2C_write=5Fhistory=5Ffile_functions?=
Message-ID: <20150207032119.52854.34646@psf.io>
https://hg.python.org/jython/rev/65f96010b35c
changeset: 7576:65f96010b35c
user: Jim Baker
date: Fri Feb 06 20:21:15 2015 -0700
summary:
Fix readline.read_history_file, write_history_file functions
These functions had not been updated with the upgrade to JLine2.
Fixes http://bugs.jython.org/issue2266
files:
Lib/readline.py | 26 ++------------------------
1 files changed, 2 insertions(+), 24 deletions(-)
diff --git a/Lib/readline.py b/Lib/readline.py
--- a/Lib/readline.py
+++ b/Lib/readline.py
@@ -39,22 +39,6 @@
"""Security manager prevents access to private field"""
-def _setup_history():
- # This is obviously not desirable, but avoids O(n) workarounds to
- # modify the history (ipython uses the function
- # remove_history_item to mutate the history relatively frequently)
- global _history_list
-
- history = _reader.history
- try:
- history_list_field = history.class.getDeclaredField("history")
- history_list_field.setAccessible(True)
- _history_list = history_list_field.get(history)
- except:
- pass
-
-_setup_history()
-
def parse_and_bind(string):
pass
@@ -68,21 +52,15 @@
warn("read_init_file: %s" % (filename,), NotImplementedWarning, "module", 2)
def read_history_file(filename="~/.history"):
- print "Reading history:", filename
expanded = os.path.expanduser(filename)
- new_history = _reader.getHistory().getClass()()
- # new_history.clear()
with open(expanded) as f:
- for line in f:
- new_history.addToHistory(line.rstrip())
- _reader.history = new_history
- _setup_history()
+ _reader.history.load(f)
def write_history_file(filename="~/.history"):
expanded = os.path.expanduser(filename)
with open(expanded, 'w') as f:
for line in _reader.history.entries():
- f.write(line)
+ f.write(line.value().encode("utf-8"))
f.write("\n")
def clear_history():
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 11 04:51:58 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Wed, 11 Feb 2015 03:51:58 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixed_issue_2224_and_added_?=
=?utf-8?q?a_CPython-like_Traverseproc-mechanism?=
Message-ID: <20150211035158.43793.23477@psf.io>
https://hg.python.org/jython/rev/3536cd7b2657
changeset: 7577:3536cd7b2657
user: Stefan Richthofer
date: Wed Feb 11 04:49:36 2015 +0100
summary:
Fixed issue 2224 and added a CPython-like Traverseproc-mechanism
along with some enhancements to the gc-module.
For details see doc of org.python.modules.gc.
While it is optional, we recommend for PyObject-subclasses to
implement the Traverseproc-mechanism (all PyObject-subclasses
bundeled with Jython already do).
For instructions see the docs in org.python.core.Traverseproc
and org.python.core.Untraversable.
Note that this commit also simplifies the implementation of
finalizable PyObject-subclasses, i.e. one does not need to
declare a finalizeTrigger-field any more. Doc in
FinalizablePyObject was updated accordingly.
files:
Lib/test/regrtest.py | 1 -
Lib/test/test_finalizers.py | 12 +-
Lib/test/test_gc.py | 760 +++
Lib/test/test_gc_jy.py | 601 ++
Lib/test/test_resurrection_attr_preserve.py | 97 +
Lib/test/test_weakref.py | 4 +-
NEWS | 5 +
src/com/ziclix/python/sql/DBApiType.java | 3 +-
src/com/ziclix/python/sql/Fetch.java | 84 +-
src/com/ziclix/python/sql/PyConnection.java | 41 +-
src/com/ziclix/python/sql/PyCursor.java | 67 +-
src/com/ziclix/python/sql/PyStatement.java | 37 +-
src/com/ziclix/python/sql/connect/Connect.java | 1 +
src/com/ziclix/python/sql/connect/Connectx.java | 8 +-
src/com/ziclix/python/sql/connect/Lookup.java | 2 +
src/com/ziclix/python/sql/util/BCP.java | 23 +-
src/com/ziclix/python/sql/zxJDBC.java | 3 +
src/org/python/antlr/AST.java | 2 +
src/org/python/antlr/PythonTree.java | 16 +-
src/org/python/antlr/ast/AssertDerived.java | 30 +-
src/org/python/antlr/ast/AssignDerived.java | 30 +-
src/org/python/antlr/ast/AttributeDerived.java | 30 +-
src/org/python/antlr/ast/AugAssignDerived.java | 30 +-
src/org/python/antlr/ast/BinOpDerived.java | 30 +-
src/org/python/antlr/ast/BoolOpDerived.java | 30 +-
src/org/python/antlr/ast/BreakDerived.java | 30 +-
src/org/python/antlr/ast/CallDerived.java | 30 +-
src/org/python/antlr/ast/ClassDefDerived.java | 30 +-
src/org/python/antlr/ast/CompareDerived.java | 30 +-
src/org/python/antlr/ast/ContinueDerived.java | 30 +-
src/org/python/antlr/ast/DeleteDerived.java | 30 +-
src/org/python/antlr/ast/DictDerived.java | 30 +-
src/org/python/antlr/ast/EllipsisDerived.java | 30 +-
src/org/python/antlr/ast/ExceptHandlerDerived.java | 30 +-
src/org/python/antlr/ast/ExecDerived.java | 30 +-
src/org/python/antlr/ast/ExprDerived.java | 30 +-
src/org/python/antlr/ast/ExpressionDerived.java | 30 +-
src/org/python/antlr/ast/ExtSliceDerived.java | 30 +-
src/org/python/antlr/ast/ForDerived.java | 30 +-
src/org/python/antlr/ast/FunctionDefDerived.java | 30 +-
src/org/python/antlr/ast/GeneratorExpDerived.java | 30 +-
src/org/python/antlr/ast/GlobalDerived.java | 30 +-
src/org/python/antlr/ast/IfDerived.java | 30 +-
src/org/python/antlr/ast/IfExpDerived.java | 30 +-
src/org/python/antlr/ast/ImportDerived.java | 30 +-
src/org/python/antlr/ast/ImportFromDerived.java | 30 +-
src/org/python/antlr/ast/IndexDerived.java | 30 +-
src/org/python/antlr/ast/InteractiveDerived.java | 30 +-
src/org/python/antlr/ast/LambdaDerived.java | 30 +-
src/org/python/antlr/ast/ListCompDerived.java | 30 +-
src/org/python/antlr/ast/ListDerived.java | 30 +-
src/org/python/antlr/ast/ModuleDerived.java | 30 +-
src/org/python/antlr/ast/NameDerived.java | 30 +-
src/org/python/antlr/ast/NumDerived.java | 30 +-
src/org/python/antlr/ast/PassDerived.java | 30 +-
src/org/python/antlr/ast/PrintDerived.java | 30 +-
src/org/python/antlr/ast/RaiseDerived.java | 30 +-
src/org/python/antlr/ast/ReprDerived.java | 30 +-
src/org/python/antlr/ast/ReturnDerived.java | 30 +-
src/org/python/antlr/ast/SliceDerived.java | 30 +-
src/org/python/antlr/ast/StrDerived.java | 30 +-
src/org/python/antlr/ast/SubscriptDerived.java | 30 +-
src/org/python/antlr/ast/SuiteDerived.java | 30 +-
src/org/python/antlr/ast/TryExceptDerived.java | 30 +-
src/org/python/antlr/ast/TryFinallyDerived.java | 30 +-
src/org/python/antlr/ast/TupleDerived.java | 30 +-
src/org/python/antlr/ast/UnaryOpDerived.java | 30 +-
src/org/python/antlr/ast/WhileDerived.java | 30 +-
src/org/python/antlr/ast/WithDerived.java | 30 +-
src/org/python/antlr/ast/YieldDerived.java | 30 +-
src/org/python/antlr/ast/aliasDerived.java | 30 +-
src/org/python/antlr/ast/arguments.java | 45 +
src/org/python/antlr/ast/argumentsDerived.java | 30 +-
src/org/python/antlr/ast/comprehension.java | 39 +
src/org/python/antlr/ast/comprehensionDerived.java | 30 +-
src/org/python/antlr/ast/keyword.java | 12 +
src/org/python/antlr/ast/keywordDerived.java | 30 +-
src/org/python/antlr/op/AddDerived.java | 30 +-
src/org/python/antlr/op/AndDerived.java | 30 +-
src/org/python/antlr/op/AugLoadDerived.java | 30 +-
src/org/python/antlr/op/AugStoreDerived.java | 30 +-
src/org/python/antlr/op/BitAndDerived.java | 30 +-
src/org/python/antlr/op/BitOrDerived.java | 30 +-
src/org/python/antlr/op/BitXorDerived.java | 30 +-
src/org/python/antlr/op/DelDerived.java | 30 +-
src/org/python/antlr/op/DivDerived.java | 30 +-
src/org/python/antlr/op/EqDerived.java | 30 +-
src/org/python/antlr/op/FloorDivDerived.java | 30 +-
src/org/python/antlr/op/GtDerived.java | 30 +-
src/org/python/antlr/op/GtEDerived.java | 30 +-
src/org/python/antlr/op/InDerived.java | 30 +-
src/org/python/antlr/op/InvertDerived.java | 30 +-
src/org/python/antlr/op/IsDerived.java | 30 +-
src/org/python/antlr/op/IsNotDerived.java | 30 +-
src/org/python/antlr/op/LShiftDerived.java | 30 +-
src/org/python/antlr/op/LoadDerived.java | 30 +-
src/org/python/antlr/op/LtDerived.java | 30 +-
src/org/python/antlr/op/LtEDerived.java | 30 +-
src/org/python/antlr/op/ModDerived.java | 30 +-
src/org/python/antlr/op/MultDerived.java | 30 +-
src/org/python/antlr/op/NotDerived.java | 30 +-
src/org/python/antlr/op/NotEqDerived.java | 30 +-
src/org/python/antlr/op/NotInDerived.java | 30 +-
src/org/python/antlr/op/OrDerived.java | 30 +-
src/org/python/antlr/op/ParamDerived.java | 30 +-
src/org/python/antlr/op/PowDerived.java | 30 +-
src/org/python/antlr/op/RShiftDerived.java | 30 +-
src/org/python/antlr/op/StoreDerived.java | 30 +-
src/org/python/antlr/op/SubDerived.java | 30 +-
src/org/python/antlr/op/UAddDerived.java | 30 +-
src/org/python/antlr/op/USubDerived.java | 30 +-
src/org/python/core/AstList.java | 20 +-
src/org/python/core/BaseBytes.java | 1 +
src/org/python/core/BaseDictionaryView.java | 14 +-
src/org/python/core/BaseSet.java | 77 +-
src/org/python/core/ClasspathPyImporter.java | 1 +
src/org/python/core/ClasspathPyImporterDerived.java | 26 +-
src/org/python/core/ContextGuard.java | 28 +-
src/org/python/core/IdImpl.java | 11 +-
src/org/python/core/JavaImporter.java | 1 +
src/org/python/core/JavaProxyList.java | 5 +-
src/org/python/core/JavaProxyMap.java | 4 +-
src/org/python/core/JavaProxySet.java | 29 +-
src/org/python/core/JyAttribute.java | 308 +
src/org/python/core/Py.java | 23 +-
src/org/python/core/Py2kBuffer.java | 1 +
src/org/python/core/PyArray.java | 23 +-
src/org/python/core/PyArrayDerived.java | 30 +-
src/org/python/core/PyBaseException.java | 27 +-
src/org/python/core/PyBaseExceptionDerived.java | 26 +-
src/org/python/core/PyBeanEvent.java | 1 +
src/org/python/core/PyBeanEventProperty.java | 1 +
src/org/python/core/PyBeanProperty.java | 1 +
src/org/python/core/PyBoolean.java | 1 +
src/org/python/core/PyBuiltinCallable.java | 1 +
src/org/python/core/PyBuiltinFunction.java | 1 +
src/org/python/core/PyBuiltinFunctionNarrow.java | 1 +
src/org/python/core/PyBuiltinFunctionSet.java | 1 +
src/org/python/core/PyBuiltinMethod.java | 14 +-
src/org/python/core/PyBuiltinMethodNarrow.java | 1 -
src/org/python/core/PyBuiltinMethodSet.java | 14 +-
src/org/python/core/PyByteArray.java | 1 +
src/org/python/core/PyByteArrayDerived.java | 30 +-
src/org/python/core/PyBytecode.java | 62 +-
src/org/python/core/PyCallIter.java | 23 +
src/org/python/core/PyCell.java | 14 +-
src/org/python/core/PyClass.java | 66 +-
src/org/python/core/PyClassMethod.java | 14 +-
src/org/python/core/PyClassMethodDerived.java | 30 +-
src/org/python/core/PyClassMethodDescr.java | 1 +
src/org/python/core/PyComplex.java | 1 +
src/org/python/core/PyComplexDerived.java | 30 +-
src/org/python/core/PyCompoundCallable.java | 29 +-
src/org/python/core/PyDataDescr.java | 1 +
src/org/python/core/PyDescriptor.java | 16 +-
src/org/python/core/PyDictProxy.java | 14 +-
src/org/python/core/PyDictionary.java | 70 +-
src/org/python/core/PyDictionaryDerived.java | 32 +-
src/org/python/core/PyEllipsis.java | 1 +
src/org/python/core/PyEnumerate.java | 23 +
src/org/python/core/PyEnumerateDerived.java | 30 +-
src/org/python/core/PyException.java | 30 +-
src/org/python/core/PyFastSequenceIter.java | 17 +
src/org/python/core/PyFile.java | 25 +-
src/org/python/core/PyFileDerived.java | 30 +-
src/org/python/core/PyFileReader.java | 2 +-
src/org/python/core/PyFileWriter.java | 1 +
src/org/python/core/PyFloat.java | 1 +
src/org/python/core/PyFloatDerived.java | 30 +-
src/org/python/core/PyFrame.java | 176 +-
src/org/python/core/PyFrozenSetDerived.java | 30 +-
src/org/python/core/PyFunction.java | 67 +-
src/org/python/core/PyGenerator.java | 33 +-
src/org/python/core/PyInstance.java | 44 +-
src/org/python/core/PyInteger.java | 1 +
src/org/python/core/PyIntegerDerived.java | 30 +-
src/org/python/core/PyIterator.java | 16 +-
src/org/python/core/PyJavaPackage.java | 33 +-
src/org/python/core/PyJavaType.java | 51 +-
src/org/python/core/PyList.java | 23 +
src/org/python/core/PyListDerived.java | 30 +-
src/org/python/core/PyLong.java | 1 +
src/org/python/core/PyLongDerived.java | 30 +-
src/org/python/core/PyMemoryView.java | 46 +-
src/org/python/core/PyMethod.java | 26 +-
src/org/python/core/PyMethodDescr.java | 14 +-
src/org/python/core/PyModule.java | 14 +-
src/org/python/core/PyModuleDerived.java | 26 +-
src/org/python/core/PyNewWrapper.java | 14 +-
src/org/python/core/PyNone.java | 1 +
src/org/python/core/PyNotImplemented.java | 1 +
src/org/python/core/PyObject.java | 88 +-
src/org/python/core/PyObjectDerived.java | 30 +-
src/org/python/core/PyProperty.java | 33 +-
src/org/python/core/PyPropertyDerived.java | 30 +-
src/org/python/core/PyProxy.java | 4 +-
src/org/python/core/PyReflectedConstructor.java | 5 +-
src/org/python/core/PyReflectedField.java | 1 +
src/org/python/core/PyReflectedFunction.java | 14 +-
src/org/python/core/PyReversedIterator.java | 16 +
src/org/python/core/PySequenceIter.java | 16 +
src/org/python/core/PySequenceList.java | 20 +-
src/org/python/core/PySetDerived.java | 32 +-
src/org/python/core/PySingleton.java | 1 +
src/org/python/core/PySlice.java | 24 +-
src/org/python/core/PySlot.java | 1 +
src/org/python/core/PyStaticMethod.java | 14 +-
src/org/python/core/PyString.java | 3 +-
src/org/python/core/PyStringDerived.java | 30 +-
src/org/python/core/PyStringMap.java | 33 +-
src/org/python/core/PySuper.java | 27 +-
src/org/python/core/PySuperDerived.java | 30 +-
src/org/python/core/PySystemState.java | 288 +-
src/org/python/core/PyTableCode.java | 1 +
src/org/python/core/PyTraceback.java | 21 +-
src/org/python/core/PyTuple.java | 46 +
src/org/python/core/PyTupleDerived.java | 30 +-
src/org/python/core/PyType.java | 79 +-
src/org/python/core/PyTypeDerived.java | 26 +-
src/org/python/core/PyUnicode.java | 1 +
src/org/python/core/PyUnicodeDerived.java | 30 +-
src/org/python/core/PyXRange.java | 1 +
src/org/python/core/PythonTraceFunction.java | 14 +-
src/org/python/core/SyspathArchive.java | 1 +
src/org/python/core/Traverseproc.java | 462 +
src/org/python/core/TraverseprocDerived.java | 17 +
src/org/python/core/Untraversable.java | 26 +
src/org/python/core/Visitproc.java | 7 +
src/org/python/core/__builtin__.java | 14 +
src/org/python/core/exceptions.java | 2 +
src/org/python/core/finalization/FinalizableBuiltin.java | 14 +-
src/org/python/core/finalization/FinalizablePyObject.java | 29 +-
src/org/python/core/finalization/FinalizablePyObjectDerived.java | 19 +-
src/org/python/core/finalization/FinalizeTrigger.java | 228 +-
src/org/python/core/finalization/FinalizeTriggerFactory.java | 4 +-
src/org/python/core/finalization/HasFinalizeTrigger.java | 13 -
src/org/python/core/finalization/PyFinalizableObject.java | 17 -
src/org/python/core/stringlib/FieldNameIterator.java | 28 +-
src/org/python/core/stringlib/MarkupIterator.java | 2 +
src/org/python/jsr223/PyScriptEngineScope.java | 19 +
src/org/python/modules/PyIOFileFactory.java | 43 +-
src/org/python/modules/PyStruct.java | 2 +
src/org/python/modules/PyStructDerived.java | 30 +-
src/org/python/modules/_codecs.java | 2 +
src/org/python/modules/_collections/PyDefaultDict.java | 46 +-
src/org/python/modules/_collections/PyDefaultDictDerived.java | 30 +-
src/org/python/modules/_collections/PyDeque.java | 59 +-
src/org/python/modules/_collections/PyDequeDerived.java | 30 +-
src/org/python/modules/_csv/PyDialect.java | 2 +
src/org/python/modules/_csv/PyDialectDerived.java | 26 +-
src/org/python/modules/_csv/PyReader.java | 30 +
src/org/python/modules/_csv/PyWriter.java | 23 +-
src/org/python/modules/_functools/PyPartial.java | 45 +-
src/org/python/modules/_functools/PyPartialDerived.java | 30 +-
src/org/python/modules/_hashlib.java | 2 +
src/org/python/modules/_io/Closer.java | 2 +-
src/org/python/modules/_io/PyFileIO.java | 2 +
src/org/python/modules/_io/PyFileIODerived.java | 26 +-
src/org/python/modules/_io/PyIOBase.java | 29 +-
src/org/python/modules/_io/PyIOBaseDerived.java | 26 +-
src/org/python/modules/_io/PyRawIOBaseDerived.java | 26 +-
src/org/python/modules/_json/Encoder.java | 56 +-
src/org/python/modules/_json/Scanner.java | 43 +-
src/org/python/modules/_jythonlib/dict_builder.java | 15 +-
src/org/python/modules/_marshal.java | 45 +-
src/org/python/modules/_threading/Condition.java | 17 +-
src/org/python/modules/_threading/Lock.java | 2 +
src/org/python/modules/_weakref/AbstractReference.java | 62 +-
src/org/python/modules/_weakref/GlobalRef.java | 140 +-
src/org/python/modules/_weakref/ReferenceType.java | 7 +-
src/org/python/modules/_weakref/ReferenceTypeDerived.java | 30 +-
src/org/python/modules/_weakref/WeakrefModule.java | 5 -
src/org/python/modules/bz2/PyBZ2Compressor.java | 2 +
src/org/python/modules/bz2/PyBZ2CompressorDerived.java | 30 +-
src/org/python/modules/bz2/PyBZ2Decompressor.java | 15 +-
src/org/python/modules/bz2/PyBZ2DecompressorDerived.java | 30 +-
src/org/python/modules/bz2/PyBZ2File.java | 9 +-
src/org/python/modules/bz2/PyBZ2FileDerived.java | 30 +-
src/org/python/modules/gc.java | 2488 +++++++++-
src/org/python/modules/itertools/PyTeeIterator.java | 47 +
src/org/python/modules/itertools/PyTeeIteratorDerived.java | 30 +-
src/org/python/modules/itertools/chain.java | 16 +
src/org/python/modules/itertools/chainDerived.java | 30 +-
src/org/python/modules/itertools/combinations.java | 17 +
src/org/python/modules/itertools/combinationsDerived.java | 30 +-
src/org/python/modules/itertools/combinationsWithReplacement.java | 17 +
src/org/python/modules/itertools/combinationsWithReplacementDerived.java | 30 +-
src/org/python/modules/itertools/compress.java | 16 +
src/org/python/modules/itertools/compressDerived.java | 30 +-
src/org/python/modules/itertools/count.java | 32 +
src/org/python/modules/itertools/countDerived.java | 30 +-
src/org/python/modules/itertools/cycle.java | 16 +
src/org/python/modules/itertools/cycleDerived.java | 30 +-
src/org/python/modules/itertools/dropwhile.java | 17 +
src/org/python/modules/itertools/dropwhileDerived.java | 30 +-
src/org/python/modules/itertools/groupby.java | 17 +
src/org/python/modules/itertools/groupbyDerived.java | 30 +-
src/org/python/modules/itertools/ifilter.java | 17 +
src/org/python/modules/itertools/ifilterDerived.java | 30 +-
src/org/python/modules/itertools/ifilterfalse.java | 17 +
src/org/python/modules/itertools/ifilterfalseDerived.java | 30 +-
src/org/python/modules/itertools/imap.java | 17 +
src/org/python/modules/itertools/islice.java | 17 +
src/org/python/modules/itertools/isliceDerived.java | 30 +-
src/org/python/modules/itertools/itertools.java | 47 +
src/org/python/modules/itertools/izip.java | 17 +
src/org/python/modules/itertools/izipDerived.java | 30 +-
src/org/python/modules/itertools/izipLongest.java | 17 +
src/org/python/modules/itertools/izipLongestDerived.java | 30 +-
src/org/python/modules/itertools/permutations.java | 17 +
src/org/python/modules/itertools/permutationsDerived.java | 30 +-
src/org/python/modules/itertools/product.java | 17 +
src/org/python/modules/itertools/productDerived.java | 30 +-
src/org/python/modules/itertools/repeat.java | 23 +
src/org/python/modules/itertools/repeatDerived.java | 30 +-
src/org/python/modules/itertools/starmap.java | 17 +
src/org/python/modules/itertools/starmapDerived.java | 30 +-
src/org/python/modules/itertools/takewhile.java | 17 +
src/org/python/modules/itertools/takewhileDerived.java | 30 +-
src/org/python/modules/jffi/ArrayCData.java | 16 +
src/org/python/modules/jffi/ByReference.java | 2 +
src/org/python/modules/jffi/CData.java | 16 +-
src/org/python/modules/jffi/DynamicLibrary.java | 2 +
src/org/python/modules/jffi/StructLayout.java | 22 +-
src/org/python/modules/operator.java | 104 +-
src/org/python/modules/posix/PosixModule.java | 6 +
src/org/python/modules/posix/PyStatResult.java | 73 +
src/org/python/modules/random/PyRandom.java | 2 +
src/org/python/modules/random/PyRandomDerived.java | 30 +-
src/org/python/modules/sre/MatchObject.java | 29 +-
src/org/python/modules/sre/PatternObject.java | 27 +-
src/org/python/modules/sre/ScannerObject.java | 24 +-
src/org/python/modules/synchronize.java | 17 +-
src/org/python/modules/thread/PyLocal.java | 57 +-
src/org/python/modules/thread/PyLocalDerived.java | 26 +-
src/org/python/modules/thread/PyLock.java | 8 +-
src/org/python/modules/time/PyTimeTuple.java | 67 +
src/org/python/modules/time/Time.java | 10 +-
src/org/python/modules/zipimport/zipimporterDerived.java | 30 +-
src/templates/dict.derived | 2 +-
src/templates/gderived-defs | 35 +-
src/templates/set.derived | 2 +-
342 files changed, 12121 insertions(+), 1475 deletions(-)
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -1311,7 +1311,6 @@
test_dummy_threading
test_eof
test_frozen # not meaningful for Jython, although it is similar to Clamp singlejar
- test_gc
test_iterlen
test_multibytecodec
test_multibytecodec_support
diff --git a/Lib/test/test_finalizers.py b/Lib/test/test_finalizers.py
--- a/Lib/test/test_finalizers.py
+++ b/Lib/test/test_finalizers.py
@@ -209,7 +209,7 @@
del DummyClass.__del__
def test_classAcquiresFinalizer_afterInstanciation_oldStyleClass(self):
- #okay to fail in Jython without the manual ensureFinalizer call
+ #okay to fail in Jython without the manual __ensure_finalizer__ call
C = DummyClass("C")
DummyClass.__del__ = delClass
try:
@@ -245,7 +245,7 @@
del DummyClassNew.__del__
def test_classAcquiresFinalizer_afterInstanciation_newStyleClass(self):
- #okay to fail in Jython without the manual ensureFinalizer call
+ #okay to fail in Jython without the manual __ensure_finalizer__ call
G = DummyClassNew("G")
DummyClassNew.__del__ = delClass
try:
@@ -270,7 +270,7 @@
def test_instanceAcquiresFinalizer_bound_newStyleClass2(self):
"""
- It seems, CPython prohibits new style instances from acquiring a finalizer.
+ In CPython, new style instances can't acquire a finalizer.
If one calls the instance-acquired __del__ manually, it works, but the gc
will still call the old one.
"""
@@ -327,7 +327,7 @@
def test_objectDoubleResurrectionAndFinalize_oldStyleClass(self):
- #okay to fail in Jython without the manual ensureFinalizer calls
+ #okay to fail in Jython without the manual __ensure_finalizer__ calls
ResurrectableDummyClass.__del__ = delK
K = ResurrectableDummyClass("K")
K = None
@@ -360,7 +360,7 @@
self.assertEqual(str(resurrectedObject_L), "L")
def test_objectDoubleResurrection_newStyleClass(self):
- #okay to fail in Jython without the manual ensureFinalizer calls
+ #okay to fail in Jython without the manual __ensure_finalizer__ calls
ResurrectableDummyClassNew.__del__ = delM
M = ResurrectableDummyClassNew("M")
M = None
@@ -382,7 +382,7 @@
self.assertEqual(str(resurrectedObject_M), "M")
def test_objectDoubleResurrectionAndFinalize_newStyleClass(self):
- #okay to fail in Jython without the manual ensureFinalizer calls
+ #okay to fail in Jython without the manual __ensure_finalizer__ calls
ResurrectableDummyClassNew.__del__ = delN
N = ResurrectableDummyClassNew("N")
N = None
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_gc.py
@@ -0,0 +1,760 @@
+import unittest
+#from test.test_support import verbose, run_unittest
+from test import test_support
+import sys
+import gc
+import weakref
+
+try:
+ import threading
+except ImportError:
+ threading = None
+
+### Support code
+###############################################################################
+
+# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
+# cyclic gc.
+
+# An instance of C1055820 has a self-loop, so becomes cyclic trash when
+# unreachable.
+class C1055820(object):
+ def __init__(self, i):
+ self.i = i
+ self.loop = self
+
+class GC_Detector(object):
+ # Create an instance I. Then gc hasn't happened again so long as
+ # I.gc_happened is false.
+
+ def __init__(self):
+ self.gc_happened = False
+
+ def it_happened(ignored):
+ self.gc_happened = True
+
+ # Create a piece of cyclic trash that triggers it_happened when
+ # gc collects it.
+ self.wr = weakref.ref(C1055820(666), it_happened)
+
+
+### Tests
+###############################################################################
+
+class GCTests(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ gc.setMonitorGlobal(True)
+ #since gc module already exists, it would not be caught by monitorGlobal.
+ #so we have to monitor it manually:
+ gc.monitorObject(gc)
+ #the finalizer-related tests need this flag to pass in Jython:
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_CYCLIC_GARBAGE)
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ def test_list(self):
+ l = []
+ l.append(l)
+ gc.collect()
+ del l
+ self.assertEqual(gc.collect(), 1)
+
+ def test_dict(self):
+ d = {}
+ d[1] = d
+ gc.collect()
+ del d
+ self.assertEqual(gc.collect(), 1)
+
+ def test_tuple(self):
+ # since tuples are immutable we close the loop with a list
+ l = []
+ t = (l,)
+ l.append(t)
+ gc.collect()
+ del t
+ del l
+ self.assertEqual(gc.collect(), 2)
+
+ def test_class(self):
+ class A:
+ pass
+ A.a = A
+ del A
+ self.assertNotEqual(gc.collect(), 0)
+
+ def test_newstyleclass(self):
+ class A(object):
+ pass
+ gc.collect()
+ del A
+ self.assertNotEqual(gc.collect(), 0)
+
+ def test_instance(self):
+ class A:
+ pass
+ a = A()
+ a.a = a
+ gc.collect()
+ del a
+ self.assertNotEqual(gc.collect(), 0)
+
+ def test_newinstance(self):
+ class A(object):
+ pass
+ a = A()
+ a.a = a
+ gc.collect()
+ del a
+ self.assertNotEqual(gc.collect(), 0)
+ class B(list):
+ pass
+ class C(B, A):
+ pass
+ a = C()
+ a.a = a
+ gc.collect()
+ del a
+ self.assertNotEqual(gc.collect(), 0)
+ del B, C
+ self.assertNotEqual(gc.collect(), 0)
+ A.a = A()
+ del A
+ self.assertNotEqual(gc.collect(), 0)
+
+ def test_method(self):
+ # Tricky: self.__init__ is a bound method, it references the instance.
+ class A:
+ def __init__(self):
+ self.init = self.__init__
+ a = A()
+ gc.collect()
+ del a
+ self.assertNotEqual(gc.collect(), 0)
+
+ def test_finalizer(self):
+ # A() is uncollectable if it is part of a cycle, make sure it shows up
+ # in gc.garbage.
+ class A:
+ def __del__(self): pass
+ class B:
+ pass
+ a = A()
+ a.a = a
+ id_a = id(a)
+ b = B()
+ b.b = b
+ gc.collect()
+ del a
+ del b
+ self.assertNotEqual(gc.collect(), 0)
+ for obj in gc.garbage:
+ if id(obj) == id_a:
+ del obj.a
+ break
+ else:
+ self.fail("didn't find obj in garbage (finalizer)")
+ gc.garbage.remove(obj)
+
+ def test_finalizer_newclass(self):
+ # A() is uncollectable if it is part of a cycle, make sure it shows up
+ # in gc.garbage.
+ class A(object):
+ def __del__(self): pass
+ class B(object):
+ pass
+ a = A()
+ a.a = a
+ id_a = id(a)
+ b = B()
+ b.b = b
+ gc.collect()
+ del a
+ del b
+ self.assertNotEqual(gc.collect(), 0)
+ for obj in gc.garbage:
+ if id(obj) == id_a:
+ del obj.a
+ break
+ else:
+ self.fail("didn't find obj in garbage (finalizer)")
+ gc.garbage.remove(obj)
+
+ def test_function(self):
+ # Tricky: f -> d -> f, code should call d.clear() after the exec to
+ # break the cycle.
+ d = {}
+ exec("def f(): pass\n") in d
+ gc.collect()
+ del d
+ self.assertEqual(gc.collect(), 2)
+
+ def test_frame(self):
+ flg = gc.getJythonGCFlags()
+ #gc.addJythonGCFlags(gc.VERBOSE)
+ #sporadically fails in Jython, no idea why.
+ def f():
+ frame = sys._getframe()
+ gc.collect()
+ f()
+ col = gc.collect()
+ gc.setJythonGCFlags(flg)
+ self.assertEqual(col, 1)
+
+
+ def test_saveall(self):
+ # Verify that cyclic garbage like lists show up in gc.garbage if the
+ # SAVEALL option is enabled.
+
+ # First make sure we don't save away other stuff that just happens to
+ # be waiting for collection.
+ gc.collect()
+ # if this fails, someone else created immortal trash
+ self.assertEqual(gc.garbage, [])
+
+ L = []
+ L.append(L)
+ id_L = id(L)
+
+ debug = gc.get_debug()
+ gc.set_debug(debug | gc.DEBUG_SAVEALL)
+ del L
+ gc.collect()
+ gc.set_debug(debug)
+ self.assertEqual(len(gc.garbage), 1)
+ obj = gc.garbage.pop()
+ self.assertEqual(id(obj), id_L)
+
+ @unittest.skipIf(test_support.is_jython,
+ '''
+ Jython neither supports disabling/enabling the gc, nor
+ setting the gc threshold.
+ ''')
+ def test_del(self):
+ # __del__ methods can trigger collection, make this to happen
+ thresholds = gc.get_threshold()
+ gc.enable()
+ gc.set_threshold(1)
+
+ class A:
+ def __del__(self):
+ dir(self)
+ a = A()
+ del a
+
+ gc.disable()
+ gc.set_threshold(*thresholds)
+
+ @unittest.skipIf(test_support.is_jython,
+ '''
+ Jython neither supports disabling/enabling the gc, nor
+ setting the gc threshold.
+ ''')
+ def test_del_newclass(self):
+ # __del__ methods can trigger collection, make this to happen
+ thresholds = gc.get_threshold()
+ gc.enable()
+ gc.set_threshold(1)
+
+ class A(object):
+ def __del__(self):
+ dir(self)
+ a = A()
+ del a
+
+ gc.disable()
+ gc.set_threshold(*thresholds)
+
+ # The following two tests are fragile:
+ # They precisely count the number of allocations,
+ # which is highly implementation-dependent.
+ # For example:
+ # - disposed tuples are not freed, but reused
+ # - the call to assertEqual somehow avoids building its args tuple
+ @unittest.skipIf(test_support.is_jython,
+ '''
+ Jython does not support to interrogate gc-internal
+ generation-wise counters.
+ ''')
+ def test_get_count(self):
+ # Avoid future allocation of method object
+ assertEqual = self._baseAssertEqual
+ gc.collect()
+ assertEqual(gc.get_count(), (0, 0, 0))
+ a = dict()
+ # since gc.collect(), we created two objects:
+ # the dict, and the tuple returned by get_count()
+ assertEqual(gc.get_count(), (2, 0, 0))
+
+ @unittest.skipIf(test_support.is_jython,
+ '''
+ Jython does not support to interrogate gc-internal
+ generation-wise counters.
+ ''')
+ def test_collect_generations(self):
+ # Avoid future allocation of method object
+ assertEqual = self.assertEqual
+ gc.collect()
+ a = dict()
+ gc.collect(0)
+ assertEqual(gc.get_count(), (0, 1, 0))
+ gc.collect(1)
+ assertEqual(gc.get_count(), (0, 0, 1))
+ gc.collect(2)
+ assertEqual(gc.get_count(), (0, 0, 0))
+
+# def test_trashcan(self):
+# class Ouch:
+# n = 0
+# def __del__(self):
+# Ouch.n = Ouch.n + 1
+# if Ouch.n % 17 == 0:
+# gc.collect()
+#
+# # "trashcan" is a hack to prevent stack overflow when deallocating
+# # very deeply nested tuples etc. It works in part by abusing the
+# # type pointer and refcount fields, and that can yield horrible
+# # problems when gc tries to traverse the structures.
+# # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
+# # most likely die via segfault.
+#
+# # Note: In 2.3 the possibility for compiling without cyclic gc was
+# # removed, and that in turn allows the trashcan mechanism to work
+# # via much simpler means (e.g., it never abuses the type pointer or
+# # refcount fields anymore). Since it's much less likely to cause a
+# # problem now, the various constants in this expensive (we force a lot
+# # of full collections) test are cut back from the 2.2 version.
+# gc.enable()
+# N = 150
+# for count in range(2):
+# t = []
+# for i in range(N):
+# t = [t, Ouch()]
+# u = []
+# for i in range(N):
+# u = [u, Ouch()]
+# v = {}
+# for i in range(N):
+# v = {1: v, 2: Ouch()}
+# gc.disable()
+
+# @unittest.skipUnless(threading, "test meaningless on builds without threads")
+# def test_trashcan_threads(self):
+# # Issue #13992: trashcan mechanism should be thread-safe
+# NESTING = 60
+# N_THREADS = 2
+#
+# def sleeper_gen():
+# """A generator that releases the GIL when closed or dealloc'ed."""
+# try:
+# yield
+# finally:
+# time.sleep(0.000001)
+#
+# class C(list):
+# # Appending to a list is atomic, which avoids the use of a lock.
+# inits = []
+# dels = []
+# def __init__(self, alist):
+# self[:] = alist
+# C.inits.append(None)
+# def __del__(self):
+# # This __del__ is called by subtype_dealloc().
+# C.dels.append(None)
+# # `g` will release the GIL when garbage-collected. This
+# # helps assert subtype_dealloc's behaviour when threads
+# # switch in the middle of it.
+# g = sleeper_gen()
+# next(g)
+# # Now that __del__ is finished, subtype_dealloc will proceed
+# # to call list_dealloc, which also uses the trashcan mechanism.
+#
+# def make_nested():
+# """Create a sufficiently nested container object so that the
+# trashcan mechanism is invoked when deallocating it."""
+# x = C([])
+# for i in range(NESTING):
+# x = [C([x])]
+# del x
+#
+# def run_thread():
+# """Exercise make_nested() in a loop."""
+# while not exit:
+# make_nested()
+#
+# old_checkinterval = sys.getcheckinterval()
+# sys.setcheckinterval(3)
+# try:
+# exit = False
+# threads = []
+# for i in range(N_THREADS):
+# t = threading.Thread(target=run_thread)
+# threads.append(t)
+# for t in threads:
+# t.start()
+# time.sleep(1.0)
+# exit = True
+# for t in threads:
+# t.join()
+# finally:
+# pass
+# sys.setcheckinterval(old_checkinterval)
+# gc.collect()
+# self.assertEqual(len(C.inits), len(C.dels))
+
+ def test_boom(self):
+ class Boom:
+ def __getattr__(self, someattribute):
+ del self.attr
+ raise AttributeError
+
+ a = Boom()
+ b = Boom()
+ a.attr = b
+ b.attr = a
+
+ gc.collect()
+ garbagelen = len(gc.garbage)
+ del a, b
+ # a<->b are in a trash cycle now. Collection will invoke
+ # Boom.__getattr__ (to see whether a and b have __del__ methods), and
+ # __getattr__ deletes the internal "attr" attributes as a side effect.
+ # That causes the trash cycle to get reclaimed via refcounts falling to
+ # 0, thus mutating the trash graph as a side effect of merely asking
+ # whether __del__ exists. This used to (before 2.3b1) crash Python.
+ # Now __getattr__ isn't called.
+ self.assertEqual(gc.collect(), 4)
+ self.assertEqual(len(gc.garbage), garbagelen)
+
+ def test_boom2(self):
+ class Boom2:
+ def __init__(self):
+ self.x = 0
+
+ def __getattr__(self, someattribute):
+ self.x += 1
+ if self.x > 1:
+ del self.attr
+ raise AttributeError
+
+ a = Boom2()
+ b = Boom2()
+ a.attr = b
+ b.attr = a
+
+ gc.collect()
+ garbagelen = len(gc.garbage)
+ del a, b
+ # Much like test_boom(), except that __getattr__ doesn't break the
+ # cycle until the second time gc checks for __del__. As of 2.3b1,
+ # there isn't a second time, so this simply cleans up the trash cycle.
+ # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
+ # reclaimed this way.
+ self.assertEqual(gc.collect(), 4)
+ self.assertEqual(len(gc.garbage), garbagelen)
+
+ def test_boom_new(self):
+ # boom__new and boom2_new are exactly like boom and boom2, except use
+ # new-style classes.
+
+ class Boom_New(object):
+ def __getattr__(self, someattribute):
+ del self.attr
+ raise AttributeError
+
+ a = Boom_New()
+ b = Boom_New()
+ a.attr = b
+ b.attr = a
+
+ gc.collect()
+ garbagelen = len(gc.garbage)
+ del a, b
+ self.assertEqual(gc.collect(), 4)
+ self.assertEqual(len(gc.garbage), garbagelen)
+
+ def test_boom2_new(self):
+ class Boom2_New(object):
+ def __init__(self):
+ self.x = 0
+
+ def __getattr__(self, someattribute):
+ self.x += 1
+ if self.x > 1:
+ del self.attr
+ raise AttributeError
+
+ a = Boom2_New()
+ b = Boom2_New()
+ a.attr = b
+ b.attr = a
+
+ gc.collect()
+ garbagelen = len(gc.garbage)
+ del a, b
+ self.assertEqual(gc.collect(), 4)
+ self.assertEqual(len(gc.garbage), garbagelen)
+
+ def test_get_referents(self):
+ alist = [1, 3, 5]
+ got = gc.get_referents(alist)
+ got.sort()
+ self.assertEqual(got, alist)
+
+ atuple = tuple(alist)
+ got = gc.get_referents(atuple)
+ got.sort()
+ self.assertEqual(got, alist)
+
+ adict = {1: 3, 5: 7}
+ expected = [1, 3, 5, 7]
+ got = gc.get_referents(adict)
+ got.sort()
+ self.assertEqual(got, expected)
+
+ got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
+ got.sort()
+ self.assertEqual(got, [0, 0] + range(5))
+
+ self.assertEqual(gc.get_referents(1, 'a', 4j), [])
+
+ def test_is_tracked(self):
+ # Atomic built-in types are not tracked, user-defined objects and
+ # mutable containers are.
+ # NOTE: types with special optimizations (e.g. tuple) have tests
+ # in their own test files instead.
+ self.assertFalse(gc.is_tracked(None))
+ self.assertFalse(gc.is_tracked(1))
+ self.assertFalse(gc.is_tracked(1.0))
+ self.assertFalse(gc.is_tracked(1.0 + 5.0j))
+ self.assertFalse(gc.is_tracked(True))
+ self.assertFalse(gc.is_tracked(False))
+ self.assertFalse(gc.is_tracked("a"))
+ self.assertFalse(gc.is_tracked(u"a"))
+ self.assertFalse(gc.is_tracked(bytearray("a")))
+ self.assertFalse(gc.is_tracked(type))
+ self.assertFalse(gc.is_tracked(int))
+ self.assertFalse(gc.is_tracked(object))
+ self.assertFalse(gc.is_tracked(object()))
+
+ class OldStyle:
+ pass
+ class NewStyle(object):
+ pass
+ self.assertTrue(gc.is_tracked(gc))
+ self.assertTrue(gc.is_tracked(OldStyle))
+ self.assertTrue(gc.is_tracked(OldStyle()))
+ self.assertTrue(gc.is_tracked(NewStyle))
+ self.assertTrue(gc.is_tracked(NewStyle()))
+ self.assertTrue(gc.is_tracked([]))
+ self.assertTrue(gc.is_tracked(set()))
+
+ def test_bug1055820b(self):
+ # Corresponds to temp2b.py in the bug report.
+ ouch = []
+ def callback(ignored):
+ ouch[:] = [wr() for wr in WRs]
+
+ Cs = [C1055820(i) for i in range(2)]
+ WRs = [weakref.ref(c, callback) for c in Cs]
+ c = None
+
+ gc.collect()
+ self.assertEqual(len(ouch), 0)
+ # Make the two instances trash, and collect again. The bug was that
+ # the callback materialized a strong reference to an instance, but gc
+ # cleared the instance's dict anyway.
+ Cs = None
+ gc.collect()
+ self.assertEqual(len(ouch), 2) # else the callbacks didn't run
+ for x in ouch:
+ # If the callback resurrected one of these guys, the instance
+ # would be damaged, with an empty __dict__.
+ self.assertEqual(x, None)
+
+ at unittest.skipIf(test_support.is_jython,
+ '''
+ GCTogglingTests are neither relevant nor applicable for Jython.
+ ''')
+class GCTogglingTests(unittest.TestCase):
+ def setUp(self):
+ gc.enable()
+
+ def tearDown(self):
+ gc.disable()
+
+ def test_bug1055820c(self):
+ # Corresponds to temp2c.py in the bug report. This is pretty
+ # elaborate.
+
+ c0 = C1055820(0)
+ # Move c0 into generation 2.
+ gc.collect()
+
+ c1 = C1055820(1)
+ c1.keep_c0_alive = c0
+ del c0.loop # now only c1 keeps c0 alive
+
+ c2 = C1055820(2)
+ c2wr = weakref.ref(c2) # no callback!
+
+ ouch = []
+ def callback(ignored):
+ ouch[:] = [c2wr()]
+
+ # The callback gets associated with a wr on an object in generation 2.
+ c0wr = weakref.ref(c0, callback)
+
+ c0 = c1 = c2 = None
+
+ # What we've set up: c0, c1, and c2 are all trash now. c0 is in
+ # generation 2. The only thing keeping it alive is that c1 points to
+ # it. c1 and c2 are in generation 0, and are in self-loops. There's a
+ # global weakref to c2 (c2wr), but that weakref has no callback.
+ # There's also a global weakref to c0 (c0wr), and that does have a
+ # callback, and that callback references c2 via c2wr().
+ #
+ # c0 has a wr with callback, which references c2wr
+ # ^
+ # |
+ # | Generation 2 above dots
+ #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
+ # | Generation 0 below dots
+ # |
+ # |
+ # ^->c1 ^->c2 has a wr but no callback
+ # | | | |
+ # <--v <--v
+ #
+ # So this is the nightmare: when generation 0 gets collected, we see
+ # that c2 has a callback-free weakref, and c1 doesn't even have a
+ # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
+ # the only object that has a weakref with a callback. gc clears c1
+ # and c2. Clearing c1 has the side effect of dropping the refcount on
+ # c0 to 0, so c0 goes away (despite that it's in an older generation)
+ # and c0's wr callback triggers. That in turn materializes a reference
+ # to c2 via c2wr(), but c2 gets cleared anyway by gc.
+
+ # We want to let gc happen "naturally", to preserve the distinction
+ # between generations.
+ junk = []
+ i = 0
+ detector = GC_Detector()
+ while not detector.gc_happened:
+ i += 1
+ if i > 10000:
+ self.fail("gc didn't happen after 10000 iterations")
+ self.assertEqual(len(ouch), 0)
+ junk.append([]) # this will eventually trigger gc
+
+ self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
+ for x in ouch:
+ # If the callback resurrected c2, the instance would be damaged,
+ # with an empty __dict__.
+ self.assertEqual(x, None)
+
+ def test_bug1055820d(self):
+ # Corresponds to temp2d.py in the bug report. This is very much like
+ # test_bug1055820c, but uses a __del__ method instead of a weakref
+ # callback to sneak in a resurrection of cyclic trash.
+
+ ouch = []
+ class D(C1055820):
+ def __del__(self):
+ ouch[:] = [c2wr()]
+
+ d0 = D(0)
+ # Move all the above into generation 2.
+ gc.collect()
+
+ c1 = C1055820(1)
+ c1.keep_d0_alive = d0
+ del d0.loop # now only c1 keeps d0 alive
+
+ c2 = C1055820(2)
+ c2wr = weakref.ref(c2) # no callback!
+
+ d0 = c1 = c2 = None
+
+ # What we've set up: d0, c1, and c2 are all trash now. d0 is in
+ # generation 2. The only thing keeping it alive is that c1 points to
+ # it. c1 and c2 are in generation 0, and are in self-loops. There's
+ # a global weakref to c2 (c2wr), but that weakref has no callback.
+ # There are no other weakrefs.
+ #
+ # d0 has a __del__ method that references c2wr
+ # ^
+ # |
+ # | Generation 2 above dots
+ #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
+ # | Generation 0 below dots
+ # |
+ # |
+ # ^->c1 ^->c2 has a wr but no callback
+ # | | | |
+ # <--v <--v
+ #
+ # So this is the nightmare: when generation 0 gets collected, we see
+ # that c2 has a callback-free weakref, and c1 doesn't even have a
+ # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
+ # c1 and c2. Clearing c1 has the side effect of dropping the refcount
+ # on d0 to 0, so d0 goes away (despite that it's in an older
+ # generation) and d0's __del__ triggers. That in turn materializes
+ # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
+
+ # We want to let gc happen "naturally", to preserve the distinction
+ # between generations.
+ detector = GC_Detector()
+ junk = []
+ i = 0
+ while not detector.gc_happened:
+ i += 1
+ if i > 10000:
+ self.fail("gc didn't happen after 10000 iterations")
+ self.assertEqual(len(ouch), 0)
+ junk.append([]) # this will eventually trigger gc
+
+ self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
+ for x in ouch:
+ # If __del__ resurrected c2, the instance would be damaged, with an
+ # empty __dict__.
+ self.assertEqual(x, None)
+
+# def test_main():
+# unittest.main()
+# enabled = gc.isenabled()
+# gc.disable()
+# assert not gc.isenabled()
+# debug = gc.get_debug()
+# gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
+#
+# try:
+# gc.collect() # Delete 2nd generation garbage
+# run_unittest(GCTests, GCTogglingTests)
+# finally:
+# gc.set_debug(debug)
+# # test gc.enable() even if GC is disabled by default
+# if verbose:
+# print "restoring automatic collection"
+# # make sure to always test gc.enable()
+# gc.enable()
+# assert gc.isenabled()
+# if not enabled:
+# gc.disable()
+
+if __name__ == "__main__":
+ #test_main()
+ unittest.main()
diff --git a/Lib/test/test_gc_jy.py b/Lib/test/test_gc_jy.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_gc_jy.py
@@ -0,0 +1,601 @@
+"""
+Tests some Jython-specific gc aspects and debugging
+features.
+"""
+
+import unittest
+#from test.test_support import verbose, run_unittest
+#import sys
+import time
+import gc
+import weakref
+from java.lang import System, Runnable
+
+# class FinalizationDummy:
+# def __del__(self):
+# time.sleep(3.5)
+# print "FinalizationDummy.__del__"
+# time.sleep(3.5)
+#
+# class ResurrectionDummy:
+# def __del__(self):
+# print "ResurrectionDummy.__del__"
+# ResurrectionDummy.resurrected = self.toResurrect
+#
+# class SelfResurrectionDummy:
+# def __del__(self):
+# print "SelfResurrectionDummy.__del__"
+# SelfResurrectionDummy.resurrected = self
+
+class GCTests_Jy_CyclicGarbage(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ #the finalizer-related tests need this flag to pass in Jython:
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_CYCLIC_GARBAGE)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ except Exception:
+ pass
+
+
+ # In contrast to the tests in test_gc, these finalizer tests shall work
+ # even if gc-monitoring is disabled.
+ def test_finalizer(self):
+ # A() is uncollectable if it is part of a cycle, make sure it shows up
+ # in gc.garbage.
+ class A:
+ def __del__(self): pass
+ class B:
+ pass
+ a = A()
+ a.a = a
+ id_a = id(a)
+ b = B()
+ b.b = b
+ gc.collect()
+ del a
+ del b
+ self.assertNotEqual(gc.collect(), 0)
+ time.sleep(4)
+ for obj in gc.garbage:
+ if id(obj) == id_a:
+ del obj.a
+ break
+ else:
+ self.fail("didn't find obj in garbage (finalizer)")
+ gc.garbage.remove(obj)
+
+
+ def test_finalizer_newclass(self):
+ # A() is uncollectable if it is part of a cycle, make sure it shows up
+ # in gc.garbage.
+ class A(object):
+ def __del__(self): pass
+ class B(object):
+ pass
+ a = A()
+ a.a = a
+ id_a = id(a)
+ b = B()
+ b.b = b
+ gc.collect()
+ del a
+ del b
+ self.assertNotEqual(gc.collect(), 0)
+ time.sleep(1)
+ for obj in gc.garbage:
+ if id(obj) == id_a:
+ del obj.a
+ break
+ else:
+ self.fail("didn't find obj in garbage (finalizer)")
+ gc.garbage.remove(obj)
+
+ def test_manual_monitoring(self):
+ # since tuples are immutable we close the loop with a list
+ l = []
+ t = (l,)
+ l.append(t)
+ gc.monitorObject(l)
+ #gc.monitorObject(t) <- intentionally only monitor one of them
+ gc.collect()
+ del t
+ del l
+ #Note that usually two collected objects would be expected - l and t.
+ #But we intentionally only monitored one of them, so only one should
+ #be counted.
+ self.assertEqual(gc.collect(), 1)
+
+
+class GCTests_Jy_preprocess_and_postprocess(unittest.TestCase):
+
+ def test_finalization_preprocess_and_postprocess(self):
+ #print "test_finalization_preprocess_and_postprocess"
+ #Note that this test is done here again (already was in another class
+ #in this module), to see that everything works as it should also with
+ #a different flag-context.
+ #print "test_finalization_preprocess_and_postprocess"
+ #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ comments = []
+ self0 = self
+ class A:
+ def __del__(self):
+ #print "del A"
+ self0.assertIn("run PreProcess", comments)
+ comments.append("A del")
+ #let's simulate a time-consuming finalizer
+ #to ensure that post finalization processing
+ #is sensitive to this
+ time.sleep(0.5)
+ comments.append("A del done")
+
+ class PreProcess(Runnable):
+ def run(self):
+ self0.assertEqual(comments, [])
+ comments.append("run PreProcess")
+
+ class PostProcess(Runnable):
+ def run(self):
+ self0.assertIn("run PreProcess", comments)
+ self0.assertIn("A del", comments)
+ self0.assertIn("A del done", comments)
+ comments.append("run PostProcess")
+
+ a = A()
+ a = None
+ prePr = PreProcess()
+ postPr = PostProcess()
+ time.sleep(1) # <- to avoid that the newly registered processes
+ # become subject to previous run
+ gc.registerPreFinalizationProcess(prePr)
+ gc.registerPostFinalizationProcess(postPr)
+ #Note that order matters here:
+ #If the flag gc.DONT_FINALIZE_RESURRECTED_OBJECTS is used,
+ #gc.registerPostFinalizationProcess(postPr, 0) would lead to failure,
+ #because postPr asserts that a's finalizer already ran. Since
+ #DONT_FINALIZE_RESURRECTED_OBJECTS also inserted a postprocess,
+ #to perform delayed finalization, the 0-index would prepend postPr
+ #before the process that actually runs the finalizers.
+ System.gc()
+ #we wait a bit longer here, since PostProcess runs asynchronous
+ #and must wait for the finalizer of A
+ time.sleep(2)
+ self.assertIn("run PostProcess", comments)
+ comments = []
+ gc.unregisterPreFinalizationProcess(prePr)
+ gc.unregisterPostFinalizationProcess(postPr)
+
+
+class GCTests_Jy_Delayed_Finalization(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ #the finalizer-related tests need this flag to pass in Jython:
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ except Exception:
+ pass
+
+ def test_finalization_preprocess_and_postprocess(self):
+ #print "test_finalization_preprocess_and_postprocess"
+ #Note that this test is done here again (already was in another class
+ #in this module), to see that everything works as it should also with
+ #a different flag-context.
+ #print "test_finalization_preprocess_and_postprocess"
+ #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ comments = []
+ self0 = self
+ class A:
+ def __del__(self):
+ #print "del A"
+ self0.assertIn("run PreProcess", comments)
+ comments.append("A del")
+ #let's simulate a time-consuming finalizer
+ #to ensure that post finalization processing
+ #is sensitive to this
+ time.sleep(0.5)
+ comments.append("A del done")
+
+ class PreProcess(Runnable):
+ def run(self):
+ self0.assertEqual(comments, [])
+ comments.append("run PreProcess")
+
+ class PostProcess(Runnable):
+ def run(self):
+ self0.assertIn("run PreProcess", comments)
+ self0.assertIn("A del", comments)
+ self0.assertIn("A del done", comments)
+ comments.append("run PostProcess")
+
+ a = A()
+ a = None
+ prePr = PreProcess()
+ postPr = PostProcess()
+ time.sleep(1) # <- to avoid that the newly registered processes
+ # become subject to previous run
+ gc.registerPreFinalizationProcess(prePr)
+ gc.registerPostFinalizationProcess(postPr)
+ #Note that order matters here:
+ #If the flag gc.DONT_FINALIZE_RESURRECTED_OBJECTS is used,
+ #gc.registerPostFinalizationProcess(postPr, 0) would lead to failure,
+ #because postPr asserts that a's finalizer already ran. Since
+ #DONT_FINALIZE_RESURRECTED_OBJECTS also inserted a postprocess,
+ #to perform delayed finalization, the 0-index would prepend postPr
+ #before the process that actually runs the finalizers.
+ System.gc()
+ #we wait a bit longer here, since PostProcess runs asynchronous
+ #and must wait for the finalizer of A
+ time.sleep(2)
+ self.assertIn("run PostProcess", comments)
+ comments = []
+ gc.unregisterPreFinalizationProcess(prePr)
+ gc.unregisterPostFinalizationProcess(postPr)
+
+
+ def test_delayedFinalization(self):
+ #gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ #gc.addJythonGCFlags(gc.VERBOSE)
+ resurrect = []
+ comments = []
+
+ class Test_Finalizable(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+
+ class Test_Resurrection(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+ if hasattr(self, "toResurrect"):
+ resurrect.append(self.toResurrect)
+
+ a = Test_Finalizable("a")
+ a.b = Test_Finalizable("b")
+ c = Test_Resurrection("c")
+ c.a = a
+ c.toResurrect = Test_Finalizable("d")
+
+ del a
+ del c
+ self.assertNotEqual(gc.collect(), 0)
+ time.sleep(1)
+ #print comments
+ #print resurrect
+ self.assertIn('del c', comments)
+ self.assertEqual(1, len(comments))
+ comments = []
+ self.assertNotEqual(gc.collect(), 0)
+ time.sleep(1)
+ #print comments
+ #print resurrect
+ self.assertIn('del a', comments)
+ self.assertEqual(1, len(comments))
+ comments = []
+ self.assertNotEqual(gc.collect(), 0)
+ time.sleep(1)
+ self.assertIn('del b', comments)
+ self.assertEqual(1, len(comments))
+ #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ #gc.removeJythonGCFlags(gc.VERBOSE)
+
+
+class GCTests_Jy_Monitoring(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ gc.setMonitorGlobal(True)
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ #since gc module already exists, it would not be caught by monitorGlobal.
+ #so we have to monitor it manually:
+ gc.monitorObject(gc)
+ #the finalizer-related tests need this flag to pass in Jython:
+ #gc.addJythonGCFlags(gc.DONT_FINALIZE_CYCLIC_GARBAGE)
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ def test_monitor_status_after_delayed_finalization(self):
+ resurrect = []
+ comments = []
+
+ class Test_Finalizable(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+
+ class Test_Resurrection(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+ if hasattr(self, "toResurrect"):
+ resurrect.append(self.toResurrect)
+
+ a = Test_Finalizable("a")
+ a.b = Test_Finalizable("b")
+ c = Test_Resurrection("c")
+ c.toResurrect = a
+ a.b.a = a
+ self.assertTrue(gc.isMonitored(a))
+ self.assertTrue(gc.isMonitored(a.b))
+ self.assertTrue(gc.isMonitored(c))
+ gc.collect()
+ del a
+ del c
+ #gc.set_debug(gc.DEBUG_SAVEALL)
+ #gc.collect()
+ self.assertEqual(gc.collect(), 0) #c is not cyclic and a, b are resurrected,
+ #so nothing to count here
+ #self.asserEqual(len(gc.garbage), 0)
+ #if we called gc.set_debug(gc.DEBUG_SAVEALL) above, it would
+ #be okay for gc.garbage to be empty, because a and b
+ #are not finalized and c is not cyclic.
+ self.assertEqual(comments, ['del c'])
+ self.assertEqual(str(resurrect), "[]")
+ self.assertTrue(gc.isMonitored(resurrect[0]))
+ self.assertTrue(gc.isMonitored(resurrect[0].b))
+
+ def test_notifyRerun_for_delayed_finalization(self):
+ gc.collect()
+ resurrect = []
+ comments = []
+
+ class Test_Finalizable(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+
+ a = Test_Finalizable("a")
+ lst = []
+ lst1 = [lst]
+ lst.append(lst1)
+ a.b = Test_Finalizable("b")
+ a.b.lst = lst
+ del lst
+ del lst1
+ self.assertTrue(gc.isMonitored(a))
+ self.assertTrue(gc.isMonitored(a.b))
+ del a
+ self.assertEqual(gc.collect(), 2) # c is not cyclic and a, b are resurrected,
+ # the cycle of two lists is counted here
+ self.assertEqual(comments, ['del a', 'del b'])
+
+
+class GCTests_Jy_Weakref(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ gc.addJythonGCFlags(gc.PRESERVE_WEAKREFS_ON_RESURRECTION)
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ def test_weakref_after_resurrection(self):
+ resurrect = []
+ comments = []
+ class Test_Finalizable(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+
+ class Test_Resurrection(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+ if hasattr(self, "toResurrect"):
+ resurrect.append(self)
+
+ def clb(ref):
+ comments.append("clb")
+
+ def clb2(ref):
+ comments.append("clb2 "+str(comments))
+
+ a = Test_Finalizable("a")
+ wa = weakref.ref(a, clb)
+ self.assertEqual(wa(), a)
+ c = Test_Resurrection("c")
+ c.toResurrect = a
+ wc = weakref.ref(c, clb2)
+ try:
+ gc.monitorObject(c)
+ except Exception:
+ pass
+ del a
+ del c
+ gc.collect()
+ self.assertIn('clb2 []', comments)
+ self.assertNotIn("clb", comments)
+ self.assertEqual(str(resurrect), "[]")
+ self.assertEqual(str(wa()), "")
+ self.assertEqual(wc(), None)
+
+ def test_weakref_after_resurrection_and_delayed_finalize(self):
+ resurrect = []
+ comments = []
+ class Test_Finalizable(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+
+ class Test_Resurrection(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+ if hasattr(self, "toResurrect"):
+ resurrect.append(self)
+
+ def clb(ref):
+ comments.append("clb")
+
+ def clb2(ref):
+ comments.append("clb2 "+str(comments))
+
+ a = Test_Finalizable("a")
+ wa = weakref.ref(a, clb)
+ self.assertEqual(wa(), a)
+ c = Test_Resurrection("c")
+ c.toResurrect = a
+ wc = weakref.ref(c, clb2)
+ try:
+ gc.monitorObject(c)
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ except Exception:
+ pass
+ del a
+ del c
+ gc.collect()
+ self.assertIn('del c', comments)
+ self.assertNotIn('del a', comments)
+ self.assertIn('clb2 []', comments)
+ self.assertNotIn("clb", comments)
+ self.assertEqual(str(resurrect), "[]")
+ self.assertEqual(str(wa()), "")
+ self.assertEqual(wc(), None)
+ try:
+ gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ except Exception:
+ pass
+
+ def test_weakref_after_resurrection_threadsafe(self):
+ resurrect = []
+ comments = []
+
+ class Test_Finalizable(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+
+ class Test_Resurrection(object):
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return "<"+self.name+">"
+
+ def __del__(self):
+ comments.append("del "+self.name)
+ if hasattr(self, "toResurrect"):
+ resurrect.append(self)
+
+ a = Test_Finalizable("a")
+ wa = weakref.ref(a)
+ c = Test_Resurrection("c")
+ c.toResurrect = a
+ wc = weakref.ref(c)
+ del a
+ del c
+ try:
+ gc.addJythonGCFlags(gc.PRESERVE_WEAKREFS_ON_RESURRECTION)
+ System.gc()
+ # We intentionally don't wait here, but want to observe
+ # the situation with gc unfinnished. Note that wa() preserves
+ # its result right away, due to thread-safe implementation.
+ # Technically, the weak reference breaks and is restored after
+ # gc-run finishes. However wa() blocks until the referent is
+ # restored or the deletion is confirmed.
+ except Exception:
+ pass
+ #self.assertEqual(str(wa()), ' ')
+ self.assertEqual(comments, [])
+ self.assertEqual(resurrect, [])
+ while comments == [] or resurrect == []:
+ self.assertEqual(str(wa()), ' ')
+ self.assertEqual(wc(), None)
+ self.assertEqual(str(wa()), ' ')
+ self.assertEqual(wc(), None)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_resurrection_attr_preserve.py b/Lib/test/test_resurrection_attr_preserve.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_resurrection_attr_preserve.py
@@ -0,0 +1,97 @@
+import unittest
+import gc
+import time
+import weakref
+
+class ReferentDummy:
+ def __init__(self, name):
+ self.name = name
+
+ def __str__(self):
+ return self.name
+
+class ResurrectionDummy:
+ def __del__(self):
+ ResurrectionDummy.resurrected = self.toResurrect
+
+class SelfResurrectionDummy:
+ def __del__(self):
+ SelfResurrectionDummy.resurrected = self
+
+class GCDetector():
+ gcIndex = 0
+
+ def __del__(self):
+ GCDetector.gcIndex += 1
+
+maxGCRun = 10
+
+def runGC():
+ """
+ This is needed for Jython, since theoretically Java gc is not guaranteed to
+ run if gc.collect is called; the run is only attempted. This method assures
+ that actually a gc run happens.
+ """
+ currentIndex = GCDetector.gcIndex
+ gcCount = 0
+ detector = GCDetector()
+ del detector
+ gc.collect()
+ time.sleep(0.2)
+ while currentIndex == GCDetector.gcIndex and gcCount < maxGCRun:
+ gc.collect()
+ gcCount += 1
+ time.sleep(0.2)
+
+class GCTests(unittest.TestCase):
+
+ def test_id_after_self_resurrection(self):
+ rd = SelfResurrectionDummy()
+ savedId = id(rd)
+ rd = None
+ runGC() #needed for Jython etc, even though no cyclic trash appears
+ self.assertEqual(id(SelfResurrectionDummy.resurrected), savedId)
+ del SelfResurrectionDummy.resurrected
+
+ def test_id_after_resurrection(self):
+ l = ["ab"]
+ rd = ResurrectionDummy()
+ rd.toResurrect = l
+ savedId = id(l)
+ l = None
+ rd = None
+ runGC() #needed for Jython etc, even though no cyclic trash appears
+ self.assertEqual(id(ResurrectionDummy.resurrected), savedId)
+ del ResurrectionDummy.resurrected
+
+#todo: Check these test regarding to CPython behavior
+# def test_weakref_consistency_after_self_resurrection(self):
+# #fails in CPython
+# rd = SelfResurrectionDummy()
+# wref = weakref.ref(rd)
+# self.assertIn(wref, weakref.getweakrefs(rd))
+# rd = None
+# runGC() #needed for Jython etc, even though no cyclic trash appears
+# self.asserIn(wref, weakref.getweakrefs(SelfResurrectionDummy.resurrected))
+# for wref2 in weakref.getweakrefs(SelfResurrectionDummy.resurrected):
+# self.assertIs(wref2(), SelfResurrectionDummy.resurrected)
+# del SelfResurrectionDummy.resurrected
+#
+# def test_weakref_consistency_after_resurrection(self):
+# l = ReferentDummy("ab")
+# rd = ResurrectionDummy()
+# rd.toResurrect = l
+# wref = weakref.ref(l)
+# self.assertIn(wref, weakref.getweakrefs(l))
+# l = None
+# rd = None
+# runGC() #needed for Jython etc, even though no cyclic trash appears
+# self.asserIn(wref, weakref.getweakrefs(ResurrectionDummy.resurrected))
+# for wref2 in weakref.getweakrefs(ResurrectionDummy.resurrected):
+# self.assertIs(wref2(), ResurrectionDummy.resurrected)
+# del ResurrectionDummy.resurrected
+
+
+
+if __name__ == '__main__':
+ unittest.main()
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
@@ -1367,7 +1367,7 @@
True
>>> del o, o2
>>> import gc # Addition for Jython
->>> gc.collect()
+>>> gc_count = gc.collect()
>>> print r()
None
@@ -1421,7 +1421,7 @@
True
>>> del a
>>> import gc # addition for Jython
->>> gc.collect()
+>>> gc_count = gc.collect()
>>> try:
... id2obj(a_id)
... except KeyError:
diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -41,6 +41,8 @@
- [ 2236 ] Interactive parser does not accept try ... except E as e: syntax
- [ 2237 ] Fully conformant math and cmath support
- [ 2244 ] More robust testing of math and cmath modules
+ - [ 2224 ] id(...) now persists object resurrection and a pattern is provided to solve similar issues
+ (i.e. attributes bound to a PyObject via a WeakHashMap) in an analogue way (See JyAttribute.java).
New features
- Full support of Python buffer protocol, along with Java ByteBuffer support
@@ -55,6 +57,9 @@
- Initial support for ensurepip module
- Callbacks can be registered/unregistered to be notified when
bytecode is loaded, using jythonlib.bytecodetools
+ - Jython now features an optional, but recommended-to-implement traverseproc-mechanism
+ like CPython. This enables some new gc-features to optionally emulate CPython-specific
+ gc-behavior. See doc in gc.java and Traverseproc.java.
Potentially backwards breaking changes, removing silent errors:
diff --git a/src/com/ziclix/python/sql/DBApiType.java b/src/com/ziclix/python/sql/DBApiType.java
--- a/src/com/ziclix/python/sql/DBApiType.java
+++ b/src/com/ziclix/python/sql/DBApiType.java
@@ -7,8 +7,8 @@
*/
package com.ziclix.python.sql;
-import org.python.core.PyClass;
import org.python.core.PyInteger;
+import org.python.core.Untraversable;
/**
* This class wraps the types from java.sql.Type in order for
@@ -17,6 +17,7 @@
*
* @author brian zimmer
*/
+ at Untraversable
public final class DBApiType extends PyInteger {
/**
diff --git a/src/com/ziclix/python/sql/Fetch.java b/src/com/ziclix/python/sql/Fetch.java
--- a/src/com/ziclix/python/sql/Fetch.java
+++ b/src/com/ziclix/python/sql/Fetch.java
@@ -13,6 +13,8 @@
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyTuple;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
@@ -48,7 +50,7 @@
*
* @author brian zimmer
*/
-abstract public class Fetch {
+abstract public class Fetch implements Traverseproc {
/**
* The total number of rows in the result set.
@@ -464,6 +466,43 @@
public boolean removeWarningListener(WarningListener listener) {
return this.listeners.remove(listener);
}
+
+
+ /* Traverseproc support for Fetch */
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = visit.visit(description, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ if (listeners != null) {
+ for (WarningListener obj: listeners) {
+ if (obj != null) {
+ if (obj instanceof PyObject) {
+ retVal = visit.visit((PyObject) obj, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ } else if (obj instanceof Traverseproc) {
+ retVal = ((Traverseproc) obj).traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ } else if (ob == description) {
+ return true;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
}
/**
@@ -691,6 +730,49 @@
this.rownumber = -1;
this.results.clear();
}
+
+
+ /* Traverseproc support for StaticFetch */
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ if (results != null) {
+ for (PyObject obj: results) {
+ if (obj != null) {
+ retVal = visit.visit((PyObject) obj, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ if (descriptions != null) {
+ for (PyObject obj: descriptions) {
+ if (obj != null) {
+ retVal = visit.visit((PyObject) obj, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ }
+ if (results != null && results.contains(ob)) {
+ return true;
+ }
+ if (descriptions != null && descriptions.contains(ob)) {
+ return true;
+ }
+ return super.refersDirectlyTo(ob);
+ }
}
/**
diff --git a/src/com/ziclix/python/sql/PyConnection.java b/src/com/ziclix/python/sql/PyConnection.java
--- a/src/com/ziclix/python/sql/PyConnection.java
+++ b/src/com/ziclix/python/sql/PyConnection.java
@@ -24,6 +24,8 @@
import org.python.core.PyString;
import org.python.core.PyUnicode;
import org.python.core.ThreadState;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
import com.ziclix.python.sql.util.PyArgParser;
@@ -32,7 +34,7 @@
*
* @author brian zimmer
*/
-public class PyConnection extends PyObject implements ClassDictInit, ContextManager {
+public class PyConnection extends PyObject implements ClassDictInit, ContextManager, Traverseproc {
/** True if closed. */
protected boolean closed;
@@ -448,6 +450,43 @@
return false;
}
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ for (PyObject ob: cursors) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ for (PyObject ob: statements) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null) {
+ return false;
+ }
+ if (cursors != null && cursors.contains(ob)) {
+ return true;
+ } else if (statements != null && statements.contains(ob)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
}
class ConnectionFunc extends PyBuiltinMethodSet {
diff --git a/src/com/ziclix/python/sql/PyCursor.java b/src/com/ziclix/python/sql/PyCursor.java
--- a/src/com/ziclix/python/sql/PyCursor.java
+++ b/src/com/ziclix/python/sql/PyCursor.java
@@ -23,6 +23,8 @@
import org.python.core.PyString;
import org.python.core.PyTuple;
import org.python.core.PyUnicode;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
import com.ziclix.python.sql.util.PyArgParser;
import org.python.core.ContextManager;
@@ -35,7 +37,8 @@
*
* @author brian zimmer
*/
-public class PyCursor extends PyObject implements ClassDictInit, WarningListener, ContextManager {
+public class PyCursor extends PyObject implements ClassDictInit, WarningListener,
+ ContextManager, Traverseproc {
/** Field fetch */
protected Fetch fetch;
@@ -900,6 +903,68 @@
close();
return false;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ if (fetch != null) {
+ retVal = fetch.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (rsType != null) {
+ retVal = visit.visit(rsType, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (rsConcur != null) {
+ retVal = visit.visit(rsConcur, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (warnings != null) {
+ retVal = visit.visit(warnings, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (lastrowid != null) {
+ retVal = visit.visit(lastrowid, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (updatecount != null) {
+ retVal = visit.visit(updatecount, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (connection != null) {
+ retVal = visit.visit(connection, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return statement != null ? visit.visit(statement, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null) {
+ return false;
+ } else if (ob == rsType || ob == rsConcur || ob == warnings || ob == lastrowid
+ || ob == updatecount || ob == connection || ob == statement) {
+ return true;
+ } else {
+ return fetch.refersDirectlyTo(ob);
+ }
+ }
}
class CursorFunc extends PyBuiltinMethodSet {
diff --git a/src/com/ziclix/python/sql/PyStatement.java b/src/com/ziclix/python/sql/PyStatement.java
--- a/src/com/ziclix/python/sql/PyStatement.java
+++ b/src/com/ziclix/python/sql/PyStatement.java
@@ -7,6 +7,7 @@
*/
package com.ziclix.python.sql;
+import org.python.core.Visitproc;
import org.python.core.codecs;
import org.python.core.Py;
import org.python.core.PyException;
@@ -14,6 +15,7 @@
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyUnicode;
+import org.python.core.Traverseproc;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
@@ -25,7 +27,7 @@
*
* @author brian zimmer
*/
-public class PyStatement extends PyObject {
+public class PyStatement extends PyObject implements Traverseproc {
/** Denotes a simple Statement with no parameters. */
public static final int STATEMENT_STATIC = 2;
@@ -278,4 +280,37 @@
closed = true;
}
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (sql != null) {
+ if (sql instanceof PyObject) {
+ int retVal = visit.visit((PyObject) sql, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ } else if (sql instanceof Traverseproc) {
+ int retVal = ((Traverseproc) sql).traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (sql == null || ob == null) {
+ return false;
+ } else if (sql instanceof PyObject) {
+ return sql == ob;
+ } else if (sql instanceof Traverseproc) {
+ return ((Traverseproc) sql).refersDirectlyTo(ob);
+ } else{
+ return false;
+ }
+ }
}
diff --git a/src/com/ziclix/python/sql/connect/Connect.java b/src/com/ziclix/python/sql/connect/Connect.java
--- a/src/com/ziclix/python/sql/connect/Connect.java
+++ b/src/com/ziclix/python/sql/connect/Connect.java
@@ -19,6 +19,7 @@
*
* @author brian zimmer
*/
+ at Untraversable
public class Connect extends PyObject {
private static final PyString _doc = new PyString("establish a connection through java.sql.DriverManager");
diff --git a/src/com/ziclix/python/sql/connect/Connectx.java b/src/com/ziclix/python/sql/connect/Connectx.java
--- a/src/com/ziclix/python/sql/connect/Connectx.java
+++ b/src/com/ziclix/python/sql/connect/Connectx.java
@@ -19,6 +19,7 @@
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.core.PyString;
+import org.python.core.Untraversable;
import com.ziclix.python.sql.PyConnection;
import com.ziclix.python.sql.zxJDBC;
@@ -29,17 +30,18 @@
*
* @author brian zimmer
*/
+ at Untraversable
public class Connectx extends PyObject {
- private final String SET = "set";
- private final PyString doc =
+ private static final String SET = "set";
+ private static final PyString _doc =
new PyString("establish a connection through a javax.sql.DataSource or "
+ "javax.sql.ConnectionPooledDataSource");
@Override
public PyObject __findattr_ex__(String name) {
if ("__doc__".equals(name)) {
- return doc;
+ return _doc;
}
return super.__findattr_ex__(name);
}
diff --git a/src/com/ziclix/python/sql/connect/Lookup.java b/src/com/ziclix/python/sql/connect/Lookup.java
--- a/src/com/ziclix/python/sql/connect/Lookup.java
+++ b/src/com/ziclix/python/sql/connect/Lookup.java
@@ -22,6 +22,7 @@
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.core.PyString;
+import org.python.core.Untraversable;
import com.ziclix.python.sql.PyConnection;
import com.ziclix.python.sql.zxJDBC;
@@ -36,6 +37,7 @@
*
* @author brian zimmer
*/
+ at Untraversable
public class Lookup extends PyObject {
private static final PyString _doc =
diff --git a/src/com/ziclix/python/sql/util/BCP.java b/src/com/ziclix/python/sql/util/BCP.java
--- a/src/com/ziclix/python/sql/util/BCP.java
+++ b/src/com/ziclix/python/sql/util/BCP.java
@@ -14,6 +14,9 @@
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyString;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
+
import com.ziclix.python.sql.PyConnection;
import com.ziclix.python.sql.zxJDBC;
import com.ziclix.python.sql.pipe.Pipe;
@@ -23,7 +26,7 @@
/**
* A class to perform efficient Bulk CoPy of database tables.
*/
-public class BCP extends PyObject implements ClassDictInit {
+public class BCP extends PyObject implements ClassDictInit, Traverseproc {
/**
* Field sourceDH, destDH
@@ -182,6 +185,24 @@
return pipe.pipe(source, sink).__sub__(Py.newInteger(1));
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (source != null) {
+ int retVal = visit.visit(source, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return destination != null ? visit.visit(destination, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == source || ob == destination);
+ }
}
/**
diff --git a/src/com/ziclix/python/sql/zxJDBC.java b/src/com/ziclix/python/sql/zxJDBC.java
--- a/src/com/ziclix/python/sql/zxJDBC.java
+++ b/src/com/ziclix/python/sql/zxJDBC.java
@@ -18,6 +18,7 @@
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyStringMap;
+import org.python.core.Untraversable;
import java.io.CharArrayWriter;
import java.io.PrintWriter;
@@ -41,6 +42,7 @@
*
* @author brian zimmer
*/
+ at Untraversable
public class zxJDBC extends PyObject implements ClassDictInit {
/**
@@ -428,6 +430,7 @@
}
}
+ at Untraversable
class zxJDBCFunc extends PyBuiltinFunctionSet {
zxJDBCFunc(String name, int index, int minargs, int maxargs, String doc) {
diff --git a/src/org/python/antlr/AST.java b/src/org/python/antlr/AST.java
--- a/src/org/python/antlr/AST.java
+++ b/src/org/python/antlr/AST.java
@@ -4,8 +4,10 @@
import org.python.core.PyException;
import org.python.core.PyObject;
import org.python.core.PyType;
+import org.python.core.Untraversable;
import org.python.expose.ExposedType;
+ at Untraversable
@ExposedType(name = "_ast.AST", base = PyObject.class)
public class AST extends PyObject {
public static final PyType TYPE = PyType.fromClass(AST.class);
diff --git a/src/org/python/antlr/PythonTree.java b/src/org/python/antlr/PythonTree.java
--- a/src/org/python/antlr/PythonTree.java
+++ b/src/org/python/antlr/PythonTree.java
@@ -4,14 +4,17 @@
import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;
+import org.python.core.PyObject;
import org.python.core.PyType;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
import org.python.antlr.ast.Name;
import org.python.antlr.ast.VisitorIF;
import java.util.ArrayList;
import java.util.List;
-public class PythonTree extends AST {
+public class PythonTree extends AST implements Traverseproc {
public boolean from_future_checked = false;
private int charStartIndex = -1;
@@ -460,4 +463,15 @@
}
}
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return parent != null ? visit.visit(parent, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == parent;
+ }
}
diff --git a/src/org/python/antlr/ast/AssertDerived.java b/src/org/python/antlr/ast/AssertDerived.java
--- a/src/org/python/antlr/ast/AssertDerived.java
+++ b/src/org/python/antlr/ast/AssertDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class AssertDerived extends Assert implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class AssertDerived extends Assert implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i {
/**
diff --git a/src/org/python/core/BaseDictionaryView.java b/src/org/python/core/BaseDictionaryView.java
--- a/src/org/python/core/BaseDictionaryView.java
+++ b/src/org/python/core/BaseDictionaryView.java
@@ -9,7 +9,7 @@
import java.util.Iterator;
import org.python.core.PyObject;
-public abstract class BaseDictionaryView extends PyObject {
+public abstract class BaseDictionaryView extends PyObject implements Traverseproc {
protected final PyDictionary dvDict;
public BaseDictionaryView(PyDictionary dvDict) {
@@ -162,4 +162,16 @@
ts.exitRepr(this);
return buf.toString();
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return dvDict != null ? visit.visit(dvDict, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && dvDict == ob;
+ }
}
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
@@ -7,7 +7,7 @@
import java.util.Iterator;
import java.util.Set;
-public abstract class BaseSet extends PyObject implements Set {
+public abstract class BaseSet extends PyObject implements Set, Traverseproc {
/** The underlying Set. */
protected Set _set;
@@ -43,9 +43,9 @@
// Skip the iteration if both are sets
set.addAll(((BaseSet)data)._set);
} else {
- for (PyObject item : data.asIterable()) {
- set.add(item);
- }
+ for (PyObject item : data.asIterable()) {
+ set.add(item);
+ }
}
return set;
}
@@ -126,24 +126,24 @@
}
final PyObject baseset_difference(PyObject other) {
- return baseset_difference(new PyObject[] {other});
+ return baseset_difference(new PyObject[] {other});
}
final PyObject baseset_difference(PyObject [] args) {
- if (args.length == 0) {
- return BaseSet.makeNewSet(getType(), this);
- }
-
- BaseSet o = BaseSet.makeNewSet(getType(), this);
- for (PyObject item: args) {
- BaseSet bs = args[0] instanceof BaseSet ? (BaseSet)item : new PySet(item);
- Set set = bs._set;
+ if (args.length == 0) {
+ return BaseSet.makeNewSet(getType(), this);
+ }
+
+ BaseSet o = BaseSet.makeNewSet(getType(), this);
+ for (PyObject item: args) {
+ BaseSet bs = args[0] instanceof BaseSet ? (BaseSet)item : new PySet(item);
+ Set set = bs._set;
- for (PyObject p : set) {
- if (_set.contains(p)) {
- o._set.remove(p);
- }
- }
+ for (PyObject p : set) {
+ if (_set.contains(p)) {
+ o._set.remove(p);
+ }
+ }
}
return o;
}
@@ -348,7 +348,7 @@
final PyObject baseset_union(PyObject [] args) {
BaseSet result = BaseSet.makeNewSet(getType(), this);
for (PyObject item: args) {
- result._update(item);
+ result._update(item);
}
return result;
}
@@ -372,15 +372,15 @@
}
final PyObject baseset_intersection(PyObject [] args) {
- BaseSet result = BaseSet.makeNewSet(getType(), this);
- if (args.length == 0) {
- return result;
+ BaseSet result = BaseSet.makeNewSet(getType(), this);
+ if (args.length == 0) {
+ return result;
}
-
- for (PyObject other: args) {
- result = (BaseSet)result.baseset_intersection(other);
- }
- return result;
+
+ for (PyObject other: args) {
+ result = (BaseSet)result.baseset_intersection(other);
+ }
+ return result;
}
final PyObject baseset_copy() {
@@ -407,8 +407,8 @@
}
final PyObject baseset_isdisjoint(PyObject other) {
- BaseSet bs = other instanceof BaseSet ? (BaseSet)other : new PySet(other);
- return Collections.disjoint(_set, bs._set) ? Py.True : Py.False;
+ BaseSet bs = other instanceof BaseSet ? (BaseSet)other : new PySet(other);
+ return Collections.disjoint(_set, bs._set) ? Py.True : Py.False;
}
public String toString() {
@@ -599,4 +599,23 @@
}
return a;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue;
+ for (PyObject ob: _set) {
+ retValue = visit.visit(ob, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return _set.contains(ob);
+ }
}
diff --git a/src/org/python/core/ClasspathPyImporter.java b/src/org/python/core/ClasspathPyImporter.java
--- a/src/org/python/core/ClasspathPyImporter.java
+++ b/src/org/python/core/ClasspathPyImporter.java
@@ -11,6 +11,7 @@
import org.python.expose.ExposedType;
import org.python.util.Generic;
+ at Untraversable
@ExposedType(name="ClasspathPyImporter")
public class ClasspathPyImporter extends importer {
diff --git a/src/org/python/core/ClasspathPyImporterDerived.java b/src/org/python/core/ClasspathPyImporterDerived.java
--- a/src/org/python/core/ClasspathPyImporterDerived.java
+++ b/src/org/python/core/ClasspathPyImporterDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class ClasspathPyImporterDerived extends ClasspathPyImporter implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class ClasspathPyImporterDerived extends ClasspathPyImporter implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,17 +26,35 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i selfSet = asSet();
for (Collection other : others) {
@@ -131,6 +135,7 @@
u.addAll(other);
return u;
}
+
protected void update(Collection other) {
asSet().addAll(other);
}
@@ -153,6 +158,7 @@
}
}
+ @Untraversable
private static class SetMethodVarargs extends SetMethod {
protected SetMethodVarargs(String name) {
super(name, 0, -1);
@@ -289,6 +295,8 @@
return Py.newBoolean(!isEqual(other) && isSubset(other));
}
};
+
+ @Untraversable
private static class IsSubsetMethod extends SetMethod {
// __le__, issubset
@@ -301,6 +309,8 @@
return Py.newBoolean(isSubset(other));
}
}
+
+ @Untraversable
private static class IsSupersetMethod extends SetMethod {
// __ge__, issuperset
@@ -313,12 +323,14 @@
return Py.newBoolean(isSuperset(other));
}
}
+
private static final SetMethod gtProxy = new SetMethod("__gt__", 1) {
@Override
public PyObject __call__(PyObject other) {
return Py.newBoolean(!isEqual(other) && isSuperset(other));
}
};
+
private static final SetMethod isDisjointProxy = new SetMethod("isdisjoint", 1) {
@Override
public PyObject __call__(PyObject other) {
@@ -332,6 +344,7 @@
return makePySet(difference(getCombinedJavaCollections(others)));
}
};
+
private static final SetMethod differenceUpdateProxy = new SetMethodVarargs("difference_update") {
@Override
public PyObject __call__(PyObject[] others) {
@@ -339,12 +352,14 @@
return Py.None;
}
};
+
private static final SetMethod subProxy = new SetMethod("__sub__", 1) {
@Override
public PyObject __call__(PyObject other) {
return makePySet(difference(getJavaSet(self, "-", other)));
}
};
+
private static final SetMethod isubProxy = new SetMethod("__isub__", 1) {
@Override
public PyObject __call__(PyObject other) {
@@ -359,6 +374,7 @@
return makePySet(intersect(getJavaCollections(others)));
}
};
+
private static final SetMethod intersectionUpdateProxy = new SetMethodVarargs("intersection_update") {
@Override
public PyObject __call__(PyObject[] others) {
@@ -366,12 +382,14 @@
return Py.None;
}
};
+
private static final SetMethod andProxy = new SetMethod("__and__", 1) {
@Override
public PyObject __call__(PyObject other) {
return makePySet(intersect(new Collection[]{getJavaSet(self, "&", other)}));
}
};
+
private static final SetMethod iandProxy = new SetMethod("__iand__", 1) {
@Override
public PyObject __call__(PyObject other) {
@@ -386,6 +404,7 @@
return makePySet(symDiff(getJavaCollection(other)));
}
};
+
private static final SetMethod symDiffUpdateProxy = new SetMethod("symmetric_difference_update", 1) {
@Override
public PyObject __call__(PyObject other) {
@@ -393,12 +412,14 @@
return Py.None;
}
};
+
private static final SetMethod xorProxy = new SetMethod("__xor__", 1) {
@Override
public PyObject __call__(PyObject other) {
return makePySet(symDiff(getJavaSet(self, "^", other)));
}
};
+
private static final SetMethod ixorProxy = new SetMethod("__ixor__", 1) {
@Override
public PyObject __call__(PyObject other) {
@@ -413,6 +434,7 @@
return makePySet(union(getCombinedJavaCollections(others)));
}
};
+
private static final SetMethod updateProxy = new SetMethodVarargs("update") {
@Override
public PyObject __call__(PyObject[] others) {
@@ -420,12 +442,14 @@
return Py.None;
}
};
+
private static final SetMethod orProxy = new SetMethod("__or__", 1) {
@Override
public PyObject __call__(PyObject other) {
return makePySet(union(getJavaSet(self, "|", other)));
}
};
+
private static final SetMethod iorProxy = new SetMethod("__ior__", 1) {
@Override
public PyObject __call__(PyObject other) {
@@ -434,6 +458,7 @@
}
};
+ @Untraversable
private static class CopyMethod extends SetMethod {
protected CopyMethod(String name) {
super(name, 0);
diff --git a/src/org/python/core/JyAttribute.java b/src/org/python/core/JyAttribute.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/core/JyAttribute.java
@@ -0,0 +1,308 @@
+package org.python.core;
+
+import java.io.Serializable;
+
+/**
+ *
+ * Manages a linked list of general purpose Object-attributes, which
+ * can be attached to arbitrary {@code PyObject}s. This method replaces
+ * the formerly used method of maintaining weak hash maps for such cases.
+ * These weak hash maps were used to map {@code PyObject}s to such
+ * attributes, for instance to attach
+ * {@link org.python.modules._weakref.GlobalRef}-objects in the
+ * {@link org.python.modules._weakref.WeakrefModule}.
+ *
+ *
+ * Attributes attached via the weak hash map method break, if the
+ * {@code PyObject} is resurrected in its finalizer. The
+ * {@code JyAttribute}-method is resurrection-safe.
+ *
+ *
+ * To reduce memory footprint of {@code PyObject}s, the fields for
+ * {@link org.python.core.finalization.FinalizeTrigger}s and
+ * {@code javaProxy} are included in the list; {@code javaProxy} always
+ * on top so there is no speed-regression,
+ * {@link org.python.core.finalization.FinalizeTrigger} on bottom,
+ * as it is usually never accessed.
+ *
+ */
+public abstract class JyAttribute implements Serializable {
+ /* ordered by priority; indices >= 0 indicate transient attributes.
+ since it is intended for rare use, 128 indices for ordinary and
+ transient attributes each should be enough in foreseeable future.
+ If needed, it would be trivial to change format to short.
+ */
+ public static final byte JAVA_PROXY_ATTR = Byte.MIN_VALUE;
+
+ /**
+ * Stores list of weak references linking to this PyObject.
+ * This list is weakref-based, so it does not keep the
+ * weakrefs alive. This is the only way to find out which
+ * weakrefs (i.e. org.python.modules._weakref.AbstractReference)
+ * linked to the object after a resurrection. A weak
+ * hashmap-based approach for this purpose would break on
+ * resurrection.
+ */
+ public static final byte WEAK_REF_ATTR = 0; //first transient
+
+ /**
+ * Reserved for use by JyNI.
+ */
+ public static final byte JYNI_HANDLE_ATTR = 1;
+
+ /**
+ * Allows the id of a {@link org.python.core.PyObject} to persist
+ * resurrection of that object.
+ */
+ public static final byte PY_ID_ATTR = 2;
+
+ /**
+ * Holds the current thread for an AbstractReference while
+ * referent-retrieval is pending due to a potentially
+ * restored-by-resurrection weak reference. After the
+ * restore has happened or the clear was confirmed, the
+ * thread is interrupted and the attribute is cleared.
+ */
+ public static final byte WEAKREF_PENDING_GET_ATTR = 3;
+
+ /**
+ * Used by gc module to mark cyclic trash. Searching for cyclic
+ * trash is usually not required by Jython. It is only done if
+ * gc features are enabled that mimic CPython behavior.
+ */
+ public static final byte GC_CYCLE_MARK_ATTR = 4;
+
+ /**
+ * Used by gc module to mark monitored objects before they
+ * become unmonitored for deletion. In case of a resurrection
+ * this is the only way for gc to detect that the object was
+ * to be monitored and should be monitored again.
+ */
+ //public static final byte GC_MONITOR_MARK_ATTR = 5;
+
+ /**
+ * Used by gc module to mark finalizable objects that might have
+ * been resurrected during a delayed finalization process.
+ */
+ public static final byte GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR = 6;
+
+ public static final byte FINALIZE_TRIGGER_ATTR = Byte.MAX_VALUE;
+ private static byte nonBuiltinAttrTypeOffset = Byte.MIN_VALUE+1;
+ private static byte nonBuiltinTransientAttrTypeOffset = 7;
+
+ /**
+ * Reserves and returns a new attr type for custom use.
+ *
+ * @return an attr type for custom use
+ */
+ public static byte reserveCustomAttrType() {
+ if (nonBuiltinAttrTypeOffset == 0) {
+ throw new IllegalStateException("No more attr types available.");
+ }
+ return nonBuiltinAttrTypeOffset++;
+ }
+
+ /**
+ * Reserves and returns a new transient attr type for custom use.
+ *
+ * @return a transient attr type for custom use
+ */
+ public static byte reserveTransientCustomAttrType() {
+ if (nonBuiltinTransientAttrTypeOffset == Byte.MAX_VALUE) {
+ throw new IllegalStateException("No more transient attr types available.");
+ }
+ return nonBuiltinTransientAttrTypeOffset++;
+ }
+
+ byte attr_type;
+
+ static class AttributeLink extends JyAttribute {
+ JyAttribute next;
+ Object value;
+
+ protected AttributeLink(byte attr_type, Object value) {
+ super(attr_type);
+ this.value = value;
+ }
+
+ protected JyAttribute getNext() {
+ return next;
+ }
+
+ protected void setNext(JyAttribute next) {
+ this.next = next;
+ }
+
+ protected Object getValue() {
+ return value;
+ }
+
+ protected void setValue(Object value) {
+ this.value = value;
+ }
+ }
+
+ static class TransientAttributeLink extends JyAttribute {
+ transient JyAttribute next;
+ transient Object value;
+
+ protected TransientAttributeLink(byte attr_type, Object value) {
+ super(attr_type);
+ this.value = value;
+ }
+
+ protected JyAttribute getNext() {
+ return next;
+ }
+
+ protected void setNext(JyAttribute next) {
+ this.next = next;
+ }
+
+ protected Object getValue() {
+ return value;
+ }
+
+ protected void setValue(Object value) {
+ this.value = value;
+ }
+ }
+
+ protected JyAttribute(byte attr_type) {
+ this.attr_type = attr_type;
+ }
+
+ protected abstract JyAttribute getNext();
+ protected abstract void setNext(JyAttribute next);
+ protected abstract Object getValue();
+ protected abstract void setValue(Object value);
+
+ /**
+ * Checks whether the given {@code PyObject} has an attribute
+ * of the given type attached.
+ */
+ public static synchronized boolean hasAttr(PyObject ob, byte attr_type) {
+ if (ob.attributes == null) {
+ return false;
+ }
+ if (!(ob.attributes instanceof JyAttribute)) {
+ return attr_type == JAVA_PROXY_ATTR;
+ }
+ JyAttribute att = (JyAttribute) ob.attributes;
+ while (att != null && att.attr_type < attr_type) {
+ att = att.getNext();
+ }
+ return att != null && att.attr_type == attr_type;
+ }
+
+ /**
+ * Retrieves the attribute of the given type from the given
+ * {@code PyObject}.
+ * If no attribute of the given type is attached, null is returned.
+ */
+ public static synchronized Object getAttr(PyObject ob, byte attr_type) {
+ if (ob.attributes == null) {
+ return null;
+ }
+ if (!(ob.attributes instanceof JyAttribute)) {
+ return attr_type == JAVA_PROXY_ATTR ? ob.attributes : null;
+ }
+ JyAttribute att = (JyAttribute) ob.attributes;
+ while (att != null && att.attr_type < attr_type) {
+ att = att.getNext();
+ }
+ return att != null && att.attr_type == attr_type ? att.getValue() : null;
+ }
+
+ public static synchronized void debugPrintAttributes(PyObject o, java.io.PrintStream out) {
+ out.println("debugPrintAttributes of "+System.identityHashCode(o)+":");
+ if (o.attributes == null) {
+ out.println("null");
+ } else if (!(o.attributes instanceof JyAttribute)) {
+ out.println("only javaProxy");
+ } else {
+ JyAttribute att = (JyAttribute) o.attributes;
+ while (att != null) {
+ out.println("att type: "+att.attr_type+" value: "+att.getValue());
+ att = att.getNext();
+ }
+ }
+ out.println("debugPrintAttributes done");
+ }
+
+ public static synchronized void setAttr(PyObject ob, byte attr_type, Object value) {
+ if (value == null) {
+ delAttr(ob, attr_type);
+ } else {
+ if (ob.attributes == null) {
+ if (attr_type == JyAttribute.JAVA_PROXY_ATTR) {
+ ob.attributes = value;
+ } else {
+ ob.attributes = attr_type < 0 ?
+ new AttributeLink(attr_type, value) :
+ new TransientAttributeLink(attr_type, value);
+ }
+ } else if (!(ob.attributes instanceof JyAttribute)) {
+ if (attr_type == JyAttribute.JAVA_PROXY_ATTR) {
+ ob.attributes = value;
+ } else {
+ ob.attributes = new AttributeLink(JyAttribute.JAVA_PROXY_ATTR, ob.attributes);
+ ((JyAttribute) ob.attributes).setNext(attr_type < 0 ?
+ new AttributeLink(attr_type, value) :
+ new TransientAttributeLink(attr_type, value));
+ }
+ } else {
+ JyAttribute att = (JyAttribute) ob.attributes;
+ if (att.attr_type > attr_type) {
+ JyAttribute newAtt = attr_type < 0 ?
+ new AttributeLink(attr_type, value) :
+ new TransientAttributeLink(attr_type, value);
+ newAtt.setNext(att);
+ ob.attributes = newAtt;
+ } else {
+ while (att.getNext() != null && att.getNext().attr_type <= attr_type) {
+ att = att.getNext();
+ }
+ if (att.attr_type == attr_type) {
+ att.setValue(value);
+ } else if (att.getNext() == null) {
+ att.setNext(attr_type < 0 ?
+ new AttributeLink(attr_type, value) :
+ new TransientAttributeLink(attr_type, value));
+ } else {
+ JyAttribute newAtt = attr_type < 0 ?
+ new AttributeLink(attr_type, value) :
+ new TransientAttributeLink(attr_type, value);
+ newAtt.setNext(att.getNext());
+ att.setNext(newAtt);
+ }
+ }
+ }
+ }
+ }
+
+ public static synchronized void delAttr(PyObject ob, byte attr_type) {
+ if (ob.attributes == null) {
+ return;
+ } else if (attr_type == JAVA_PROXY_ATTR && !(ob.attributes instanceof JyAttribute)) {
+ ob.attributes = null;
+ }
+ JyAttribute att = (JyAttribute) ob.attributes;
+ if (att.attr_type == attr_type) {
+ ob.attributes = att.getNext();
+ } else {
+ while (att.getNext() != null && att.getNext().attr_type < attr_type) {
+ att = att.getNext();
+ }
+ if (att.getNext() != null && att.getNext().attr_type == attr_type) {
+ att.setNext(att.getNext().getNext());
+ }
+ }
+ if (ob.attributes != null) {
+ att = (JyAttribute) ob.attributes;
+ if (att.getNext() == null && att.attr_type == JyAttribute.JAVA_PROXY_ATTR) {
+ ob.attributes = att.getValue();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/org/python/core/Py.java b/src/org/python/core/Py.java
--- a/src/org/python/core/Py.java
+++ b/src/org/python/core/Py.java
@@ -34,6 +34,8 @@
import org.python.antlr.base.mod;
import org.python.core.adapter.ClassicPyObjectAdapter;
import org.python.core.adapter.ExtensiblePyObjectAdapter;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
import org.python.modules.posix.PosixModule;
import org.python.util.Generic;
@@ -988,10 +990,10 @@
PyObject instance = (PyObject)(ThreadContext.initializingProxy.get()[0]);
ThreadState ts = Py.getThreadState();
if (instance != null) {
- if (instance.javaProxy != null) {
+ if (JyAttribute.hasAttr(instance, JyAttribute.JAVA_PROXY_ATTR)) {
throw Py.TypeError("Proxy instance reused");
}
- instance.javaProxy = proxy;
+ JyAttribute.setAttr(instance, JyAttribute.JAVA_PROXY_ATTR, proxy);
proxy._setPyInstance(instance);
proxy._setPySystemState(ts.systemState);
return;
@@ -1014,7 +1016,7 @@
pargs = Py.javas2pys(args);
}
instance = pyc.__call__(pargs);
- instance.javaProxy = proxy;
+ JyAttribute.setAttr(instance, JyAttribute.JAVA_PROXY_ATTR, proxy);
proxy._setPyInstance(instance);
proxy._setPySystemState(ts.systemState);
}
@@ -2273,7 +2275,7 @@
/**
* A code object wrapper for a python function.
*/
-class JavaCode extends PyCode {
+class JavaCode extends PyCode implements Traverseproc {
private PyObject func;
@@ -2337,12 +2339,25 @@
PyObject[] defaults, PyObject closure) {
return func.__call__(arg1, arg2, arg3, arg4);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return func != null ? visit.visit(func, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == func;
+ }
}
/**
* A function object wrapper for a java method which comply with the
* PyArgsKeywordsCall standard.
*/
+ at Untraversable
class JavaFunc extends PyObject {
Method method;
diff --git a/src/org/python/core/Py2kBuffer.java b/src/org/python/core/Py2kBuffer.java
--- a/src/org/python/core/Py2kBuffer.java
+++ b/src/org/python/core/Py2kBuffer.java
@@ -21,6 +21,7 @@
* arguments the same (one-dimensional byte-array) types. Their behaviour differs as detailed in the
* documentation.
*/
+ at Untraversable
@ExposedType(name = "buffer", doc = BuiltinDocs.buffer_doc, base = PyObject.class,
isBaseType = false)
public class Py2kBuffer extends PySequence implements BufferProtocol {
diff --git a/src/org/python/core/PyArray.java b/src/org/python/core/PyArray.java
--- a/src/org/python/core/PyArray.java
+++ b/src/org/python/core/PyArray.java
@@ -22,6 +22,7 @@
import org.python.expose.ExposedNew;
import org.python.expose.ExposedType;
import org.python.expose.MethodType;
+import org.python.modules.gc;
/**
* A wrapper class around native java arrays.
@@ -30,8 +31,9 @@
*
* See also the jarray module.
*/
+
@ExposedType(name = "array.array", base = PyObject.class)
-public class PyArray extends PySequence implements Cloneable, BufferProtocol {
+public class PyArray extends PySequence implements Cloneable, BufferProtocol, Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyArray.class);
@@ -2150,4 +2152,23 @@
}
}
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (data == null || !gc.canLinkToPyObject(data.getClass(), true)) {
+ return 0;
+ }
+ return gc.traverseByReflection(data, visit, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob)
+ throws UnsupportedOperationException {
+ if (data == null || !gc.canLinkToPyObject(data.getClass(), true)) {
+ return false;
+ }
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/src/org/python/core/PyArrayDerived.java b/src/org/python/core/PyArrayDerived.java
--- a/src/org/python/core/PyArrayDerived.java
+++ b/src/org/python/core/PyArrayDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyArrayDerived extends PyArray implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyArrayDerived extends PyArray implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i extends PyObject {
public Method addMethod;
diff --git a/src/org/python/core/PyBeanEventProperty.java b/src/org/python/core/PyBeanEventProperty.java
--- a/src/org/python/core/PyBeanEventProperty.java
+++ b/src/org/python/core/PyBeanEventProperty.java
@@ -9,6 +9,7 @@
import org.python.util.Generic;
+ at Untraversable
public class PyBeanEventProperty extends PyObject {
private static Map> adapterClasses = Generic.map();
diff --git a/src/org/python/core/PyBeanProperty.java b/src/org/python/core/PyBeanProperty.java
--- a/src/org/python/core/PyBeanProperty.java
+++ b/src/org/python/core/PyBeanProperty.java
@@ -2,6 +2,7 @@
package org.python.core;
import java.lang.reflect.*;
+ at Untraversable
public class PyBeanProperty extends PyReflectedField {
public Method getMethod, setMethod;
public Class> myType;
diff --git a/src/org/python/core/PyBoolean.java b/src/org/python/core/PyBoolean.java
--- a/src/org/python/core/PyBoolean.java
+++ b/src/org/python/core/PyBoolean.java
@@ -10,6 +10,7 @@
* The builtin python bool. It would be nice if it didn't extend PyInteger,
* but too hard to avoid pre-Python 2.2 semantics here.
*/
+ at Untraversable
@ExposedType(name = "bool", isBaseType = false, doc = BuiltinDocs.bool_doc)
public class PyBoolean extends PyInteger {
diff --git a/src/org/python/core/PyBuiltinCallable.java b/src/org/python/core/PyBuiltinCallable.java
--- a/src/org/python/core/PyBuiltinCallable.java
+++ b/src/org/python/core/PyBuiltinCallable.java
@@ -6,6 +6,7 @@
import org.python.expose.ExposedGet;
import org.python.expose.ExposedType;
+ at Untraversable
@ExposedType(name = "builtin_function_or_method", isBaseType = false)
public abstract class PyBuiltinCallable extends PyObject {
diff --git a/src/org/python/core/PyBuiltinFunction.java b/src/org/python/core/PyBuiltinFunction.java
--- a/src/org/python/core/PyBuiltinFunction.java
+++ b/src/org/python/core/PyBuiltinFunction.java
@@ -2,6 +2,7 @@
import org.python.expose.ExposeAsSuperclass;
+ at Untraversable
public class PyBuiltinFunction extends PyBuiltinCallable implements ExposeAsSuperclass {
protected PyBuiltinFunction(String name, String doc) {
diff --git a/src/org/python/core/PyBuiltinFunctionNarrow.java b/src/org/python/core/PyBuiltinFunctionNarrow.java
--- a/src/org/python/core/PyBuiltinFunctionNarrow.java
+++ b/src/org/python/core/PyBuiltinFunctionNarrow.java
@@ -1,5 +1,6 @@
package org.python.core;
+ at Untraversable
public class PyBuiltinFunctionNarrow extends PyBuiltinFunction {
protected PyBuiltinFunctionNarrow(String name, int minargs, int maxargs, String doc) {
diff --git a/src/org/python/core/PyBuiltinFunctionSet.java b/src/org/python/core/PyBuiltinFunctionSet.java
--- a/src/org/python/core/PyBuiltinFunctionSet.java
+++ b/src/org/python/core/PyBuiltinFunctionSet.java
@@ -8,6 +8,7 @@
* method with a switch on the index number.
*
*/
+ at Untraversable
public class PyBuiltinFunctionSet extends PyBuiltinFunctionNarrow {
// used as an index into a big switch statement in the various derived
diff --git a/src/org/python/core/PyBuiltinMethod.java b/src/org/python/core/PyBuiltinMethod.java
--- a/src/org/python/core/PyBuiltinMethod.java
+++ b/src/org/python/core/PyBuiltinMethod.java
@@ -3,7 +3,7 @@
import org.python.expose.ExposeAsSuperclass;
public abstract class PyBuiltinMethod extends PyBuiltinCallable implements ExposeAsSuperclass,
- Cloneable {
+ Cloneable, Traverseproc {
protected PyObject self;
@@ -71,4 +71,16 @@
int compareTo = info.getName().compareTo(otherMethod.info.getName());
return compareTo < 0 ? -1 : compareTo > 0 ? 1 : 0;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return self != null ? visit.visit(self, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == self;
+ }
}
diff --git a/src/org/python/core/PyBuiltinMethodNarrow.java b/src/org/python/core/PyBuiltinMethodNarrow.java
--- a/src/org/python/core/PyBuiltinMethodNarrow.java
+++ b/src/org/python/core/PyBuiltinMethodNarrow.java
@@ -15,7 +15,6 @@
this(name, numArgs, numArgs);
}
-
/**
* Creates a method for the given name that takes at least minArgs
and at most
* maxArgs
arguments.
diff --git a/src/org/python/core/PyBuiltinMethodSet.java b/src/org/python/core/PyBuiltinMethodSet.java
--- a/src/org/python/core/PyBuiltinMethodSet.java
+++ b/src/org/python/core/PyBuiltinMethodSet.java
@@ -1,7 +1,7 @@
/* Copyright (c) Jython Developers */
package org.python.core;
-public class PyBuiltinMethodSet extends PyBuiltinFunctionSet implements Cloneable {
+public class PyBuiltinMethodSet extends PyBuiltinFunctionSet implements Cloneable, Traverseproc {
private Class> type;
@@ -57,4 +57,16 @@
public String toString() {
return String.format("", info.getName());
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return visit.visit(__self__, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob == __self__;
+ }
}
diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java
--- a/src/org/python/core/PyByteArray.java
+++ b/src/org/python/core/PyByteArray.java
@@ -22,6 +22,7 @@
* This may relate to parameters, or to the target object itself (in text that applies equally to
* base or sibling classes).
*/
+ at Untraversable
@ExposedType(name = "bytearray", base = PyObject.class, doc = BuiltinDocs.bytearray_doc)
public class PyByteArray extends BaseBytes implements BufferProtocol {
diff --git a/src/org/python/core/PyByteArrayDerived.java b/src/org/python/core/PyByteArrayDerived.java
--- a/src/org/python/core/PyByteArrayDerived.java
+++ b/src/org/python/core/PyByteArrayDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyByteArrayDerived extends PyByteArray implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyByteArrayDerived extends PyByteArray implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i", exception.type, exception.value, exception.traceback);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return exception != null ? exception.traverse(visit, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && exception.refersDirectlyTo(ob);
+ }
}
private static String stringify_blocks(PyFrame f) {
@@ -257,7 +270,7 @@
protected PyObject interpret(PyFrame f, ThreadState ts) {
final PyStack stack = new PyStack(co_stacksize);
int next_instr = -1;
- int opcode; /* Current opcode */
+ int opcode; /* Current opcode */
int oparg = 0; /* Current opcode argument, if any */
Why why = Why.NOT;
PyObject retval = null;
@@ -1479,13 +1492,14 @@
}
}
+ @Untraversable
private static class PyTryBlock extends PyObject { // purely to sit on top of the existing PyFrame in f_exits!!!
- int b_type; /* what kind of block this is */
+ int b_type; /* what kind of block this is */
- int b_handler; /* where to jump to find handler */
+ int b_handler; /* where to jump to find handler */
- int b_level; /* value stack level to pop to */
+ int b_level; /* value stack level to pop to */
PyTryBlock(int type, int handler, int level) {
@@ -1609,6 +1623,36 @@
}
return x;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue;
+ if (co_consts != null) {
+ for (PyObject ob: co_consts) {
+ if (ob != null) {
+ retValue = visit.visit(ob, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null || co_consts == null) {
+ return false;
+ } else {
+ for (PyObject obj: co_consts) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
}
-
-
diff --git a/src/org/python/core/PyCallIter.java b/src/org/python/core/PyCallIter.java
--- a/src/org/python/core/PyCallIter.java
+++ b/src/org/python/core/PyCallIter.java
@@ -1,6 +1,7 @@
package org.python.core;
public class PyCallIter extends PyIterator {
+ //note: Already implements Traverseproc, inheriting it from PyIterator
private PyObject callable;
@@ -36,4 +37,26 @@
}
return result;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue = super.traverse(visit, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ if (callable != null) {
+ retValue = visit.visit(callable, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ return sentinel != null ? visit.visit(sentinel, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == callable || ob == sentinel || super.refersDirectlyTo(ob));
+ }
}
diff --git a/src/org/python/core/PyCell.java b/src/org/python/core/PyCell.java
--- a/src/org/python/core/PyCell.java
+++ b/src/org/python/core/PyCell.java
@@ -10,7 +10,7 @@
* Cells are used to implement variables referenced by multiple scopes.
*/
@ExposedType(name = "cell", isBaseType = false)
-public class PyCell extends PyObject {
+public class PyCell extends PyObject implements Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyCell.class);
@@ -37,4 +37,16 @@
return String.format("", Py.idstr(this),
ob_ref.getType().getName(), Py.idstr(ob_ref));
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return ob_ref != null ? visit.visit(ob_ref, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob_ref == ob;
+ }
}
diff --git a/src/org/python/core/PyClass.java b/src/org/python/core/PyClass.java
--- a/src/org/python/core/PyClass.java
+++ b/src/org/python/core/PyClass.java
@@ -9,7 +9,7 @@
* The classic Python class.
*/
@ExposedType(name = "classobj", isBaseType = false)
-public class PyClass extends PyObject {
+public class PyClass extends PyObject implements Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyClass.class);
@@ -188,7 +188,7 @@
PyInstance inst;
inst = new PyInstance(this);
if (__del__ != null) {
- inst.finalizeTrigger = FinalizeTrigger.makeTrigger(inst);
+ FinalizeTrigger.ensureFinalizer(inst);
}
inst.__init__(args, keywords);
return inst;
@@ -281,4 +281,66 @@
}
__name__ = name;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ if (__bases__ != null) {
+ retVal = visit.visit(__bases__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__dict__ != null) {
+ retVal = visit.visit(__dict__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ //CPython also traverses the name, which is not stored
+ //as a PyObject in Jython.
+ //Py_VISIT(o->cl_name);
+ if (__getattr__ != null) {
+ retVal = visit.visit(__getattr__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__setattr__ != null) {
+ retVal = visit.visit(__setattr__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__delattr__ != null) {
+ retVal = visit.visit(__delattr__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+
+ /* Jython-only */
+ if (__tojava__ != null) {
+ retVal = visit.visit(__tojava__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__del__ != null) {
+ retVal = visit.visit(__del__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return __contains__ != null ? visit.visit(__contains__, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (__dict__ == ob || __bases__ == ob
+ || __getattr__ == ob || __setattr__ == ob || __delattr__ == ob
+ || __tojava__ == ob || __del__ == ob || __contains__ == ob);
+ }
}
diff --git a/src/org/python/core/PyClassMethod.java b/src/org/python/core/PyClassMethod.java
--- a/src/org/python/core/PyClassMethod.java
+++ b/src/org/python/core/PyClassMethod.java
@@ -9,7 +9,7 @@
* The classmethod descriptor.
*/
@ExposedType(name = "classmethod", doc = BuiltinDocs.classmethod_doc)
-public class PyClassMethod extends PyObject {
+public class PyClassMethod extends PyObject implements Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyClassMethod.class);
@@ -50,4 +50,16 @@
}
return new PyMethod(callable, type, type.getType());
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return callable != null ? visit.visit(callable, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == callable;
+ }
}
diff --git a/src/org/python/core/PyClassMethodDerived.java b/src/org/python/core/PyClassMethodDerived.java
--- a/src/org/python/core/PyClassMethodDerived.java
+++ b/src/org/python/core/PyClassMethodDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyClassMethodDerived extends PyClassMethod implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyClassMethodDerived extends PyClassMethod implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i callables = Generic.list();
@@ -31,4 +31,31 @@
public String toString() {
return "";
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue;
+ if (systemState != null) {
+ retValue = visit.visit(systemState, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ if (callables != null) {
+ for (PyObject ob: callables) {
+ retValue = visit.visit(ob, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == systemState || callables.contains(ob));
+ }
}
diff --git a/src/org/python/core/PyDataDescr.java b/src/org/python/core/PyDataDescr.java
--- a/src/org/python/core/PyDataDescr.java
+++ b/src/org/python/core/PyDataDescr.java
@@ -12,6 +12,7 @@
* those methods, their respective implementsDescr* methods should be overriden
* as well.
*/
+ at Untraversable
@ExposedType(name = "getset_descriptor", base = PyObject.class, isBaseType = false)
public abstract class PyDataDescr extends PyDescriptor {
diff --git a/src/org/python/core/PyDescriptor.java b/src/org/python/core/PyDescriptor.java
--- a/src/org/python/core/PyDescriptor.java
+++ b/src/org/python/core/PyDescriptor.java
@@ -1,6 +1,6 @@
package org.python.core;
-public abstract class PyDescriptor extends PyObject {
+public abstract class PyDescriptor extends PyObject implements Traverseproc {
protected PyType dtype;
@@ -22,5 +22,17 @@
String msg = String.format("descriptor '%s' for '%s' objects doesn't apply to '%s' object",
name, dtype.fastGetName(), type.fastGetName());
throw Py.TypeError(msg);
- }
+ }
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return dtype != null ? visit.visit(dtype, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob == dtype;
+ }
}
diff --git a/src/org/python/core/PyDictProxy.java b/src/org/python/core/PyDictProxy.java
--- a/src/org/python/core/PyDictProxy.java
+++ b/src/org/python/core/PyDictProxy.java
@@ -10,7 +10,7 @@
*
*/
@ExposedType(name = "dictproxy", isBaseType = false)
-public class PyDictProxy extends PyObject {
+public class PyDictProxy extends PyObject implements Traverseproc {
/** The dict proxied to. */
PyObject dict;
@@ -145,4 +145,16 @@
public boolean isSequenceType() {
return false;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return dict == null ? 0 : visit.visit(dict, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == dict;
+ }
}
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
@@ -29,7 +29,7 @@
* A builtin python dictionary.
*/
@ExposedType(name = "dict", doc = BuiltinDocs.dict_doc)
-public class PyDictionary extends PyObject implements ConcurrentMap {
+public class PyDictionary extends PyObject implements ConcurrentMap, Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyDictionary.class);
{
@@ -300,38 +300,38 @@
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.dict___lt___doc)
final PyObject dict___lt__(PyObject otherObj) {
- int result = __cmp__(otherObj);
- if (result == -2) {
- return null;
- }
- return result < 0 ? Py.True : Py.False;
+ int result = __cmp__(otherObj);
+ if (result == -2) {
+ return null;
+ }
+ return result < 0 ? Py.True : Py.False;
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.dict___gt___doc)
final PyObject dict___gt__(PyObject otherObj) {
- int result = __cmp__(otherObj);
- if (result == -2) {
- return null;
- }
- return result > 0 ? Py.True : Py.False;
+ int result = __cmp__(otherObj);
+ if (result == -2) {
+ return null;
+ }
+ return result > 0 ? Py.True : Py.False;
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.dict___le___doc)
final PyObject dict___le__(PyObject otherObj) {
- int result = __cmp__(otherObj);
- if (result == -2) {
- return null;
- }
- return result <= 0 ? Py.True : Py.False;
+ int result = __cmp__(otherObj);
+ if (result == -2) {
+ return null;
+ }
+ return result <= 0 ? Py.True : Py.False;
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.dict___ge___doc)
final PyObject dict___ge__(PyObject otherObj) {
- int result = __cmp__(otherObj);
- if (result == -2) {
- return null;
- }
- return result >= 0 ? Py.True : Py.False;
+ int result = __cmp__(otherObj);
+ if (result == -2) {
+ return null;
+ }
+ return result >= 0 ? Py.True : Py.False;
}
@Override
@@ -716,7 +716,7 @@
*/
@ExposedMethod(doc = BuiltinDocs.dict_viewkeys_doc)
public PyObject viewkeys() {
- return new PyDictionaryViewKeys(this);
+ return new PyDictionaryViewKeys(this);
}
/**
@@ -732,7 +732,7 @@
*/
@ExposedMethod(doc = BuiltinDocs.dict_viewvalues_doc)
public PyObject viewvalues() {
- return new PyDictionaryViewValues(this);
+ return new PyDictionaryViewValues(this);
}
@ExposedMethod(doc = BuiltinDocs.dict_itervalues_doc)
@@ -1165,6 +1165,30 @@
return tojava(getMap().replace(Py.java2py(key), Py.java2py(value)));
}
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ for (Map.Entry ent: internalMap.entrySet()) {
+ retVal = visit.visit(ent.getKey(), arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ if (ent.getValue() != null) {
+ retVal = visit.visit(ent.getValue(), arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (internalMap.containsKey(ob) || internalMap.containsValue(ob));
+ }
}
/** Basic implementation of Entry that just holds onto a key and value and returns them. */
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
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyDictionaryDerived extends PyDictionary implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyDictionaryDerived extends PyDictionary implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i... object.
*/
+ at Untraversable
@ExposedType(name = "ellipsis", base = PyObject.class, isBaseType = false)
public class PyEllipsis extends PySingleton implements Serializable {
diff --git a/src/org/python/core/PyEnumerate.java b/src/org/python/core/PyEnumerate.java
--- a/src/org/python/core/PyEnumerate.java
+++ b/src/org/python/core/PyEnumerate.java
@@ -10,6 +10,7 @@
*/
@ExposedType(name = "enumerate", base = PyObject.class, doc = BuiltinDocs.enumerate_doc)
public class PyEnumerate extends PyIterator {
+ //note: Already implements Traverseproc, inheriting it from PyIterator
public static final PyType TYPE = PyType.fromClass(PyEnumerate.class);
@@ -88,4 +89,26 @@
return next;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue = super.traverse(visit, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ if (index != null) {
+ retValue = visit.visit(index, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ return sit == null ? 0 : visit.visit(sit, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == index || ob == sit || super.refersDirectlyTo(ob));
+ }
}
diff --git a/src/org/python/core/PyEnumerateDerived.java b/src/org/python/core/PyEnumerateDerived.java
--- a/src/org/python/core/PyEnumerateDerived.java
+++ b/src/org/python/core/PyEnumerateDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyEnumerateDerived extends PyEnumerate implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyEnumerateDerived extends PyEnumerate implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;itype
* field and value or class instance is stored in the value field.
*/
-public class PyException extends RuntimeException
+public class PyException extends RuntimeException implements Traverseproc
{
/**
@@ -332,4 +332,32 @@
public static String exceptionClassName(PyObject obj) {
return obj instanceof PyClass ? ((PyClass)obj).__name__ : ((PyType)obj).fastGetName();
}
+
+
+ /* Traverseproc support */
+
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue;
+ if (type != null) {
+ retValue = visit.visit(type, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ } if (value != null) {
+ retValue = visit.visit(value, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ } if (traceback != null) {
+ retValue = visit.visit(traceback, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ return 0;
+ }
+
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (type == ob || value == ob || traceback == ob);
+ }
}
diff --git a/src/org/python/core/PyFastSequenceIter.java b/src/org/python/core/PyFastSequenceIter.java
--- a/src/org/python/core/PyFastSequenceIter.java
+++ b/src/org/python/core/PyFastSequenceIter.java
@@ -9,6 +9,7 @@
*/
@ExposedType(name = "fastsequenceiterator", base = PyObject.class, isBaseType = false)
public class PyFastSequenceIter extends PyIterator {
+ //note: Already implements Traverseproc, inheriting it from PyIterator
public static final PyType TYPE = PyType.fromClass(PyFastSequenceIter.class);
@@ -48,4 +49,20 @@
}
return result;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue = super.traverse(visit, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ return seq == null ? 0 : visit.visit(seq, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == seq || super.refersDirectlyTo(ob));
+ }
}
diff --git a/src/org/python/core/PyFile.java b/src/org/python/core/PyFile.java
--- a/src/org/python/core/PyFile.java
+++ b/src/org/python/core/PyFile.java
@@ -35,7 +35,7 @@
* The Python file type. Wraps an {@link TextIOBase} object.
*/
@ExposedType(name = "file", doc = BuiltinDocs.file_doc)
-public class PyFile extends PyObject implements FinalizableBuiltin {
+public class PyFile extends PyObject implements FinalizableBuiltin, Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyFile.class);
@@ -82,26 +82,24 @@
/** The file's closer object; ensures the file is closed at
* shutdown */
private Closer closer;
-
- public FinalizeTrigger finalizeTrigger;
- public PyFile() {finalizeTrigger = FinalizeTrigger.makeTrigger(this);}
+ public PyFile() {FinalizeTrigger.ensureFinalizer(this);}
public PyFile(PyType subType) {
super(subType);
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
public PyFile(RawIOBase raw, String name, String mode, int bufsize) {
parseMode(mode);
file___init__(raw, name, mode, bufsize);
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
public PyFile(InputStream istream, String name, String mode, int bufsize, boolean closefd) {
parseMode(mode);
file___init__(new StreamIO(istream, closefd), name, mode, bufsize);
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
/**
@@ -133,7 +131,7 @@
public PyFile(OutputStream ostream, String name, String mode, int bufsize, boolean closefd) {
parseMode(mode);
file___init__(new StreamIO(ostream, closefd), name, mode, bufsize);
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
/**
@@ -161,7 +159,7 @@
public PyFile(String name, String mode, int bufsize) {
file___init__(new FileIO(name, parseMode(mode)), name, mode, bufsize);
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
@ExposedNew
@@ -733,5 +731,14 @@
}
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return name == null ? 0 : visit.visit(name, arg);
+ }
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == name;
+ }
}
diff --git a/src/org/python/core/PyFileDerived.java b/src/org/python/core/PyFileDerived.java
--- a/src/org/python/core/PyFileDerived.java
+++ b/src/org/python/core/PyFileDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyFileDerived extends PyFile implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyFileDerived extends PyFile implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;if_exc_type);
+// Py_VISIT(f->f_exc_value);
+// Py_VISIT(f->f_exc_traceback);
+
+ /* locals */
+ if (f_fastlocals != null) {
+ for (PyObject ob: f_fastlocals) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+
+ if (f_savedlocals != null) {
+ for (Object ob: f_savedlocals) {
+ if (ob != null) {
+ if (ob instanceof PyObject) {
+ retVal = visit.visit((PyObject) ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ } else if (ob instanceof Traverseproc) {
+ retVal = ((Traverseproc) ob).traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ }
+
+ /* Jython-only miscellaneous */
+ if (f_env != null) {
+ for (PyCell ob: f_env) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+
+ if (generatorInput != null) {
+ if (generatorInput instanceof PyObject) {
+ retVal = visit.visit((PyObject) generatorInput, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ } else if (generatorInput instanceof Traverseproc) {
+ retVal = ((Traverseproc) generatorInput).traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+
+ if (f_exits != null) {
+ for (PyObject ob: f_exits) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+
+// CPython also traverses the stack. This seems to be not necessary
+// in Jython since there is no stack-equivalent in PyFrame:
+//
+// /* stack */
+// if (f->f_stacktop != NULL) {
+// for (p = f->f_valuestack; p < f->f_stacktop; p++)
+// Py_VISIT(*p);
+// }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ } else if (ob == f_back || ob == f_code || ob == f_builtins
+ || ob == f_globals || ob == f_locals || ob == generatorInput) {
+ return true;
+ }
+
+ if (f_fastlocals != null) {
+ for (PyObject obj: f_fastlocals) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ if (f_env != null) {
+ for (PyObject obj: f_env) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ if (f_exits != null) {
+ for (PyObject obj: f_exits) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ if (f_savedlocals != null) {
+ for (Object obj: f_savedlocals) {
+ if (obj == ob) {
+ return true;
+ } else if (obj != null && obj instanceof Traverseproc
+ &&((Traverseproc) obj).refersDirectlyTo(ob)) {
+ return true;
+ }
+ }
+ }
+ if (tracefunc != null && tracefunc instanceof Traverseproc
+ &&((Traverseproc) tracefunc).refersDirectlyTo(ob)) {
+ return true;
+ }
+ return generatorInput instanceof Traverseproc ?
+ ((Traverseproc) generatorInput).refersDirectlyTo(ob) : false;
+ }
}
diff --git a/src/org/python/core/PyFrozenSetDerived.java b/src/org/python/core/PyFrozenSetDerived.java
--- a/src/org/python/core/PyFrozenSetDerived.java
+++ b/src/org/python/core/PyFrozenSetDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyFrozenSetDerived extends PyFrozenSet implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyFrozenSetDerived extends PyFrozenSet implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;ifunc_name);
+
+ if (__dict__ != null) {
+ retVal = visit.visit(__dict__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return __closure__ != null ? visit.visit(__closure__, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null) {
+ return false;
+ }
+ if (__defaults__ != null) {
+ for (PyObject obj: __defaults__) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ return ob == __doc__ || ob == __globals__ || ob == __code__
+ || ob == __dict__ || ob == __closure__ || ob == __module__;
+ }
}
diff --git a/src/org/python/core/PyGenerator.java b/src/org/python/core/PyGenerator.java
--- a/src/org/python/core/PyGenerator.java
+++ b/src/org/python/core/PyGenerator.java
@@ -22,8 +22,6 @@
protected boolean gi_running;
private PyObject closure;
-
- public FinalizeTrigger finalizeTrigger;
public PyGenerator(PyFrame frame, PyObject closure) {
super(TYPE);
@@ -32,7 +30,7 @@
gi_code = gi_frame.f_code;
}
this.closure = closure;
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
public PyObject send(PyObject value) {
@@ -173,4 +171,33 @@
}
return result;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retValue = super.traverse(visit, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ if (gi_frame != null) {
+ retValue = visit.visit(gi_frame, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ if (gi_code != null) {
+ retValue = visit.visit(gi_code, arg);
+ if (retValue != 0) {
+ return retValue;
+ }
+ }
+ return closure == null ? 0 : visit.visit(closure, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == gi_frame || ob == gi_code
+ || ob == closure || super.refersDirectlyTo(ob));
+ }
}
diff --git a/src/org/python/core/PyInstance.java b/src/org/python/core/PyInstance.java
--- a/src/org/python/core/PyInstance.java
+++ b/src/org/python/core/PyInstance.java
@@ -13,11 +13,10 @@
* An instance of a classic Python class.
*/
@ExposedType(name = "instance", isBaseType = false)
-public class PyInstance extends PyObject implements FinalizablePyObject {
+public class PyInstance extends PyObject implements FinalizablePyObject, Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyInstance.class);
- public FinalizeTrigger finalizeTrigger;
private static JavaFunc __ensure_finalizer__Function;
// xxx doc, final name
@@ -81,7 +80,6 @@
private void writeObject(java.io.ObjectOutputStream out)
throws java.io.IOException {
- //System.out.println("writing: "+getClass().getName());
out.defaultWriteObject();
PyObject name = instclass.__findattr__("__module__");
if (!(name instanceof PyString) || name == Py.None) {
@@ -154,8 +152,7 @@
}
public static void ensureFinalizer(PyObject[] args, String[] kws) {
- ((PyInstance) args[0]).finalizeTrigger = FinalizeTrigger.makeTrigger(
- (PyInstance) args[0]);
+ FinalizeTrigger.ensureFinalizer((PyInstance) args[0]);
}
private static JavaFunc makeFunction__ensure_finalizer__() {
@@ -171,9 +168,9 @@
if (name == "__class__") return instclass;
if (name == "__ensure_finalizer__") {
if (__ensure_finalizer__Function == null) {
- __ensure_finalizer__Function = makeFunction__ensure_finalizer__();
+ __ensure_finalizer__Function = makeFunction__ensure_finalizer__();
}
- return new PyMethod(__ensure_finalizer__Function, this, instclass);
+ return new PyMethod(__ensure_finalizer__Function, this, instclass);
}
if (__dict__ == null) return null;
@@ -288,8 +285,9 @@
if (name == "__class__") {
if (value instanceof PyClass) {
instclass = (PyClass) value;
- if (instclass.__del__ != null && finalizeTrigger == null) {
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ if (instclass.__del__ != null &&
+ !JyAttribute.hasAttr(this, JyAttribute.FINALIZE_TRIGGER_ATTR)) {
+ FinalizeTrigger.ensureFinalizer(this);
}
} else {
throw Py.TypeError("__class__ must be set to a class");
@@ -306,8 +304,8 @@
} else {
__dict__.__setitem__(name, value);
}
- if (name == "__del__" && finalizeTrigger == null) {
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ if (name == "__del__" && !JyAttribute.hasAttr(this, JyAttribute.FINALIZE_TRIGGER_ATTR)) {
+ FinalizeTrigger.ensureFinalizer(this);
}
}
@@ -1942,7 +1940,7 @@
@Override
public void __del__() {
- try {
+ try {
PyObject method = __findattr__("__del__");
if (method != null) {
method.__call__();
@@ -1960,4 +1958,26 @@
Py.writeUnraisable(exc, method);
}
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+// Potential PyObject refs in PyInstance:
+// public transient PyClass instclass;
+// public PyObject __dict__;
+ if (instclass != null) {
+ int retVal = visit.visit(instclass, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ //__dict__ cannot be null
+ return visit.visit(__dict__, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == instclass || ob == __dict__);
+ }
}
diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java
--- a/src/org/python/core/PyInteger.java
+++ b/src/org/python/core/PyInteger.java
@@ -19,6 +19,7 @@
/**
* A builtin python int.
*/
+ at Untraversable
@ExposedType(name = "int", doc = BuiltinDocs.int_doc)
public class PyInteger extends PyObject {
diff --git a/src/org/python/core/PyIntegerDerived.java b/src/org/python/core/PyIntegerDerived.java
--- a/src/org/python/core/PyIntegerDerived.java
+++ b/src/org/python/core/PyIntegerDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyIntegerDerived extends PyInteger implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyIntegerDerived extends PyInteger implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i {
+public abstract class PyIterator extends PyObject implements Iterable, Traverseproc {
protected PyException stopException;
@@ -75,7 +75,7 @@
return iterator();
}
if (c.isAssignableFrom(Collection.class)) {
- List list = new ArrayList();
+ List list = new ArrayList<>();
for (Object obj : this) {
list.add(obj);
}
@@ -83,4 +83,16 @@
}
return super.__tojava__(c);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return stopException != null ? stopException.traverse(visit, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && stopException != null && stopException.refersDirectlyTo(ob);
+ }
}
diff --git a/src/org/python/core/PyJavaPackage.java b/src/org/python/core/PyJavaPackage.java
--- a/src/org/python/core/PyJavaPackage.java
+++ b/src/org/python/core/PyJavaPackage.java
@@ -4,25 +4,22 @@
package org.python.core;
import org.python.core.packagecache.PackageManager;
-
import java.util.StringTokenizer;
/**
* A representation of java package.
*/
-
-public class PyJavaPackage extends PyObject {
+public class PyJavaPackage extends PyObject implements Traverseproc {
public String __name__;
public PyStringMap __dict__;
- //public String _unparsedAll;
+
/** Its keys are the names of statically known classes.
* E.g. from jars pre-scan.
*/
public PyStringMap clsSet;
public String __file__;
- //public PyList __all__;
/** (Control) package manager whose hierarchy contains this java pkg.
*/
@@ -134,7 +131,7 @@
return addPackage(name);
}
- Class c = __mgr__.findClass(__name__,name);
+ Class> c = __mgr__.findClass(__name__,name);
if (c != null) return addClass(name,c);
if (name == "__name__") return new PyString(__name__);
@@ -170,4 +167,28 @@
public String toString() {
return "";
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ //__dict__ cannot be null
+ int retVal = visit.visit(__dict__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+
+ //clsSet cannot be null
+ retVal = visit.visit(clsSet, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ //__mgr__ and __mgr__.topLevelPackage cannot be null
+ return visit.visit(__mgr__.topLevelPackage, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == __dict__ || ob == clsSet || ob == __mgr__.topLevelPackage);
+ }
}
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
@@ -84,7 +84,7 @@
public static PyObject wrapJavaObject(Object o) {
PyObject obj = new PyObjectDerived(PyType.fromClass(o.getClass(), false));
- obj.javaProxy = o;
+ JyAttribute.setAttr(obj, JyAttribute.JAVA_PROXY_ATTR, o);
return obj;
}
@@ -233,7 +233,7 @@
computeLinearMro(baseClass);
} else {
needsInners.add(this);
- javaProxy = forClass;
+ JyAttribute.setAttr(this, JyAttribute.JAVA_PROXY_ATTR, forClass);
objtype = PyType.fromClassSkippingInners(Class.class, needsInners);
// Wrapped Java types fill in their mro first using all of their interfaces then their
// super class.
@@ -253,11 +253,11 @@
}
visibleBases.add(PyType.fromClassSkippingInners(iface, needsInners));
}
- if (javaProxy == Object.class) {
+ if (JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) == Object.class) {
base = PyType.fromClassSkippingInners(PyObject.class, needsInners);
} else if(baseClass == null) {
base = PyType.fromClassSkippingInners(Object.class, needsInners);
- }else if (javaProxy == Class.class) {
+ }else if (JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) == Class.class) {
base = PyType.fromClassSkippingInners(PyType.class, needsInners);
} else {
base = PyType.fromClassSkippingInners(baseClass, needsInners);
@@ -893,6 +893,9 @@
protected abstract boolean getResult(int comparison);
}
+ // Traverseproc-note: Usually we would have to traverse this class, but we can
+ // leave this out, since CollectionProxies is only used locally in private
+ // static fields.
private static class CollectionProxies {
final Map, PyBuiltinMethod[]> proxies;
final Map, PyBuiltinMethod[]> postProxies;
@@ -923,7 +926,7 @@
* injected methods.
*/
private static Map, PyBuiltinMethod[]> buildCollectionProxies() {
- final Map, PyBuiltinMethod[]> proxies = new HashMap();
+ final Map, PyBuiltinMethod[]> proxies = new HashMap<>();
PyBuiltinMethodNarrow iterableProxy = new PyBuiltinMethodNarrow("__iter__") {
@Override
@@ -983,10 +986,46 @@
}
private static Map, PyBuiltinMethod[]> buildPostCollectionProxies() {
- final Map, PyBuiltinMethod[]> postProxies = new HashMap();
+ final Map, PyBuiltinMethod[]> postProxies = new HashMap<>();
postProxies.put(List.class, JavaProxyList.getPostProxyMethods());
postProxies.put(Map.class, JavaProxyMap.getPostProxyMethods());
postProxies.put(Set.class, JavaProxySet.getPostProxyMethods());
return Collections.unmodifiableMap(postProxies);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ if (conflicted != null) {
+ for (PyObject ob: conflicted) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ }
+ if (conflicted != null) {
+ for (PyObject obj: conflicted) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ return super.refersDirectlyTo(ob);
+ }
}
diff --git a/src/org/python/core/PyList.java b/src/org/python/core/PyList.java
--- a/src/org/python/core/PyList.java
+++ b/src/org/python/core/PyList.java
@@ -1224,4 +1224,27 @@
public synchronized boolean remove(Object o) {
return list.remove(Py.java2py(o));
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (list != null) {
+ int retVal;
+ for (PyObject ob: list) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return list == null ? false : list.contains(ob);
+ }
}
diff --git a/src/org/python/core/PyListDerived.java b/src/org/python/core/PyListDerived.java
--- a/src/org/python/core/PyListDerived.java
+++ b/src/org/python/core/PyListDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyListDerived extends PyList implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyListDerived extends PyList implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i, Class>> primitiveMap = Generic.map();
@@ -58,6 +74,8 @@
public PyObject(PyType objtype) {
this.objtype = objtype;
+ if (gcMonitorGlobal)
+ gc.monitorObject(this);
}
/**
@@ -66,6 +84,8 @@
**/
public PyObject() {
objtype = PyType.fromClass(getClass(), false);
+ if (gcMonitorGlobal)
+ gc.monitorObject(this);
}
/**
@@ -74,6 +94,8 @@
*/
PyObject(boolean ignored) {
objtype = (PyType)this;
+ if (gcMonitorGlobal)
+ gc.monitorObject(this);
}
@ExposedNew
@@ -156,6 +178,7 @@
*/
void proxyInit() {
Class> c = getType().getProxyType();
+ Object javaProxy = JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR);
if (javaProxy != null || c == null) {
return;
}
@@ -183,6 +206,7 @@
} finally {
ThreadContext.initializingProxy.set(previous);
}
+ javaProxy = JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR);
if (javaProxy != null && javaProxy != proxy) {
throw Py.TypeError("Proxy instance already initialized");
}
@@ -190,7 +214,7 @@
if (proxyInstance != null && proxyInstance != this) {
throw Py.TypeError("Proxy initialized with another instance");
}
- javaProxy = proxy;
+ JyAttribute.setAttr(this, JyAttribute.JAVA_PROXY_ATTR, proxy);
}
/**
@@ -306,7 +330,7 @@
**/
public Object __tojava__(Class> c) {
if ((c == Object.class || c == Serializable.class) && getJavaProxy() != null) {
- return javaProxy;
+ return JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR);
}
if (c.isInstance(this)) {
return this;
@@ -318,7 +342,7 @@
}
}
if (c.isInstance(getJavaProxy())) {
- return javaProxy;
+ return JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR);
}
// convert faux floats
@@ -338,10 +362,10 @@
}
protected synchronized Object getJavaProxy() {
- if (javaProxy == null) {
+ if (!JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR)) {
proxyInit();
}
- return javaProxy;
+ return JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR);
}
/**
@@ -1684,7 +1708,10 @@
public PyObject _is(PyObject o) {
// Access javaProxy directly here as is is for object identity, and at best getJavaProxy
// will initialize a new object with a different identity
- return this == o || (javaProxy != null && javaProxy == o.javaProxy) ? Py.True : Py.False;
+ //return this == o || (javaProxy != null && javaProxy == o.javaProxy) ? Py.True : Py.False;
+ return this == o || (JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) &&
+ JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) ==
+ JyAttribute.getAttr(o, JyAttribute.JAVA_PROXY_ATTR)) ? Py.True : Py.False;
}
/**
@@ -1696,7 +1723,10 @@
public PyObject _isnot(PyObject o) {
// Access javaProxy directly here as is is for object identity, and at best getJavaProxy
// will initialize a new object with a different identity
- return this != o && (javaProxy == null || javaProxy != o.javaProxy) ? Py.True : Py.False;
+ //return this != o && (javaProxy == null || javaProxy != o.javaProxy) ? Py.True : Py.False;
+ return this != o && (!JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) ||
+ JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) !=
+ JyAttribute.getAttr(o, JyAttribute.JAVA_PROXY_ATTR)) ? Py.True : Py.False;
}
/**
@@ -4205,7 +4235,7 @@
* by hashing and comparing its elements by identity.
*/
-class PyIdentityTuple extends PyObject {
+class PyIdentityTuple extends PyObject implements Traverseproc {
PyObject[] list;
@@ -4241,4 +4271,34 @@
return true;
}
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (list != null) {
+ int retVal;
+ for (PyObject ob: list) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null || list == null) {
+ return false;
+ }
+ for (PyObject obj: list) {
+ if (ob == obj) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/org/python/core/PyObjectDerived.java b/src/org/python/core/PyObjectDerived.java
--- a/src/org/python/core/PyObjectDerived.java
+++ b/src/org/python/core/PyObjectDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyObjectDerived extends PyObject implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyObjectDerived extends PyObject implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i
* An instance of the proxy class. The _getPyInstance() will return a reference to the
* PyInstance.
- * An instance of PyInstance. The PyInstance.javaProxy contains a reference to the proxy class
- * instance.
+ * An instance of PyInstance. The PyInstance's java proxy attribute contains a reference to the
+ * proxy class instance.
*
*
* All proxy classes implement this interface.
diff --git a/src/org/python/core/PyReflectedConstructor.java b/src/org/python/core/PyReflectedConstructor.java
--- a/src/org/python/core/PyReflectedConstructor.java
+++ b/src/org/python/core/PyReflectedConstructor.java
@@ -6,6 +6,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.InstantiationException;
+ at Untraversable
public class PyReflectedConstructor extends PyReflectedFunction {
public PyReflectedConstructor(String name) {
@@ -106,7 +107,7 @@
} else if (Modifier.isAbstract(mods)) {
throw Py.TypeError("can't instantiate abstract class (" + declaringClass.getName() + ")");
}
- if (self.javaProxy != null) {
+ if (JyAttribute.hasAttr(self, JyAttribute.JAVA_PROXY_ATTR)) {
Class> sup = javaClass;
if (PyProxy.class.isAssignableFrom(sup)) {
sup = sup.getSuperclass();
@@ -224,7 +225,7 @@
} finally {
ThreadContext.initializingProxy.set(previous);
}
- obj.javaProxy = jself;
+ JyAttribute.setAttr(obj, JyAttribute.JAVA_PROXY_ATTR, jself);
}
@Override
diff --git a/src/org/python/core/PyReflectedField.java b/src/org/python/core/PyReflectedField.java
--- a/src/org/python/core/PyReflectedField.java
+++ b/src/org/python/core/PyReflectedField.java
@@ -7,6 +7,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+ at Untraversable
public class PyReflectedField extends PyObject {
public Field field;
diff --git a/src/org/python/core/PyReflectedFunction.java b/src/org/python/core/PyReflectedFunction.java
--- a/src/org/python/core/PyReflectedFunction.java
+++ b/src/org/python/core/PyReflectedFunction.java
@@ -7,7 +7,7 @@
import org.python.util.Generic;
-public class PyReflectedFunction extends PyObject {
+public class PyReflectedFunction extends PyObject implements Traverseproc {
public String __name__;
@@ -335,4 +335,16 @@
public String toString() {
return "";
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return __doc__ != null ? visit.visit(__doc__, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == __doc__;
+ }
}
diff --git a/src/org/python/core/PyReversedIterator.java b/src/org/python/core/PyReversedIterator.java
--- a/src/org/python/core/PyReversedIterator.java
+++ b/src/org/python/core/PyReversedIterator.java
@@ -31,4 +31,20 @@
private PyObject seq;
private int idx;
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ return seq == null ? 0 : visit.visit(seq, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == seq || super.refersDirectlyTo(ob));
+ }
}
diff --git a/src/org/python/core/PySequenceIter.java b/src/org/python/core/PySequenceIter.java
--- a/src/org/python/core/PySequenceIter.java
+++ b/src/org/python/core/PySequenceIter.java
@@ -34,4 +34,20 @@
}
return result;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ return seq == null ? 0 : visit.visit(seq, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == seq || super.refersDirectlyTo(ob));
+ }
}
diff --git a/src/org/python/core/PySequenceList.java b/src/org/python/core/PySequenceList.java
--- a/src/org/python/core/PySequenceList.java
+++ b/src/org/python/core/PySequenceList.java
@@ -5,7 +5,7 @@
import java.util.List;
import java.util.ListIterator;
-public abstract class PySequenceList extends PySequence {
+public abstract class PySequenceList extends PySequence implements Traverseproc {
protected PySequenceList(PyType type) {
super(type);
@@ -79,4 +79,22 @@
public abstract String toString();
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ for (int i = 0; i < size(); ++i) {
+ retVal = visit.visit(pyget(i), arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
+ }
}
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
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PySetDerived extends PySet implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PySetDerived extends PySet implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i ent: table.entrySet()) {
+ key = ent.getKey();
+ value = ent.getValue();
+ if (key instanceof PyObject) {
+ retVal = visit.visit((PyObject) key, arg);
+ if (retVal != 0) return retVal;
+ }
+ if (value != null) {
+ retVal = visit.visit(value, arg);
+ if (retVal != 0) return retVal;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (table.containsKey(ob) || table.containsValue(ob));
+ }
}
diff --git a/src/org/python/core/PySuper.java b/src/org/python/core/PySuper.java
--- a/src/org/python/core/PySuper.java
+++ b/src/org/python/core/PySuper.java
@@ -10,7 +10,7 @@
* The Python super type.
*/
@ExposedType(name = "super", doc = BuiltinDocs.super_doc)
-public class PySuper extends PyObject {
+public class PySuper extends PyObject implements Traverseproc {
public static final PyType TYPE = PyType.fromClass(PySuper.class);
@@ -163,4 +163,29 @@
public PyType getObjType() {
return objType;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ if (superType != null) {
+ retVal = visit.visit(superType, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (obj != null) {
+ retVal = visit.visit(obj, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return objType == null ? 0 : visit.visit(objType, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == superType || ob == obj || ob == objType);
+ }
}
diff --git a/src/org/python/core/PySuperDerived.java b/src/org/python/core/PySuperDerived.java
--- a/src/org/python/core/PySuperDerived.java
+++ b/src/org/python/core/PySuperDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PySuperDerived extends PySuper implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PySuperDerived extends PySuper implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i>> ");
+// public PyObject ps2 = new PyString("... ");
+// public PyObject executable;
+// public PyObject stdout, stderr, stdin;
+// public PyObject __stdout__, __stderr__, __stdin__;
+// public PyObject __displayhook__, __excepthook__;
+// public PyObject last_value = Py.None;
+// public PyObject last_type = Py.None;
+// public PyObject last_traceback = Py.None;
+// public PyObject __name__ = new PyString("sys");
+// public PyObject __dict__;
+ int retVal;
+ if (argv != null) {
+ retVal = visit.visit(argv, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (modules != null) {
+ retVal = visit.visit(modules, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (path != null) {
+ retVal = visit.visit(path, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (warnoptions != null) {
+ retVal = visit.visit(warnoptions, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (builtins != null) {
+ retVal = visit.visit(builtins, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (platform != null) {
+ retVal = visit.visit(platform, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (meta_path != null) {
+ retVal = visit.visit(meta_path, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (path_hooks != null) {
+ retVal = visit.visit(path_hooks, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (path_importer_cache != null) {
+ retVal = visit.visit(path_importer_cache, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (ps1 != null) {
+ retVal = visit.visit(ps1, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (ps2 != null) {
+ retVal = visit.visit(ps2, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (executable != null) {
+ retVal = visit.visit(executable, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (stdout != null) {
+ retVal = visit.visit(stdout, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (stderr != null) {
+ retVal = visit.visit(stderr, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (stdin != null) {
+ retVal = visit.visit(stdin, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__stdout__ != null) {
+ retVal = visit.visit(__stdout__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__stderr__ != null) {
+ retVal = visit.visit(__stderr__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__stdin__ != null) {
+ retVal = visit.visit(__stdin__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__displayhook__ != null) {
+ retVal = visit.visit(__displayhook__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__excepthook__ != null) {
+ retVal = visit.visit(__excepthook__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (last_value != null) {
+ retVal = visit.visit(last_value, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (last_type != null) {
+ retVal = visit.visit(last_type, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (last_traceback != null) {
+ retVal = visit.visit(last_traceback, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (__name__ != null) {
+ retVal = visit.visit(__name__, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return __dict__ == null ? 0 : visit.visit(__dict__, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == argv || ob == modules || ob == path
+ || ob == warnoptions || ob == builtins || ob == platform
+ || ob == meta_path || ob == path_hooks || ob == path_importer_cache
+ || ob == ps1 || ob == ps2 || ob == executable || ob == stdout
+ || ob == stderr || ob == stdin || ob == __stdout__ || ob == __stderr__
+ || ob == __stdin__ || ob == __displayhook__ || ob == __excepthook__
+ || ob == last_value || ob == last_type || ob == last_traceback
+ || ob ==__name__ || ob == __dict__);
+ }
+
+
/**
* Helper abstracting common code from {@link ShutdownCloser#run()} and
* {@link PySystemStateCloser#cleanup()} to close resources (such as still-open files). The
@@ -1654,10 +1841,10 @@
*
* @param resourceClosers to be called in turn
*/
-
}
+ at Untraversable
class PySystemStateFunctions extends PyBuiltinFunctionSet {
PySystemStateFunctions(String name, int index, int minargs, int maxargs) {
@@ -1692,6 +1879,7 @@
* Value of a class or instance variable when the corresponding attribute is deleted. Used only in
* PySystemState for now.
*/
+ at Untraversable
class PyAttributeDeleted extends PyObject {
final static PyAttributeDeleted INSTANCE = new PyAttributeDeleted();
@@ -1760,6 +1948,81 @@
Py.newLong(1) // FLT_ROUNDS
);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (max != null) {
+ retVal = visit.visit(max, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (max_exp != null) {
+ retVal = visit.visit(max_exp, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (max_10_exp != null) {
+ retVal = visit.visit(max_10_exp, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (min != null) {
+ retVal = visit.visit(min, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (min_exp != null) {
+ retVal = visit.visit(min_exp, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (min_10_exp != null) {
+ retVal = visit.visit(min_10_exp, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (dig != null) {
+ retVal = visit.visit(dig, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (mant_dig != null) {
+ retVal = visit.visit(mant_dig, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (epsilon != null) {
+ retVal = visit.visit(epsilon, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (radix != null) {
+ retVal = visit.visit(radix, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return rounds == null ? 0 : visit.visit(rounds, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == max || ob == max_exp || ob == max_10_exp || ob == min
+ || ob == min_exp || ob == min_10_exp || ob == dig
+ || ob == mant_dig || ob == epsilon || ob == radix || ob == rounds);
+ }
}
@@ -1783,4 +2046,23 @@
static public LongInfo getInfo() {
return new LongInfo(Py.newLong(30), Py.newLong(4));
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (bits_per_digit != null) {
+ retVal = visit.visit(bits_per_digit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return sizeof_digit == null ? 0 : visit.visit(sizeof_digit, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == bits_per_digit || ob == sizeof_digit);
+ }
}
diff --git a/src/org/python/core/PyTableCode.java b/src/org/python/core/PyTableCode.java
--- a/src/org/python/core/PyTableCode.java
+++ b/src/org/python/core/PyTableCode.java
@@ -8,6 +8,7 @@
import org.python.modules._systemrestart;
+ at Untraversable
public class PyTableCode extends PyBaseCode
{
diff --git a/src/org/python/core/PyTraceback.java b/src/org/python/core/PyTraceback.java
--- a/src/org/python/core/PyTraceback.java
+++ b/src/org/python/core/PyTraceback.java
@@ -9,7 +9,7 @@
* A python traceback object.
*/
@ExposedType(name = "traceback", isBaseType = false)
-public class PyTraceback extends PyObject {
+public class PyTraceback extends PyObject implements Traverseproc {
public static final PyType TYPE = PyType.fromClass(PyTraceback.class);
@@ -120,4 +120,23 @@
dumpStack(buf);
return buf.toString();
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (tb_next != null) {
+ int retVal = visit.visit(tb_next, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ //__dict__ cannot be null
+ return tb_frame == null ? 0 : visit.visit(tb_frame, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == tb_next || ob == tb_frame);
+ }
}
diff --git a/src/org/python/core/PyTuple.java b/src/org/python/core/PyTuple.java
--- a/src/org/python/core/PyTuple.java
+++ b/src/org/python/core/PyTuple.java
@@ -606,4 +606,50 @@
}
return converted;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ for (PyObject ob: array) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ if (cachedList != null) {
+ for (PyObject ob: cachedList) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null) {
+ return false;
+ }
+ for (PyObject obj: array) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ if (cachedList != null) {
+ for (PyObject obj: cachedList) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/org/python/core/PyTupleDerived.java b/src/org/python/core/PyTupleDerived.java
--- a/src/org/python/core/PyTupleDerived.java
+++ b/src/org/python/core/PyTupleDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyTupleDerived extends PyTuple implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyTupleDerived extends PyTuple implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,9 +26,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i proxyClass = MakeProxies.makeProxy(baseProxyClass, interfaces, name, proxyName,
dict);
- javaProxy = proxyClass;
+ JyAttribute.setAttr(this, JyAttribute.JAVA_PROXY_ATTR, proxyClass);
PyType proxyType = PyType.fromClass(proxyClass, false);
List cleanedBases = Generic.list();
@@ -932,7 +932,7 @@
* Returns the Java Class that this type inherits from, or null if this type is Python-only.
*/
public Class> getProxyType() {
- return (Class>)javaProxy;
+ return (Class>) JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR);
}
private synchronized void attachSubclass(PyType subtype) {
@@ -1022,7 +1022,8 @@
PyObject[] computeMro(MROMergeState[] toMerge, List mro) {
boolean addedProxy = false;
- PyType proxyAsType = javaProxy == null ? null : PyType.fromClass(((Class>)javaProxy), false);
+ PyType proxyAsType = !JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) ?
+ null : PyType.fromClass(((Class>)JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR)), false);
scan : for (int i = 0; i < toMerge.length; i++) {
if (toMerge[i].isMerged()) {
continue scan;
@@ -1035,9 +1036,11 @@
}
}
if (!addedProxy && !(this instanceof PyJavaType) && candidate instanceof PyJavaType
- && candidate.javaProxy != null
- && PyProxy.class.isAssignableFrom(((Class>)candidate.javaProxy))
- && candidate.javaProxy != javaProxy) {
+ && JyAttribute.hasAttr(candidate, JyAttribute.JAVA_PROXY_ATTR)
+ && PyProxy.class.isAssignableFrom(
+ ((Class>)JyAttribute.getAttr(candidate, JyAttribute.JAVA_PROXY_ATTR)))
+ && JyAttribute.getAttr(candidate, JyAttribute.JAVA_PROXY_ATTR) !=
+ JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR)) {
// If this is a subclass of a Python class that subclasses a Java class, slip the
// proxy for this class in before the proxy class in the superclass' mro.
// This exposes the methods from the proxy generated for this class in addition to
@@ -1235,6 +1238,7 @@
* @return found object or null
*/
public PyObject lookup_where(String name, PyObject[] where) {
+ if (methodCache == null) System.out.println("method cache is null");
return methodCache.lookup_where(this, name, where);
}
@@ -2094,4 +2098,65 @@
}
}
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ if (base != null) {
+ retVal = visit.visit(base, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ //bases cannot be null
+ for (PyObject ob: bases) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ if (dict != null) {
+ retVal = visit.visit(dict, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (mro != null) {
+ for (PyObject ob: mro) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ //don't traverse subclasses since they are weak refs.
+ //ReferenceQueue subclasses_refq = new ReferenceQueue();
+ //Set> subclasses = Generic.set();
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ }
+ //bases cannot be null
+ for (PyObject obj: bases) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ if (mro != null) {
+ for (PyObject obj: mro) {
+ if (obj == ob) {
+ return true;
+ }
+ }
+ }
+ return ob == base || ob == dict;
+ }
}
diff --git a/src/org/python/core/PyTypeDerived.java b/src/org/python/core/PyTypeDerived.java
--- a/src/org/python/core/PyTypeDerived.java
+++ b/src/org/python/core/PyTypeDerived.java
@@ -5,9 +5,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyTypeDerived extends PyType implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyTypeDerived extends PyType implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -28,17 +26,35 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i
+ * This interface defines a CPython equivalent traverse mechanism allowing
+ * to detect reference cycles. While this is crucial for cyclic gc support
+ * in CPython, it only serves debugging purposes in Jython. As a side effect
+ * it allows a more complete implementation of the gc module.
+ * |
+ *
+ * Note that implementing this interface is only OPTIONAL. Gc will work fine
+ * in Jython without it. Still we took care to have all core classes implement
+ * it and recommend third party extension providers to do so as well with
+ * custom PyObject-implementations.
+ *
+ *
+ * Of course this interface shall only be implemented by PyObjects that
+ * potentially own direct references to other PyObjects. Note that indirect
+ * references via non-PyObjects should also be treated as "direct" (c.f.
+ * tracefunc in {@link org.python.core.PyFrame}).
+ * PyObjects that don't own references to other PyObjects under any condition
+ * and neither inherit such references from a superclass are strictly recommended
+ * to be annotated {@code {@literal @}Untraversable}
+ * (see {@link org.python.core.Untraversable} for details).
+ *
+ *
+ * Jython's traverse mechanism serves debugging purposes to ease finding memory
+ * leaks and compare gc behavior with CPython. While it is of course not required
+ * that gc behaviors of Jython and CPython equal, there might arise subtle bugs
+ * from these different behaviors. Jython's traverse mechanism is intended to
+ * allow finding such bugs by comparing gc behavior to CPython and isolating
+ * the Python code that is not robust enough to work invariant under different
+ * gc behaviors.
+ *
+ *
+ * Further this mechanism is crucial for some aspects of gc-support of the JyNI
+ * project. JyNI does not strictly depend on it to emulate CPython's gc
+ * for extensions, but would have to perform inefficient reflection-based
+ * traversal in some edge-cases (which might also conflict security managers).
+ *
+ *
+ * Note that the slots-array and - if existent - the user-dict of fooDerived classes
+ * is traversed by {@link org.python.core.TraverseProcDerived}.
+ * The gc module takes care of exploiting both traverse methods in its static traverse
+ * method. So for manual traversion one should always use {@code gc.traverse} rather
+ * than directly calling methods in this interface.
+ *
+ *
+ * Also note that {@code objtype} is not subject to {@code Traverseproc}s
+ * by default. In CPython only objects with heap-types traverse their
+ * ob_type field. In Jython, {@code fooDerived}-classes are the
+ * equivalents of heapTypes. For such classes, objtype is actually
+ * traversed (along with the user dict).
+ *
+ *
+ * Note for implementing:
+ * Every non-static strong referenced PyObject should be passed to the
+ * {@link org.python.core.Visitproc}. If {@code Object}s or {@code interface}-types are
+ * referenced where it is not known, whether it is a {@code PyObject} or
+ * references other {@code PyObjects}, one should check for {@code PyObject}
+ * via {@code instanceof} and otherwise also check whether it at least
+ * implements {@code Traverseproc} itself. In latter case one should traverse
+ * it by delegating to its {@code Traverseproc} methods.
+ * Warning:
+ * If one lets non-{@code PyObject}s implement {@code Traverseproc}, extreme
+ * care must be taken, whether the traverse call shall be passed on to other
+ * non-{@code PyObject} {@code Traverseproc}-implementers, as this can cause
+ * infinite traverse cycles.
+ * Examples for non-{@code PyObject}s that implement {@code Traverseproc} are
+ * {@link org.python.core.PyException} and {@link com.ziclix.python.sql.Fetch}.
+ *
+ *
+ * It follows a list of PyObject subclasses in Jython, excluding derived classes.
+ * PyObject subclasses in Jython checked for need of Traverseproc:
+ *
+ *
+ * org.python.core:
+ * __builtin__:
+ * BuiltinFunctions - no refs, untraversable
+ * ImportFunction - no refs, untraversable
+ * SortedFunction - no refs, untraversable
+ * AllFunction - no refs, untraversable
+ * AnyFunction - no refs, untraversable
+ * FormatFunction - no refs, untraversable
+ * PrintFunction - no refs, untraversable
+ * MaxFunction - no refs, untraversable
+ * MinFunction - no refs, untraversable
+ * RoundFunction - no refs, untraversable
+ * CompileFunction - no refs, untraversable
+ * OpenFunction - no refs, untraversable
+ * NextFunction - no refs, untraversable
+ * BinFunction - no refs, untraversable
+ * AstList - Traverseproc
+ * BaseBytes - no refs, untraversable
+ * IndexDelegate - no PyObject
+ * BaseDictionaryView - Traverseproc
+ * BaseSet - Traverseproc
+ * ClasspathPyImporter - no refs, untraversable
+ * ContextGuard: - no PyObject
+ * ContextCode - Traverseproc
+ * GeneratorContextManager - Traverseproc
+ * exceptions - no refs, untraversable
+ * BoundStaticJavaMethod - no refs, untraversable
+ * JavaImporter - no refs, untraversable
+ * JavaProxyList:
+ * ListMethod - no refs, untraversable (extends PyBuiltinMethodNarrow)
+ * ListMulProxyClass - no refs, untraversable
+ * JavaProxyMap:
+ * MapMethod - no refs, untraversable (extends PyBuiltinMethodNarrow)
+ * MapClassMethod - no refs, untraversable (extends PyBuiltinClassMethodNarrow)
+ * JavaProxySet:
+ * SetMethod - no refs, untraversable (extends PyBuiltinMethodNarrow)
+ * SetMethodVarargs - no refs, untraversable (extends SetMethod)
+ * CopyMethod - no refs, untraversable
+ * IsSubsetMethod - no refs, untraversable
+ * IsSupersetMethod - no refs, untraversable
+ * Py:
+ * JavaCode - Traverseproc
+ * JavaFunc - no refs, untraversable
+ * Py2kBuffer - no refs, untraversable
+ * PyArray - Traverseproc, traverses via reflection
+ * PyBaseCode - no refs, abstract class
+ * PyBaseException - Traverseproc
+ * PyBaseString - no refs, abstract class
+ * PyBeanEvent - no refs, untraversable
+ * PyBeanEventProperty - no refs, untraversable
+ * PyBeanProperty - no refs, untraversable
+ * PyBoolean - no refs, untraversable
+ * PyBuffer - no PyObject
+ * PyBuiltinCallable - no refs, untraversable
+ * PyBuiltinClassMethodNarrow - no refs, abstract class
+ * PyBuiltinFunction - no refs, untraversable
+ * PyBuiltinFunctionNarrow - no refs, untraversable
+ * PyBuiltinFunctionSet - no refs, untraversable
+ * PyBuiltinMethod - Traverseproc
+ * PyBuiltinMethodNarrow - no refs, abstract class
+ * PyBuiltinMethodSet - Traverseproc
+ * PyByteArray - no refs, untraversable
+ * PyBytecode - Traverseproc
+ * PyStackWhy - no refs, untraversable
+ * PyStackException - Traverseproc
+ * PyTryBlock - no refs, untraversable
+ * PyCallIter - Traverseproc (with call to super)
+ * PyCell - Traverseproc
+ * PyClass - Traverseproc
+ * PyClassMethod - Traverseproc
+ * PyClassMethodDescr - no refs, untraversable
+ * PyCode - no refs, abstract class
+ * PyComplex - no refs, untraversable
+ * PyCompoundCallable - Traverseproc
+ * PyDataDescr - no refs, untraversable
+ * PyDescriptor - Traverseproc
+ * PyDictionary - Traverseproc
+ * ValuesIter - no refs, extends PyIterator
+ * ItemsIter - no refs, extends PyIterator
+ * PyMapKeyValSet - no PyObject
+ * PyMapEntrySet - no PyObject
+ * PyDictProxy - Traverseproc
+ * PyEllipsis - no refs, untraversable
+ * PyEnumerate - Traverseproc
+ * PyFastSequenceIter - Traverseproc
+ * PyFile - Traverseproc
+ * PyFileReader - no refs, untraversable
+ * PyFileWriter - no refs, untraversable
+ * PyFloat - no refs, untraversable
+ * PyFrame - Traverseproc
+ * PyFunction - Traverseproc
+ * PyGenerator - Traverseproc (with call to super)
+ * PyIndentationError - no PyObject
+ * PyInstance - Traverseproc
+ * PyInteger - no refs, untraversable
+ * PyIterator - Traverseproc
+ * PyJavaPackage - Traverseproc
+ * PyJavaType - Traverseproc (with call to super)
+ * EnumerationIter - no refs, extends PyIterator
+ * ComparableMethod - no refs, abstract class
+ * PyList - Traverseproc
+ * PyLong - no refs, untraversable
+ * PyMemoryView - Traverseproc
+ * PyMethod - Traverseproc
+ * PyMethodDescr - Traverseproc
+ * PyModule - Traverseproc
+ * PyNewWrapper - Traverseproc
+ * PyNone - no refs, untraversable
+ * PyNotImplemented - no refs, untraversable
+ * PyObject - no refs (objtype is special case)
+ * PyIdentityTuple - Traverseproc
+ * PyOverridableNew - no refs, abstract class
+ * PyProperty - Traverseproc
+ * PyReflectedConstructor - no refs, untraversable
+ * PyReflectedField - no refs, untraversable
+ * PyReflectedFunction - Traverseproc
+ * PyReversedIterator - Traverseproc (with call to super)
+ * PySequence - no refs, abstract class (default Traverseproc implementation)
+ * PySequenceIter - Traverseproc (with call to super)
+ * PySequenceList - no refs, abstract class
+ * PySingleton - no refs, untraversable
+ * PySlice - Traverseproc
+ * PySlot - no refs, untraversable
+ * PyStaticMethod - Traverseproc
+ * PyString - no refs, untraversable (assuming baseBuffer is not a PyObject)
+ * PyStringMap - Traverseproc
+ * StringMapIter - no refs, extends PyIterator, abstract class
+ * ItemsIter - no refs, extends StringMapIter
+ * KeysIter - no refs, extends StringMapIter
+ * ValuesIter - no refs, extends StringMapIter
+ * PySuper - Traverseproc
+ * PySyntaxError - no PyObject
+ * PySystemState - Traverseproc
+ * PySystemStateFunctions - no refs, untraversable
+ * PyAttributeDeleted - no refs, untraversable
+ * FloatInfo - Traverseproc
+ * LongInfo - Traverseproc
+ * PyTableCode - no refs, untraversable
+ * PyTraceback - Traverseproc
+ * PyTuple - Traverseproc
+ * PyType - Traverseproc
+ * PyUnicode - no refs, untraversable
+ * PyXRange - no refs, untraversable
+ * PyXRangeIter - no refs, extends PyIterator
+ * SyspathArchive - no refs, untraversable
+ *
+ * org.python.core.stringlib:
+ * FieldNameIterator - no refs, traverses via reflection
+ * MarkupIterator - no refs, untraversable
+ *
+ * org.python.core.util:
+ * importer - no refs, abstract class
+ *
+ * org.python.jsr223:
+ * PyScriptEngineScope - no refs, untraversable
+ * ScopeIterator - Traverseproc
+ *
+ * org.python.modules:
+ * _codecs:
+ * EncodingMap - no refs, untraversable
+ * _hashlib:
+ * Hash - no refs, untraversable
+ * _marshal:
+ * Marshaller - Traverseproc
+ * Unmarshaller - Traverseproc
+ * cStringIO:
+ * StringIO - no refs, extends PyIterator
+ * operator:
+ * OperatorFunctions - no refs, untraversable
+ * operator - no refs, untraversable
+ * PyAttrGetter - Traverseproc
+ * PyItemGetter - Traverseproc
+ * PyMethodCaller - Traverseproc
+ * PyStruct - no refs, untraversable
+ * synchronize:
+ * SynchronizedCallable - Traverseproc
+ *
+ * org.python.modules._collections:
+ * PyDefaultDict - Traverseproc (with call to super)
+ * PyDeque - Traverseproc (assuming, Nodes can't build cycles)
+ * PyDequeIter - Traverseproc (with call to super)
+ *
+ * org.python.modules._csv:
+ * PyDialect - no refs, untraversable
+ * PyReader - Traverseproc (with call to super)
+ * PyWriter - Traverseproc
+ *
+ * org.python.modules._functools:
+ * PyPartial - Traverseproc
+ *
+ * org.python.modules._io:
+ * PyFileIO - no refs, untraversable (there is a final PyString "mode", which is guarenteed to be a PyString and no subclass; as such it needs not be traversed since it cannot have refs itself)
+ * PyIOBase - Traverseproc
+ * PyRawIOBase - no refs, extends PyIOBase
+ *
+ * org.python.modules._json:
+ * Encoder - Traverseproc
+ * Scanner - Traverseproc
+ *
+ * org.python.modules._jythonlib:
+ * dict_builder - Traverseproc
+ *
+ * org.python.modules._threading:
+ * Condition - Traverseproc
+ * Lock - no refs, untraversable
+ *
+ * org.python.modules._weakref:
+ * AbstractReference - Traverseproc
+ * ReferenceType - no refs, extends AbstractReference
+ * ProxyType - no refs, extends AbstractReference
+ * CallableProxyType - no refs, extends ProxyType
+ *
+ * org.python.modules.bz2:
+ * PyBZ2Compressor - no refs, untraversable
+ * PyBZ2Decompressor - Traverseproc
+ * PyBZ2File - no refs, untraversable
+ * BZ2FileIterator - no refs, extends PyIterator
+ *
+ * org.python.modules.itertools:
+ * chain - Traverseproc (with call to super)
+ * combinations - Traverseproc (with call to super)
+ * combinationsWithReplacement - Traverseproc (with call to super)
+ * compress - Traverseproc (with call to super)
+ * count - Traverseproc (with call to super)
+ * cycle - Traverseproc (with call to super)
+ * dropwhile - Traverseproc (with call to super)
+ * groupby - Traverseproc (with call to super)
+ * ifilter - Traverseproc (with call to super)
+ * ifiIterfalse - Traverseproc (with call to super)
+ * imap - Traverseproc (with call to super)
+ * islice - Traverseproc (with call to super)
+ * itertools:
+ * ItertoolsIterator - no refs, extends PyIterator, abstract class
+ * FilterIterator - Traverseproc, extends ItertoolsIterator
+ * WhileIterator - Traverseproc, extends ItertoolsIterator
+ * izip - Traverseproc (with call to super)
+ * izipLongest - Traverseproc (with call to super)
+ * permutations - Traverseproc (with call to super)
+ * product - Traverseproc (with call to super)
+ * PyTeeIterator - Traverseproc (with call to super)
+ * repeat - Traverseproc (with call to super)
+ * starmap - Traverseproc (with call to super)
+ * takewhile - Traverseproc (with call to super)
+ *
+ * org.python.modules.jffi:
+ * ArrayCData - Traverseproc (with call to super; maybe check referenceMemory field whether it extends PyObject)
+ * ArrayIter - no refs, extends PyIterator
+ * BasePointer - no refs, abstract class
+ * ByReference - no refs, untraversable (maybe check memory field whether it extends PyObject)
+ * CData - Traverseproc (maybe check referenceMemory field whether it extends PyObject)
+ * CType - no refs, abstract class
+ * DynamicLibrary - no refs, untraversable
+ * StructLayout:
+ * Field - Traverseproc
+ *
+ * org.python.modules.posix:
+ * PosixModule:
+ * FstatFunction - no refs, untraversable
+ * LstatFunction - no refs, untraversable
+ * StatFunction - no refs, untraversable
+ * WindowsStatFunction - no refs, untraversable
+ * PyStatResult - Traverseproc (with call to super)
+ *
+ * org.python.modules.random:
+ * PyRandom - no refs, untraversable
+ *
+ * org.python.modules.sre:
+ * MatchObject - Traverseproc
+ * PatternObject - Traverseproc
+ * ScannerObject - Traverseproc
+ *
+ * org.python.modules.thread:
+ * PyLocal - Traverseproc
+ * PyLock - no refs, untraversable
+ *
+ * org.python.modules.time:
+ * PyTimeTuple - Traverseproc (with call to super)
+ * Time:
+ * TimeFunctions - no refs, untraversable
+ *
+ * org.python.util:
+ * InteractiveInterpreter - no PyObject
+ *
+ * com.ziclix.python.sql:
+ * DBApiType - no refs, untraversable
+ * PyConnection - Traverseproc
+ * ConnectionFunc - no refs, extends PyBuiltinMethodSet
+ * PyCursor - Traverseproc
+ * CursorFunc - no refs, extends PyBuiltinMethodSet
+ * PyExtendedCursor - no refs, extends PyCursor
+ * ExtendedCursorFunc - no refs, extends PyBuiltinMethodSet
+ * PyStatement - Traverseproc (because Object sql could be a PyObject or Traverseproc)
+ * zxJDBC - no refs, untraversable
+ * zxJDBCFunc - no refs, untraversable
+ *
+ * com.ziclix.python.sql.connect:
+ * Connect - no refs, untraversable
+ * Connectx - no refs, untraversable
+ * Lookup - no refs, untraversable
+ *
+ * com.ziclix.python.sql.util:
+ * BCP - Traverseproc
+ * BCPFunc - no refs, extends PyBuiltinMethodSet
+ *
+ * org.python.antlr:
+ * AnalyzingParser:
+ * AnalyzerTreeAdaptor - no PyObject
+ * AST - no refs, untraversable
+ * PythonErrorNode - no refs, extends PythonTree
+ * PythonTree - Traverseproc
+ *
+ * org.python.antlr.ast:
+ * alias - no refs, extends PythonTree
+ * arguments - Traverseproc (with call to super)
+ * comprehension - Traverseproc (with call to super)
+ * keyword - Traverseproc (with call to super)
+ *
+ * org.python.antlr.base:
+ * boolop - no refs, extends PythonTree
+ * cmpop - no refs, extends PythonTree
+ * excepthandler - no refs, extends PythonTree
+ * expr_context - no refs, extends PythonTree
+ * expr - no refs, extends PythonTree
+ * mod - no refs, extends PythonTree
+ * operator - no refs, extends PythonTree
+ * slice - no refs, extends PythonTree
+ * stmt - no refs, extends PythonTree
+ * unaryop - no refs, extends PythonTree
+ *
+ * org.python.antlr.op:
+ * Add - no refs, extends PythonTree
+ * And - no refs, extends PythonTree
+ * AugLoad - no refs, extends PythonTree
+ * AugStore - no refs, extends PythonTree
+ * BitAnd - no refs, extends PythonTree
+ * BitOr - no refs, extends PythonTree
+ * BitXor - no refs, extends PythonTree
+ * Del - no refs, extends PythonTree
+ * Div - no refs, extends PythonTree
+ * Eq - no refs, extends PythonTree
+ * FloorDiv - no refs, extends PythonTree
+ * Gt - no refs, extends PythonTree
+ * GtE - no refs, extends PythonTree
+ * In - no refs, extends PythonTree
+ * Invert - no refs, extends PythonTree
+ * Is - no refs, extends PythonTree
+ * IsNot - no refs, extends PythonTree
+ * Load - no refs, extends PythonTree
+ * LShift - no refs, extends PythonTree
+ * Lt - no refs, extends PythonTree
+ * LtE - no refs, extends PythonTree
+ * Mod - no refs, extends PythonTree
+ * Mult - no refs, extends PythonTree
+ * Not - no refs, extends PythonTree
+ * NotEq - no refs, extends PythonTree
+ * NotIn - no refs, extends PythonTree
+ * Or - no refs, extends PythonTree
+ * Param - no refs, extends PythonTree
+ * Pow - no refs, extends PythonTree
+ * RShift - no refs, extends PythonTree
+ * Store - no refs, extends PythonTree
+ * Sub - no refs, extends PythonTree
+ * UAdd - no refs, extends PythonTree
+ * USub - no refs, extends PythonTree
+ *
+ */
+public interface Traverseproc {
+
+ /**
+ * Traverses all reachable {@code PyObject}s.
+ * Like in CPython, {@code arg} must be passed
+ * unmodified to {@code visit} as its second parameter.
+ * If {@code visit.visit} returns nonzero, this return value
+ * must be returned immediately by traverse.
+ */
+ public int traverse(Visitproc visit, Object arg);
+
+ /**
+ * Optional operation.
+ * Should only be implemented if it is more efficient
+ * than calling {@code traverse} with a visitproc
+ * that just watches out for {@code ob}.
+ * Must return {@code false} if {@code ob} is {@code null}.
+ */
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException;
+}
diff --git a/src/org/python/core/TraverseprocDerived.java b/src/org/python/core/TraverseprocDerived.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/core/TraverseprocDerived.java
@@ -0,0 +1,17 @@
+package org.python.core;
+
+/**
+ * This is used like Traverseproc, but traverses only the slots[] array
+ * of fooDerived classes. This way it is avoided that the traverse
+ * method of a traversable PyObject is overwritten by the derived
+ * version. The gc module takes care of exploiting both traverse methods.
+ *
+ */
+public interface TraverseprocDerived {
+ /**
+ * Traverses all reachable {@code PyObject}s.
+ * Like in CPython, {@code arg} must be passed
+ * unmodified to {@code visit} as its second parameter.
+ */
+ public int traverseDerived(Visitproc visit, Object arg);
+}
diff --git a/src/org/python/core/Untraversable.java b/src/org/python/core/Untraversable.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/core/Untraversable.java
@@ -0,0 +1,26 @@
+package org.python.core;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a given class is not traversable and does
+ * intentionally not implement {@link org.python.core.Traverseproc}.
+ * This annotation is ignored if the class implements {@code Traverseproc},
+ * i.e. it cannot be used to remove {@code Traverseproc} support of a
+ * superclass. Thus it is well defined what happens if both
+ * {@code Traverseproc} and {@code {@literal @}Untraversable}
+ * are present: {@code Traverseproc} wins.
+ * If a class does not implement {@code Traverseproc} and is not
+ * annotated with {@code {@literal @}Untraversable}, gc assumes
+ * that the programmers were not aware of Jython's traverse
+ * mechanism and attempts to traverse the target object by using
+ * Java-reflection (which is assumably very inefficient).
+ */
+ at Retention(RetentionPolicy.RUNTIME)
+ at Target(ElementType.TYPE)
+public @interface Untraversable {
+ //this is a pure marker interface
+}
diff --git a/src/org/python/core/Visitproc.java b/src/org/python/core/Visitproc.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/core/Visitproc.java
@@ -0,0 +1,7 @@
+package org.python.core;
+
+public interface Visitproc {
+ /**Must not be called with {@code null}.
+ */
+ public int visit(PyObject object, Object arg);
+}
diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java
--- a/src/org/python/core/__builtin__.java
+++ b/src/org/python/core/__builtin__.java
@@ -18,6 +18,7 @@
import org.python.core.util.RelativeFile;
import org.python.modules._functools._functools;
+ at Untraversable
class BuiltinFunctions extends PyBuiltinFunctionSet {
public static final PyObject module = Py.newString("__builtin__");
@@ -1249,6 +1250,7 @@
}
}
+ at Untraversable
class ImportFunction extends PyBuiltinFunction {
ImportFunction() {
super("__import__",
@@ -1280,6 +1282,7 @@
}
}
+ at Untraversable
class SortedFunction extends PyBuiltinFunction {
SortedFunction() {
super("sorted", "sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list");
@@ -1315,6 +1318,7 @@
}
}
+ at Untraversable
class AllFunction extends PyBuiltinFunctionNarrow {
AllFunction() {
super("all", 1, 1,
@@ -1337,6 +1341,7 @@
}
}
+ at Untraversable
class AnyFunction extends PyBuiltinFunctionNarrow {
AnyFunction() {
super("any", 1, 1,
@@ -1359,6 +1364,7 @@
}
}
+ at Untraversable
class FormatFunction extends PyBuiltinFunctionNarrow {
FormatFunction() {
super("format", 1, 2,
@@ -1382,6 +1388,7 @@
}
}
+ at Untraversable
class PrintFunction extends PyBuiltinFunction {
PrintFunction() {
@@ -1456,6 +1463,7 @@
}
}
+ at Untraversable
class MaxFunction extends PyBuiltinFunction {
MaxFunction() {
super("max",
@@ -1514,6 +1522,7 @@
}
+ at Untraversable
class MinFunction extends PyBuiltinFunction {
MinFunction() {
super("min",
@@ -1571,6 +1580,7 @@
}
}
+ at Untraversable
class RoundFunction extends PyBuiltinFunction {
RoundFunction() {
super("round", "round(number[, ndigits]) -> floating point number\n\n" +
@@ -1594,6 +1604,7 @@
}
}
+ at Untraversable
class CompileFunction extends PyBuiltinFunction {
CompileFunction() {
super("compile",
@@ -1684,6 +1695,7 @@
}
}
+ at Untraversable
class OpenFunction extends PyBuiltinFunction {
OpenFunction() {
super("open", "Open a file using the file() type, returns a file object. This is the\n"
@@ -1714,6 +1726,7 @@
}
}
+ at Untraversable
class NextFunction extends PyBuiltinFunction {
NextFunction() {
super("next", "next(iterator[, default])\n\n"
@@ -1745,6 +1758,7 @@
}
}
+ at Untraversable
class BinFunction extends PyBuiltinFunction {
BinFunction() {
super("bin", "bin(number)\n\n"
diff --git a/src/org/python/core/exceptions.java b/src/org/python/core/exceptions.java
--- a/src/org/python/core/exceptions.java
+++ b/src/org/python/core/exceptions.java
@@ -10,6 +10,7 @@
* The builtin exceptions module. The entire module should be imported from
* python. None of the methods defined here should be called from java.
*/
+ at Untraversable
public class exceptions extends PyObject implements ClassDictInit {
public static String __doc__ = "Python's standard exception class hierarchy.\n"
@@ -640,6 +641,7 @@
return new BoundStaticJavaMethod(name, javaMethod);
}
+ @Untraversable
static class BoundStaticJavaMethod extends PyBuiltinMethod {
/** The Java Method to be bound. Its signature must be:
diff --git a/src/org/python/core/finalization/FinalizableBuiltin.java b/src/org/python/core/finalization/FinalizableBuiltin.java
--- a/src/org/python/core/finalization/FinalizableBuiltin.java
+++ b/src/org/python/core/finalization/FinalizableBuiltin.java
@@ -4,15 +4,15 @@
* See documentation of {@link FinalizablePyObject}.
*/
-public interface FinalizableBuiltin extends HasFinalizeTrigger {
- /**
- * {@code __del_builtin__} is the built-in's own finalizer, while
- * {@code __del_derived__} refers to an instance's in-dict {@code __del__}.
- * A FinalizeTrigger calls {@code __del_derived__} first and
+public interface FinalizableBuiltin {
+ /**
+ * {@code __del_builtin__} is the built-in's own finalizer, while
+ * {@code __del_derived__} refers to an instance's in-dict {@code __del__}.
+ * A FinalizeTrigger calls {@code __del_derived__} first and
* - if existent - {@code __del_builtin__} after that. A plain {@code __del__}
* would behave as overwritten by {@code __del__Derived}, i.e. won't be called
* if the type implements {@code FinalizablePyObjectDerived} while
* {@code __del_builtin__} is called in any case.
- */
- public void __del_builtin__();
+ */
+ public void __del_builtin__();
}
diff --git a/src/org/python/core/finalization/FinalizablePyObject.java b/src/org/python/core/finalization/FinalizablePyObject.java
--- a/src/org/python/core/finalization/FinalizablePyObject.java
+++ b/src/org/python/core/finalization/FinalizablePyObject.java
@@ -1,5 +1,7 @@
package org.python.core.finalization;
+import org.python.core.JyAttribute;
+
/**
*
* This interface allows {@code PyObject}s to have finalizers.
@@ -24,31 +26,18 @@
*
*
* If you are writing a custom built-in that shall directly
- * extend {@link org.python.core.PyObject} and have a finalizer, you can simply
- * extend {@link org.python.core.finalization.PyFinalizableObject}
- * and overwrite its {@code __del__}-method.
- * Follow the instructions below, starting at 4).
+ * extend {@link org.python.core.PyObject} or some other not-yet-finalizable
+ * builtin and have a finalizer, follow the instructions below.
*
*
- * If you want to extend some subclass of PyObject that does not yet implement
- * this interface, you have to take care of the following steps:
*
*
* Let your subclass implement {@code FinalizablePyObject}
* (or {@link org.python.core.finalization.FinalizableBuiltin}).
*
*
- * Let it have a member
- * {@code public FinalizeTrigger finalizeTrigger;}
- * Other scopes also work, but might fail with security managers.
- *
- *
- * In every constructor initialize this member via
- * {@code finalizeTrigger = FinalizeTrigger.makeTrigger(this);}
- * or
+ * In every constructor call
* {@code FinalizeTrigger.ensureFinalizer(this);}
- * The latter is a better abstraction, but slightly less performant,
- * since it uses reflection.
*
*
* Write your {@code __del__}-method however you intend it.
@@ -72,7 +61,7 @@
*
* Note: Regarding to object resurrection, Jython currently behaves like CPython >= 3.4.
* That means the finalizer {@code __del__} or {@code __del_builtin__} is called only the
- * first time an object gets gc'ed. If pre 3.4. behavior is required for some reason (i.e.
+ * first time an object gets gc'ed. If pre-3.4.-behavior is required for some reason (i.e.
* have the finalizer called repeatedly on every collection after a resurrection), one can
* achieve this manually via step 5).
*
@@ -98,12 +87,12 @@
*
*
* To turn off the finalizer, call
- * {@code finalizeTrigger.clear();}
+ * {@code ((FinalizeTrigger) JyAttribute.getAttr(this, JyAttribute.FINALIZE_TRIGGER_ATTR)).clear();}
* To turn it on again, call
- * {@code finalizeTrigger.trigger(this);}
+ * {@code ((FinalizeTrigger) JyAttribute.getAttr(this, JyAttribute.FINALIZE_TRIGGER_ATTR)).trigger(this);}
*
*/
-public interface FinalizablePyObject extends HasFinalizeTrigger {
+public interface FinalizablePyObject {
public void __del__();
}
diff --git a/src/org/python/core/finalization/FinalizablePyObjectDerived.java b/src/org/python/core/finalization/FinalizablePyObjectDerived.java
--- a/src/org/python/core/finalization/FinalizablePyObjectDerived.java
+++ b/src/org/python/core/finalization/FinalizablePyObjectDerived.java
@@ -1,23 +1,24 @@
package org.python.core.finalization;
/**
- * This interface should never be used directly in any hand-written code.
+ * This interface should never be used directly in any hand-written code
+ * (except in FinalizeTrigger.java).
* It should only appear in automatically generated {@code fooDerived}-classes.
*
* To use finalizers in hand-written classes read the instructions at
* {@link org.python.core.finalization.FinalizablePyObject}.
*
*/
-public interface FinalizablePyObjectDerived extends HasFinalizeTrigger {
-
- /**
- * {@code __del_builtin__} is the built-in's own finalizer, while
- * {@code __del_derived__} refers to an instance's in-dict {@code __del__}.
- * A FinalizeTrigger calls {@code __del_derived__} first and
+public interface FinalizablePyObjectDerived {
+
+ /**
+ * {@code __del_builtin__} is the built-in's own finalizer, while
+ * {@code __del_derived__} refers to an instance's in-dict {@code __del__}.
+ * A FinalizeTrigger calls {@code __del_derived__} first and
* - if existent - {@code __del_builtin__} after that. A plain {@code __del__}
* would behave as overwritten by {@code __del_derived__}, i.e. won't be called
* if the type implements {@code FinalizablePyObjectDerived} while
* {@code __del_builtin__} is called in any case.
- */
- public void __del_derived__();
+ */
+ public void __del_derived__();
}
diff --git a/src/org/python/core/finalization/FinalizeTrigger.java b/src/org/python/core/finalization/FinalizeTrigger.java
--- a/src/org/python/core/finalization/FinalizeTrigger.java
+++ b/src/org/python/core/finalization/FinalizeTrigger.java
@@ -1,12 +1,60 @@
package org.python.core.finalization;
-import java.lang.reflect.Field;
+import org.python.core.PyObject;
+import org.python.core.JyAttribute;
+import org.python.core.Py;
+import org.python.modules.gc;
/**
* To use finalizers on {@code PyObject}s, read the documentation of
* {@link org.python.core.finalization.FinalizablePyObject}.
*/
public class FinalizeTrigger {
+ /**
+ * This flag tells the finalize trigger to call
+ * gc.notifyFinalize after it called the finalizer.
+ */
+ public static final byte NOTIFY_GC_FLAG = (1<<0);
+
+ /**
+ * This flag tells the finalize trigger to refrain from actually
+ * running the PyObject's {@code __del__} method (or variants for
+ * derived or builtins).
+ * It can be used to have finalize triggers for debugging and
+ * monitoring purposes. The actual purpose is for Jython gc's
+ * {@code DONT_FINALIZE_CYCLIC_GARBAGE} flag that tells the gc to emulate
+ * CPython's <3.4 policy never to finalize cyclic garbage.
+ */
+ //public static final byte INHIBIT_FINALIZER_FLAG = (1<<1);
+
+ /**
+ * Tells the finalizer to add the finalized PyObject to the gc's
+ * garbage list. This allows gc to mimic CPython's way to deal
+ * with cyclic finalizable objects prior 3.4
+ * (c.f. CPython's gc's DEBUG_SAVEALL flag).
+ */
+ //public static final byte ADD_TO_GARBAGE_LIST_FLAG = (1<<2);
+
+ /**
+ * Similar to {@code INHIBIT_FINALIZER_FLAG}, but indicates that the
+ * underlying PyObject was never intended to be finalized, while
+ * {@code INHIBIT_FINALIZER_FLAG} indicates that there actually *is* a
+ * finalizer that is just not processed due to special
+ * circumstances (i.e. inactive {@code DONT_FINALIZE_CYCLIC_GARBAGE} flag).
+ */
+ public static final byte NOT_FINALIZABLE_FLAG = (1<<3);
+
+ /**
+ * Indicates that only
+ * {@link org.python.core.finalization.FinalizableBuiltin}
+ * shall be called.
+ */
+ public static final byte ONLY_BUILTIN_FLAG = (1<<4);
+
+ /**
+ * Indicates that this trigger was already finalized.
+ */
+ public static final byte FINALIZED_FLAG = (1<<5);
/**
* This factory hook is reserved for use by JyNI.
@@ -15,7 +63,7 @@
*/
public static FinalizeTriggerFactory factory;
- public static FinalizeTrigger makeTrigger(HasFinalizeTrigger toFinalize) {
+ public static FinalizeTrigger makeTrigger(PyObject toFinalize) {
if (factory != null) {
return factory.makeTrigger(toFinalize);
} else {
@@ -23,80 +71,162 @@
}
}
+ public static boolean hasActiveTrigger(PyObject obj) {
+ Object fn = JyAttribute.getAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ return fn != null && ((FinalizeTrigger) fn).isActive();
+ }
+
+ public static boolean isFinalizable(PyObject obj) {
+ return obj instanceof FinalizablePyObject || obj instanceof FinalizableBuiltin
+ || obj instanceof FinalizablePyObjectDerived;
+ }
+
/**
* Recreates the {@code FinalizeTrigger} of the given object. This makes sure that
* once the resurrected object is gc'ed again, its {@code __del__}-method will be
* called again.
*/
- public static void ensureFinalizer(HasFinalizeTrigger resurrect) {
- FinalizeTrigger trigger = makeTrigger(resurrect);
- setFinalizeTrigger(resurrect, trigger);
+ public static void ensureFinalizer(PyObject resurrect) {
+ JyAttribute.setAttr(resurrect, JyAttribute.FINALIZE_TRIGGER_ATTR,
+ makeTrigger(resurrect));
}
- public static void setFinalizeTrigger(HasFinalizeTrigger toFinalize, FinalizeTrigger trigger) {
- Field triggerField;
- try {
- triggerField = toFinalize.getClass().getDeclaredField("finalizeTrigger");
- } catch (NoSuchFieldException nfe) {
- throw new IllegalArgumentException(toFinalize.getClass()+" must have a field finalizeTrigger.");
+ public static void runFinalizer(PyObject toFinalize) {
+ runFinalizer(toFinalize, false);
+ }
+
+ public static void runFinalizer(PyObject toFinalize, boolean runBuiltinOnly) {
+ if (!runBuiltinOnly) {
+ if (toFinalize instanceof FinalizablePyObjectDerived) {
+ try {
+ ((FinalizablePyObjectDerived) toFinalize).__del_derived__();
+ } catch (Exception e) {}
+ } else if (toFinalize instanceof FinalizablePyObject) {
+ try {
+ ((FinalizablePyObject) toFinalize).__del__();
+ } catch (Exception e) {}
+ }
}
- try {
- triggerField.set(toFinalize, trigger);
- } catch (IllegalAccessException iae) {
+ if (toFinalize instanceof FinalizableBuiltin) {
try {
- triggerField.setAccessible(true);
- triggerField.set(toFinalize, trigger);
- } catch (Exception e) {
- throw new IllegalArgumentException("finalizeTrigger in "+toFinalize.getClass()+" must be accessible.");
- }
+ ((FinalizableBuiltin) toFinalize).__del_builtin__();
+ } catch (Exception e) {}
}
}
- public static FinalizeTrigger getFinalizeTrigger(HasFinalizeTrigger toFinalize) {
- Field triggerField;
- try {
- triggerField = toFinalize.getClass().getDeclaredField("finalizeTrigger");
- } catch (NoSuchFieldException nfe) {
- throw new IllegalArgumentException(toFinalize.getClass()+" must have a field finalizeTrigger.");
- }
- try {
- return (FinalizeTrigger) triggerField.get(toFinalize);
- } catch (IllegalAccessException iae) {
- try {
- triggerField.setAccessible(true);
- return (FinalizeTrigger) triggerField.get(toFinalize);
- } catch (Exception e) {
- throw new IllegalArgumentException("finalizeTrigger in "+toFinalize.getClass()+" must be accessible.");
- }
+ public static void appendFinalizeTriggerForBuiltin(PyObject obj) {
+ if (obj instanceof FinalizableBuiltin) {
+ FinalizeTrigger ft = makeTrigger(obj);
+ ft.flags = ONLY_BUILTIN_FLAG;
+ JyAttribute.setAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR, ft);
+ } else {
+ JyAttribute.delAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR);
}
}
-
- protected HasFinalizeTrigger toFinalize;
+ protected PyObject toFinalize;
+ public byte flags = 0;
public void clear() {
toFinalize = null;
}
- public void trigger(HasFinalizeTrigger toFinalize)
+ public void trigger(PyObject toFinalize)
{
this.toFinalize = toFinalize;
}
- protected FinalizeTrigger(HasFinalizeTrigger toFinalize) {
+ public boolean isActive() {
+ return toFinalize != null;
+ }
+
+ protected FinalizeTrigger(PyObject toFinalize) {
this.toFinalize = toFinalize;
}
- protected void finalize() throws Throwable {
- if (toFinalize != null) {
- if (toFinalize instanceof FinalizablePyObjectDerived) {
- ((FinalizablePyObjectDerived) toFinalize).__del_derived__();
- } else if (toFinalize instanceof FinalizablePyObject) {
- ((FinalizablePyObject) toFinalize).__del__();
- }
- if (toFinalize instanceof FinalizableBuiltin) {
- ((FinalizableBuiltin) toFinalize).__del_builtin__();
- }
+ protected boolean isCyclic() {
+ gc.CycleMarkAttr cm = (gc.CycleMarkAttr)
+ JyAttribute.getAttr(toFinalize, JyAttribute.GC_CYCLE_MARK_ATTR);
+ if (cm != null && cm.isCyclic()) {
+ return true;
+ } else {
+ gc.markCyclicObjects(toFinalize, (flags & NOT_FINALIZABLE_FLAG) == 0);
+ cm = (gc.CycleMarkAttr)
+ JyAttribute.getAttr(toFinalize, JyAttribute.GC_CYCLE_MARK_ATTR);
+ return cm != null && cm.isCyclic();
}
}
+
+ protected boolean isUncollectable() {
+ gc.CycleMarkAttr cm = (gc.CycleMarkAttr)
+ JyAttribute.getAttr(toFinalize, JyAttribute.GC_CYCLE_MARK_ATTR);
+ if (cm != null && cm.isUncollectable()) {
+ return true;
+ } else {
+ gc.markCyclicObjects(toFinalize, (flags & NOT_FINALIZABLE_FLAG) == 0);
+ cm = (gc.CycleMarkAttr)
+ JyAttribute.getAttr(toFinalize, JyAttribute.GC_CYCLE_MARK_ATTR);
+ return cm != null && cm.isUncollectable();
+ }
+ }
+
+ public void performFinalization() {
+ if (toFinalize != null) {
+ byte saveGarbage = 0;
+ if ((gc.getJythonGCFlags() & gc.DONT_FINALIZE_CYCLIC_GARBAGE) != 0) {
+ if (isUncollectable()) {
+ saveGarbage = 1;
+ } else if (!isCyclic()) {
+ saveGarbage = -1;
+ runFinalizer(toFinalize, (flags & ONLY_BUILTIN_FLAG) != 0);
+ }
+ } else {
+ if ((flags & NOT_FINALIZABLE_FLAG) == 0) {
+ runFinalizer(toFinalize, (flags & ONLY_BUILTIN_FLAG) != 0);
+ }
+ }
+ if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
+ Py.writeDebug("gc", "finalization of "+toFinalize);
+ }
+ if (saveGarbage == 1 || (saveGarbage == 0 &&
+ (gc.get_debug() & gc.DEBUG_SAVEALL) != 0 && isCyclic())) {
+ if ((flags & NOT_FINALIZABLE_FLAG) == 0) {
+ //Finalizable objects in gc.garbage get a special FinalizeTrigger
+ //that only runs the builtin finalizer. This is needed because
+ //from Python the user can't call the builtin-part of the
+ //finalizer by hand.
+ appendFinalizeTriggerForBuiltin(toFinalize);
+ }
+ gc.garbage.add(toFinalize);
+ if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
+ Py.writeDebug("gc", toFinalize+" added to garbage.");
+ }
+ }
+ }
+ if ((flags & NOTIFY_GC_FLAG) != 0) {
+ if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
+ Py.writeDebug("gc", "notify finalization of "+toFinalize);
+ }
+ gc.notifyFinalize(toFinalize);
+ flags &= ~NOTIFY_GC_FLAG;
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ flags |= FINALIZED_FLAG;
+ gc.notifyPreFinalization();
+ if (gc.delayedFinalizationEnabled() && toFinalize != null) {
+ if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
+ Py.writeDebug("gc", "delayed finalization for "+toFinalize);
+ }
+ gc.registerForDelayedFinalization(toFinalize);
+ } else {
+ performFinalization();
+ }
+ gc.notifyPostFinalization();
+ }
+
+ public boolean isFinalized() {
+ return (flags & FINALIZED_FLAG) != 0;
+ }
}
diff --git a/src/org/python/core/finalization/FinalizeTriggerFactory.java b/src/org/python/core/finalization/FinalizeTriggerFactory.java
--- a/src/org/python/core/finalization/FinalizeTriggerFactory.java
+++ b/src/org/python/core/finalization/FinalizeTriggerFactory.java
@@ -1,9 +1,11 @@
package org.python.core.finalization;
+import org.python.core.PyObject;
+
/**
* Reserved for use by JyNI.
*/
public interface FinalizeTriggerFactory {
- public FinalizeTrigger makeTrigger(HasFinalizeTrigger toFinalize);
+ public FinalizeTrigger makeTrigger(PyObject toFinalize);
}
diff --git a/src/org/python/core/finalization/HasFinalizeTrigger.java b/src/org/python/core/finalization/HasFinalizeTrigger.java
deleted file mode 100644
--- a/src/org/python/core/finalization/HasFinalizeTrigger.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.python.core.finalization;
-
-/**
- * This is a pure marker-interface to indicate that a
- * {@link org.python.core.PyObject} has a field declaration
- * {@code FinalizeTrigger finalizeTrigger;}
- * and thus can be treated by Jython's finalization API.
- *
- * For detailed instructions how to use finalizers in Jython, see
- * {@link org.python.core.finalization.FinalizablePyObject}.
- */
-public interface HasFinalizeTrigger {
-}
diff --git a/src/org/python/core/finalization/PyFinalizableObject.java b/src/org/python/core/finalization/PyFinalizableObject.java
deleted file mode 100644
--- a/src/org/python/core/finalization/PyFinalizableObject.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.python.core.finalization;
-
-import org.python.core.PyObject;;
-
-/**
- * For detailed intructions how to use finalizers on PyObjects,
- * read the documentation of {@link org.python.core.finalization.FinalizablePyObject}.
- */
-public abstract class PyFinalizableObject extends PyObject implements FinalizablePyObject {
-
- public FinalizeTrigger finalizeTrigger;
-
- public PyFinalizableObject() {
- super();
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
- }
-}
diff --git a/src/org/python/core/stringlib/FieldNameIterator.java b/src/org/python/core/stringlib/FieldNameIterator.java
--- a/src/org/python/core/stringlib/FieldNameIterator.java
+++ b/src/org/python/core/stringlib/FieldNameIterator.java
@@ -8,8 +8,11 @@
import org.python.core.PyTuple;
import org.python.core.PyType;
import org.python.core.PyUnicode;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedType;
+import org.python.modules.gc;
/**
* This class is an implementation of the iterator object returned by
@@ -20,8 +23,9 @@
* function, since as well as "being" the iterator, the object has an extra method {@link #head()}
* to return the required first member of the pair.
*/
+
@ExposedType(name = "fieldnameiterator", base = PyObject.class, isBaseType = false)
-public class FieldNameIterator extends PyObject {
+public class FieldNameIterator extends PyObject implements Traverseproc {
public static final PyType TYPE = PyType.fromClass(FieldNameIterator.class);
@@ -219,4 +223,26 @@
/** Integer or String. */
public Object value;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ if (head == null || !gc.canLinkToPyObject(head.getClass(), true)) {
+ return 0;
+ }
+ return gc.traverseByReflection(head, visit, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob)
+ throws UnsupportedOperationException {
+ if (ob != null && ob == head) {
+ return true;
+ }
+ if (!gc.canLinkToPyObject(head.getClass(), true)) {
+ return false;
+ }
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/src/org/python/core/stringlib/MarkupIterator.java b/src/org/python/core/stringlib/MarkupIterator.java
--- a/src/org/python/core/stringlib/MarkupIterator.java
+++ b/src/org/python/core/stringlib/MarkupIterator.java
@@ -6,6 +6,7 @@
import org.python.core.PyTuple;
import org.python.core.PyType;
import org.python.core.PyUnicode;
+import org.python.core.Untraversable;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedType;
@@ -14,6 +15,7 @@
* is an iterator returning successive 4-tuples, the sequence being equivalent to the original
* string.
*/
+ at Untraversable
@ExposedType(name = "formatteriterator", base = PyObject.class, isBaseType = false)
public class MarkupIterator extends PyObject {
diff --git a/src/org/python/jsr223/PyScriptEngineScope.java b/src/org/python/jsr223/PyScriptEngineScope.java
--- a/src/org/python/jsr223/PyScriptEngineScope.java
+++ b/src/org/python/jsr223/PyScriptEngineScope.java
@@ -14,6 +14,8 @@
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyType;
+import org.python.core.Visitproc;
+import org.python.core.Untraversable;
import org.python.expose.ExposedType;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
@@ -24,6 +26,7 @@
* with its own bindings. We adapt this multi-scope object for use as both
* a local and global dictionary.
*/
+ at Untraversable
@ExposedType(name = "scope", isBaseType = false)
public final class PyScriptEngineScope extends PyObject {
public static final PyType TYPE = PyType.fromClass(PyScriptEngineScope.class);
@@ -202,5 +205,21 @@
}
return result;
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ return _keys != null ? visit.visit(_keys, arg) : 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == _keys || super.refersDirectlyTo(ob));
+ }
}
}
diff --git a/src/org/python/modules/PyIOFileFactory.java b/src/org/python/modules/PyIOFileFactory.java
--- a/src/org/python/modules/PyIOFileFactory.java
+++ b/src/org/python/modules/PyIOFileFactory.java
@@ -7,6 +7,8 @@
import org.python.core.PyString;
import org.python.core.PyType;
import org.python.core.__builtin__;
+import org.python.core.Traverseproc;
+import org.python.core.Visitproc;
// XXX - add support for StringIO, not just cStringIO
@@ -59,7 +61,7 @@
// Use a PyFile as a file.
- static class FileIOFile implements PyIOFile {
+ static class FileIOFile implements PyIOFile, Traverseproc {
PyFile file;
@@ -89,11 +91,23 @@
String line = file.readline().toString();
return line.substring(0, line.length() - 1);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return file == null ? 0 : visit.visit(file, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && ob == file;
+ }
}
// Use any python object as a file.
- static class ObjectIOFile implements PyIOFile {
+ static class ObjectIOFile implements PyIOFile, Traverseproc {
char[] charr = new char[1];
StringBuilder buff = new StringBuilder();
@@ -136,6 +150,31 @@
String line = readline.__call__().toString();
return line.substring(0, line.length() - 1);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal;
+ if (write != null) {
+ retVal = visit.visit(write, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (read != null) {
+ retVal = visit.visit(read, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return readline == null ? 0 : visit.visit(readline, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == write || ob == read || ob == readline);
+ }
}
}
diff --git a/src/org/python/modules/PyStruct.java b/src/org/python/modules/PyStruct.java
--- a/src/org/python/modules/PyStruct.java
+++ b/src/org/python/modules/PyStruct.java
@@ -8,11 +8,13 @@
import org.python.core.PyString;
import org.python.core.PyTuple;
import org.python.core.PyType;
+import org.python.core.Untraversable;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedType;
+ at Untraversable
@ExposedType(name = "struct.Struct", base = PyObject.class)
public class PyStruct extends PyObject {
public static final PyType TYPE = PyType.fromClass(PyStruct.class);
diff --git a/src/org/python/modules/PyStructDerived.java b/src/org/python/modules/PyStructDerived.java
--- a/src/org/python/modules/PyStructDerived.java
+++ b/src/org/python/modules/PyStructDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyStructDerived extends PyStruct implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyStructDerived extends PyStruct implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i ent: backingMap.asMap().entrySet()) {
+ retVal = visit.visit(ent.getKey(), arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ if (ent.getValue() != null) {
+ retVal = visit.visit(ent.getValue(), arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ if (ob == null) {
+ return false;
+ } else if (super.refersDirectlyTo(ob)) {
+ return true;
+ }
+ if (backingMap == null) {
+ return false;
+ }
+ return backingMap.asMap().containsKey(ob) || backingMap.asMap().containsValue(ob);
+ }
}
diff --git a/src/org/python/modules/_collections/PyDefaultDictDerived.java b/src/org/python/modules/_collections/PyDefaultDictDerived.java
--- a/src/org/python/modules/_collections/PyDefaultDictDerived.java
+++ b/src/org/python/modules/_collections/PyDefaultDictDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyDefaultDictDerived extends PyDefaultDict implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyDefaultDictDerived extends PyDefaultDict implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i= 0) {
@@ -625,7 +627,7 @@
@ExposedMethod
final PyObject deque___copy__() {
- PyDeque pd = (PyDeque)this.getType().__call__();
+ PyDeque pd = (PyDeque)this.getType().__call__();
pd.deque_extend(this);
return pd;
}
@@ -674,5 +676,56 @@
return null;
}
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ return lastReturned == null ? 0 : traverseNode(lastReturned, visit, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ } else if (super.refersDirectlyTo(ob)) {
+ return true;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+
+ /* Traverseproc implementation */
+ private static int traverseNode(Node node, Visitproc visit, Object arg) {
+ int retVal;
+ if (node.data != null) {
+ retVal = visit.visit(node.data, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (node.left != null) {
+ retVal = traverseNode(node.left, visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ return node.right == null ? 0 : traverseNode(node.right, visit, arg);
+ }
+
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ return header == null ? 0 : traverseNode(header, visit, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ throw new UnsupportedOperationException();
}
}
diff --git a/src/org/python/modules/_collections/PyDequeDerived.java b/src/org/python/modules/_collections/PyDequeDerived.java
--- a/src/org/python/modules/_collections/PyDequeDerived.java
+++ b/src/org/python/modules/_collections/PyDequeDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyDequeDerived extends PyDeque implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyDequeDerived extends PyDeque implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i client;
/** Interpreter state that will call {@link #call()} on shutdown. */
- private PySystemState sys;
+ protected PySystemState sys;
public Closer(C toClose, PySystemState sys) {
this.client = new WeakReference(toClose);
diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java
--- a/src/org/python/modules/_io/PyFileIO.java
+++ b/src/org/python/modules/_io/PyFileIO.java
@@ -19,6 +19,7 @@
import org.python.core.PyString;
import org.python.core.PyType;
import org.python.core.PyUnicode;
+import org.python.core.Untraversable;
import org.python.core.io.FileIO;
import org.python.core.io.RawIOBase;
import org.python.core.io.StreamIO;
@@ -30,6 +31,7 @@
import jnr.constants.platform.Errno;
+ at Untraversable
@ExposedType(name = "_io.FileIO", base = PyRawIOBase.class)
public class PyFileIO extends PyRawIOBase {
diff --git a/src/org/python/modules/_io/PyFileIODerived.java b/src/org/python/modules/_io/PyFileIODerived.java
--- a/src/org/python/modules/_io/PyFileIODerived.java
+++ b/src/org/python/modules/_io/PyFileIODerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyFileIODerived extends PyFileIO implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyFileIODerived extends PyFileIO implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,17 +27,35 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i closer;
-
- public FinalizeTrigger finalizeTrigger;
protected PyIOBase() {
this(TYPE);
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
protected PyIOBase(PyType subtype) {
super(subtype);
closer = new Closer(this, Py.getSystemState());
- finalizeTrigger = FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
/**
@@ -910,4 +910,23 @@
+ "fp is closed after the suite of the with statement is complete:\n" + "\n"
+ "with open('spam.txt', 'r') as fp:\n" + " fp.write('Spam and eggs!')\n";
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ //closer cannot be null
+ if (closer.sys != null) {
+ int retVal = visit.visit(closer.sys, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ //__dict__ cannot be null
+ return visit.visit(__dict__, arg);
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) {
+ return ob != null && (ob == closer.sys || ob == __dict__);
+ }
}
diff --git a/src/org/python/modules/_io/PyIOBaseDerived.java b/src/org/python/modules/_io/PyIOBaseDerived.java
--- a/src/org/python/modules/_io/PyIOBaseDerived.java
+++ b/src/org/python/modules/_io/PyIOBaseDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyIOBaseDerived extends PyIOBase implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyIOBaseDerived extends PyIOBase implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,17 +27,35 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i {
/**
* This reference's hashCode: the System.identityHashCode of the referent. Only used
@@ -33,14 +34,25 @@
/** Whether pythonHashCode was already determined. */
private boolean havePythonHashCode;
- private List references = new ArrayList();
+ /**
+ * This boolean is set true when the callback is processed. If the reference is
+ * cleared it might potentially be restored until this boolean is set true.
+ * If weak reference restoring is activated (c.f.
+ * gc.PRESERVE_WEAKREFS_ON_RESURRECTION), AbstractReference.get would block
+ * until a consistent state is reached (i.e. referent is non-null or
+ * cleared == true).
+ */
+ protected boolean cleared = false;
- private static ReferenceQueue referenceQueue = new ReferenceQueue();
+ private List> references = new ArrayList<>();
+
+ private static ReferenceQueue referenceQueue = new ReferenceQueue<>();
private static Thread reaperThread;
private static ReentrantReadWriteLock reaperLock = new ReentrantReadWriteLock();
private static ConcurrentMap objects = Generic.concurrentMap();
+ private static List delayedCallbacks;
public GlobalRef(PyObject object) {
super(object, referenceQueue);
@@ -48,17 +60,17 @@
}
public synchronized void add(AbstractReference ref) {
- Reference r = new WeakReference(ref);
+ WeakReference r = new WeakReference<>(ref);
references.add(r);
}
private final AbstractReference getReferenceAt(int idx) {
- WeakReference wref = (WeakReference)references.get(idx);
- return (AbstractReference)wref.get();
+ WeakReference wref = references.get(idx);
+ return wref.get();
}
/**
- * Search for a reusable refrence. To be reused, it must be of the
+ * Search for a reusable reference. To be reused, it must be of the
* same class and it must not have a callback.
*/
synchronized AbstractReference find(Class cls) {
@@ -77,16 +89,57 @@
* Call each of the registered references.
*/
synchronized void call() {
- for (int i = references.size() - 1; i >= 0; i--) {
- AbstractReference r = getReferenceAt(i);
- if (r == null) {
- references.remove(i);
- } else {
- r.call();
+ if (!cleared) {
+ cleared = true;
+ for (int i = references.size() - 1; i >= 0; i--) {
+ AbstractReference r = getReferenceAt(i);
+ if (r == null) {
+ references.remove(i);
+ } else {
+ Thread pendingGet = (Thread) JyAttribute.getAttr(
+ r, JyAttribute.WEAKREF_PENDING_GET_ATTR);
+ if (pendingGet != null) {
+ pendingGet.interrupt();
+ }
+ r.call();
+ }
}
}
}
+ /**
+ * Call all callbacks that were enqueued via delayedCallback method.
+ */
+ public static void processDelayedCallbacks() {
+ if (delayedCallbacks != null) {
+ synchronized (delayedCallbacks) {
+ for (GlobalRef gref: delayedCallbacks) {
+ gref.call();
+ }
+ delayedCallbacks.clear();
+ }
+ }
+ }
+
+ /**
+ * Stores the callback for later processing. This is needed if
+ * weak reference restoration (c.f. gc.PRESERVE_WEAKREFS_ON_RESURRECTION)
+ * is activated. In this case the callback is delayed until it was
+ * determined whether a resurrection restored the reference.
+ */
+ private static void delayedCallback(GlobalRef cl) {
+ if (delayedCallbacks == null) {
+ delayedCallbacks = new ArrayList<>();
+ }
+ synchronized (delayedCallbacks) {
+ delayedCallbacks.add(cl);
+ }
+ }
+
+ public static boolean hasDelayedCallbacks() {
+ return delayedCallbacks != null && !delayedCallbacks.isEmpty();
+ }
+
synchronized public int count() {
for (int i = references.size() - 1; i >= 0; i--) {
AbstractReference r = getReferenceAt(i);
@@ -98,7 +151,7 @@
}
synchronized public PyList refs() {
- List list = new ArrayList();
+ List list = new ArrayList<>();
for (int i = references.size() - 1; i >= 0; i--) {
AbstractReference r = getReferenceAt(i);
if (r == null) {
@@ -123,10 +176,57 @@
GlobalRef ref = objects.putIfAbsent(newRef, newRef);
if (ref == null) {
ref = newRef;
+ JyAttribute.setAttr(object, JyAttribute.WEAK_REF_ATTR, ref);
+ } else {
+ // We clear the not-needed Global ref so that it won't
+ // pop up in ref-reaper thread's activity.
+ newRef.clear();
+ newRef.cleared = true;
}
return ref;
}
+ /**
+ * Restores this weak reference to its former referent.
+ * This actually means that a fresh GlobalRef is created
+ * and inserted into all adjacent AbstractRefs. The
+ * current GlobalRef is disbanded.
+ * If the given PyObject is not the former referent of
+ * this weak reference, an IllegalArgumentException is
+ * thrown.
+ */
+ public void restore(PyObject formerReferent) {
+ if (JyAttribute.getAttr(formerReferent, JyAttribute.WEAK_REF_ATTR) != this) {
+ throw new IllegalArgumentException(
+ "Argument is not former referent of this GlobalRef.");
+ }
+ if (delayedCallbacks != null) {
+ synchronized (delayedCallbacks) {
+ delayedCallbacks.remove(this);
+ }
+ }
+ clear();
+ createReaperThreadIfAbsent();
+ GlobalRef restore = new GlobalRef(formerReferent);
+ restore.references = references;
+ objects.remove(this);
+ objects.put(restore, restore);
+ AbstractReference aref;
+ for (int i = references.size() - 1; i >= 0; i--) {
+ aref = getReferenceAt(i);
+ if (aref == null) {
+ references.remove(i);
+ } else {
+ aref.gref = restore;
+ Thread pendingGet = (Thread) JyAttribute.getAttr(
+ aref, JyAttribute.WEAKREF_PENDING_GET_ATTR);
+ if (pendingGet != null) {
+ pendingGet.interrupt();
+ }
+ }
+ }
+ }
+
private static void createReaperThreadIfAbsent() {
reaperLock.readLock().lock();
try {
@@ -233,12 +333,17 @@
private Thread thread;
public void collect() throws InterruptedException {
- GlobalRef gr = (GlobalRef)referenceQueue.remove();
- gr.call();
+ GlobalRef gr = (GlobalRef) referenceQueue.remove();
+ if ((gc.getJythonGCFlags() & gc.PRESERVE_WEAKREFS_ON_RESURRECTION) == 0) {
+ gr.call();
+ } else {
+ delayedCallback(gr);
+ }
objects.remove(gr);
gr = null;
}
+ @Override
public void run() {
// Store the actual reaper thread so that when PySystemState.cleanup()
// is called this thread can be interrupted and die.
@@ -264,7 +369,6 @@
this.thread.interrupt();
this.thread = null;
}
-
return null;
}
}
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,10 +3,8 @@
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;
@@ -44,7 +42,6 @@
if (callback == null) {
ReferenceType ret = (ReferenceType)gref.find(ReferenceType.class);
if (ret != null) {
- // We can re-use an existing reference.
return ret;
}
}
@@ -92,11 +89,11 @@
@ExposedMethod
final PyObject weakref___call__(PyObject args[], String keywords[]) {
new ArgParser("__call__", args, keywords, Py.NoKeywords, 0);
- return Py.java2py(gref.get());
+ return Py.java2py(get());
}
public String toString() {
- PyObject obj = (PyObject)gref.get();
+ PyObject obj = get();
if (obj == null) {
return String.format("", Py.idstr(this));
}
diff --git a/src/org/python/modules/_weakref/ReferenceTypeDerived.java b/src/org/python/modules/_weakref/ReferenceTypeDerived.java
--- a/src/org/python/modules/_weakref/ReferenceTypeDerived.java
+++ b/src/org/python/modules/_weakref/ReferenceTypeDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class ReferenceTypeDerived extends ReferenceType implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class ReferenceTypeDerived extends ReferenceType implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i
+ * If this flag is not set, gc warns whenever an object would be subject to
+ * reflection-based traversion.
+ * Note that if this flag is not set, the warning will occur even if
+ * reflection-based traversion is not active. The purpose of this behavior is
+ * to identify objects that don't properly support the traverseproc-mechanism,
+ * i.e. instances of PyObject-subclasses that neither implement
+ * {@link org.python.core.Traverseproc},
+ * nor are annotated with the {@link org.python.core.Untraversable}-annotation.
+ *
+ *
+ * A SUPPRESS-flag was chosen rather than a WARN-flag, so that warning is the
+ * default behavior - the user must actively set this flag in order to not to
+ * be warned.
+ * This is because in an ideal implementation reflection-based traversion never
+ * occurs; it is only an inefficient fallback.
+ *
+ */
+ public static final short SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING = (1<<5);
+
+ /**
+ * In Jython one usually uses {@code Py.writeDebug} for debugging output.
+ * However that method is only verbose if an appropriate verbose-level
+ * was set. In CPython it is enough to set gc-{@code DEBUG} flags to get
+ * gc-messages, no matter what overall verbose level is selected.
+ * This flag tells Jython to use {@code Py.writeDebug} for debugging output.
+ * If it is not set (default-case), gc-debugging output (if gc-{@code VERBOSE}
+ * or -{@code DEBUG} flags are set) is directly written to {@code System.err}.
+ */
+ public static final short USE_PY_WRITE_DEBUG = (1<<6);
+
+ public static final short VERBOSE_COLLECT = (1<<7);
+ public static final short VERBOSE_WEAKREF = (1<<8);
+ public static final short VERBOSE_DELAYED = (1<<9);
+ public static final short VERBOSE_FINALIZE = (1<<10);
+ public static final short VERBOSE =
+ VERBOSE_COLLECT | VERBOSE_WEAKREF | VERBOSE_DELAYED | VERBOSE_FINALIZE;
+
+ /* set for debugging information */
+ /**
+ * print collection statistics
+ * (in Jython scoped on monitored objects)
+ */
+ public static final int DEBUG_STATS = (1<<0);
+
+ /**
+ * print collectable objects
+ * (in Jython scoped on monitored objects)
+ */
+ public static final int DEBUG_COLLECTABLE = (1<<1);
+
+ /**
+ * print uncollectable objects
+ * (in Jython scoped on monitored objects)
+ */
+ public static final int DEBUG_UNCOLLECTABLE = (1<<2);
+
+ /**
+ * print instances
+ * (in Jython scoped on monitored objects)
+ */
+ public static final int DEBUG_INSTANCES = (1<<3);
+
+ /**
+ * print other objects
+ * (in Jython scoped on monitored objects)
+ */
+ public static final int DEBUG_OBJECTS = (1<<4);
+
+ /**
+ * save all garbage in gc.garbage
+ * (in Jython scoped on monitored objects)
+ */
+ public static final int DEBUG_SAVEALL = (1<<5);
+ public static final int DEBUG_LEAK = DEBUG_COLLECTABLE |
+ DEBUG_UNCOLLECTABLE |
+ DEBUG_INSTANCES |
+ DEBUG_OBJECTS |
+ DEBUG_SAVEALL;
+
+ private static short gcFlags = DONT_TRAVERSE_BY_REFLECTION;
+ private static int debugFlags = 0;
+ private static boolean monitorNonTraversable = false;
+ private static boolean waitingForFinalizers = false;
+ private static AtomicBoolean gcRunning = new AtomicBoolean(false);
+ private static HashSet monitoredObjects;
+ private static ReferenceQueue gcTrash;
+ private static int finalizeWaitCount = 0;
+ private static int initWaitTime = 10, defaultWaitFactor = 2;
+ private static long lastRemoveTimeStamp = -1, maxWaitTime = initWaitTime;
+ private static int gcMonitoredRunCount = 0;
+ public static long gcRecallTime = 4000;
+ public static PyList garbage = new PyList();
+
+ //Finalization preprocess/postprocess-related declarations:
+ private static List preFinalizationProcess, postFinalizationProcess;
+ private static List preFinalizationProcessRemove, postFinalizationProcessRemove;
+ private static Thread postFinalizationProcessor;
+ private static long postFinalizationTimeOut = 100;
+ private static long postFinalizationTimestamp = System.currentTimeMillis()-2*postFinalizationTimeOut;
+ private static int openFinalizeCount = 0;
+ private static boolean postFinalizationPending = false;
+ private static boolean lockPostFinalization = false;
+
+ //Resurrection-safe finalizer- and weakref-related declarations:
+ private static IdentityHashMap delayedFinalizables, resurrectionCritics;
+ private static int abortedCyclicFinalizers = 0;
+ //Some modes to control aspects of delayed finalization:
+ private static final byte DO_NOTHING_SPECIAL = 0;
+ private static final byte MARK_REACHABLE_CRITICS = 1;
+ private static final byte NOTIFY_FOR_RERUN = 2;
+ private static byte delayedFinalizationMode = DO_NOTHING_SPECIAL;
+ private static boolean notifyRerun = false;
+
+ public static final String __doc__ =
+ "This module provides access to the garbage collector.\n" +
+ "\n" +
+ "enable() -- Enable automatic garbage collection (does nothing).\n" +
+ "isenabled() -- Returns True because Java garbage collection cannot be disabled.\n" +
+ "collect() -- Trigger a Java garbage collection (potentially expensive).\n" +
+ "get_debug() -- Get debugging flags (returns 0).\n" +
+ "\n" +
+ "Other functions raise NotImplementedError because they do not apply to Java.\n";
+
+ public static final String __name__ = "gc";
+
+
+ public static class CycleMarkAttr {
+ private boolean cyclic = false;
+ private boolean uncollectable = false;
+ public boolean monitored = false;
+
+ CycleMarkAttr() {
+ }
+
+ CycleMarkAttr(boolean cyclic, boolean uncollectable) {
+ this.cyclic = cyclic;
+ this.uncollectable = uncollectable;
+ }
+
+ public boolean isCyclic() {
+ return cyclic || uncollectable;
+ }
+
+ public boolean isUncollectable() {
+ return uncollectable;
+ }
+
+ public void setFlags(boolean cyclic, boolean uncollectable) {
+ this.cyclic = cyclic;
+ this.uncollectable = uncollectable;
+ }
+ }
+
+ private static class WeakReferenceGC extends WeakReference {
+ int hashCode;
+ public String str = null, inst_str = null;
+ public String cls;
+ boolean isInstance;
+ boolean hasFinalizer = false;
+ CycleMarkAttr cycleMark;
+
+ WeakReferenceGC(PyObject referent) {
+ super(referent);
+ isInstance = referent instanceof PyInstance;
+ cycleMark = (CycleMarkAttr)
+ JyAttribute.getAttr(referent, JyAttribute.GC_CYCLE_MARK_ATTR);
+ hashCode = System.identityHashCode(referent);
+ cls = referent.getClass().getName();
+ updateHasFinalizer();
+ }
+
+ WeakReferenceGC(PyObject referent, ReferenceQueue q) {
+ super(referent, q);
+ isInstance = referent instanceof PyInstance;
+ cycleMark = (CycleMarkAttr)
+ JyAttribute.getAttr(referent, JyAttribute.GC_CYCLE_MARK_ATTR);
+ hashCode = System.identityHashCode(referent);
+ cls = referent.getClass().getName();
+ updateHasFinalizer();
+ }
+
+ public void updateHasFinalizer() {
+ PyObject gt = get();
+ Object fn = JyAttribute.getAttr(gt, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ hasFinalizer = fn != null && ((FinalizeTrigger) fn).isActive();
+ }
+
+ public void initStr(PyObject referent) {
+ PyObject ref = referent;
+ if (referent == null) {
+ ref = get();
+ }
+ try {
+ if (ref instanceof PyInstance) {
+ String name = ((PyInstance) ref).fastGetClass().__name__;
+ if (name == null) {
+ name = "?";
+ }
+ inst_str = String.format("<%.100s instance at %s>",
+ name, Py.idstr(ref));
+ }
+ str = String.format("<%.100s %s>",
+ ref.getType().getName(), Py.idstr(ref));
+ } catch (Exception e) {
+ str = "<"+ref.getClass().getSimpleName()+" "
+ +System.identityHashCode(ref)+">";
+ }
+ }
+
+ public String toString() {
+ return str;
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public boolean equals(Object ob) {
+ if (ob instanceof WeakReferenceGC) {
+ return ((WeakReferenceGC) ob).get().equals(get())
+ && ((WeakReferenceGC) ob).hashCode() == hashCode();
+ } else if (ob instanceof WeakrefGCCompareDummy) {
+ return ((WeakrefGCCompareDummy) ob).compare != null
+ && ((WeakrefGCCompareDummy) ob).compare.equals(get());
+ } else {
+ return false;
+ }
+ }
+ }
+
+ private static class WeakrefGCCompareDummy {
+ public static WeakrefGCCompareDummy defaultInstance =
+ new WeakrefGCCompareDummy();
+ PyObject compare;
+ int hashCode;
+
+ public void setCompare(PyObject compare) {
+ this.compare = compare;
+ hashCode = System.identityHashCode(compare);
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public boolean equals(Object ob) {
+ if (ob instanceof Reference) {
+ return compare.equals(((Reference) ob).get());
+ } else if (ob instanceof WeakrefGCCompareDummy) {
+ return compare.equals(((WeakrefGCCompareDummy) ob).compare);
+ } else {
+ return compare.equals(ob);
+ }
+ }
+ }
+
+ private static class GCSentinel {
+ Thread waiting;
+
+ public GCSentinel(Thread notifyOnFinalize) {
+ waiting = notifyOnFinalize;
+ }
+
+ protected void finalize() throws Throwable {
+ //TODO: Find out why this would cause test to fail:
+ //notifyPreFinalization();
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "Sentinel finalizer called...");
+ }
+ if (lastRemoveTimeStamp != -1) {
+ long diff = maxWaitTime*defaultWaitFactor-System.currentTimeMillis()+lastRemoveTimeStamp;
+ while (diff > 0) {
+ try {
+ Thread.sleep(diff);
+ } catch (InterruptedException ie) {}
+ diff = maxWaitTime*defaultWaitFactor-System.currentTimeMillis()+lastRemoveTimeStamp;
+ }
+ }
+ if (waiting != null) {
+ waiting.interrupt();
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "Sentinel finalizer done");
+ }
+ //notifyPostFinalization();
+ }
+ }
+
+ private static void writeDebug(String type, String msg) {
+ if ((gcFlags & USE_PY_WRITE_DEBUG) != 0) {
+ Py.writeDebug(type, msg);
+ } else {
+ System.err.println(type + ": " + msg);
+ }
+ }
+
+ //----------delayed finalization section-----------------------------------
+
+ private static class DelayedFinalizationProcess implements Runnable {
+ static DelayedFinalizationProcess defaultInstance =
+ new DelayedFinalizationProcess();
+
+ private void performFinalization(PyObject del) {
+ if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "delayed finalize of "+del);
+ }
+ FinalizeTrigger ft = (FinalizeTrigger)
+ JyAttribute.getAttr(del, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ if (ft != null) {
+ ft.performFinalization();
+ } else if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "no FinalizeTrigger");
+ }
+ }
+
+ private void restoreFinalizer(PyObject obj, boolean cyclic) {
+ FinalizeTrigger ft =
+ (FinalizeTrigger) JyAttribute.getAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ FinalizeTrigger.ensureFinalizer(obj);
+ boolean notify = false;
+ if (ft != null) {
+ ((FinalizeTrigger)
+ JyAttribute.getAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR)).flags
+ = ft.flags;
+ notify = (ft.flags & FinalizeTrigger.NOTIFY_GC_FLAG) != 0;
+ }
+ if ((gcFlags & VERBOSE_DELAYED) != 0 || (gcFlags & VERBOSE_FINALIZE) != 0) {
+ writeDebug("gc", "restore finalizer of "+obj+"; cyclic? "+cyclic);
+ }
+ CycleMarkAttr cm = (CycleMarkAttr)
+ JyAttribute.getAttr(obj, JyAttribute.GC_CYCLE_MARK_ATTR);
+ if (cm != null && cm.monitored) {
+ monitorObject(obj, true);
+ }
+ if (notify) {
+ if ((gcFlags & VERBOSE_DELAYED) != 0 || (gcFlags & VERBOSE_FINALIZE) != 0) {
+ writeDebug("gc", "notify finalizer abort.");
+ }
+ notifyAbortFinalize(obj, cyclic);
+ }
+ }
+
+ public void run() {
+ if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "run delayed finalization. Index: "+
+ gcMonitoredRunCount);
+ }
+ Set critics = resurrectionCritics.keySet();
+ Set cyclicCritics = removeNonCyclic(critics);
+ cyclicCritics.retainAll(critics);
+ critics.removeAll(cyclicCritics);
+ Set criticReachablePool = findReachables(critics);
+ //to avoid concurrent modification:
+ ArrayList criticReachables = new ArrayList<>();
+ FinalizeTrigger fn;
+ if (delayedFinalizationMode == MARK_REACHABLE_CRITICS) {
+ for (PyObject obj: criticReachablePool) {
+ fn = (FinalizeTrigger) JyAttribute.getAttr(obj,
+ JyAttribute.FINALIZE_TRIGGER_ATTR);
+ if (fn != null && fn.isActive() && fn.isFinalized()) {
+ criticReachables.add(obj);
+ JyAttribute.setAttr(obj,
+ JyAttribute.GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR,
+ Integer.valueOf(gcMonitoredRunCount));
+ }
+ }
+ } else {
+ for (PyObject obj: criticReachablePool) {
+ fn = (FinalizeTrigger) JyAttribute.getAttr(obj,
+ JyAttribute.FINALIZE_TRIGGER_ATTR);
+ if (fn != null && fn.isActive() && fn.isFinalized()) {
+ criticReachables.add(obj);
+ }
+ }
+ }
+ critics.removeAll(criticReachables);
+ if ((gcFlags & PRESERVE_WEAKREFS_ON_RESURRECTION) != 0) {
+ if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "restore potentially resurrected weak references...");
+ }
+ GlobalRef toRestore;
+ for (PyObject rst: criticReachablePool) {
+ toRestore = (GlobalRef)
+ JyAttribute.getAttr(rst, JyAttribute.WEAK_REF_ATTR);
+ if (toRestore != null) {
+ toRestore.restore(rst);
+ }
+ }
+ GlobalRef.processDelayedCallbacks();
+ }
+ criticReachablePool.clear();
+ if ((gcFlags & DONT_FINALIZE_RESURRECTED_OBJECTS) != 0) {
+ //restore all finalizers that might belong to resurrected
+ //objects:
+ if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "restore "+criticReachables.size()+
+ " potentially resurrected finalizers...");
+ }
+ for (PyObject obj: criticReachables) {
+ CycleMarkAttr cm = (CycleMarkAttr)
+ JyAttribute.getAttr(obj, JyAttribute.GC_CYCLE_MARK_ATTR);
+ if (cm != null && cm.isUncollectable()) {
+ restoreFinalizer(obj, true);
+ } else {
+ gc.markCyclicObjects(obj, true);
+ cm = (CycleMarkAttr)
+ JyAttribute.getAttr(obj, JyAttribute.GC_CYCLE_MARK_ATTR);
+ restoreFinalizer(obj, cm != null && cm.isUncollectable());
+ }
+ }
+ } else {
+ if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "delayed finalization of "+criticReachables.size()+
+ " potentially resurrected finalizers...");
+ }
+ for (PyObject del: criticReachables) {
+ performFinalization(del);
+ }
+ }
+ cyclicCritics.removeAll(criticReachables);
+ if ((gcFlags & VERBOSE_DELAYED) != 0 && !delayedFinalizables.isEmpty()) {
+ writeDebug("gc", "process "+delayedFinalizables.size()+
+ " delayed finalizers...");
+ }
+ for (PyObject del: delayedFinalizables.keySet()) {
+ performFinalization(del);
+ }
+ if ((gcFlags & VERBOSE_DELAYED) != 0 && !cyclicCritics.isEmpty()) {
+ writeDebug("gc", "process "+cyclicCritics.size()+" cyclic delayed finalizers...");
+ }
+ for (PyObject del: cyclicCritics) {
+ performFinalization(del);
+ }
+ if ((gcFlags & VERBOSE_DELAYED) != 0 && !critics.isEmpty()) {
+ writeDebug("gc", "calling "+critics.size()+
+ " critic finalizers not reachable by other critic finalizers...");
+ }
+ if (delayedFinalizationMode == MARK_REACHABLE_CRITICS &&
+ !critics.isEmpty() && !criticReachables.isEmpty()) {
+ // This means some critic-reachables might be not critic-reachable any more.
+ // In a synchronized gc collection approach System.gc should run again while
+ // something like this is found. (Yes, not exactly a cheap task, but since this
+ // is for debugging, correctness counts.)
+ notifyRerun = true;
+ }
+ if (delayedFinalizationMode == NOTIFY_FOR_RERUN && !notifyRerun) {
+ for (PyObject del: critics) {
+ if (!notifyRerun) {
+ Object m = JyAttribute.getAttr(del,
+ JyAttribute.GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR);
+ if (m != null && ((Integer) m).intValue() == gcMonitoredRunCount) {
+ notifyRerun = true;
+ }
+ }
+ performFinalization(del);
+ }
+ } else {
+ for (PyObject del: critics) {
+ performFinalization(del);
+ }
+ }
+ delayedFinalizables.clear();
+ resurrectionCritics.clear();
+ if ((gcFlags & VERBOSE_DELAYED) != 0) {
+ writeDebug("gc", "delayed finalization run done");
+ }
+ }
+ }
+
+ public static boolean delayedFinalizationEnabled() {
+ return (gcFlags & (PRESERVE_WEAKREFS_ON_RESURRECTION |
+ DONT_FINALIZE_RESURRECTED_OBJECTS)) != 0;
+ }
+
+ private static void updateDelayedFinalizationState() {
+ if (delayedFinalizationEnabled()) {
+ resumeDelayedFinalization();
+ } else if (indexOfPostFinalizationProcess(
+ DelayedFinalizationProcess.defaultInstance) != -1) {
+ suspendDelayedFinalization();
+ }
+ if ((gcFlags & PRESERVE_WEAKREFS_ON_RESURRECTION) == 0) {
+ if (GlobalRef.hasDelayedCallbacks()) {
+ Thread dlcProcess = new Thread() {
+ public void run() {
+ GlobalRef.processDelayedCallbacks();
+ }
+ };
+ dlcProcess.start();
+ }
+ }
+ }
+
+ private static void resumeDelayedFinalization() {
+ if (delayedFinalizables == null) {
+ delayedFinalizables = new IdentityHashMap<>();
+ }
+ if (resurrectionCritics == null) {
+ resurrectionCritics = new IdentityHashMap<>();
+ }
+ //add post-finalization process (and cancel pending suspension process if any)
+ try {
+ synchronized(postFinalizationProcessRemove) {
+ postFinalizationProcessRemove.remove(
+ DelayedFinalizationProcess.defaultInstance);
+ if (postFinalizationProcessRemove.isEmpty()) {
+ postFinalizationProcessRemove = null;
+ }
+ }
+ } catch (NullPointerException npe) {}
+ if (indexOfPostFinalizationProcess(
+ DelayedFinalizationProcess.defaultInstance) == -1) {
+ registerPostFinalizationProcess(
+ DelayedFinalizationProcess.defaultInstance);
+ }
+ }
+
+ private static void suspendDelayedFinalization() {
+ unregisterPostFinalizationProcessAfterNextRun(
+ DelayedFinalizationProcess.defaultInstance);
+ }
+
+ private static boolean isResurrectionCritic(PyObject ob) {
+ return (isTraversable(ob))
+ && FinalizeTrigger.hasActiveTrigger(ob);
+ }
+
+ public static void registerForDelayedFinalization(PyObject ob) {
+ if (isResurrectionCritic(ob)) {
+ resurrectionCritics.put(ob, ob);
+ } else {
+ delayedFinalizables.put(ob, ob);
+ }
+ }
+ //----------end of delayed finalization section----------------------------
+
+
+
+
+ //----------Finalization preprocess/postprocess section--------------------
+
+ protected static class PostFinalizationProcessor implements Runnable {
+ protected static PostFinalizationProcessor defaultInstance =
+ new PostFinalizationProcessor();
+
+ public void run() {
+ // We wait until last postFinalizationTimestamp is at least timeOut ago.
+ // This should only be measured when openFinalizeCount is zero.
+ long current = System.currentTimeMillis();
+ while (true) {
+ if (!lockPostFinalization && openFinalizeCount == 0
+ && current - postFinalizationTimestamp
+ > postFinalizationTimeOut) {
+ break;
+ }
+ try {
+ long time = postFinalizationTimeOut - current + postFinalizationTimestamp;
+ if (openFinalizeCount != 0 || lockPostFinalization || time < 0) {
+ time = gcRecallTime;
+ }
+ Thread.sleep(time);
+ } catch (InterruptedException ie) {
+ }
+ current = System.currentTimeMillis();
+ }
+ postFinalizationProcessor = null;
+ postFinalizationProcess();
+ synchronized(PostFinalizationProcessor.class) {
+ postFinalizationPending = false;
+ PostFinalizationProcessor.class.notify();
+ }
+ }
+ }
+
+ /**
+ *
+ * Registers a process that will be called before any finalization during gc-run
+ * takes place ("finalization" refers to Jython-style finalizers ran by
+ * {@link org.python.core.finalization.FinalizeTrigger}s;
+ * to care for other finalizers these must call
+ * {@code gc.notifyPreFinalization()} before anything else is done and
+ * {@code gc.notifyPostFinalization()} afterwards; between these calls the finalizer
+ * must not terminate by throwing an exception). (Note: Using this for extern
+ * finalizers is currently experimental and needs more testing.)
+ * This works independently from monitoring, which is mainly needed to allow
+ * counting of cyclic garbage in {@code gc.collect}.
+ *
+ *
+ * This feature compensates that Java's gc does not provide any guarantees about
+ * finalization order. Java not even guarantees that when a weak reference is
+ * added to a reference queue, its finalizer already ran or not yet ran, if any.
+ *
+ *
+ * The only guarantee is that {@link java.lang.ref.PhantomReference}s are enqueued
+ * after finalization of their referents, but this happens in another gc-cycle then.
+ *
+ *
+ * Actually there are still situations that can cause pre-finalization process to
+ * run again during finalization phase. This can happen if external frameworks use
+ * their own finalizers. This can be cured by letting these finalizers call
+ * {@code gc.notifyPreFinalization()} before anything else is done and
+ * {@code gc.notifyPostFinalization()} right before the finalization method returns.
+ * Between these calls the finalizer must not terminate by throwing an exception.
+ * (Note: Using this for extern finalizers is currently experimental and needs more testing.)
+ *
+ *
+ * We recommend to use this feature in a way such that false-positive runs are
+ * not critically harmful, e.g. use it to enhance performance, but don't let it
+ * cause a crash if preprocess is rerun unexpectedly.
+ *
+ */
+ public static void registerPreFinalizationProcess(Runnable process) {
+ registerPreFinalizationProcess(process, -1);
+ }
+
+ /**
+ * See doc of {@core registerPreFinalizationProcess(Runnable process)}.
+ */
+ public static void registerPreFinalizationProcess(Runnable process, int index) {
+ while (true) {
+ try {
+ synchronized (preFinalizationProcess) {
+ preFinalizationProcess.add(index < 0 ?
+ index+preFinalizationProcess.size()+1 : index, process);
+ }
+ return;
+ } catch (NullPointerException npe) {
+ preFinalizationProcess = new ArrayList<>(1);
+ }
+ }
+ }
+
+ public static int indexOfPreFinalizationProcess(Runnable process) {
+ try {
+ synchronized (preFinalizationProcess) {
+ return preFinalizationProcess.indexOf(process);
+ }
+ } catch (NullPointerException npe) {
+ return -1;
+ }
+ }
+
+ public static boolean unregisterPreFinalizationProcess(Runnable process) {
+ try {
+ synchronized (preFinalizationProcess) {
+ boolean result = preFinalizationProcess.remove(process);
+ if (result && preFinalizationProcess.isEmpty()) {
+ preFinalizationProcess = null;
+ }
+ return result;
+ }
+ } catch (NullPointerException npe) {
+ return false;
+ }
+ }
+
+ /**
+ * Useful if a process wants to remove another one or itself during its execution.
+ * This asynchronous unregister method circumvents the synchronized-state on
+ * pre-finalization process list.
+ */
+ public static void unregisterPreFinalizationProcessAfterNextRun(Runnable process) {
+ while (true) {
+ try {
+ synchronized (preFinalizationProcessRemove) {
+ preFinalizationProcessRemove.add(process);
+ }
+ return;
+ } catch (NullPointerException npe) {
+ preFinalizationProcessRemove = new ArrayList<>(1);
+ }
+ }
+ }
+
+ /**
+ *
+ * Registers a process that will be called after all finalization during gc-run
+ * is done ("finalization" refers to Jython-style finalizers ran by
+ * {@link org.python.core.finalization.FinalizeTrigger}s;
+ * to care for other finalizers these must call
+ * {@code gc.notifyPreFinalization()} before anything else is done and
+ * {@code gc.notifyPostFinalization()} afterwards; between these calls the finalizer
+ * must not terminate by throwing an exception). (Note: Using this for extern
+ * finalizers is currently experimental and needs more testing.)
+ * This works independently from monitoring (which is mainly needed to allow
+ * garbage counting in {@code gc.collect}).
+ *
+ *
+ * This feature compensates that Java's gc does not provide any guarantees about
+ * finalization order. Java not even guarantees that when a weak reference is
+ * added to a reference queue, its finalizer already ran or not yet ran, if any.
+ *
+ *
+ * The only guarantee is that {@link java.lang.ref.PhantomReference}s are
+ * enqueued after finalization of the referents, but this
+ * happens - however - in another gc-cycle then.
+ *
+ *
+ * There are situations that can cause post finalization process to run
+ * already during finalization phase. This can happen if external frameworks use
+ * their own finalizers. This can be cured by letting these finalizers call
+ * {@code gc.notifyPreFinalization()} before anything else is done and
+ * {@code notifyPostFinalization()} right before the finalization method returns.
+ * Between these calls the finalizer must not terminate by throwing an exception.
+ * (Note: Using this for extern finalizers is currently experimental and needs more testing.)
+ *
+ *
+ * If it runs too early, we can at least guarantee that it will run again after
+ * finalization was really done. So we recommend to use this feature in a way
+ * such that false-positive runs are not critically harmful.
+ *
+ */
+ public static void registerPostFinalizationProcess(Runnable process) {
+ registerPostFinalizationProcess(process, -1);
+ }
+
+ /**
+ * See doc of {@code registerPostFinalizationProcess(Runnable process)}.
+ */
+ public static void registerPostFinalizationProcess(Runnable process, int index) {
+ while (true) {
+ try {
+ synchronized (postFinalizationProcess) {
+ postFinalizationProcess.add(index < 0 ?
+ index+postFinalizationProcess.size()+1 : index, process);
+ }
+ return;
+ } catch (NullPointerException npe) {
+ postFinalizationProcess = new ArrayList<>(1);
+ }
+ }
+ }
+
+ public static int indexOfPostFinalizationProcess(Runnable process) {
+ try {
+ synchronized (postFinalizationProcess) {
+ return postFinalizationProcess.indexOf(process);
+ }
+ } catch (NullPointerException npe) {
+ return -1;
+ }
+ }
+
+ public static boolean unregisterPostFinalizationProcess(Runnable process) {
+ try {
+ synchronized (postFinalizationProcess) {
+ boolean result = postFinalizationProcess.remove(process);
+ if (result && postFinalizationProcess.isEmpty()) {
+ postFinalizationProcess = null;
+ }
+ return result;
+ }
+ } catch (NullPointerException npe) {
+ return false;
+ }
+ }
+
+ /**
+ * Useful if a process wants to remove another one or itself during its execution.
+ * This asynchronous unregister method circumvents the synchronized-state on
+ * post-finalization process list.
+ */
+ public static void unregisterPostFinalizationProcessAfterNextRun(Runnable process) {
+ while (true) {
+ try {
+ synchronized (postFinalizationProcessRemove) {
+ postFinalizationProcessRemove.add(process);
+ }
+ return;
+ } catch (NullPointerException npe) {
+ postFinalizationProcessRemove = new ArrayList<>(1);
+ }
+ }
+ }
+
+ public static void notifyPreFinalization() {
+ ++openFinalizeCount;
+ if (System.currentTimeMillis() - postFinalizationTimestamp
+ < postFinalizationTimeOut) {
+ return;
+ }
+ try {
+ synchronized(preFinalizationProcess) {
+ for (Runnable r: preFinalizationProcess) {
+ try {
+ r.run();
+ } catch (Exception preProcessError) {
+ Py.writeError("gc", "Finalization preprocess "+r+" caused error: "
+ +preProcessError);
+ }
+ }
+ try {
+ synchronized (preFinalizationProcessRemove) {
+ preFinalizationProcess.removeAll(preFinalizationProcessRemove);
+ preFinalizationProcessRemove = null;
+ }
+ if (preFinalizationProcess.isEmpty()) {
+ preFinalizationProcess = null;
+ }
+ } catch (NullPointerException npe0) {}
+ }
+ } catch (NullPointerException npe) {
+ preFinalizationProcessRemove = null;
+ }
+
+ try {
+ synchronized(postFinalizationProcess) {
+ if (!postFinalizationProcess.isEmpty() &&
+ postFinalizationProcessor == null) {
+ postFinalizationPending = true;
+ postFinalizationProcessor = new Thread(
+ PostFinalizationProcessor.defaultInstance);
+ postFinalizationProcessor.start();
+ }
+ }
+ } catch (NullPointerException npe) {}
+ }
+
+ public static void notifyPostFinalization() {
+ postFinalizationTimestamp = System.currentTimeMillis();
+ --openFinalizeCount;
+ if (openFinalizeCount == 0 && postFinalizationProcessor != null) {
+ postFinalizationProcessor.interrupt();
+ }
+ }
+
+ protected static void postFinalizationProcess() {
+ try {
+ synchronized(postFinalizationProcess) {
+ for (Runnable r: postFinalizationProcess) {
+ try {
+ r.run();
+ } catch (Exception postProcessError) {
+ System.err.println("Finalization postprocess "+r+" caused error:");
+ System.err.println(postProcessError);
+ }
+ }
+ try {
+ synchronized (postFinalizationProcessRemove) {
+ postFinalizationProcess.removeAll(postFinalizationProcessRemove);
+ postFinalizationProcessRemove = null;
+ }
+ if (postFinalizationProcess.isEmpty()) {
+ postFinalizationProcess = null;
+ }
+ } catch (NullPointerException npe0) {}
+ }
+ } catch (NullPointerException npe) {
+ postFinalizationProcessRemove = null;
+ }
+ }
+ //----------end of Finalization preprocess/postprocess section-------------
+
+
+
+
+ //----------Monitoring section---------------------------------------------
+
+ public static void monitorObject(PyObject ob) {
+ monitorObject(ob, false);
+ }
+
+ public static void monitorObject(PyObject ob, boolean initString) {
+ //Already collected garbage should not be monitored,
+ //thus also not the garbage list:
+ if (ob == garbage) {
+ return;
+ }
+ if (!monitorNonTraversable && !isTraversable(ob)) {
+ if (!JyAttribute.hasAttr(ob, JyAttribute.FINALIZE_TRIGGER_ATTR)) {
+ return;
+ }
+ }
+ if (gcTrash == null) {
+ gcTrash = new ReferenceQueue<>();
+ }
+ while (true) {
+ try {
+ synchronized(monitoredObjects) {
+ if (!isMonitored(ob)) {
+ CycleMarkAttr cm = new CycleMarkAttr();
+ JyAttribute.setAttr(ob, JyAttribute.GC_CYCLE_MARK_ATTR, cm);
+ WeakReferenceGC refPut = new WeakReferenceGC(ob, gcTrash);
+ if (initString) {
+ refPut.initStr(ob);
+ }
+ monitoredObjects.add(refPut);
+ cm.monitored = true;
+ }
+ }
+ return;
+ } catch (NullPointerException npe) {
+ monitoredObjects = new HashSet();
+ }
+ }
+ }
+
+ /**
+ * Avoid to use this method. It is inefficient and no intended purpose of the
+ * backing Set of objects. In normal business it should not be needed and only
+ * exists for bare debugging purposes.
+ */
+ public static WeakReferenceGC getMonitorReference(PyObject ob) {
+ try {
+ synchronized(monitoredObjects) {
+ for (WeakReferenceGC ref: monitoredObjects) {
+ if (ref.equals(ob)) {
+ return ref;
+ }
+ }
+ }
+ } catch (NullPointerException npe) {}
+ return null;
+ }
+
+ public static boolean isMonitoring() {
+ try {
+ synchronized(monitoredObjects) {
+ return !monitoredObjects.isEmpty();
+ }
+ } catch (NullPointerException npe) {
+ return false;
+ }
+ }
+
+ public static boolean isMonitored(PyObject ob) {
+ try {
+ synchronized(monitoredObjects) {
+ WeakrefGCCompareDummy.defaultInstance.setCompare(ob);
+ boolean result = monitoredObjects.contains(
+ WeakrefGCCompareDummy.defaultInstance);
+ WeakrefGCCompareDummy.defaultInstance.compare = null;
+ return result;
+ }
+ } catch (NullPointerException npe) {
+ return false;
+ }
+ }
+
+ public static boolean unmonitorObject(PyObject ob) {
+ try {
+ synchronized(monitoredObjects) {
+ WeakrefGCCompareDummy.defaultInstance.setCompare(ob);
+ WeakReferenceGC rem = getMonitorReference(ob);
+ if (rem != null) {
+ rem.clear();
+ }
+ boolean result = monitoredObjects.remove(
+ WeakrefGCCompareDummy.defaultInstance);
+ WeakrefGCCompareDummy.defaultInstance.compare = null;
+ JyAttribute.delAttr(ob, JyAttribute.GC_CYCLE_MARK_ATTR);
+ FinalizeTrigger ft = (FinalizeTrigger)
+ JyAttribute.getAttr(ob, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ if (ft != null) {
+ ft.flags &= ~FinalizeTrigger.NOTIFY_GC_FLAG;
+ }
+ return result;
+ }
+ } catch (NullPointerException npe) {
+ return false;
+ }
+ }
+
+ public static void unmonitorAll() {
+ try {
+ synchronized(monitoredObjects) {
+ FinalizeTrigger ft;
+ for (WeakReferenceGC mo: monitoredObjects) {
+ PyObject rfrt = mo.get();
+ if (rfrt != null) {
+ JyAttribute.delAttr(rfrt, JyAttribute.GC_CYCLE_MARK_ATTR);
+ ft = (FinalizeTrigger)
+ JyAttribute.getAttr(rfrt, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ if (ft != null) {
+ ft.flags &= ~FinalizeTrigger.NOTIFY_GC_FLAG;
+ }
+ }
+ mo.clear();
+ }
+ monitoredObjects.clear();
+ }
+ } catch (NullPointerException npe) {
+ }
+ }
+
+ public static void stopMonitoring() {
+ setMonitorGlobal(false);
+ if (monitoredObjects != null) {
+ unmonitorAll();
+ monitoredObjects = null;
+ }
+ }
+
+ public static boolean getMonitorGlobal() {
+ return PyObject.gcMonitorGlobal;
+ }
+
+ public static void setMonitorGlobal(boolean flag) {
+ if (flag) {
+ gcFlags |= MONITOR_GLOBAL;
+ } else {
+ gcFlags &= ~MONITOR_GLOBAL;
+ }
+ PyObject.gcMonitorGlobal = flag;
+ }
+ //----------end of Monitoring section--------------------------------------
+
+
+ public static short getJythonGCFlags() {
+ if (((gcFlags & MONITOR_GLOBAL) != 0) != PyObject.gcMonitorGlobal) {
+ if (PyObject.gcMonitorGlobal) {
+ gcFlags |= MONITOR_GLOBAL;
+ } else {
+ gcFlags &= ~MONITOR_GLOBAL;
+ }
+ }
+ return gcFlags;
+ }
+
+ public static void setJythonGCFlags(short flags) {
+ gcFlags = flags;
+ PyObject.gcMonitorGlobal = (gcFlags & MONITOR_GLOBAL) != 0;
+ updateDelayedFinalizationState();
+ }
+
+ /**
+ * This is a convenience method to add flags via bitwise or.
+ */
+ public static void addJythonGCFlags(short flags) {
+ gcFlags |= flags;
+ PyObject.gcMonitorGlobal = (gcFlags & MONITOR_GLOBAL) != 0;
+ updateDelayedFinalizationState();
+ }
+
+ /**
+ * This is a convenience method to remove flags via bitwise and-not.
+ */
+ public static void removeJythonGCFlags(short flags) {
+ gcFlags &= ~flags;
+ PyObject.gcMonitorGlobal = (gcFlags & MONITOR_GLOBAL) != 0;
+ updateDelayedFinalizationState();
+ }
+
+ /**
+ * Do not call this method manually.
+ * It should only be called by
+ * {@link org.python.core.finalization.FinalizeTrigger}.
+ */
+ public static void notifyFinalize(PyObject finalized) {
+ if (--finalizeWaitCount == 0 && waitingForFinalizers) {
+ synchronized(GCSentinel.class) {
+ GCSentinel.class.notify();
+ }
+ }
+ }
+
+ /**
+ * For now this just calls {@code notifyFinalize}, as the only current
+ * purpose is to decrement the open finalizer count.
+ */
+ private static void notifyAbortFinalize(PyObject abort, boolean cyclic) {
+ if (cyclic) {
+ ++abortedCyclicFinalizers;
+ }
+ notifyFinalize(abort);
+ }
+
+ public static void enable() {}
+
+ public static void disable() {
+ throw Py.NotImplementedError("can't disable Java GC");
+ }
+
+ public static boolean isenabled() { return true; }
+
+ /**
+ * The generation parameter is only for compatibility with
+ * CPython {@code gc.collect} and is ignored.
+ * @param generation (ignored)
+ * @return Collected monitored cyclic trash-objects or
+ * {@code gc.UNKNOWN_COUNT} if nothing is monitored or -1 if
+ * an error occurred and collection did not complete.
+ */
+ public static int collect(int generation) {
+ return collect();
+ }
+
+ private static boolean needsTrashPrinting() {
+ return ((debugFlags & DEBUG_COLLECTABLE) != 0 ||
+ (debugFlags & DEBUG_UNCOLLECTABLE) != 0) &&
+ ((debugFlags & DEBUG_INSTANCES) != 0 ||
+ (debugFlags & DEBUG_OBJECTS) != 0);
+ }
+
+ private static boolean needsCollectBuffer() {
+ return (debugFlags & DEBUG_STATS) != 0 || needsTrashPrinting();
+ }
+
+ /**
+ * If no objects are monitored, this just delegates to
+ * {@code System.gc} and returns {@code gc.UNKNOWN_COUNT} as a
+ * non-erroneous default value. If objects are monitored,
+ * it emulates a synchronous gc run in the sense that it waits
+ * until all collected monitored objects were finalized.
+ *
+ * @return Number of collected monitored cyclic trash-objects
+ * or {@code gc.UNKNOWN_COUNT} if nothing is monitored or -1
+ * if an error occurred and collection did not complete.
+ */
+ public static int collect() {
+ try {
+ return collect_intern();
+ } catch (java.util.ConcurrentModificationException cme) {
+ cme.printStackTrace();
+ } catch (NullPointerException npe) {
+ npe.printStackTrace();
+ }
+ return -1;
+ }
+
+ private static int collect_intern() {
+ long t1 = 0;
+ int result;
+ if ((debugFlags & DEBUG_STATS) != 0) {
+ t1 = System.currentTimeMillis();
+ }
+ if (!isMonitoring()) {
+ if ((debugFlags & DEBUG_STATS) != 0) {
+ writeDebug("gc", "collecting generation x...");
+ writeDebug("gc", "objects in each generation: unknown");
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "no monitoring; perform ordinary async System.gc...");
+ }
+ System.gc();
+ result = UNKNOWN_COUNT; //indicates unknown result (-1 would indicate error)
+ } else {
+ if (!gcRunning.compareAndSet(false, true)) {
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "collect already running...");
+ }
+ //We must fail fast in this case to avoid deadlocks.
+ //Deadlock would for instance occur if a finalizer calls
+ //gc.collect (like is done in some tests in test_gc).
+ //Former version: throw new IllegalStateException("GC is already running.");
+ return -1; //better not throw exception here, as calling code
+ //is usually not prepared for that
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "perform monitored sync gc run...");
+ }
+ if (needsTrashPrinting() || (gcFlags & VERBOSE) != 0) {
+ // When the weakrefs are enqueued, the referents won't be available
+ // any more to provide their string representations, so we must
+ // save the string representations in the weak ref objects while
+ // the referents are still alive.
+ // We cannot save the string representations in the moment when the
+ // objects get monitored, because with monitorGlobal activated
+ // the objects get monitored just when they are created and some
+ // of them are in an invalid state then and cannot directly obtain
+ // their string representation (produce overflow errors and such bad
+ // stuff). So we do it here...
+ List lst = new ArrayList<>();
+ for (WeakReferenceGC wr: monitoredObjects) {
+ if (wr.str == null) {
+ lst.add(wr);
+ }
+ }
+ for (WeakReferenceGC ol: lst) {
+ ol.initStr(null);
+ }
+ lst.clear();
+ }
+ ++gcMonitoredRunCount;
+ delayedFinalizationMode = MARK_REACHABLE_CRITICS;
+ notifyRerun = false;
+
+ int[] stat = {0, 0};
+
+ syncCollect(stat, (debugFlags & DEBUG_STATS) != 0);
+ delayedFinalizationMode = NOTIFY_FOR_RERUN;
+
+ if (notifyRerun) {
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "initial sync collect done.");
+ }
+ while (notifyRerun) {
+ notifyRerun = false;
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "rerun gc...");
+ }
+ syncCollect(stat, false);
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "all sync collect runs done.");
+ }
+ } else if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "sync collect done.");
+ }
+
+ delayedFinalizationMode = DO_NOTHING_SPECIAL;
+ gcRunning.set(false);
+ result = stat[0];
+ if ((debugFlags & DEBUG_STATS) != 0) {
+ if (result != UNKNOWN_COUNT) {
+ StringBuilder sb = new StringBuilder("done, ");
+ sb.append(stat[0]);
+ sb.append(" unreachable, ");
+ sb.append(stat[1]);
+ sb.append(" uncollectable");
+ if (t1 != 0) {
+ sb.append(", ");
+ sb.append((System.currentTimeMillis()-t1)/1000.0);
+ sb.append("s elapsed");
+ }
+ sb.append(".");
+ writeDebug("gc", sb.toString());
+ }
+ }
+ }
+ if ((debugFlags & DEBUG_STATS) != 0 && result == UNKNOWN_COUNT) {
+ StringBuilder sb = new StringBuilder("done");
+ if (t1 != 0) {
+ sb.append(", ");
+ sb.append((System.currentTimeMillis()-t1)/1000.0);
+ sb.append("s elapsed");
+ }
+ sb.append(".");
+ writeDebug("gc", sb.toString());
+ }
+ return result;
+ }
+
+ private static void syncCollect(int[] stat, boolean debugStat) {
+ abortedCyclicFinalizers = 0;
+ lockPostFinalization = true;
+ Reference extends Object> trash;
+ try {
+ trash = gcTrash.remove(initWaitTime);
+ if (trash != null && (gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "monitored objects from previous gc-run found.");
+ }
+ } catch (InterruptedException ie) {
+ trash = null;
+ }
+ Set cyclic;
+ IdentityHashMap cyclicLookup;
+ synchronized(monitoredObjects) {
+ if (trash != null) {
+ while (trash != null) {
+ monitoredObjects.remove(trash);
+ try {
+ trash = gcTrash.remove(initWaitTime);
+ } catch (InterruptedException ie) {
+ trash = null;
+ }
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "cleaned up previous trash.");
+ }
+ }
+ FinalizeTrigger ft;
+ for (WeakReferenceGC wrg: monitoredObjects) {
+ wrg.updateHasFinalizer();
+ if (wrg.hasFinalizer) {
+ ft = (FinalizeTrigger)
+ JyAttribute.getAttr(wrg.get(), JyAttribute.FINALIZE_TRIGGER_ATTR);
+ ft.flags |= FinalizeTrigger.NOTIFY_GC_FLAG;
+ //The NOTIFY_GC_FLAG is needed, because monitor state changes during
+ //collection. So the FinalizeTriggers can't use gc.isMonitored to know
+ //whether gc notification is needed.
+ }
+ }
+
+ //Typically this line causes a gc-run:
+ cyclicLookup = removeNonCyclicWeakRefs(monitoredObjects);
+ cyclic = new HashSet<>(cyclicLookup.values());
+ if (debugStat) {
+ writeDebug("gc", "collecting generation x...");
+ writeDebug("gc", "objects in each generation: "+cyclic.size());
+ }
+
+ if ((debugFlags & DEBUG_SAVEALL) != 0
+ || (gcFlags & DONT_FINALIZE_RESURRECTED_OBJECTS) != 0) {
+ cyclic.retainAll(monitoredObjects);
+ for (WeakReferenceGC wrg: cyclic) {
+ if (!wrg.hasFinalizer) {
+ PyObject obj = wrg.get();
+ FinalizeTrigger.ensureFinalizer(obj);
+ wrg.updateHasFinalizer();
+ ft = (FinalizeTrigger)
+ JyAttribute.getAttr(obj, JyAttribute.FINALIZE_TRIGGER_ATTR);
+ ft.flags |= FinalizeTrigger.NOT_FINALIZABLE_FLAG;
+ ft.flags |= FinalizeTrigger.NOTIFY_GC_FLAG;
+ }
+ }
+ }
+ }
+ maxWaitTime = initWaitTime;
+ WeakReference sentRef =
+ new WeakReference<>(new GCSentinel(Thread.currentThread()), gcTrash);
+ lastRemoveTimeStamp = System.currentTimeMillis();
+ if (finalizeWaitCount != 0) {
+ System.err.println("Finalize wait count should be initially 0!");
+ finalizeWaitCount = 0;
+ }
+
+ // We tidy up a bit... (Because it is not unlikely that
+ // the preparation-stuff done so far has caused a gc-run.)
+ // This is not entirely safe as gc could interfere with
+ // this process at any time again. Since this is intended
+ // for debugging, this solution is sufficient in practice.
+ // Maybe we will include more mechanisms to ensure safety
+ // in the future.
+
+ try {
+ trash = gcTrash.remove(initWaitTime);
+ if (trash != null && (gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "monitored objects from interferring gc-run found.");
+ }
+ } catch (InterruptedException ie) {
+ trash = null;
+ }
+ if (trash != null) {
+ while (trash != null) {
+ monitoredObjects.remove(trash);
+ if (cyclic.remove(trash) && (gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "cyclic interferring trash: "+trash);
+ } else if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "interferring trash: "+trash);
+ }
+ try {
+ trash = gcTrash.remove(initWaitTime);
+ } catch (InterruptedException ie) {
+ trash = null;
+ }
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "cleaned up interferring trash.");
+ }
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "call System.gc.");
+ }
+ cyclicLookup = null;
+ System.gc();
+ List collectBuffer = null;
+ if (needsCollectBuffer()) {
+ collectBuffer = new ArrayList<>();
+ }
+ long removeTime;
+ try {
+ while(true) {
+ removeTime = System.currentTimeMillis()-lastRemoveTimeStamp;
+ if (removeTime > maxWaitTime) {
+ maxWaitTime = removeTime;
+ }
+ lastRemoveTimeStamp = System.currentTimeMillis();
+ trash = gcTrash.remove(Math.max(gcRecallTime, maxWaitTime*defaultWaitFactor));
+ if (trash != null) {
+ if (trash instanceof WeakReferenceGC) {
+ synchronized(monitoredObjects) {
+ monitoredObjects.remove(trash);
+ }
+ //We avoid counting jython-specific objects in order to
+ //obtain CPython-comparable results.
+ if (cyclic.contains(trash) && !((WeakReferenceGC) trash).cls.contains("Java")) {
+ ++stat[0];
+ if (collectBuffer != null) {
+ collectBuffer.add((WeakReferenceGC) trash);
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "Collected cyclic object: "+trash);
+ }
+ }
+ if (((WeakReferenceGC) trash).hasFinalizer) {
+ ++finalizeWaitCount;
+ if ((gcFlags & VERBOSE_FINALIZE) != 0) {
+ writeDebug("gc", "Collected finalizable object: "+trash);
+ writeDebug("gc", "New finalizeWaitCount: "+finalizeWaitCount);
+ }
+ }
+ } else if (trash == sentRef && (gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "Sentinel collected.");
+ }
+ } else {
+ System.gc();
+ }
+ }
+ } catch (InterruptedException iex) {}
+
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "all objects from run enqueud in trash queue.");
+ writeDebug("gc", "pending finalizers: "+finalizeWaitCount);
+ }
+ //lockPostFinalization assures that postFinalization process
+ //only runs once per syncCollect-call.
+ lockPostFinalization = false;
+ if (postFinalizationProcessor != null) {
+ //abort the remaining wait-time if a postFinalizationProcessor is waiting
+ postFinalizationProcessor.interrupt();
+ }
+ waitingForFinalizers = true;
+ if (finalizeWaitCount != 0) {
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "waiting for "+finalizeWaitCount+
+ " pending finalizers.");
+ if (finalizeWaitCount < 0) {
+ //Maybe even throw exception here?
+ Py.writeError("gc", "There should never be "+
+ "less than zero pending finalizers!");
+ }
+ }
+ // It is important to have the while *inside* the synchronized block.
+ // Otherwise the notify might come just between the check and the wait,
+ // causing an endless waiting.
+ synchronized(GCSentinel.class) {
+ while (finalizeWaitCount != 0) {
+ try {
+ GCSentinel.class.wait();
+ } catch (InterruptedException ie2) {}
+ }
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "no more finalizers pending.");
+ }
+ }
+ waitingForFinalizers = false;
+
+ if (postFinalizationPending) {
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc",
+ "waiting for pending post-finalization process.");
+ }
+ // It is important to have the while (which is actually an "if" since the
+ // InterruptedException is very unlikely to occur) *inside* the synchronized
+ // block. Otherwise the notify might come just between the check and the wait,
+ // causing an endless waiting. This is no pure academic consideration, but was
+ // actually observed to happen from time to time, especially on faster systems.
+ synchronized(PostFinalizationProcessor.class) {
+ while (postFinalizationPending) {
+ try {
+ PostFinalizationProcessor.class.wait();
+ } catch (InterruptedException ie3) {}
+ }
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", "post-finalization finished.");
+ }
+ }
+ if (collectBuffer != null) {
+ /* There is a little discrepancy in CPython's behavior.
+ * The documentation tells that all uncollectable objects
+ * are stored in gc.garbage, but it actually stores only
+ * those uncollectable objects that have finalizers.
+ * In contrast to that the uncollectable counting and
+ * listing related to DEBUG_X-flags also counts/lists
+ * objects that participate in a cycle with uncollectable
+ * finalizable objects.
+ *
+ * Comprehension:
+ * An object is uncollectable if it is in a ref cycle and
+ * has a finalizer.
+ *
+ * CPython
+ *
+ * - counts and prints the whole uncollectable cycle in context
+ * of DEBUG_X-flags.
+ *
+ * - stores only those objects from the cycle that actually have
+ * finalizers in gc.garbage.
+ *
+ * While slightly contradictionary to the doc, we reproduce this
+ * behavior here.
+ */
+ if ((debugFlags & gc.DEBUG_COLLECTABLE) != 0 &&
+ ( (debugFlags & gc.DEBUG_OBJECTS) != 0 ||
+ (debugFlags & gc.DEBUG_INSTANCES) != 0)) {
+ //note that all cycleMarks should have been initialized when
+ //objects became monitored.
+
+ for (WeakReferenceGC wrg: collectBuffer) {
+ if (!wrg.cycleMark.isUncollectable()) {
+ if (wrg.isInstance) {
+ writeDebug("gc", "collectable "+
+ ((debugFlags & gc.DEBUG_INSTANCES) != 0 ?
+ wrg.inst_str : wrg.str));
+ } else if ((debugFlags & gc.DEBUG_OBJECTS) != 0) {
+ writeDebug("gc", "collectable "+wrg.str);
+ }
+ } else {
+ ++stat[1];
+ }
+ }
+ } else if ((debugFlags & gc.DEBUG_STATS) != 0) {
+ for (WeakReferenceGC wrg: collectBuffer) {
+ if (wrg.cycleMark.isUncollectable()) {
+ ++stat[1];
+ }
+ }
+ }
+ if ((debugFlags & gc.DEBUG_UNCOLLECTABLE) != 0 &&
+ ( (debugFlags & gc.DEBUG_OBJECTS) != 0 ||
+ (debugFlags & gc.DEBUG_INSTANCES) != 0)) {
+ for (WeakReferenceGC wrg: collectBuffer) {
+ if (wrg.cycleMark.isUncollectable()) {
+ if (wrg.isInstance) {
+ writeDebug("gc", "uncollectable "+
+ ((debugFlags & gc.DEBUG_INSTANCES) != 0 ?
+ wrg.inst_str : wrg.str));
+ } else if ((debugFlags & gc.DEBUG_OBJECTS) != 0) {
+ writeDebug("gc", "uncollectable "+wrg.str);
+ }
+ }
+ }
+ }
+ }
+ if ((gcFlags & VERBOSE_COLLECT) != 0) {
+ writeDebug("gc", abortedCyclicFinalizers+
+ " finalizers aborted.");
+ }
+ stat[0] -= abortedCyclicFinalizers;
+ stat[1] -= abortedCyclicFinalizers;
+ }
+
+ public static PyObject get_count() {
+ throw Py.NotImplementedError("not applicable to Java GC");
+ }
+
+ public static void set_debug(int flags) {
+ debugFlags = flags;
+ }
+
+ public static int get_debug() {
+ return debugFlags;
+ }
+
+ public static void set_threshold(PyObject[] args, String[] kwargs) {
+ throw Py.NotImplementedError("not applicable to Java GC");
+ }
+
+ public static PyObject get_threshold() {
+ throw Py.NotImplementedError("not applicable to Java GC");
+ }
+
+ public static PyObject get_objects() {
+ throw Py.NotImplementedError("not applicable to Java GC");
+ }
+
+ /**
+ * Only works reliably if {@code monitorGlobal} is active, as it depends on
+ * monitored objects to search for referrers. It only finds referrers that
+ * properly implement the traverseproc mechanism (unless reflection-based
+ * traversion is activated and works stable).
+ * Further note that the resulting list will contain referrers in no specific
+ * order and may even include duplicates.
+ */
+ public static PyObject get_referrers(PyObject[] args, String[] kwargs) {
+ if (!isMonitoring()) {
+ throw Py.NotImplementedError(
+ "not applicable in Jython if gc module is not monitoring PyObjects");
+ }
+ if (args == null) {
+ return Py.None;
+ }
+ PyObject result = new PyList();
+ PyObject[] coll = {null, result};
+ PyObject src;
+ synchronized(monitoredObjects) {
+ for (PyObject ob: args) {
+ for (WeakReferenceGC src0: monitoredObjects) {
+ src = (PyObject) src0.get(); //Sentinels should not be in monitoredObjects
+ if (src instanceof Traverseproc) {
+ try {
+ if (((Traverseproc) src).refersDirectlyTo(ob)) {
+ result.__add__(src);
+ }
+ } catch (UnsupportedOperationException uoe) {
+ coll[0] = ob;
+ traverse(ob, ReferrerFinder.defaultInstance, coll);
+ }
+ } else if (isTraversable(src)) {
+ coll[0] = ob;
+ traverse(ob, ReferrerFinder.defaultInstance, coll);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Only works reliably if all objects in args properly
+ * implement the Traverseproc mechanism (unless reflection-based traversion
+ * is activated and works stable).
+ * Further note that the resulting list will contain referents in no
+ * specific order and may even include duplicates.
+ */
+ public static PyObject get_referents(PyObject[] args, String[] kwargs) {
+ if (args == null) {
+ return Py.None;
+ }
+ PyObject result = new PyList();
+ for (PyObject ob: args) {
+ traverse(ob, ReferentsFinder.defaultInstance, result);
+ }
+ return result;
+ }
+
+ /**
+ * {@code is_tracked} is - in Jython case - interpreted in the sense that
+ * {@code gc.collect} will be able to count the object as collected if it
+ * participates in a cycle. This mimics CPython behavior and passes
+ * the corresponding unit test in {@code test_gc.py}.
+ */
+ public static PyObject is_tracked(PyObject[] args, String[] kwargs) {
+ if (isTraversable(args[0]) &&
+ (monitoredObjects == null || isMonitored(args[0]))) {
+ return Py.True;
+ } else {
+ return Py.False;
+ }
+ }
+
+ /**
+ * Returns all objects from {@code pool} that are part of reference cycles as a new set.
+ * If a reference-cycle is not entirely contained in {@code pool}, it will be entirely
+ * contained in the resulting set, i.e. missing participants will be added.
+ * This method completely operates on weak references to ensure that the returned
+ * set does not manipulate gc-behavior.
+ *
+ * Note that this method is not thread-safe. Within the gc-module it is only used
+ * by the collect-method which ensures thread-safety by a synchronized block.
+ */
+ private static IdentityHashMap
+ removeNonCyclicWeakRefs(Iterable pool) {
+ @SuppressWarnings("unchecked")
+ IdentityHashMap[] pools = new IdentityHashMap[2];
+
+ pools[0] = new IdentityHashMap();
+ pools[1] = new IdentityHashMap();
+ PyObject referent;
+ if (monitorNonTraversable) {
+ //this means there might be non-traversable objects in the pool we must filter out now
+ for (WeakReferenceGC ref: pool) {
+ referent = ref.get();
+ if (referent != null && isTraversable(referent)) {
+ pools[0].put(referent, ref);
+ }
+ }
+ } else {
+ //this means the pool is already entirely traversable
+ for (WeakReferenceGC ref: pool) {
+ referent = ref.get();
+ if (referent != null)
+ pools[0].put(referent, ref);
+ }
+ }
+ IdentityHashMap tmp;
+ IdentityHashMap toProcess = new IdentityHashMap<>();
+ //We complete pools[0] with all reachable objects.
+ for (WeakReferenceGC ref: pools[0].values()) {
+ traverse((PyObject) ref.get(), ReachableFinderWeakRefs.defaultInstance, pools);
+ }
+ while (!pools[1].isEmpty()) {
+ tmp = pools[1];
+ pools[1] = toProcess;
+ toProcess = tmp;
+ pools[0].putAll(toProcess);
+ for (WeakReferenceGC ref: toProcess.values()) {
+ traverse((PyObject) ref.get(), ReachableFinderWeakRefs.defaultInstance, pools);
+ }
+ toProcess.clear();
+ }
+ //pools[0] should now be a closed set in the sense that it contains all PyObjects
+ //reachable from pools[0]. Now we are going to remove non-cyclic objects:
+
+ boolean done = false;
+ while (!done) {
+ done = true;
+ //After this loop pools[1] contains all objects from pools[0]
+ //that some object in pools[0] points to.
+ //toRemove will contain all objects from pools[0] that don't
+ //point to any object in pools[0]. Removing toRemove from
+ //pools[1] and repeating this procedure until nothing changes
+ //any more will let only cyclic trash remain.
+ for (WeakReferenceGC ref: pools[0].values()) {
+ RefInListFinder.defaultInstance.found = false;
+ referent = ref.get();
+ traverse(referent , RefInListFinder.defaultInstance, pools);
+ if (!RefInListFinder.defaultInstance.found) {
+ toProcess.put(referent, ref);
+ done = false;
+ }
+ }
+ for (PyObject ref: toProcess.keySet()) {
+ pools[1].remove(ref);
+ }
+ toProcess.clear();
+ done = done && pools[0].size() == pools[1].size();
+ tmp = pools[0];
+ tmp.clear();
+ pools[0] = pools[1];
+ pools[1] = tmp;
+ }
+ return pools[0];
+ }
+
+ /**
+ * Computes the set of objects reachable from {@code pool}, not necessarily
+ * including {@code pool} itself; only those objects from {@code pool} that are
+ * reachable from at least one other object in {@code pool} will be included
+ * in the result.
+ */
+ private static Set findReachables(Iterable pool) {
+ @SuppressWarnings("unchecked")
+ IdentityHashMap[] pools = new IdentityHashMap[2];
+
+ pools[0] = new IdentityHashMap();
+ pools[1] = new IdentityHashMap();
+ IdentityHashMap tmp;
+ IdentityHashMap toProcess = new IdentityHashMap<>();
+
+ //We complete pools[0] with all reachable objects.
+ //Note the difference to the implementation in removeNonCyclic.
+ //There pools[0] was initialized with the contents of pool and
+ //then used here as iteration source. In contrast to that we don't
+ //want to have pool contained in the reachable set in any case here.
+ for (PyObject obj: pool) {
+ if (isTraversable(obj)) {
+ traverse(obj, ReachableFinder.defaultInstance, pools);
+ }
+ }
+ while (!pools[1].isEmpty()) {
+ tmp = pools[1];
+ pools[1] = toProcess;
+ toProcess = tmp;
+ pools[0].putAll(toProcess);
+ for (PyObject obj: toProcess.keySet()) {
+ traverse(obj, ReachableFinder.defaultInstance, pools);
+ }
+ toProcess.clear();
+ }
+ //pools[0] should now be a closed set in the sense that it contains all PyObjects
+ //reachable from pools[0].
+ return pools[0].keySet();
+ }
+
+ /**
+ * Returns all objects from {@code pool} that are part of reference-cycles as a new set.
+ * If a reference-cycle is not entirely contained in {@code pool}, it will be entirely
+ * contained in the resulting set, i.e. missing participants will be added.
+ * This method completely operates on weak references to ensure that the returned
+ * set does not manipulate gc-behavior.
+ *
+ * Note that this method is not thread-safe. Within the gc-module it is only used
+ * by the collect-method which ensures thread-safety by a synchronized block.
+ */
+ private static Set removeNonCyclic(Iterable pool) {
+ @SuppressWarnings("unchecked")
+ IdentityHashMap[] pools = new IdentityHashMap[2];
+
+ pools[0] = new IdentityHashMap();
+ pools[1] = new IdentityHashMap();
+ if (monitorNonTraversable) {
+ //this means there might be non-traversable objects in the pool we must filter out now
+ for (PyObject obj: pool) {
+ if (isTraversable(obj)) {
+ pools[0].put(obj, obj);
+ }
+ }
+ } else {
+ //this means the pool is already entirely traversable
+ for (PyObject obj: pool) {
+ pools[0].put(obj, obj);
+ }
+ }
+ IdentityHashMap tmp;
+ IdentityHashMap toProcess = new IdentityHashMap<>();
+
+ //We complete pools[0] with all reachable objects.
+ for (PyObject obj: pools[0].keySet()) {
+ traverse(obj, ReachableFinder.defaultInstance, pools);
+ }
+ while (!pools[1].isEmpty()) {
+ tmp = pools[1];
+ pools[1] = toProcess;
+ toProcess = tmp;
+ pools[0].putAll(toProcess);
+ for (PyObject obj: toProcess.keySet()) {
+ traverse(obj, ReachableFinder.defaultInstance, pools);
+ }
+ toProcess.clear();
+ }
+ //pools[0] now is a closed set in the sense that it contains all PyObjects
+ //reachable from pools[0]. Now we are going to remove non-cyclic objects:
+
+ boolean done = false;
+ while (!done) {
+ done = true;
+ // After this loop pools[1] contains all objects from pools[0]
+ // that some object in pools[0] points to.
+ // toRemove will contain all objects from pools[0] that don't
+ // point to any object in pools[0]. Removing toRemove from
+ // pools[1] and repeating this procedure until nothing changes
+ // any more will let only cyclic trash remain.
+ for (PyObject obj: pools[0].keySet()) {
+ ObjectInListFinder.defaultInstance.found = false;
+ traverse(obj, ObjectInListFinder.defaultInstance, pools);
+ if (!ObjectInListFinder.defaultInstance.found) {
+ toProcess.put(obj, obj);
+ done = false;
+ }
+ }
+ for (PyObject obj: toProcess.keySet()) {
+ pools[1].remove(obj);
+ }
+ toProcess.clear();
+ done = done && pools[0].size() == pools[1].size();
+ tmp = pools[0];
+ tmp.clear();
+ pools[0] = pools[1];
+ pools[1] = tmp;
+ }
+ return pools[0].keySet();
+ }
+
+ /**
+ * Mark all objects that are reachable from start AND can reach start,
+ * thus participate in a cycle with start.
+ */
+ public static void markCyclicObjects(PyObject start, boolean uncollectable) {
+ Set search = findCyclicObjects(start);
+ if (search == null) {
+ return;
+ }
+ //Search contains the cyclic objects that participate in a cycle with start,
+ //i.e. which are reachable from start AND can reach start.
+ //Mark these...
+ CycleMarkAttr cm;
+ for (PyObject obj: search) {
+ cm = (CycleMarkAttr) JyAttribute.getAttr(obj, JyAttribute.GC_CYCLE_MARK_ATTR);
+ if (cm == null) {
+ cm = new CycleMarkAttr(true, uncollectable);
+ JyAttribute.setAttr(obj,
+ JyAttribute.GC_CYCLE_MARK_ATTR, cm);
+ } else {
+ cm.setFlags(true, uncollectable);
+ }
+ }
+ }
+
+ /**
+ * Return objects that are reachable from start AND can reach start,
+ * thus participate in a cycle with start.
+ * Returns {@code null} if start does not participate in any cycle.
+ */
+ public static Set findCyclicObjects(PyObject start) {
+ IdentityHashMap map = findCyclicObjectsIntern(start);
+ return map == null ? null : map.keySet();
+ }
+
+ private static IdentityHashMap findCyclicObjectsIntern(PyObject start) {
+ if (!isTraversable(start)) {
+ return null;
+ }
+ //first determine the reachable set:
+ @SuppressWarnings("unchecked")
+ IdentityHashMap[] reachSearch =
+ (IdentityHashMap[]) new IdentityHashMap[2];
+ reachSearch[0] = new IdentityHashMap();
+ reachSearch[1] = new IdentityHashMap();
+ IdentityHashMap tmp, search = new IdentityHashMap();
+ traverse(start, ReachableFinder.defaultInstance, reachSearch);
+ tmp = search;
+ search = reachSearch[1];
+ tmp.clear();
+ reachSearch[1] = tmp;
+ while (!search.isEmpty()) {
+ reachSearch[0].putAll(search);
+ for (PyObject obj: search.keySet()) {
+ traverse(obj, ReachableFinder.defaultInstance, reachSearch);
+ }
+ tmp = search;
+ search = reachSearch[1];
+ tmp.clear();
+ reachSearch[1] = tmp;
+ }
+ //reachSearch[0] is now the reachable set, but still contains non-cyclic objects
+ if (!reachSearch[0].containsKey(start)) {
+ return null;
+ }
+ search.clear();
+ search.put(start, start);
+ boolean changed = true;
+ while (changed) {
+ changed = false;
+ for (PyObject obj: reachSearch[0].keySet()) {
+ if (traverse(obj, RefersToSetFinder.defaultInstance, search.keySet()) == 1) {
+ changed = true;
+ tmp.put(obj, obj);
+ }
+ }
+ //move all objects that can reach start from reachSearch[0] to search
+ search.putAll(tmp);
+ for (PyObject key: tmp.keySet()) {
+ reachSearch[0].remove(key);
+ }
+ tmp.clear();
+ }
+ return search;
+ }
+
+ public static int traverse(PyObject ob, Visitproc visit, Object arg) {
+ int retVal;
+ boolean traversed = false;
+ if (ob instanceof Traverseproc) {
+ retVal = ((Traverseproc) ob).traverse(visit, arg);
+ traversed = true;
+ if (retVal != 0) return retVal;
+ }
+ if (ob instanceof TraverseprocDerived) {
+ retVal = ((TraverseprocDerived) ob).traverseDerived(visit, arg);
+ traversed = true;
+ if (retVal != 0) return retVal;
+ }
+ if ((gcFlags & SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING) == 0) {
+ if (! (ob instanceof Traverseproc || ob instanceof TraverseprocDerived ||
+ ob.getClass() == PyObject.class ||
+ ob.getClass().isAnnotationPresent(Untraversable.class)) ) {
+ Py.writeWarning("gc", "The PyObject-subclass "+ob.getClass().getName()+"\n" +
+ "should either implement Traverseproc or be marked with the\n" +
+ "@Untraversable annotation. See the instructions\n" +
+ "in javadoc of org.python.core.Traverseproc.java.");
+ }
+ }
+ if ((gcFlags & DONT_TRAVERSE_BY_REFLECTION) != 0) {
+ return 0;
+ }
+ Class> cls = ob.getClass();
+ if (traversed || cls == PyObject.class ||
+ cls.isAnnotationPresent(Untraversable.class)) {
+ return 0;
+ }
+ if ((gcFlags & SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING) == 0) {
+ Py.writeWarning("gc", "Traverse by reflection: "+ob.getClass().getName()+"\n" +
+ "This is an inefficient procedure. It is recommended to\n" +
+ "implement the traverseproc mechanism properly.");
+ }
+ return traverseByReflection(ob, visit, arg);
+ }
+
+ /**
+ *
+ * This method recursively traverses fields of {@code ob}.
+ * If a field is a PyObject, it is passed to {@code visit}.
+ * and recursion ends in that branch.
+ * If a field is an array, the elements are checked whether
+ * they are PyObjects. {@code PyObject}-elements are passed to
+ * {@code visit}. Elements that are arrays themselves are again
+ * processed elementwise and so on.
+ *
+ *
+ * Through the whole search this method fails fast if
+ * {@code visit} returns non-zero.
+ *
+ *
+ * Note that we intentionally don't traverse iterables by
+ * iterating them. Since we perform recursion, this should reach
+ * all contained references anyway - in Java every object
+ * can only contain references as fields or arrays.
+ * On the one hand, exploiting iterables would ease the
+ * access to private fields, but on the other hand during
+ * iteration they might change inner state, create
+ * new (Py)Objects or obtain objects from native methods.
+ * Additionally we might run into concurrent modification issues.
+ * So all in all the traversal is cleaner and safer if just
+ * fields and arrays are traversed.
+ *
+ */
+ public static int traverseByReflection(Object ob, Visitproc visit, Object arg) {
+ IdentityHashMap alreadyTraversed = new IdentityHashMap<>();
+ alreadyTraversed.put(ob, ob);
+ return traverseByReflectionIntern(ob, alreadyTraversed, visit, arg);
+ }
+
+ private static int traverseByReflectionIntern(Object ob,
+ IdentityHashMap alreadyTraversed, Visitproc visit, Object arg) {
+ Class extends Object> cls = ob.getClass();
+ int result;
+ Object element;
+ if (cls.isArray() && canLinkToPyObject(cls.getComponentType(), false)) {
+ for (int i = 0; i < Array.getLength(ob); ++i) {
+ element = Array.get(ob, i);
+ if (element != null && !alreadyTraversed.containsKey(element)) {
+ alreadyTraversed.put(element, element);
+ if (element instanceof PyObject) {
+ result = visit.visit((PyObject) element, arg);
+ } else {
+ result = traverseByReflectionIntern(element,
+ alreadyTraversed, visit, arg);
+ }
+ if (result != 0) {
+ return result;
+ }
+ }
+ }
+ } else {
+ while (cls != Object.class && cls != PyObject.class) {
+ Field[] declFields = cls.getDeclaredFields();
+ for (int i = 0; i < declFields.length; ++i) {
+ if (!Modifier.isStatic(declFields[i].getModifiers()) &&
+ !declFields[i].getType().isPrimitive()) {
+ if (!declFields[i].isAccessible()) {
+ declFields[i].setAccessible(true);
+ }
+ if (canLinkToPyObject(declFields[i].getType(), false)) {
+ try {
+ element = declFields[i].get(ob);
+ if (!alreadyTraversed.containsKey(element)) {
+ alreadyTraversed.put(element, element);
+ if (element instanceof PyObject) {
+ result = visit.visit((PyObject) element, arg);
+ } else {
+ result = traverseByReflectionIntern(element,
+ alreadyTraversed, visit, arg);
+ }
+ if (result != 0) {
+ return result;
+ }
+ }
+ } catch (Exception e) {}
+ }
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ *
+ * This method checks via type-checking-only, whether an object
+ * of the given class can in principle hold a ref to a {@code PyObject}.
+ * Especially if arrays are involved, this can safe a lot performance.
+ * For now, no generic-type info is exploited.
+ *
+ *
+ * If {@code actual} is true, the answer will hold for an object
+ * that is an instance of the given class.
+ * Otherwise it is assumed that cls is the type of a field holding an
+ * object, so cls is considered as upper bound for an objects actual
+ * type.
+ *
+ *
+ * One should call with {@code actual == true}, if cls was obtained
+ * by {@code ob.getClass()} and with {@code actual == false}, if cls
+ * was obtained as a field-type or component-type of an array.
+ *
+ */
+ public static boolean canLinkToPyObject(Class> cls, boolean actual) {
+ // At first some quick fail fast/succeed fast-checks:
+ if (quickCheckCannotLinkToPyObject(cls)) {
+ return false;
+ }
+ if (!actual && (!Modifier.isFinal(cls.getModifiers()))) {
+ return true; //a subclass could contain anything
+ }
+ if (quickCheckCanLinkToPyObject(cls)) {
+ return true;
+ }
+ if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers())) {
+ return true;
+ }
+ if (cls.isArray()) {
+ return canLinkToPyObject(cls.getComponentType(), false);
+ }
+ Class> cls2 = cls;
+
+ // Fail fast if no fields exist in cls:
+ int fieldCount = cls2.getDeclaredFields().length;
+ while (fieldCount == 0 && cls2 != Object.class) {
+ cls2 = cls2.getSuperclass();
+ fieldCount += cls.getDeclaredFields().length;
+ }
+ if (fieldCount == 0) {
+ return false;
+ }
+ IdentityHashMap, Class>> alreadyChecked = new IdentityHashMap<>();
+ alreadyChecked.put(cls, cls);
+ cls2 = cls;
+ Class> ft;
+ while (cls2 != Object.class) {
+ for (Field f: cls2.getDeclaredFields()) {
+ if (!Modifier.isStatic(f.getModifiers())) {
+ ft = f.getType();
+ if (!ft.isPrimitive() && !alreadyChecked.containsKey(ft)) {
+ alreadyChecked.put(ft, ft);
+ if (canLinkToPyObjectIntern(ft, alreadyChecked)) {
+ return true;
+ }
+ }
+ }
+ }
+ cls2 = cls2.getSuperclass();
+ }
+ return false;
+ }
+
+ private static boolean quickCheckCanLinkToPyObject(Class> cls) {
+ if (!Modifier.isFinal(cls.getModifiers())) {
+ return true;
+ }
+ if (cls.isAssignableFrom(PyObject.class)) {
+ return true;
+ }
+ if (PyObject.class.isAssignableFrom(cls)) {
+ return true;
+ }
+ if (cls.isArray()) {
+ return quickCheckCanLinkToPyObject(cls.getComponentType());
+ }
+ return false;
+ }
+
+ private static boolean quickCheckCannotLinkToPyObject(Class> cls) {
+ if (cls.isPrimitive()) {
+ return true;
+ }
+ if (cls == String.class || cls == Class.class ||
+ cls == Field.class || cls == java.lang.reflect.Method.class) {
+ return true;
+ }
+ if (cls.isArray()) {
+ return quickCheckCannotLinkToPyObject(cls.getComponentType());
+ }
+ return false;
+ }
+
+ private static boolean canLinkToPyObjectIntern(Class> cls,
+ IdentityHashMap, Class>> alreadyChecked) {
+ if (quickCheckCanLinkToPyObject(cls)) {
+ return true;
+ }
+ if (quickCheckCannotLinkToPyObject(cls)) {
+ return false;
+ }
+ if (cls.isArray()) {
+ return canLinkToPyObjectIntern(cls.getComponentType(), alreadyChecked);
+ }
+ Class> cls2 = cls;
+ Class> ft;
+ while (cls2 != Object.class) {
+ for (Field f: cls2.getDeclaredFields()) {
+ if (!Modifier.isStatic(f.getModifiers())) {
+ ft = f.getType();
+ if (!ft.isPrimitive() && !alreadyChecked.containsKey(ft)) {
+ alreadyChecked.put(ft, ft);
+ if (canLinkToPyObjectIntern(ft, alreadyChecked)) {
+ return true;
+ }
+ }
+ }
+ }
+ cls2 = cls2.getSuperclass();
+ }
+ return false;
+ }
+
+ public static boolean isTraversable(PyObject ob) {
+ if (ob == null) {
+ return false;
+ }
+ if (ob instanceof Traverseproc || ob instanceof TraverseprocDerived) {
+ return true;
+ }
+ if ((gcFlags & DONT_TRAVERSE_BY_REFLECTION) != 0) {
+ return false;
+ }
+ Class> cls = ob.getClass();
+ return !(cls == PyObject.class ||
+ cls.isAnnotationPresent(Untraversable.class));
+ }
+
+ //----------Visitproc section----------------------------------------------
+
+ static class ReferentsFinder implements Visitproc {
+ public static ReferentsFinder defaultInstance = new ReferentsFinder();
+
+ /**
+ * Expects arg to be a list-like {@code PyObject} where the
+ * referents will be inserted.
+ */
+ public int visit(PyObject object, Object arg) {
+ ((org.python.core.PySequenceList) arg).pyadd(object);
+ return 0;
+ }
+ }
+
+ /**
+ * Helper to find the reachable set of an object. {@code arg} must be a
+ * 2-component array of type {@code IdentityHashMap[]}.
+ * Although these are maps, the components of {@code arg} are conceptually
+ * used as sets. {@code arg[0]} shall contain all objects already known to
+ * be reachable. The visitproc adds all newly discovered objects to
+ * {@code arg[1]}, so the user can later add these to the reachable set and
+ * knows they need to be explored further. Only traversable objects are
+ * considered by this visitproc.
+ */
+ static class ReachableFinder implements Visitproc {
+ public static ReachableFinder defaultInstance = new ReachableFinder();
+
+ /**
+ * Expects arg to be a list-like {@code PyObject} where the
+ * referents will be inserted.
+ */
+ @SuppressWarnings("unchecked")
+ public int visit(PyObject object, Object arg) {
+ IdentityHashMap[] reachSearch =
+ (IdentityHashMap[]) arg;
+ if ((isTraversable(object)) &&
+ !reachSearch[0].containsKey(object)) {
+ reachSearch[1].put(object, object);
+ }
+ return 0;
+ }
+ }
+
+ static class ReachableFinderWeakRefs implements Visitproc {
+ public static ReachableFinderWeakRefs defaultInstance = new ReachableFinderWeakRefs();
+
+ @SuppressWarnings("unchecked")
+ public int visit(PyObject object, Object arg) {
+ if (isTraversable(object)) {
+ IdentityHashMap[] pools =
+ (IdentityHashMap[]) arg;
+ WeakReferenceGC ref = pools[0].get(object);
+ if (ref == null) {
+ ref = new WeakReferenceGC(object);
+ pools[1].put(object, ref);
+ }
+ }
+ return 0;
+ }
+ }
+
+ static class ReferrerFinder implements Visitproc {
+ public static ReferrerFinder defaultInstance = new ReferrerFinder();
+
+ /**
+ * Expects {@code arg} to be a 2-component array ({@code PyObject[]})
+ * consisting of the {@code PyObject} to be referred to at
+ * {@code arg[0]} and the destination list (a list-like {@code PyObject}
+ * where the referrers will be inserted) at {@code arg[1]}.
+ */
+ public int visit(PyObject object, Object arg) {
+ if (((PyObject[]) arg)[0].__eq__(object).__nonzero__()) {
+ ((org.python.core.PySequenceList) ((PyObject[]) arg)[1]).pyadd(object);
+ }
+ return 0;
+ }
+ }
+
+ /**
+ * Like {@code RefInListFinder} this visitproc looks whether the traversed object
+ * refers to one of the objects in a given set. Here we perform fail-fast
+ * behavior. This method is useful if one is not interested in the referrers,
+ * but only wants to know (quickly) whether a connection exists or not.
+ */
+ static class RefersToSetFinder implements Visitproc {
+ public static RefersToSetFinder defaultInstance = new RefersToSetFinder();
+
+ @SuppressWarnings("unchecked")
+ public int visit(PyObject object, Object arg) {
+ return ((Set) arg).contains(object) ? 1 : 0;
+ }
+ }
+
+ /**
+ * This visitproc looks whether an object refers to one of the objects in
+ * a given set.
+ * {@code arg} must be a 2-component-array of
+ * {@code HashMap}.
+ * These maps are actually used as sets, but resolve the strongref/weakref
+ * views to the objects.
+ * {@code arg[0]} is the pool we search referrers for. When the traverse method
+ * iterates through the referents of a source object, this visitproc checks
+ * for each referent, whether it is in {@code arg[0]}. If it is, then it is added
+ * to {@code arg[1]} (no double entries here since it is a set-like structure)
+ * and {@code found} is set to {@code true}.
+ * By repeated use one can collect all objects referring to a given set
+ * of objects in another set.
+ */
+ static class RefInListFinder implements Visitproc {
+ public static RefInListFinder defaultInstance = new RefInListFinder();
+ public boolean found = false;
+
+ /**
+ * Expects {@code arg} to be a 2-component array of
+ * {@link java.util.Map}s.
+ */
+ public int visit(PyObject object, Object arg) {
+ @SuppressWarnings("unchecked")
+ IdentityHashMap[] pools =
+ (IdentityHashMap[]) arg;
+ WeakReferenceGC ref = pools[0].get(object);
+ if (ref != null) {
+ pools[1].put(object, ref);
+ found = true;
+ }
+ return 0;
+ }
+ }
+
+ static class ObjectInListFinder implements Visitproc {
+ public static ObjectInListFinder defaultInstance = new ObjectInListFinder();
+ public boolean found = false;
+
+ /**
+ * Expects {@code arg} to be a 2-component array of
+ * {@link java.util.Map}s.
+ */
+ public int visit(PyObject object, Object arg) {
+ @SuppressWarnings("unchecked")
+ IdentityHashMap[] pools =
+ (IdentityHashMap[]) arg;
+ if (pools[0].containsKey(object)) {
+ pools[1].put(object, object);
+ found = true;
+ }
+ return 0;
+ }
+ }
+ //----------end of Visitproc section---------------------------------------
+}
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
@@ -8,6 +8,7 @@
import org.python.core.PyNewWrapper;
import org.python.core.PyObject;
import org.python.core.PyType;
+import org.python.core.Visitproc;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedType;
@@ -140,4 +141,50 @@
public final PyTeeIterator tee___copy__() {
return new PyTeeIterator(teeData);
}
+
+
+ /* Traverseproc implementation */
+ @Override
+ public int traverse(Visitproc visit, Object arg) {
+ int retVal = super.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ if (teeData != null) {
+ if (teeData.iterator != null) {
+ retVal = visit.visit(teeData.iterator, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ if (teeData.buffer != null) {
+ for (PyObject ob: teeData.buffer.values()) {
+ if (ob != null) {
+ retVal = visit.visit(ob, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ }
+ if (teeData.stopException != null) {
+ retVal = teeData.stopException.traverse(visit, arg);
+ if (retVal != 0) {
+ return retVal;
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
+ if (ob == null) {
+ return false;
+ } else if (super.refersDirectlyTo(ob)) {
+ return true;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
}
diff --git a/src/org/python/modules/itertools/PyTeeIteratorDerived.java b/src/org/python/modules/itertools/PyTeeIteratorDerived.java
--- a/src/org/python/modules/itertools/PyTeeIteratorDerived.java
+++ b/src/org/python/modules/itertools/PyTeeIteratorDerived.java
@@ -6,9 +6,7 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.core.finalization.FinalizablePyObjectDerived;
-public class PyTeeIteratorDerived extends PyTeeIterator implements Slotted,FinalizablePyObjectDerived {
-
- public FinalizeTrigger finalizeTrigger;
+public class PyTeeIteratorDerived extends PyTeeIterator implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived {
public PyObject getSlot(int index) {
return slots[index];
@@ -29,9 +27,23 @@
}
public void __ensure_finalizer__() {
- finalizeTrigger=FinalizeTrigger.makeTrigger(this);
+ FinalizeTrigger.ensureFinalizer(this);
}
+ /* TraverseprocDerived implementation */
+ public int traverseDerived(Visitproc visit,Object arg) {
+ int retVal;
+ for(int i=0;i
https://hg.python.org/jython/rev/3d3e16363dc1
changeset: 7578:3d3e16363dc1
user: Jim Baker
date: Wed Feb 11 10:07:13 2015 -0700
summary:
Do not attempt to expand ! in the console.
Such expansion is the default behavior in JLine2, so it just needed to
be turned off. Fixes http://bugs.jython.org/issue2268
files:
src/org/python/util/JLineConsole.java | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/src/org/python/util/JLineConsole.java b/src/org/python/util/JLineConsole.java
--- a/src/org/python/util/JLineConsole.java
+++ b/src/org/python/util/JLineConsole.java
@@ -112,6 +112,9 @@
// We find the bell too noisy
reader.setBellEnabled(false);
+ // Do not attempt to expand ! in the input
+ reader.setExpandEvents(false);
+
/*
* Everybody else, using sys.stdout or java.lang.System.out, gets to write on a special
* PrintStream that keeps the last incomplete line in case it turns out to be a console
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 11 21:49:29 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 11 Feb 2015 20:49:29 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Skip_test=5Fcloserange_if_o?=
=?utf-8?q?s=2Edup_not_present?=
Message-ID: <20150211204922.43793.9306@psf.io>
https://hg.python.org/jython/rev/c4b2de5cb824
changeset: 7580:c4b2de5cb824
user: Jim Baker
date: Wed Feb 11 13:47:29 2015 -0700
summary:
Skip test_closerange if os.dup not present
files:
Lib/test/test_os.py | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -35,6 +35,7 @@
@unittest.skipIf(test_support.is_jython and os._name == "nt",
"Does not properly close files under Windows")
+ @unittest.skipUnless(hasattr(os, "dup"), "No os.dup function")
def test_closerange(self):
first = os.open(test_support.TESTFN, os.O_CREAT|os.O_RDWR)
# We must allocate two consecutive file descriptors, otherwise
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 11 21:49:29 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 11 Feb 2015 20:49:29 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_compileall_so_that_it_c?=
=?utf-8?q?reates_=24py=2Eclass_files_even_if_=2Epyc_file_exists?=
Message-ID: <20150211204922.11116.60806@psf.io>
https://hg.python.org/jython/rev/6c38d2eda569
changeset: 7579:6c38d2eda569
user: Paolo Dina
date: Wed Feb 11 10:14:18 2015 -0700
summary:
Fix compileall so that it creates $py.class files even if .pyc file exists
The compileall module would previously skip compiling a Python module
to Java bytecode ($py.class) if a Python bytecode file existed (.pyc
file).
Fixes http://bugs.jython.org/issue1672
files:
ACKNOWLEDGMENTS | 1 +
Lib/compileall.py | 227 ++++++++++++++++++++++++
bugtests/test403.py | 34 +++
bugtests/test403/greeter.py | 2 +
bugtests/test403/test.py | 3 +
5 files changed, 267 insertions(+), 0 deletions(-)
diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS
--- a/ACKNOWLEDGMENTS
+++ b/ACKNOWLEDGMENTS
@@ -149,6 +149,7 @@
Nathaniel Kenmir
Jiwon Seo
Dieter Vandenbussche
+ Paolo Dina
Local Variables:
mode: indented-text
diff --git a/Lib/compileall.py b/Lib/compileall.py
new file mode 100644
--- /dev/null
+++ b/Lib/compileall.py
@@ -0,0 +1,227 @@
+"""Module/script to byte-compile all .py files to .pyc (or .pyo) files.
+
+When called as a script with arguments, this compiles the directories
+given as arguments recursively; the -l option prevents it from
+recursing into directories.
+
+Without arguments, if compiles all modules on sys.path, without
+recursing into subdirectories. (Even though it should do so for
+packages -- for now, you'll have to deal with packages separately.)
+
+See module py_compile for details of the actual byte-compilation.
+"""
+import os
+import sys
+import py_compile
+import struct
+import imp
+
+__all__ = ["compile_dir","compile_file","compile_path"]
+
+def compile_dir(dir, maxlevels=10, ddir=None,
+ force=0, rx=None, quiet=0):
+ """Byte-compile all modules in the given directory tree.
+
+ Arguments (only dir is required):
+
+ dir: the directory to byte-compile
+ maxlevels: maximum recursion level (default 10)
+ ddir: the directory that will be prepended to the path to the
+ file as it is compiled into each byte-code file.
+ force: if 1, force compilation, even if timestamps are up-to-date
+ quiet: if 1, be quiet during compilation
+ """
+ if not quiet:
+ print 'Listing', dir, '...'
+ try:
+ names = os.listdir(dir)
+ except os.error:
+ print "Can't list", dir
+ names = []
+ names.sort()
+ success = 1
+ for name in names:
+ fullname = os.path.join(dir, name)
+ if ddir is not None:
+ dfile = os.path.join(ddir, name)
+ else:
+ dfile = None
+ if not os.path.isdir(fullname):
+ if not compile_file(fullname, ddir, force, rx, quiet):
+ success = 0
+ elif maxlevels > 0 and \
+ name != os.curdir and name != os.pardir and \
+ os.path.isdir(fullname) and \
+ not os.path.islink(fullname):
+ if not compile_dir(fullname, maxlevels - 1, dfile, force, rx,
+ quiet):
+ success = 0
+ return success
+
+def compile_file(fullname, ddir=None, force=0, rx=None, quiet=0):
+ """Byte-compile one file.
+
+ Arguments (only fullname is required):
+
+ fullname: the file to byte-compile
+ ddir: if given, the directory name compiled in to the
+ byte-code file.
+ force: if 1, force compilation, even if timestamps are up-to-date
+ quiet: if 1, be quiet during compilation
+ """
+ success = 1
+ name = os.path.basename(fullname)
+ if ddir is not None:
+ dfile = os.path.join(ddir, name)
+ else:
+ dfile = None
+ if rx is not None:
+ mo = rx.search(fullname)
+ if mo:
+ return success
+ if os.path.isfile(fullname):
+ head, tail = name[:-3], name[-3:]
+ if tail == '.py':
+ if not force:
+ try:
+ mtime = int(os.stat(fullname).st_mtime)
+ expect = struct.pack('<4sl', imp.get_magic(), mtime)
+ cfile = fullname.replace('.py', '$py.class')
+ with open(cfile, 'rb') as chandle:
+ actual = chandle.read(8)
+ if expect == actual:
+ return success
+ except IOError:
+ pass
+ if not quiet:
+ print 'Compiling', fullname, '...'
+ try:
+ ok = py_compile.compile(fullname, None, dfile, True)
+ except py_compile.PyCompileError,err:
+ if quiet:
+ print 'Compiling', fullname, '...'
+ print err.msg
+ success = 0
+ except IOError, e:
+ print "Sorry", e
+ success = 0
+ else:
+ if ok == 0:
+ success = 0
+ return success
+
+def compile_path(skip_curdir=1, maxlevels=0, force=0, quiet=0):
+ """Byte-compile all module on sys.path.
+
+ Arguments (all optional):
+
+ skip_curdir: if true, skip current directory (default true)
+ maxlevels: max recursion level (default 0)
+ force: as for compile_dir() (default 0)
+ quiet: as for compile_dir() (default 0)
+ """
+ success = 1
+ for dir in sys.path:
+ if (not dir or dir == os.curdir) and skip_curdir:
+ print 'Skipping current directory'
+ else:
+ success = success and compile_dir(dir, maxlevels, None,
+ force, quiet=quiet)
+ return success
+
+def expand_args(args, flist):
+ """read names in flist and append to args"""
+ expanded = args[:]
+ if flist:
+ try:
+ if flist == '-':
+ fd = sys.stdin
+ else:
+ fd = open(flist)
+ while 1:
+ line = fd.readline()
+ if not line:
+ break
+ expanded.append(line[:-1])
+ except IOError:
+ print "Error reading file list %s" % flist
+ raise
+ return expanded
+
+def main():
+ """Script main program."""
+ import getopt
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'lfqd:x:i:')
+ except getopt.error, msg:
+ print msg
+ print "usage: python compileall.py [-l] [-f] [-q] [-d destdir] " \
+ "[-x regexp] [-i list] [directory|file ...]"
+ print
+ print "arguments: zero or more file and directory names to compile; " \
+ "if no arguments given, "
+ print " defaults to the equivalent of -l sys.path"
+ print
+ print "options:"
+ print "-l: don't recurse into subdirectories"
+ print "-f: force rebuild even if timestamps are up-to-date"
+ print "-q: output only error messages"
+ print "-d destdir: directory to prepend to file paths for use in " \
+ "compile-time tracebacks and in"
+ print " runtime tracebacks in cases where the source " \
+ "file is unavailable"
+ print "-x regexp: skip files matching the regular expression regexp; " \
+ "the regexp is searched for"
+ print " in the full path of each file considered for " \
+ "compilation"
+ print "-i file: add all the files and directories listed in file to " \
+ "the list considered for"
+ print ' compilation; if "-", names are read from stdin'
+
+ sys.exit(2)
+ maxlevels = 10
+ ddir = None
+ force = 0
+ quiet = 0
+ rx = None
+ flist = None
+ for o, a in opts:
+ if o == '-l': maxlevels = 0
+ if o == '-d': ddir = a
+ if o == '-f': force = 1
+ if o == '-q': quiet = 1
+ if o == '-x':
+ import re
+ rx = re.compile(a)
+ if o == '-i': flist = a
+ if ddir:
+ if len(args) != 1 and not os.path.isdir(args[0]):
+ print "-d destdir require exactly one directory argument"
+ sys.exit(2)
+ success = 1
+ try:
+ if args or flist:
+ try:
+ if flist:
+ args = expand_args(args, flist)
+ except IOError:
+ success = 0
+ if success:
+ for arg in args:
+ if os.path.isdir(arg):
+ if not compile_dir(arg, maxlevels, ddir,
+ force, rx, quiet):
+ success = 0
+ else:
+ if not compile_file(arg, ddir, force, rx, quiet):
+ success = 0
+ else:
+ success = compile_path()
+ except KeyboardInterrupt:
+ print "\n[interrupted]"
+ success = 0
+ return success
+
+if __name__ == '__main__':
+ exit_status = int(not main())
+ sys.exit(exit_status)
diff --git a/bugtests/test403.py b/bugtests/test403.py
new file mode 100644
--- /dev/null
+++ b/bugtests/test403.py
@@ -0,0 +1,34 @@
+"""
+test fix for bug #1672
+
+"""
+
+import os
+import compileall
+
+PACKAGE = "test403"
+PYC_GREETER = os.path.join(PACKAGE, "greeter.pyc")
+PYCLASS_GREETER = os.path.join(PACKAGE, "greeter$py.class")
+PYCLASS_TEST = os.path.join(PACKAGE, "test$py.class")
+
+def cleanup():
+ try:
+ for f in (PYC_GREETER, PYCLASS_TEST, PYCLASS_GREETER):
+ os.unlink(f)
+ except OSError:
+ pass
+
+
+# setup
+cleanup()
+open(PYC_GREETER, "a").close()
+
+# test
+compileall.compile_dir(PACKAGE)
+print PYCLASS_TEST
+print PYCLASS_GREETER
+assert os.path.exists(PYCLASS_TEST)
+assert os.path.exists(PYCLASS_GREETER)
+
+# teardown
+cleanup()
diff --git a/bugtests/test403/greeter.py b/bugtests/test403/greeter.py
new file mode 100644
--- /dev/null
+++ b/bugtests/test403/greeter.py
@@ -0,0 +1,2 @@
+def greet():
+ print 'Hello world'
diff --git a/bugtests/test403/test.py b/bugtests/test403/test.py
new file mode 100644
--- /dev/null
+++ b/bugtests/test403/test.py
@@ -0,0 +1,3 @@
+from greeter import greet
+
+greet()
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 11 21:49:29 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 11 Feb 2015 20:49:29 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Move_testing_of_compileall_?=
=?utf-8?q?into_Python_regrtest?=
Message-ID: <20150211204923.29538.34249@psf.io>
https://hg.python.org/jython/rev/d40cb4d16450
changeset: 7581:d40cb4d16450
user: Jim Baker
date: Wed Feb 11 13:49:17 2015 -0700
summary:
Move testing of compileall into Python regrtest
files:
Lib/test/test_compile_jy.py | 62 ++++++++++++++++++++++--
bugtests/test403.py | 34 -------------
bugtests/test403/greeter.py | 2 -
bugtests/test403/test.py | 3 -
4 files changed, 55 insertions(+), 46 deletions(-)
diff --git a/Lib/test/test_compile_jy.py b/Lib/test/test_compile_jy.py
--- a/Lib/test/test_compile_jy.py
+++ b/Lib/test/test_compile_jy.py
@@ -1,10 +1,14 @@
+import __builtin__
+import compileall
+import os
+import py_compile
+import shutil
+import subprocess
+import sys
+import textwrap
import unittest
-import os
-import sys
-import shutil
-import __builtin__
-import py_compile
-from test.test_support import run_unittest, TESTFN, is_jython
+from test.test_support import TESTFN, is_jython, run_unittest, temp_cwd
+
class TestMtime(unittest.TestCase):
@@ -50,8 +54,52 @@
finally:
shutil.rmtree(TESTFN)
+
+class TestCompileall(unittest.TestCase):
+
+ def write_code(self, package, name, code):
+ with open(os.path.join(package, name), "w") as f:
+ f.write(textwrap.dedent(code))
+
+ def test_compileall(self):
+ with temp_cwd():
+ PACKAGE = os.path.realpath("./greetings")
+ PYC_GREETER = os.path.join(PACKAGE, "greeter.pyc")
+ PYCLASS_GREETER = os.path.join(PACKAGE, "greeter$py.class")
+ PYCLASS_TEST = os.path.join(PACKAGE, "test$py.class")
+
+ os.mkdir(PACKAGE)
+ self.write_code(
+ PACKAGE, "greeter.py",
+ """
+ def greet():
+ print 'Hello world!'
+ """)
+ self.write_code(
+ PACKAGE, "test.py",
+ """
+ from greeter import greet
+ greet()
+ """)
+
+ # pretend we have a Python bytecode compiler by touching this file
+ open(PYC_GREETER, "a").close()
+
+ compileall.compile_dir(PACKAGE)
+ self.assertTrue(os.path.exists(PYC_GREETER)) # still exists
+ self.assertTrue(os.path.exists(PYCLASS_TEST)) # along with these new compiled files
+ self.assertTrue(os.path.exists(PYCLASS_GREETER))
+
+ # verify we can work with just compiled files
+ os.unlink(os.path.join(PACKAGE, "greeter.py"))
+ self.assertEqual(
+ subprocess.check_output([sys.executable, os.path.join(PACKAGE, "test.py")]).rstrip(),
+ "Hello world!")
+
+
def test_main():
- run_unittest(TestMtime)
+ run_unittest(TestMtime, TestCompileall)
+
if __name__ == "__main__":
test_main()
diff --git a/bugtests/test403.py b/bugtests/test403.py
deleted file mode 100644
--- a/bugtests/test403.py
+++ /dev/null
@@ -1,34 +0,0 @@
-"""
-test fix for bug #1672
-
-"""
-
-import os
-import compileall
-
-PACKAGE = "test403"
-PYC_GREETER = os.path.join(PACKAGE, "greeter.pyc")
-PYCLASS_GREETER = os.path.join(PACKAGE, "greeter$py.class")
-PYCLASS_TEST = os.path.join(PACKAGE, "test$py.class")
-
-def cleanup():
- try:
- for f in (PYC_GREETER, PYCLASS_TEST, PYCLASS_GREETER):
- os.unlink(f)
- except OSError:
- pass
-
-
-# setup
-cleanup()
-open(PYC_GREETER, "a").close()
-
-# test
-compileall.compile_dir(PACKAGE)
-print PYCLASS_TEST
-print PYCLASS_GREETER
-assert os.path.exists(PYCLASS_TEST)
-assert os.path.exists(PYCLASS_GREETER)
-
-# teardown
-cleanup()
diff --git a/bugtests/test403/greeter.py b/bugtests/test403/greeter.py
deleted file mode 100644
--- a/bugtests/test403/greeter.py
+++ /dev/null
@@ -1,2 +0,0 @@
-def greet():
- print 'Hello world'
diff --git a/bugtests/test403/test.py b/bugtests/test403/test.py
deleted file mode 100644
--- a/bugtests/test403/test.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from greeter import greet
-
-greet()
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Thu Feb 12 15:10:15 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Thu, 12 Feb 2015 14:10:15 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Documentation_cosmetics=2E?=
Message-ID: <20150212140929.43783.74861@psf.io>
https://hg.python.org/jython/rev/0e9c94b28eb5
changeset: 7582:0e9c94b28eb5
user: Stefan Richthofer
date: Thu Feb 12 15:09:19 2015 +0100
summary:
Documentation cosmetics.
files:
src/org/python/core/JyAttribute.java | 74 +++++----
src/org/python/core/TraverseprocDerived.java | 24 +-
src/org/python/core/Visitproc.java | 8 +-
src/org/python/modules/_weakref/AbstractReference.java | 27 +-
src/org/python/modules/_weakref/GlobalRef.java | 50 +++--
5 files changed, 103 insertions(+), 80 deletions(-)
diff --git a/src/org/python/core/JyAttribute.java b/src/org/python/core/JyAttribute.java
--- a/src/org/python/core/JyAttribute.java
+++ b/src/org/python/core/JyAttribute.java
@@ -4,16 +4,18 @@
/**
*
- * Manages a linked list of general purpose Object-attributes, which
- * can be attached to arbitrary {@code PyObject}s. This method replaces
- * the formerly used method of maintaining weak hash maps for such cases.
- * These weak hash maps were used to map {@code PyObject}s to such
- * attributes, for instance to attach
+ * Manages a linked list of general purpose Object-attributes that
+ * can be attached to arbitrary {@link org.python.core.PyObject}s.
+ * This method replaces the formerly used method of maintaining weak
+ * hash-maps ({@link java.util.WeakHashMap}) for such cases.
+ * These weak hash-maps were used to map
+ * {@code PyObject}s to such attributes, for instance
+ * to attach
* {@link org.python.modules._weakref.GlobalRef}-objects in the
* {@link org.python.modules._weakref.WeakrefModule}.
*
*
- * Attributes attached via the weak hash map method break, if the
+ * Attributes attached via the weak hash-map-method break, if the
* {@code PyObject} is resurrected in its finalizer. The
* {@code JyAttribute}-method is resurrection-safe.
*
@@ -35,18 +37,18 @@
public static final byte JAVA_PROXY_ATTR = Byte.MIN_VALUE;
/**
- * Stores list of weak references linking to this PyObject.
+ * Stores list of weak references linking to this {@code PyObject}.
* This list is weakref-based, so it does not keep the
* weakrefs alive. This is the only way to find out which
* weakrefs (i.e. org.python.modules._weakref.AbstractReference)
* linked to the object after a resurrection. A weak
- * hashmap-based approach for this purpose would break on
+ * hash-map-based approach for this purpose would break on
* resurrection.
*/
public static final byte WEAK_REF_ATTR = 0; //first transient
/**
- * Reserved for use by JyNI.
+ * Reserved for use by JyNI .
*/
public static final byte JYNI_HANDLE_ATTR = 1;
@@ -57,8 +59,9 @@
public static final byte PY_ID_ATTR = 2;
/**
- * Holds the current thread for an AbstractReference while
- * referent-retrieval is pending due to a potentially
+ * Holds the current thread for an
+ * {@link org.python.modules._weakref.AbstractReference}
+ * while referent-retrieval is pending due to a potentially
* restored-by-resurrection weak reference. After the
* restore has happened or the clear was confirmed, the
* thread is interrupted and the attribute is cleared.
@@ -66,34 +69,28 @@
public static final byte WEAKREF_PENDING_GET_ATTR = 3;
/**
- * Used by gc module to mark cyclic trash. Searching for cyclic
- * trash is usually not required by Jython. It is only done if
- * gc features are enabled that mimic CPython behavior.
+ * Used by {@link org.python.modules.gc}-module to mark cyclic
+ * trash. Searching for cyclic trash is usually not required
+ * by Jython. It is only done if gc-features are enabled that
+ * mimic CPython behavior.
*/
public static final byte GC_CYCLE_MARK_ATTR = 4;
/**
- * Used by gc module to mark monitored objects before they
- * become unmonitored for deletion. In case of a resurrection
- * this is the only way for gc to detect that the object was
- * to be monitored and should be monitored again.
+ * Used by {@link org.python.modules.gc}-module to mark
+ * finalizable objects that might have been resurrected
+ * during a delayed finalization process.
*/
- //public static final byte GC_MONITOR_MARK_ATTR = 5;
-
- /**
- * Used by gc module to mark finalizable objects that might have
- * been resurrected during a delayed finalization process.
- */
- public static final byte GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR = 6;
+ public static final byte GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR = 5;
public static final byte FINALIZE_TRIGGER_ATTR = Byte.MAX_VALUE;
private static byte nonBuiltinAttrTypeOffset = Byte.MIN_VALUE+1;
- private static byte nonBuiltinTransientAttrTypeOffset = 7;
+ private static byte nonBuiltinTransientAttrTypeOffset = 6;
/**
- * Reserves and returns a new attr type for custom use.
+ * Reserves and returns a new non-transient attr type for custom use.
*
- * @return an attr type for custom use
+ * @return a non-transient attr type for custom use
*/
public static byte reserveCustomAttrType() {
if (nonBuiltinAttrTypeOffset == 0) {
@@ -178,8 +175,8 @@
protected abstract void setValue(Object value);
/**
- * Checks whether the given {@code PyObject} has an attribute
- * of the given type attached.
+ * Checks whether the given {@link org.python.core.PyObject}
+ * has an attribute of the given type attached.
*/
public static synchronized boolean hasAttr(PyObject ob, byte attr_type) {
if (ob.attributes == null) {
@@ -197,7 +194,7 @@
/**
* Retrieves the attribute of the given type from the given
- * {@code PyObject}.
+ * {@link org.python.core.PyObject}.
* If no attribute of the given type is attached, null is returned.
*/
public static synchronized Object getAttr(PyObject ob, byte attr_type) {
@@ -214,6 +211,11 @@
return att != null && att.attr_type == attr_type ? att.getValue() : null;
}
+ /**
+ * Prints the current state of the attribute-list of the
+ * given object to the given stream.
+ * (Intended for debugging)
+ */
public static synchronized void debugPrintAttributes(PyObject o, java.io.PrintStream out) {
out.println("debugPrintAttributes of "+System.identityHashCode(o)+":");
if (o.attributes == null) {
@@ -230,6 +232,11 @@
out.println("debugPrintAttributes done");
}
+ /**
+ * Sets the attribute of type {@code attr_type} in {@code ob} to {@code value}.
+ * If no corresponding attribute exists yet, one is created. If {@value == null},
+ * the attribute is removed (if it existed at all).
+ */
public static synchronized void setAttr(PyObject ob, byte attr_type, Object value) {
if (value == null) {
delAttr(ob, attr_type);
@@ -281,6 +288,11 @@
}
}
+ /**
+ * Removes the attribute of given type from the given object's attribute-list
+ * (if it existed at all). This is equivalent to calling
+ * {@code setAttr(ob, attr_type, null)}.
+ */
public static synchronized void delAttr(PyObject ob, byte attr_type) {
if (ob.attributes == null) {
return;
diff --git a/src/org/python/core/TraverseprocDerived.java b/src/org/python/core/TraverseprocDerived.java
--- a/src/org/python/core/TraverseprocDerived.java
+++ b/src/org/python/core/TraverseprocDerived.java
@@ -1,17 +1,19 @@
package org.python.core;
/**
- * This is used like Traverseproc, but traverses only the slots[] array
- * of fooDerived classes. This way it is avoided that the traverse
- * method of a traversable PyObject is overwritten by the derived
- * version. The gc module takes care of exploiting both traverse methods.
- *
+ * This is used like {@link org.python.core.Traverseproc},
+ * but traverses only the {@code slots[]}-array of
+ * {@code fooDerived}-classes. This way we avoid that the traverse
+ * method of a traversable {@link org.python.core.PyObject} is
+ * overwritten by the derived version.
+ * The {@link org.python.modules.gc}-module takes care of
+ * exploiting both traverse methods.
*/
public interface TraverseprocDerived {
- /**
- * Traverses all reachable {@code PyObject}s.
- * Like in CPython, {@code arg} must be passed
- * unmodified to {@code visit} as its second parameter.
- */
- public int traverseDerived(Visitproc visit, Object arg);
+ /**
+ * Traverses all reachable {@code PyObject}s.
+ * Like in CPython, {@code arg} must be passed
+ * unmodified to {@code visit} as its second parameter.
+ */
+ public int traverseDerived(Visitproc visit, Object arg);
}
diff --git a/src/org/python/core/Visitproc.java b/src/org/python/core/Visitproc.java
--- a/src/org/python/core/Visitproc.java
+++ b/src/org/python/core/Visitproc.java
@@ -1,7 +1,9 @@
package org.python.core;
public interface Visitproc {
- /**Must not be called with {@code null}.
- */
- public int visit(PyObject object, Object arg);
+
+ /**
+ * Must not be called with {@code object == null}.
+ */
+ public int visit(PyObject object, Object arg);
}
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
@@ -84,9 +84,9 @@
return null;
}
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
- Py.writeDebug("gc", "pending in get of abstract ref "+this+": "+
- Thread.currentThread().getId());
- }
+ Py.writeDebug("gc", "pending in get of abstract ref "+this+": "+
+ Thread.currentThread().getId());
+ }
JyAttribute.setAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR, Thread.currentThread());
while (!gref.cleared && result == null) {
try {
@@ -96,22 +96,23 @@
}
JyAttribute.delAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR);
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
- Py.writeDebug("gc", "pending of "+this+" resolved: "+
- Thread.currentThread().getId());
- if (gref.cleared) {
- Py.writeDebug("gc", "reference was cleared.");
- } else if (result != null){
- Py.writeDebug("gc", "reference was restored.");
- } else {
- Py.writeDebug("gc", "something went very wrong.");
- }
- }
+ Py.writeDebug("gc", "pending of "+this+" resolved: "+
+ Thread.currentThread().getId());
+ if (gref.cleared) {
+ Py.writeDebug("gc", "reference was cleared.");
+ } else if (result != null){
+ Py.writeDebug("gc", "reference was restored.");
+ } else {
+ Py.writeDebug("gc", "something went very wrong.");
+ }
+ }
return result;
} else {
return result;
}
}
+
/* Traverseproc implementation */
@Override
public int traverse(Visitproc visit, Object arg) {
diff --git a/src/org/python/modules/_weakref/GlobalRef.java b/src/org/python/modules/_weakref/GlobalRef.java
--- a/src/org/python/modules/_weakref/GlobalRef.java
+++ b/src/org/python/modules/_weakref/GlobalRef.java
@@ -38,9 +38,9 @@
* This boolean is set true when the callback is processed. If the reference is
* cleared it might potentially be restored until this boolean is set true.
* If weak reference restoring is activated (c.f.
- * gc.PRESERVE_WEAKREFS_ON_RESURRECTION), AbstractReference.get would block
- * until a consistent state is reached (i.e. referent is non-null or
- * cleared == true).
+ * {@code gc.PRESERVE_WEAKREFS_ON_RESURRECTION}), {@code AbstractReference.get}
+ * would block until a consistent state is reached (i.e. referent is
+ * non-{@code null} or {@code cleared == true}).
*/
protected boolean cleared = false;
@@ -73,7 +73,7 @@
* Search for a reusable reference. To be reused, it must be of the
* same class and it must not have a callback.
*/
- synchronized AbstractReference find(Class cls) {
+ synchronized AbstractReference find(Class> cls) {
for (int i = references.size() - 1; i >= 0; i--) {
AbstractReference r = getReferenceAt(i);
if (r == null) {
@@ -123,8 +123,9 @@
/**
* Stores the callback for later processing. This is needed if
- * weak reference restoration (c.f. gc.PRESERVE_WEAKREFS_ON_RESURRECTION)
- * is activated. In this case the callback is delayed until it was
+ * weak reference restoration (c.f.
+ * {@code gc.PRESERVE_WEAKREFS_ON_RESURRECTION})
+ * is active. In this case the callback is delayed until it was
* determined whether a resurrection restored the reference.
*/
private static void delayedCallback(GlobalRef cl) {
@@ -164,10 +165,10 @@
}
/**
- * Create a new tracked GlobalRef.
+ * Create a new tracked {@code GlobalRef}.
*
- * @param object a PyObject to reference
- * @return a new tracked GlobalRef
+ * @param object a {@link org.python.core.PyObject} to reference
+ * @return a new tracked {@code GlobalRef}
*/
public static GlobalRef newInstance(PyObject object) {
createReaperThreadIfAbsent();
@@ -188,12 +189,13 @@
/**
* Restores this weak reference to its former referent.
- * This actually means that a fresh GlobalRef is created
- * and inserted into all adjacent AbstractRefs. The
- * current GlobalRef is disbanded.
- * If the given PyObject is not the former referent of
- * this weak reference, an IllegalArgumentException is
- * thrown.
+ * This actually means that a fresh {@code GlobalRef} is created
+ * and inserted into all adjacent
+ * {@link org.python.modules._weakref.AbstractReference}s. The
+ * current {@code GlobalRef} is disbanded.
+ * If the given {@link org.python.core.PyObject} is not the former
+ * referent of this weak reference, an
+ * {@link java.lang.IllegalArgumentException} is thrown.
*/
public void restore(PyObject formerReferent) {
if (JyAttribute.getAttr(formerReferent, JyAttribute.WEAK_REF_ATTR) != this) {
@@ -248,7 +250,8 @@
}
/**
- * Return the number of references to the specified PyObject.
+ * Return the number of references to the specified
+ * {@link org.python.core.PyObject}.
*
* @param object a PyObject
* @return an int reference count
@@ -259,7 +262,8 @@
}
/**
- * Return a list of references to the specified PyObject.
+ * Return a list of references to the specified
+ * {@link org.python.core.PyObject}.
*
* @param object a PyObject
* @return a PyList of references. may be empty
@@ -270,7 +274,7 @@
}
/**
- * Allow GlobalRef's to be used as hashtable keys.
+ * Allow {@code GlobalRef}s to be used as hashtable-keys.
*/
public boolean equals(Object o) {
if (this == o) {
@@ -292,18 +296,20 @@
}
/**
- * Allows GlobalRef to be used as hashtable keys.
+ * Allows {@code GlobalRef} to be used as hashtable-keys.
*
- * @return a hashCode int value
+ * @return a hashCode {@code int}-value
*/
public int hashCode() {
return hashCode;
}
/**
- * The publicly used hashCode, for the AbstractReference wrapper.
+ * The publicly used {@code hashCode}, for the
+ * {@link org.python.modules._weakref.AbstractReference}
+ * wrapper.
*
- * @return a hashCode int value
+ * @return a hashCode {@code int}-value
*/
public int pythonHashCode() {
if (havePythonHashCode) {
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Fri Feb 13 05:10:07 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Fri, 13 Feb 2015 04:10:07 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Various_doc-_and_style-impr?=
=?utf-8?q?ovements=2E?=
Message-ID: <20150213041001.11132.66363@psf.io>
https://hg.python.org/jython/rev/17d18c6dde96
changeset: 7583:17d18c6dde96
user: Stefan Richthofer
date: Fri Feb 13 05:09:50 2015 +0100
summary:
Various doc- and style-improvements.
files:
src/org/python/core/PyBytecode.java | 15 +-
src/org/python/core/PyObject.java | 23 +-
src/org/python/core/Traverseproc.java | 18 +-
src/org/python/core/TraverseprocDerived.java | 2 +-
src/org/python/core/finalization/FinalizeTrigger.java | 38 +-
src/org/python/modules/_weakref/AbstractReference.java | 10 +-
src/org/python/modules/_weakref/GlobalRef.java | 33 +-
src/org/python/modules/_weakref/ReferenceType.java | 6 +-
src/org/python/modules/gc.java | 425 +++++++++-
9 files changed, 483 insertions(+), 87 deletions(-)
diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java
--- a/src/org/python/core/PyBytecode.java
+++ b/src/org/python/core/PyBytecode.java
@@ -164,13 +164,13 @@
enum Why {
- NOT, /* No error */
+ NOT, /* No error */
EXCEPTION, /* Exception occurred */
- RERAISE, /* Exception re-raised by 'finally' */
- RETURN, /* 'return' statement */
- BREAK, /* 'break' statement */
- CONTINUE, /* 'continue' statement */
- YIELD /* 'yield' operator */
+ RERAISE, /* Exception re-raised by 'finally' */
+ RETURN, /* 'return' statement */
+ BREAK, /* 'break' statement */
+ CONTINUE, /* 'continue' statement */
+ YIELD /* 'yield' operator */
};
@@ -1054,7 +1054,8 @@
break;
case Opcode.WITH_CLEANUP: {
- /* TOP is the context.__exit__ bound method.
+ /*
+ TOP is the context.__exit__ bound method.
Below that are 1-3 values indicating how/why
we entered the finally clause:
- SECOND = None
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
@@ -20,7 +20,7 @@
/**
* All objects known to the Jython runtime system are represented by an instance
- * of the class PyObject
or one of its subclasses.
+ * of the class {@code PyObject} or one of its subclasses.
*/
@ExposedType(name = "object", doc = BuiltinDocs.object_doc)
public class PyObject implements Serializable {
@@ -28,10 +28,14 @@
public static final PyType TYPE = PyType.fromClass(PyObject.class);
/**
- * This should have been suited at org.python.modules.gc, but that would cause
- * a dependency cycle in the init-phases of gc.class and PyObject.class.
- * Now this boolean mirrors the presence of the MONITOR_GLOBAL-flag of
- * Jython's gc module.
+ * This should have been suited at {@link org.python.modules.gc},
+ * but that would cause a dependency cycle in the init-phases of
+ * {@code gc.class} and {@code PyObject.class}. Now this boolean
+ * mirrors the presence of the
+ * {@link org.python.modules.gc#MONITOR_GLOBAL}-flag in Jython's
+ * gc module.
+ *
+ * Do not change manually.
*/
public static boolean gcMonitorGlobal = false;
@@ -45,12 +49,15 @@
* objects can be accessed by the methods and keys in
* {@link org.python.core.JyAttribute}.
* A notable attribute is the javaProxy (accessible via
- * {@code JyAttribute.getAttr(this, JAVA_PROXY_ATTR)}),
+ * {@code JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR)}),
* an underlying Java instance that this object is wrapping or is a
* subclass of. Anything attempting to use the proxy should go through
* {@link #getJavaProxy()} which ensures that it's initialized.
+ *
+ * @see org.python.core.JyAttribute
+ * @see org.python.core.JyAttribute#JAVA_PROXY_ATTR
+ * @see #getJavaProxy()
*/
- //protected Object javaProxy;
protected Object attributes;
/** Primitives classes their wrapper classes. */
@@ -1708,7 +1715,6 @@
public PyObject _is(PyObject o) {
// Access javaProxy directly here as is is for object identity, and at best getJavaProxy
// will initialize a new object with a different identity
- //return this == o || (javaProxy != null && javaProxy == o.javaProxy) ? Py.True : Py.False;
return this == o || (JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) &&
JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) ==
JyAttribute.getAttr(o, JyAttribute.JAVA_PROXY_ATTR)) ? Py.True : Py.False;
@@ -1723,7 +1729,6 @@
public PyObject _isnot(PyObject o) {
// Access javaProxy directly here as is is for object identity, and at best getJavaProxy
// will initialize a new object with a different identity
- //return this != o && (javaProxy == null || javaProxy != o.javaProxy) ? Py.True : Py.False;
return this != o && (!JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) ||
JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) !=
JyAttribute.getAttr(o, JyAttribute.JAVA_PROXY_ATTR)) ? Py.True : Py.False;
diff --git a/src/org/python/core/Traverseproc.java b/src/org/python/core/Traverseproc.java
--- a/src/org/python/core/Traverseproc.java
+++ b/src/org/python/core/Traverseproc.java
@@ -20,8 +20,7 @@
* tracefunc in {@link org.python.core.PyFrame}).
* PyObjects that don't own references to other PyObjects under any condition
* and neither inherit such references from a superclass are strictly recommended
- * to be annotated {@code {@literal @}Untraversable}
- * (see {@link org.python.core.Untraversable} for details).
+ * to be annotated {@link org.python.core.Untraversable}.
*
*
* Jython's traverse mechanism serves debugging purposes to ease finding memory
@@ -42,14 +41,16 @@
* Note that the slots-array and - if existent - the user-dict of fooDerived classes
* is traversed by {@link org.python.core.TraverseProcDerived}.
* The gc module takes care of exploiting both traverse methods in its static traverse
- * method. So for manual traversion one should always use {@code gc.traverse} rather
+ * method. So for manual traversion one should always use
+ * {@link org.python.modules.gc#traverse(PyObject, Visitproc, Object)} rather
* than directly calling methods in this interface.
*
*
- * Also note that {@code objtype} is not subject to {@code Traverseproc}s
- * by default. In CPython only objects with heap-types traverse their
- * ob_type field. In Jython, {@code fooDerived}-classes are the
- * equivalents of heapTypes. For such classes, objtype is actually
+ * Also note that {@link org.python.core.PyObject#objtype} is not subject to
+ * {@code Traverseproc}s by default. In CPython only objects with heap-types
+ * traverse their {@code ob_type}-field. In Jython, {@code fooDerived}-classes
+ * are the equivalents of heapTypes. For such classes
+ * {@link org.python.core.PyObject#objtype} is actually
* traversed (along with the user dict).
*
*
@@ -439,6 +440,9 @@
* UAdd - no refs, extends PythonTree
* USub - no refs, extends PythonTree
*
+ * @see org.python.core.Untraversable
+ * @see org.python.core.Visitproc
+ * @see org.python.modules.gc#traverse(PyObject, Visitproc, Object)
*/
public interface Traverseproc {
diff --git a/src/org/python/core/TraverseprocDerived.java b/src/org/python/core/TraverseprocDerived.java
--- a/src/org/python/core/TraverseprocDerived.java
+++ b/src/org/python/core/TraverseprocDerived.java
@@ -6,7 +6,7 @@
* {@code fooDerived}-classes. This way we avoid that the traverse
* method of a traversable {@link org.python.core.PyObject} is
* overwritten by the derived version.
- * The {@link org.python.modules.gc}-module takes care of
+ * {@link org.python.modules.gc#traverse(PyObject, Visitproc, Object)} takes care of
* exploiting both traverse methods.
*/
public interface TraverseprocDerived {
diff --git a/src/org/python/core/finalization/FinalizeTrigger.java b/src/org/python/core/finalization/FinalizeTrigger.java
--- a/src/org/python/core/finalization/FinalizeTrigger.java
+++ b/src/org/python/core/finalization/FinalizeTrigger.java
@@ -12,35 +12,15 @@
public class FinalizeTrigger {
/**
* This flag tells the finalize trigger to call
- * gc.notifyFinalize after it called the finalizer.
+ * {@link gc#notifyFinalize(PyObject)} after it called the finalizer.
*/
public static final byte NOTIFY_GC_FLAG = (1<<0);
-
- /**
- * This flag tells the finalize trigger to refrain from actually
- * running the PyObject's {@code __del__} method (or variants for
- * derived or builtins).
- * It can be used to have finalize triggers for debugging and
- * monitoring purposes. The actual purpose is for Jython gc's
- * {@code DONT_FINALIZE_CYCLIC_GARBAGE} flag that tells the gc to emulate
- * CPython's <3.4 policy never to finalize cyclic garbage.
- */
- //public static final byte INHIBIT_FINALIZER_FLAG = (1<<1);
-
- /**
- * Tells the finalizer to add the finalized PyObject to the gc's
- * garbage list. This allows gc to mimic CPython's way to deal
- * with cyclic finalizable objects prior 3.4
- * (c.f. CPython's gc's DEBUG_SAVEALL flag).
- */
- //public static final byte ADD_TO_GARBAGE_LIST_FLAG = (1<<2);
/**
- * Similar to {@code INHIBIT_FINALIZER_FLAG}, but indicates that the
- * underlying PyObject was never intended to be finalized, while
- * {@code INHIBIT_FINALIZER_FLAG} indicates that there actually *is* a
- * finalizer that is just not processed due to special
- * circumstances (i.e. inactive {@code DONT_FINALIZE_CYCLIC_GARBAGE} flag).
+ * Indicates that the underlying PyObject was never intended to be finalized.
+ * It is actually not finalizable and the trigger only exists to notify
+ * {@link org.python.modules.gc} that the underlying object was finalized.
+ * This is needed for some advanced gc-functionality.
*/
public static final byte NOT_FINALIZABLE_FLAG = (1<<3);
@@ -186,7 +166,7 @@
}
}
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", "finalization of "+toFinalize);
+ gc.writeDebug("gc", "finalization of "+toFinalize);
}
if (saveGarbage == 1 || (saveGarbage == 0 &&
(gc.get_debug() & gc.DEBUG_SAVEALL) != 0 && isCyclic())) {
@@ -199,13 +179,13 @@
}
gc.garbage.add(toFinalize);
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", toFinalize+" added to garbage.");
+ gc.writeDebug("gc", toFinalize+" added to garbage.");
}
}
}
if ((flags & NOTIFY_GC_FLAG) != 0) {
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", "notify finalization of "+toFinalize);
+ gc.writeDebug("gc", "notify finalization of "+toFinalize);
}
gc.notifyFinalize(toFinalize);
flags &= ~NOTIFY_GC_FLAG;
@@ -217,7 +197,7 @@
gc.notifyPreFinalization();
if (gc.delayedFinalizationEnabled() && toFinalize != null) {
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", "delayed finalization for "+toFinalize);
+ gc.writeDebug("gc", "delayed finalization for "+toFinalize);
}
gc.registerForDelayedFinalization(toFinalize);
} else {
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
@@ -84,7 +84,7 @@
return null;
}
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
- Py.writeDebug("gc", "pending in get of abstract ref "+this+": "+
+ gc.writeDebug("gc", "pending in get of abstract ref "+this+": "+
Thread.currentThread().getId());
}
JyAttribute.setAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR, Thread.currentThread());
@@ -96,14 +96,14 @@
}
JyAttribute.delAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR);
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
- Py.writeDebug("gc", "pending of "+this+" resolved: "+
+ gc.writeDebug("gc", "pending of "+this+" resolved: "+
Thread.currentThread().getId());
if (gref.cleared) {
- Py.writeDebug("gc", "reference was cleared.");
+ gc.writeDebug("gc", "reference was cleared.");
} else if (result != null){
- Py.writeDebug("gc", "reference was restored.");
+ gc.writeDebug("gc", "reference was restored.");
} else {
- Py.writeDebug("gc", "something went very wrong.");
+ gc.writeDebug("gc", "something went very wrong.");
}
}
return result;
diff --git a/src/org/python/modules/_weakref/GlobalRef.java b/src/org/python/modules/_weakref/GlobalRef.java
--- a/src/org/python/modules/_weakref/GlobalRef.java
+++ b/src/org/python/modules/_weakref/GlobalRef.java
@@ -20,8 +20,8 @@
public class GlobalRef extends WeakReference {
/**
- * This reference's hashCode: the System.identityHashCode of the referent. Only used
- * internally.
+ * This reference's hashCode: The {@code System.identityHashCode} of the referent.
+ * Only used internally.
*/
private int hashCode;
@@ -31,16 +31,19 @@
*/
private int pythonHashCode;
- /** Whether pythonHashCode was already determined. */
+ /** Whether {@link #pythonHashCode} was already determined. */
private boolean havePythonHashCode;
/**
- * This boolean is set true when the callback is processed. If the reference is
- * cleared it might potentially be restored until this boolean is set true.
- * If weak reference restoring is activated (c.f.
- * {@code gc.PRESERVE_WEAKREFS_ON_RESURRECTION}), {@code AbstractReference.get}
+ * This boolean is set {@code true} when the callback is processed.
+ * If the reference is cleared it might potentially be restored until
+ * this boolean is set true. If weak reference restoring is activated (c.f.
+ * {@link gc#PRESERVE_WEAKREFS_ON_RESURRECTION}), {@link AbstractReference#get()}
* would block until a consistent state is reached (i.e. referent is
* non-{@code null} or {@code cleared == true}).
+ *
+ * @see gc#PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see AbstractReference#get()
*/
protected boolean cleared = false;
@@ -108,7 +111,10 @@
}
/**
- * Call all callbacks that were enqueued via delayedCallback method.
+ * Call all callbacks that were enqueued via
+ * {@link #delayedCallback(GlobalRef)} method.
+ *
+ * @see #delayedCallback(GlobalRef)
*/
public static void processDelayedCallbacks() {
if (delayedCallbacks != null) {
@@ -124,9 +130,11 @@
/**
* Stores the callback for later processing. This is needed if
* weak reference restoration (c.f.
- * {@code gc.PRESERVE_WEAKREFS_ON_RESURRECTION})
+ * {@link gc#PRESERVE_WEAKREFS_ON_RESURRECTION})
* is active. In this case the callback is delayed until it was
* determined whether a resurrection restored the reference.
+ *
+ * @see gc#PRESERVE_WEAKREFS_ON_RESURRECTION
*/
private static void delayedCallback(GlobalRef cl) {
if (delayedCallbacks == null) {
@@ -196,6 +204,9 @@
* If the given {@link org.python.core.PyObject} is not the former
* referent of this weak reference, an
* {@link java.lang.IllegalArgumentException} is thrown.
+ *
+ * @throws java.lang.IllegalArgumentException if {@code formerReferent} is not
+ * the actual former referent.
*/
public void restore(PyObject formerReferent) {
if (JyAttribute.getAttr(formerReferent, JyAttribute.WEAK_REF_ATTR) != this) {
@@ -265,8 +276,8 @@
* Return a list of references to the specified
* {@link org.python.core.PyObject}.
*
- * @param object a PyObject
- * @return a PyList of references. may be empty
+ * @param object a {@link org.python.core.PyObject}
+ * @return a {@link org.python.core.PyList} of references. May be empty.
*/
public static PyList getRefs(PyObject object) {
GlobalRef ref = objects.get(new GlobalRef(object));
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
@@ -68,9 +68,9 @@
* to passthru).
*
* @param funcName the name of the caller
- * @param args PyObject array of args
- * @param keywords String array of keywords
- * @return an ArgParser instance
+ * @param args {@link or.python.core.PyObject} array of args
+ * @param keywords {@code String}-array of keywords
+ * @return an {@link or.python.core.ArgParser} instance
*/
private static ArgParser parseInitArgs(String funcName, PyObject[] args, String[] keywords) {
if (keywords.length > 0) {
diff --git a/src/org/python/modules/gc.java b/src/org/python/modules/gc.java
--- a/src/org/python/modules/gc.java
+++ b/src/org/python/modules/gc.java
@@ -18,6 +18,7 @@
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyInstance;
+import org.python.core.PyString;
import org.python.core.Traverseproc;
import org.python.core.TraverseprocDerived;
import org.python.core.Visitproc;
@@ -25,9 +26,11 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.modules._weakref.GlobalRef;
+import com.sun.management.GarbageCollectionNotificationInfo;
+
public class gc {
/**
- * A constant that can occur as result of {@code gc.collect} and
+ * A constant that can occur as result of {@link #collect()} and
* indicates an unknown number of collected cyclic trash.
* It is intentionally not valued -1 as that value is
* reserved to indicate an error.
@@ -36,9 +39,14 @@
/* Jython-specific gc-flags: */
/**
- * Tells every newly created PyObject to register for
- * gc-monitoring. This allows {@code gc.collect} to report the
+ * This flag tells every newly created PyObject to register for
+ * gc-monitoring. This allows {@link #collect()} to report the
* number of collected objects.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short MONITOR_GLOBAL = (1<<0);
@@ -47,6 +55,11 @@
* PyObjects, while Jython does this by default. This flag
* tells Jython's gc to mimic CPython <3.4 behavior (i.e.
* add such objects to {@code gc.garbage} list instead).
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short DONT_FINALIZE_CYCLIC_GARBAGE = (1<<1);
@@ -65,6 +78,11 @@
* delay garbage collection of some weak referenced objects
* for several gc cycles if activated. So we recommend to
* use it only for debugging.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short PRESERVE_WEAKREFS_ON_RESURRECTION = (1<<2);
@@ -80,6 +98,11 @@
* significant cost as it can delay collection of many objects
* for several gc-cycles. Its main intention is for debugging
* resurrection-sensitive code.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short DONT_FINALIZE_RESURRECTED_OBJECTS = (1<<3);
@@ -88,6 +111,11 @@
* is deactivated by default for now. This means that
* {@code DONT_TRAVERSE_BY_REFLECTION} is set by default.
* Once it is stable, reflection-based traversion will be active by default.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short DONT_TRAVERSE_BY_REFLECTION = (1<<4);
@@ -109,6 +137,11 @@
* This is because in an ideal implementation reflection-based traversion never
* occurs; it is only an inefficient fallback.
*
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING = (1<<5);
@@ -119,14 +152,65 @@
* gc-messages, no matter what overall verbose level is selected.
* This flag tells Jython to use {@code Py.writeDebug} for debugging output.
* If it is not set (default-case), gc-debugging output (if gc-{@code VERBOSE}
- * or -{@code DEBUG} flags are set) is directly written to {@code System.err}.
+ * or -{@code DEBUG} flags are set) is directly written to {@code System.err}.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short USE_PY_WRITE_DEBUG = (1<<6);
+ /**
+ * Enables collection-related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_COLLECT = (1<<7);
+
+ /**
+ * Enables weakref-related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_WEAKREF = (1<<8);
+
+ /**
+ * Enables delayed finalization related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_DELAYED = (1<<9);
+
+ /**
+ * Enables finalization-related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_FINALIZE = (1<<10);
+
+ /**
+ * Bit-combination of the flags {@link #VERBOSE_COLLECT},
+ * {@link #VERBOSE_WEAKREF}, {@link #VERBOSE_DELAYED},
+ * {@link #VERBOSE_FINALIZE}.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE =
VERBOSE_COLLECT | VERBOSE_WEAKREF | VERBOSE_DELAYED | VERBOSE_FINALIZE;
@@ -134,38 +218,65 @@
/**
* print collection statistics
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_STATS = (1<<0);
/**
* print collectable objects
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_COLLECTABLE = (1<<1);
/**
* print uncollectable objects
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_UNCOLLECTABLE = (1<<2);
/**
* print instances
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_INSTANCES = (1<<3);
/**
* print other objects
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_OBJECTS = (1<<4);
/**
* save all garbage in gc.garbage
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_SAVEALL = (1<<5);
+
+ /**
+ * Bit-combination of the flags {@link #DEBUG_COLLECTABLE},
+ * {@link #DEBUG_UNCOLLECTABLE}, {@link #DEBUG_INSTANCES},
+ * {@link #DEBUG_OBJECTS}, {@link #DEBUG_SAVEALL}.
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
+ */
public static final int DEBUG_LEAK = DEBUG_COLLECTABLE |
DEBUG_UNCOLLECTABLE |
DEBUG_INSTANCES |
@@ -184,6 +295,10 @@
private static long lastRemoveTimeStamp = -1, maxWaitTime = initWaitTime;
private static int gcMonitoredRunCount = 0;
public static long gcRecallTime = 4000;
+
+ /**
+ * list of uncollectable objects
+ */
public static PyList garbage = new PyList();
//Finalization preprocess/postprocess-related declarations:
@@ -207,17 +322,113 @@
private static boolean notifyRerun = false;
public static final String __doc__ =
- "This module provides access to the garbage collector.\n" +
+ "This module provides access to the garbage collector for reference cycles.\n" +
"\n" +
- "enable() -- Enable automatic garbage collection (does nothing).\n" +
+ "enable() -- Enable automatic garbage collection (does nothing in Jython).\n" +
+ "disable() -- Disable automatic garbage collection (raises NotImplementedError in Jython).\n" +
"isenabled() -- Returns True because Java garbage collection cannot be disabled.\n" +
- "collect() -- Trigger a Java garbage collection (potentially expensive).\n" +
- "get_debug() -- Get debugging flags (returns 0).\n" +
- "\n" +
- "Other functions raise NotImplementedError because they do not apply to Java.\n";
+ "collect() -- Do a full collection right now (potentially expensive).\n" +
+ "get_count() -- Return the current collection counts (raises NotImplementedError in Jython).\n" +
+ "set_debug() -- Set debugging flags.\n" +
+ "get_debug() -- Get debugging flags.\n" +
+ "set_threshold() -- Set the collection thresholds (raise NotImplementedError in Jython).\n" +
+ "get_threshold() -- Return the current the collection thresholds (raise NotImplementedError in Jython).\n" +
+ "get_objects() -- Return a list of all objects tracked by the collector (raises NotImplementedError in Jython).\n" +
+ "is_tracked() -- Returns true if a given object is tracked (i.e. monitored in Jython).\n" +
+ "get_referrers() -- Return the list of objects that refer to an object (only finds monitored referrers in Jython).\n" +
+ "get_referents() -- Return the list of objects that an object refers to.\n";
public static final String __name__ = "gc";
+ public static final PyString __doc__enable = new PyString(
+ "enable() -> None\n" +
+ "\n" +
+ "Enable automatic garbage collection.\n" +
+ "(does nothing in Jython)\n");
+
+ public static final PyString __doc__disable = new PyString(
+ "disable() -> None\n" +
+ "\n" +
+ "Disable automatic garbage collection.\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__isenabled = new PyString(
+ "isenabled() -> status\n" +
+ "\n" +
+ "Returns true if automatic garbage collection is enabled.\n");
+
+ public static final PyString __doc__collect = new PyString(
+ "collect([generation]) -> n\n" +
+ "\n" +
+ "With no arguments, run a full collection. The optional argument\n" +
+ "may be an integer specifying which generation to collect. A ValueError\n" +
+ "is raised if the generation number is invalid.\n\n" +
+ "The number of unreachable objects is returned.\n" +
+ "(Jython emulates CPython cyclic trash counting if objects are monitored.\n" +
+ "If no objects are monitored, returns -2\n");
+
+ public static final PyString __doc__get_count = new PyString(
+ "get_count() -> (count0, count1, count2)\n" +
+ "\n" +
+ "Return the current collection counts\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__set_debug = new PyString(
+ "set_debug(flags) -> None\n" +
+ "\n" +
+ "Set the garbage collection debugging flags. Debugging information is\n" +
+ "written to sys.stderr.\n" +
+ "\n" +
+ "flags is an integer and can have the following bits turned on:\n" +
+ "\n" +
+ " DEBUG_STATS - Print statistics during collection.\n" +
+ " DEBUG_COLLECTABLE - Print collectable objects found.\n" +
+ " DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n" +
+ " DEBUG_INSTANCES - Print instance objects.\n" +
+ " DEBUG_OBJECTS - Print objects other than instances.\n" +
+ " DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n" +
+ " DEBUG_LEAK - Debug leaking programs (everything but STATS).\n");
+
+ public static final PyString __doc__get_debug = new PyString(
+ "get_debug() -> flags\n" +
+ "\n" +
+ "Get the garbage collection debugging flags.\n");
+
+ public static final PyString __doc__set_thresh = new PyString(
+ "set_threshold(threshold0, [threshold1, threshold2]) -> None\n" +
+ "\n" +
+ "Sets the collection thresholds. Setting threshold0 to zero disables\n" +
+ "collection.\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__get_thresh = new PyString(
+ "get_threshold() -> (threshold0, threshold1, threshold2)\n" +
+ "\n" +
+ "Return the current collection thresholds\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__get_objects = new PyString(
+ "get_objects() -> [...]\n" +
+ "\n" +
+ "Return a list of objects tracked by the collector (excluding the list\n" +
+ "returned).\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__is_tracked = new PyString(
+ "is_tracked(obj) -> bool\n" +
+ "\n" +
+ "Returns true if the object is tracked by the garbage collector.\n" +
+ "(i.e. monitored in Jython)\n");
+
+ public static final PyString __doc__get_referrers = new PyString(
+ "get_referrers(*objs) -> list\n" +
+ "Return the list of objects that directly refer to any of objs.\n" +
+ "(only finds monitored referrers in Jython)");
+
+ public static final PyString __doc__get_referents = new PyString(
+ "get_referents(*objs) -> list\n" +
+ "Return the list of objects that are directly referred to by objs.");
+
public static class CycleMarkAttr {
private boolean cyclic = false;
@@ -382,7 +593,17 @@
}
}
- private static void writeDebug(String type, String msg) {
+ /**
+ * Works like {@link org.python.core.Py#writeDebug(String, String)},
+ * but prints to {@link org.python.core.Py#writeDebug(String, String)}
+ * (i.e. subject to Jython's verbose level)
+ * or directly to {@code System.err}, according to
+ * {@link #USE_PY_WRITE_DEBUG}.
+ *
+ * @see #USE_PY_WRITE_DEBUG
+ * @see org.python.core.Py#writeDebug(String, String)
+ */
+ public static void writeDebug(String type, String msg) {
if ((gcFlags & USE_PY_WRITE_DEBUG) != 0) {
Py.writeDebug(type, msg);
} else {
@@ -1102,6 +1323,25 @@
//----------end of Monitoring section--------------------------------------
+ /**
+ * Gets the current Jython-specific gc-flags.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #setJythonGCFlags(short)
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static short getJythonGCFlags() {
if (((gcFlags & MONITOR_GLOBAL) != 0) != PyObject.gcMonitorGlobal) {
if (PyObject.gcMonitorGlobal) {
@@ -1113,6 +1353,44 @@
return gcFlags;
}
+ /**
+ * Sets the current Jython-specific gc-flags.
+ *
+ * {@code flags} is a {@code short} and can have the following bits turned on:
+ *
+ * {@link #MONITOR_GLOBAL} - Automatically monitors all PyObjects created from now on.
+ * {@link #DONT_FINALIZE_CYCLIC_GARBAGE} - Adds cyclic finalizable PyObjects to {@link #garbage}.
+ * {@link #PRESERVE_WEAKREFS_ON_RESURRECTION} - Keeps weak references alive if the referent is resurrected.
+ * {@link #DONT_FINALIZE_RESURRECTED_OBJECTS} -
+ * Emulates CPython behavior regarding resurrected objects and finalization.
+ * {@link #DONT_TRAVERSE_BY_REFLECTION} - Inhibits reflection-based traversion.
+ * {@link #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING} -
+ * Suppress warnings for PyObjects that neither implement {@link org.python.core.Traverseproc} nor
+ * are marked as {@link org.python.core.Untraversable}.
+ * {@link #USE_PY_WRITE_DEBUG} - uses {@link org.python.core.Py#writeDebug(String, String)} for
+ * debugging output instead of directly writing to {@link java.lang.System#err}.
+ * {@link #VERBOSE_COLLECT} - Enable collection-related verbose output.
+ * {@link #VERBOSE_WEAKREF} - Enable weakref-related verbose output.
+ * {@link #VERBOSE_DELAYED} - Enable delayed finalization-related verbose output.
+ * {@link #VERBOSE_FINALIZE} - Enable finalization-related verbose output.
+ * {@link #VERBOSE} - All previous verbose-flags combined.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static void setJythonGCFlags(short flags) {
gcFlags = flags;
PyObject.gcMonitorGlobal = (gcFlags & MONITOR_GLOBAL) != 0;
@@ -1121,6 +1399,22 @@
/**
* This is a convenience method to add flags via bitwise or.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #getJythonGCFlags()
+ * @see #setJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static void addJythonGCFlags(short flags) {
gcFlags |= flags;
@@ -1130,6 +1424,22 @@
/**
* This is a convenience method to remove flags via bitwise and-not.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #getJythonGCFlags()
+ * @see #setJythonGCFlags(short)
+ * @see #addJythonGCFlags(short)
*/
public static void removeJythonGCFlags(short flags) {
gcFlags &= ~flags;
@@ -1161,21 +1471,34 @@
notifyFinalize(abort);
}
+ /**
+ * Does nothing in Jython as Java-gc is always enabled.
+ */
public static void enable() {}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static void disable() {
throw Py.NotImplementedError("can't disable Java GC");
}
+ /**
+ * Always returns {@code true} in Jython.
+ */
public static boolean isenabled() { return true; }
/**
* The generation parameter is only for compatibility with
- * CPython {@code gc.collect} and is ignored.
+ * CPython {@link gc.collect()} and is ignored.
* @param generation (ignored)
* @return Collected monitored cyclic trash-objects or
* {@code gc.UNKNOWN_COUNT} if nothing is monitored or -1 if
* an error occurred and collection did not complete.
+ * @see #collect()
*/
public static int collect(int generation) {
return collect();
@@ -1194,14 +1517,16 @@
/**
* If no objects are monitored, this just delegates to
- * {@code System.gc} and returns {@code gc.UNKNOWN_COUNT} as a
+ * {@code System.gc()} and returns {@link #UNKNOWN_COUNT} as a
* non-erroneous default value. If objects are monitored,
- * it emulates a synchronous gc run in the sense that it waits
+ * it emulates a synchronous gc-run in the sense that it waits
* until all collected monitored objects were finalized.
*
* @return Number of collected monitored cyclic trash-objects
- * or {@code gc.UNKNOWN_COUNT} if nothing is monitored or -1
+ * or {@link #UNKNOWN_COUNT} if nothing is monitored or -1
* if an error occurred and collection did not complete.
+ * @see #UNKNOWN_COUNT
+ * @see #collect(int)
*/
public static int collect() {
try {
@@ -1619,26 +1944,79 @@
stat[1] -= abortedCyclicFinalizers;
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static PyObject get_count() {
throw Py.NotImplementedError("not applicable to Java GC");
}
+ /**
+ * Copied from CPython-doc:
+ *
+ * Set the garbage collection debugging flags. Debugging information is
+ * written to {@code System.err}.
+ *
+ * {@flags} flags is an {@code int}eger and can have the following bits turned on:
+ *
+ * {@link #DEBUG_STATS} - Print statistics during collection.
+ * {@link #DEBUG_COLLECTABLE} - Print collectable objects found.
+ * {@link #DEBUG_UNCOLLECTABLE} - Print unreachable but uncollectable objects found.
+ * {@link #DEBUG_INSTANCES} - Print instance objects.
+ * {@link #DEBUG_OBJECTS} - Print objects other than instances.
+ * {@link #DEBUG_SAVEALL} - Save objects to gc.garbage rather than freeing them.
+ * {@link #DEBUG_LEAK} - Debug leaking programs (everything but STATS).
+ *
+ * @see #DEBUG_STATS
+ * @see #DEBUG_COLLECTABLE
+ * @see #DEBUG_UNCOLLECTABLE
+ * @see #DEBUG_INSTANCES
+ * @see #DEBUG_OBJECTS
+ * @see #DEBUG_SAVEALL
+ * @see #DEBUG_LEAK
+ */
public static void set_debug(int flags) {
debugFlags = flags;
}
+ /**
+ * Copied from CPython-doc:
+ *
+ * Get the garbage collection debugging flags.
+ */
public static int get_debug() {
return debugFlags;
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static void set_threshold(PyObject[] args, String[] kwargs) {
throw Py.NotImplementedError("not applicable to Java GC");
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static PyObject get_threshold() {
throw Py.NotImplementedError("not applicable to Java GC");
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static PyObject get_objects() {
throw Py.NotImplementedError("not applicable to Java GC");
}
@@ -2007,6 +2385,23 @@
return search;
}
+ /**
+ * Does its best to traverse the given {@link org.python.core.PyObject}
+ * {@code ob}. It exploits both
+ * {@link org.python.core.Traverseproc#traverse(Visitproc, Object)} and
+ * {@link org.python.core.TraverseprocDerived#traverseDerived(Visitproc, Object)}.
+ * If {@code ob} neither implements {@link org.python.core.Traverseproc} nor
+ * {@link org.python.core.Traverseproc} and is not annotated with
+ * {@link org.python.core.Untraversable}, reflection-based traversion via
+ * {@link #traverseByReflection(Object, Visitproc, Object)} may be attempted
+ * according to {@link #DONT_TRAVERSE_BY_REFLECTION}.
+ *
+ * @see org.python.core.Traverseproc#traverse(Visitproc, Object)
+ * @see org.python.core.TraverseprocDerived#traverseDerived(Visitproc, Object)
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see org.python.core.Untraversable
+ * @see #traverseByReflection(Object, Visitproc, Object)
+ */
public static int traverse(PyObject ob, Visitproc visit, Object arg) {
int retVal;
boolean traversed = false;
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Fri Feb 13 05:59:25 2015
From: jython-checkins at python.org (frank.wierzbicki)
Date: Fri, 13 Feb 2015 04:59:25 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Added_tag_v2=2E7b4_for_chan?=
=?utf-8?q?geset_17d18c6dde96?=
Message-ID: <20150213045902.37939.50572@psf.io>
https://hg.python.org/jython/rev/3672e624962a
changeset: 7584:3672e624962a
user: Frank Wierzbicki
date: Fri Feb 13 04:58:54 2015 +0000
summary:
Added tag v2.7b4 for changeset 17d18c6dde96
files:
.hgtags | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -84,3 +84,5 @@
6e650bf6302fbc142e9fa0397ac2e8f342e16aaf v2.7b4
6e650bf6302fbc142e9fa0397ac2e8f342e16aaf v2.7b4
e270e881c84cbe519035c215fde09677362a4953 v2.7b4
+e270e881c84cbe519035c215fde09677362a4953 v2.7b4
+17d18c6dde9693129d977affb8ca20406d6a6585 v2.7b4
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 17 03:59:45 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Tue, 17 Feb 2015 02:59:45 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Adjusted_test=5Fgc=2Epy_and?=
=?utf-8?q?_test=5Fgc=5Fjy=2Epy_such_that_they_also_pass_in_CPython_=28i?=
=?utf-8?b?LmUu?=
Message-ID: <20150217025941.4777.53913@psf.io>
https://hg.python.org/jython/rev/efda62bbec80
changeset: 7585:efda62bbec80
user: Stefan Richthofer
date: Tue Feb 17 03:59:27 2015 +0100
summary:
Adjusted test_gc.py and test_gc_jy.py such that they also pass in CPython (i.e. isolated Jython-specific code by skips or try-blocks)
files:
Lib/test/test_gc.py | 259 +++++++++++++++-------------
Lib/test/test_gc_jy.py | 209 +++++++++++-----------
2 files changed, 236 insertions(+), 232 deletions(-)
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -1,9 +1,10 @@
import unittest
-#from test.test_support import verbose, run_unittest
+from test.test_support import verbose, run_unittest
from test import test_support
import sys
import gc
import weakref
+import time
try:
import threading
@@ -59,6 +60,7 @@
@classmethod
def tearDownClass(cls):
+ #Jython-specific block:
try:
gc.setJythonGCFlags(cls.savedJythonGCFlags)
gc.stopMonitoring()
@@ -203,17 +205,12 @@
self.assertEqual(gc.collect(), 2)
def test_frame(self):
- flg = gc.getJythonGCFlags()
- #gc.addJythonGCFlags(gc.VERBOSE)
- #sporadically fails in Jython, no idea why.
def f():
frame = sys._getframe()
gc.collect()
f()
col = gc.collect()
- gc.setJythonGCFlags(flg)
self.assertEqual(col, 1)
-
def test_saveall(self):
# Verify that cyclic garbage like lists show up in gc.garbage if the
@@ -316,104 +313,115 @@
gc.collect(2)
assertEqual(gc.get_count(), (0, 0, 0))
-# def test_trashcan(self):
-# class Ouch:
-# n = 0
-# def __del__(self):
-# Ouch.n = Ouch.n + 1
-# if Ouch.n % 17 == 0:
-# gc.collect()
-#
-# # "trashcan" is a hack to prevent stack overflow when deallocating
-# # very deeply nested tuples etc. It works in part by abusing the
-# # type pointer and refcount fields, and that can yield horrible
-# # problems when gc tries to traverse the structures.
-# # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
-# # most likely die via segfault.
-#
-# # Note: In 2.3 the possibility for compiling without cyclic gc was
-# # removed, and that in turn allows the trashcan mechanism to work
-# # via much simpler means (e.g., it never abuses the type pointer or
-# # refcount fields anymore). Since it's much less likely to cause a
-# # problem now, the various constants in this expensive (we force a lot
-# # of full collections) test are cut back from the 2.2 version.
-# gc.enable()
-# N = 150
-# for count in range(2):
-# t = []
-# for i in range(N):
-# t = [t, Ouch()]
-# u = []
-# for i in range(N):
-# u = [u, Ouch()]
-# v = {}
-# for i in range(N):
-# v = {1: v, 2: Ouch()}
-# gc.disable()
+ def test_trashcan(self):
+ class Ouch:
+ n = 0
+ def __del__(self):
+ Ouch.n = Ouch.n + 1
+ if Ouch.n % 17 == 0:
+ gc.collect()
+
+ # "trashcan" is a hack to prevent stack overflow when deallocating
+ # very deeply nested tuples etc. It works in part by abusing the
+ # type pointer and refcount fields, and that can yield horrible
+ # problems when gc tries to traverse the structures.
+ # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
+ # most likely die via segfault.
+
+ # Note: In 2.3 the possibility for compiling without cyclic gc was
+ # removed, and that in turn allows the trashcan mechanism to work
+ # via much simpler means (e.g., it never abuses the type pointer or
+ # refcount fields anymore). Since it's much less likely to cause a
+ # problem now, the various constants in this expensive (we force a lot
+ # of full collections) test are cut back from the 2.2 version.
+ gc.enable()
+ N = 150
+ for count in range(2):
+ t = []
+ for i in range(N):
+ t = [t, Ouch()]
+ u = []
+ for i in range(N):
+ u = [u, Ouch()]
+ v = {}
+ for i in range(N):
+ v = {1: v, 2: Ouch()}
+ try:
+ gc.disable()
+ except NotImplementedError:
+ #i.e. Jython is running (or other non-CPython interpreter without gc-disabling)
+ pass
-# @unittest.skipUnless(threading, "test meaningless on builds without threads")
-# def test_trashcan_threads(self):
-# # Issue #13992: trashcan mechanism should be thread-safe
-# NESTING = 60
-# N_THREADS = 2
-#
-# def sleeper_gen():
-# """A generator that releases the GIL when closed or dealloc'ed."""
-# try:
-# yield
-# finally:
-# time.sleep(0.000001)
-#
-# class C(list):
-# # Appending to a list is atomic, which avoids the use of a lock.
-# inits = []
-# dels = []
-# def __init__(self, alist):
-# self[:] = alist
-# C.inits.append(None)
-# def __del__(self):
-# # This __del__ is called by subtype_dealloc().
-# C.dels.append(None)
-# # `g` will release the GIL when garbage-collected. This
-# # helps assert subtype_dealloc's behaviour when threads
-# # switch in the middle of it.
-# g = sleeper_gen()
-# next(g)
-# # Now that __del__ is finished, subtype_dealloc will proceed
-# # to call list_dealloc, which also uses the trashcan mechanism.
-#
-# def make_nested():
-# """Create a sufficiently nested container object so that the
-# trashcan mechanism is invoked when deallocating it."""
-# x = C([])
-# for i in range(NESTING):
-# x = [C([x])]
-# del x
-#
-# def run_thread():
-# """Exercise make_nested() in a loop."""
-# while not exit:
-# make_nested()
-#
-# old_checkinterval = sys.getcheckinterval()
-# sys.setcheckinterval(3)
-# try:
-# exit = False
-# threads = []
-# for i in range(N_THREADS):
-# t = threading.Thread(target=run_thread)
-# threads.append(t)
-# for t in threads:
-# t.start()
-# time.sleep(1.0)
-# exit = True
-# for t in threads:
-# t.join()
-# finally:
-# pass
-# sys.setcheckinterval(old_checkinterval)
-# gc.collect()
-# self.assertEqual(len(C.inits), len(C.dels))
+ @unittest.skipIf(test_support.is_jython,
+ '''
+ Jython does not have a trashcan mechanism.
+ This test should still not fail but currently does.
+ This is because the massive referencing in this test brings
+ sync gc emulation to its limit. Making this more robust is
+ of no priority for now.
+ ''')
+ @unittest.skipUnless(threading, "test meaningless on builds without threads")
+ def test_trashcan_threads(self):
+ # Issue #13992: trashcan mechanism should be thread-safe
+ NESTING = 60
+ N_THREADS = 2
+
+ def sleeper_gen():
+ """A generator that releases the GIL when closed or dealloc'ed."""
+ try:
+ yield
+ finally:
+ time.sleep(0.000001)
+
+ class C(list):
+ # Appending to a list is atomic, which avoids the use of a lock.
+ inits = []
+ dels = []
+ def __init__(self, alist):
+ self[:] = alist
+ C.inits.append(None)
+ def __del__(self):
+ # This __del__ is called by subtype_dealloc().
+ C.dels.append(None)
+ # `g` will release the GIL when garbage-collected. This
+ # helps assert subtype_dealloc's behaviour when threads
+ # switch in the middle of it.
+ g = sleeper_gen()
+ next(g)
+ # Now that __del__ is finished, subtype_dealloc will proceed
+ # to call list_dealloc, which also uses the trashcan mechanism.
+
+ def make_nested():
+ """Create a sufficiently nested container object so that the
+ trashcan mechanism is invoked when deallocating it."""
+ x = C([])
+ for i in range(NESTING):
+ x = [C([x])]
+ del x
+
+ def run_thread():
+ """Exercise make_nested() in a loop."""
+ while not exit:
+ make_nested()
+
+ old_checkinterval = sys.getcheckinterval()
+ sys.setcheckinterval(3)
+ try:
+ exit = False
+ threads = []
+ for i in range(N_THREADS):
+ t = threading.Thread(target=run_thread)
+ threads.append(t)
+ for t in threads:
+ t.start()
+ time.sleep(0.01)
+ exit = True
+ for t in threads:
+ t.join()
+ finally:
+ pass
+ sys.setcheckinterval(old_checkinterval)
+ self.assertEqual(len(C.inits), len(C.dels))
def test_boom(self):
class Boom:
@@ -733,28 +741,29 @@
# empty __dict__.
self.assertEqual(x, None)
-# def test_main():
-# unittest.main()
-# enabled = gc.isenabled()
-# gc.disable()
-# assert not gc.isenabled()
-# debug = gc.get_debug()
-# gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
-#
-# try:
-# gc.collect() # Delete 2nd generation garbage
-# run_unittest(GCTests, GCTogglingTests)
-# finally:
-# gc.set_debug(debug)
-# # test gc.enable() even if GC is disabled by default
-# if verbose:
-# print "restoring automatic collection"
-# # make sure to always test gc.enable()
-# gc.enable()
-# assert gc.isenabled()
-# if not enabled:
-# gc.disable()
+def test_main():
+ enabled = gc.isenabled()
+ try:
+ gc.disable()
+ assert not gc.isenabled()
+ except NotImplementedError:
+ pass
+ debug = gc.get_debug()
+ gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
+
+ try:
+ gc.collect() # Delete 2nd generation garbage
+ run_unittest(GCTests, GCTogglingTests)
+ finally:
+ gc.set_debug(debug)
+ # test gc.enable() even if GC is disabled by default
+ if verbose:
+ print "restoring automatic collection"
+ # make sure to always test gc.enable()
+ gc.enable()
+ assert gc.isenabled()
+ if not enabled:
+ gc.disable()
if __name__ == "__main__":
- #test_main()
unittest.main()
diff --git a/Lib/test/test_gc_jy.py b/Lib/test/test_gc_jy.py
--- a/Lib/test/test_gc_jy.py
+++ b/Lib/test/test_gc_jy.py
@@ -1,31 +1,21 @@
"""
Tests some Jython-specific gc aspects and debugging
features.
+Skips and try-blocks assure that this test-script is
+still runnable with CPython and passes there as far
+as not skipped.
"""
import unittest
-#from test.test_support import verbose, run_unittest
-#import sys
+from test import test_support
import time
import gc
import weakref
-from java.lang import System, Runnable
-
-# class FinalizationDummy:
-# def __del__(self):
-# time.sleep(3.5)
-# print "FinalizationDummy.__del__"
-# time.sleep(3.5)
-#
-# class ResurrectionDummy:
-# def __del__(self):
-# print "ResurrectionDummy.__del__"
-# ResurrectionDummy.resurrected = self.toResurrect
-#
-# class SelfResurrectionDummy:
-# def __del__(self):
-# print "SelfResurrectionDummy.__del__"
-# SelfResurrectionDummy.resurrected = self
+try:
+ from java.lang import System, Runnable
+except ImportError:
+ #i.e. Jython is running
+ pass
class GCTests_Jy_CyclicGarbage(unittest.TestCase):
@@ -101,6 +91,8 @@
self.fail("didn't find obj in garbage (finalizer)")
gc.garbage.remove(obj)
+ @unittest.skipUnless(test_support.is_jython,
+ 'CPython has no monitor state')
def test_manual_monitoring(self):
# since tuples are immutable we close the loop with a list
l = []
@@ -117,6 +109,8 @@
self.assertEqual(gc.collect(), 1)
+ at unittest.skipUnless(test_support.is_jython,
+ 'CPython has no gc preprocess and postprocess features')
class GCTests_Jy_preprocess_and_postprocess(unittest.TestCase):
def test_finalization_preprocess_and_postprocess(self):
@@ -156,6 +150,82 @@
prePr = PreProcess()
postPr = PostProcess()
time.sleep(1) # <- to avoid that the newly registered processes
+ # become subject to previous run (remember: We
+ # are not in monitor-mode, i.e. gc runs async.
+ gc.registerPreFinalizationProcess(prePr)
+ gc.registerPostFinalizationProcess(postPr)
+ #Note that order matters here:
+ #If the flag gc.DONT_FINALIZE_RESURRECTED_OBJECTS is used,
+ #gc.registerPostFinalizationProcess(postPr, 0) would lead to failure,
+ #because postPr asserts that a's finalizer already ran. Since
+ #DONT_FINALIZE_RESURRECTED_OBJECTS also inserted a postprocess,
+ #to perform delayed finalization, the 0-index would prepend postPr
+ #before the process that actually runs the finalizers.
+ System.gc()
+ #we wait a bit longer here, since PostProcess runs asynchronous
+ #and must wait for the finalizer of A
+ time.sleep(2)
+ self.assertIn("run PostProcess", comments)
+ comments = []
+ gc.unregisterPreFinalizationProcess(prePr)
+ gc.unregisterPostFinalizationProcess(postPr)
+
+
+ at unittest.skipUnless(test_support.is_jython,
+ 'This class tests detailed Jython-specific behavior.')
+class GCTests_Jy_Delayed_Finalization(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ #the finalizer-related tests need this flag to pass in Jython:
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ except Exception:
+ pass
+
+ def test_finalization_preprocess_and_postprocess(self):
+ #Note that this test is done here again (already was in another class
+ #in this module), to see that everything works as it should also with
+ #a different flag-context.
+ comments = []
+ self0 = self
+ class A:
+ def __del__(self):
+ self0.assertIn("run PreProcess", comments)
+ comments.append("A del")
+ #let's simulate a time-consuming finalizer
+ #to ensure that post finalization processing
+ #is sensitive to this
+ time.sleep(0.5)
+ comments.append("A del done")
+
+ class PreProcess(Runnable):
+ def run(self):
+ self0.assertEqual(comments, [])
+ comments.append("run PreProcess")
+
+ class PostProcess(Runnable):
+ def run(self):
+ self0.assertIn("run PreProcess", comments)
+ self0.assertIn("A del", comments)
+ self0.assertIn("A del done", comments)
+ comments.append("run PostProcess")
+
+ a = A()
+ a = None
+ prePr = PreProcess()
+ postPr = PostProcess()
+ time.sleep(1) # <- to avoid that the newly registered processes
# become subject to previous run
gc.registerPreFinalizationProcess(prePr)
gc.registerPostFinalizationProcess(postPr)
@@ -176,86 +246,7 @@
gc.unregisterPostFinalizationProcess(postPr)
-class GCTests_Jy_Delayed_Finalization(unittest.TestCase):
-
- @classmethod
- def setUpClass(cls):
- #Jython-specific block:
- try:
- cls.savedJythonGCFlags = gc.getJythonGCFlags()
- #the finalizer-related tests need this flag to pass in Jython:
- gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- gc.stopMonitoring()
- except Exception:
- pass
-
- @classmethod
- def tearDownClass(cls):
- try:
- gc.setJythonGCFlags(cls.savedJythonGCFlags)
- except Exception:
- pass
-
- def test_finalization_preprocess_and_postprocess(self):
- #print "test_finalization_preprocess_and_postprocess"
- #Note that this test is done here again (already was in another class
- #in this module), to see that everything works as it should also with
- #a different flag-context.
- #print "test_finalization_preprocess_and_postprocess"
- #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- comments = []
- self0 = self
- class A:
- def __del__(self):
- #print "del A"
- self0.assertIn("run PreProcess", comments)
- comments.append("A del")
- #let's simulate a time-consuming finalizer
- #to ensure that post finalization processing
- #is sensitive to this
- time.sleep(0.5)
- comments.append("A del done")
-
- class PreProcess(Runnable):
- def run(self):
- self0.assertEqual(comments, [])
- comments.append("run PreProcess")
-
- class PostProcess(Runnable):
- def run(self):
- self0.assertIn("run PreProcess", comments)
- self0.assertIn("A del", comments)
- self0.assertIn("A del done", comments)
- comments.append("run PostProcess")
-
- a = A()
- a = None
- prePr = PreProcess()
- postPr = PostProcess()
- time.sleep(1) # <- to avoid that the newly registered processes
- # become subject to previous run
- gc.registerPreFinalizationProcess(prePr)
- gc.registerPostFinalizationProcess(postPr)
- #Note that order matters here:
- #If the flag gc.DONT_FINALIZE_RESURRECTED_OBJECTS is used,
- #gc.registerPostFinalizationProcess(postPr, 0) would lead to failure,
- #because postPr asserts that a's finalizer already ran. Since
- #DONT_FINALIZE_RESURRECTED_OBJECTS also inserted a postprocess,
- #to perform delayed finalization, the 0-index would prepend postPr
- #before the process that actually runs the finalizers.
- System.gc()
- #we wait a bit longer here, since PostProcess runs asynchronous
- #and must wait for the finalizer of A
- time.sleep(2)
- self.assertIn("run PostProcess", comments)
- comments = []
- gc.unregisterPreFinalizationProcess(prePr)
- gc.unregisterPostFinalizationProcess(postPr)
-
-
def test_delayedFinalization(self):
- #gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- #gc.addJythonGCFlags(gc.VERBOSE)
resurrect = []
comments = []
@@ -291,15 +282,17 @@
del c
self.assertNotEqual(gc.collect(), 0)
time.sleep(1)
- #print comments
- #print resurrect
+ #Note that CPython would collect a, b and c in one run.
+ #With gc.DONT_FINALIZE_RESURRECTED_OBJECTS set, Jython
+ #Would not collect a and b in the same run with c
+ #because a and b might have been resurrected by c and
+ #Java allows not to detect such resurrection in any
+ #other way than waiting for the next gc-run.
self.assertIn('del c', comments)
self.assertEqual(1, len(comments))
comments = []
self.assertNotEqual(gc.collect(), 0)
time.sleep(1)
- #print comments
- #print resurrect
self.assertIn('del a', comments)
self.assertEqual(1, len(comments))
comments = []
@@ -307,8 +300,6 @@
time.sleep(1)
self.assertIn('del b', comments)
self.assertEqual(1, len(comments))
- #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- #gc.removeJythonGCFlags(gc.VERBOSE)
class GCTests_Jy_Monitoring(unittest.TestCase):
@@ -336,6 +327,7 @@
except Exception:
pass
+ @unittest.skipUnless(test_support.is_jython, 'CPython has no monitor-state.')
def test_monitor_status_after_delayed_finalization(self):
resurrect = []
comments = []
@@ -409,8 +401,11 @@
a.b.lst = lst
del lst
del lst1
- self.assertTrue(gc.isMonitored(a))
- self.assertTrue(gc.isMonitored(a.b))
+ try:
+ self.assertTrue(gc.isMonitored(a))
+ self.assertTrue(gc.isMonitored(a.b))
+ except AttributeError:
+ pass
del a
self.assertEqual(gc.collect(), 2) # c is not cyclic and a, b are resurrected,
# the cycle of two lists is counted here
@@ -543,6 +538,7 @@
except Exception:
pass
+ @unittest.skipUnless(test_support.is_jython, '')
def test_weakref_after_resurrection_threadsafe(self):
resurrect = []
comments = []
@@ -587,7 +583,6 @@
# restored or the deletion is confirmed.
except Exception:
pass
- #self.assertEqual(str(wa()), '')
self.assertEqual(comments, [])
self.assertEqual(resurrect, [])
while comments == [] or resurrect == []:
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 18 08:38:17 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 18 Feb 2015 07:38:17 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Updated_bundled_pip_wheel_t?=
=?utf-8?q?o_latest_version_of?=
Message-ID: <20150218073817.12766.84933@psf.io>
https://hg.python.org/jython/rev/a5e9aecc8d9c
changeset: 7586:a5e9aecc8d9c
parent: 7581:d40cb4d16450
user: Jim Baker
date: Wed Feb 11 13:58:54 2015 -0700
summary:
Updated bundled pip wheel to latest version of https://github.com/jythontools/pip (3cfa4001ab)
files:
Lib/ensurepip/_bundled/pip-1.6-py2.py3-none-any.whl | Bin
1 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/Lib/ensurepip/_bundled/pip-1.6-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-1.6-py2.py3-none-any.whl
index 68605e592a267c90d057577ae34b1ba9fadaedd2..8433b71cbec1bdf41dc7d8b41c77b80bf59ae296
GIT binary patch
[stripped]
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 18 08:38:17 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 18 Feb 2015 07:38:17 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Quiet_compileall_printing_s?=
=?utf-8?q?o_regrtest_doesn=27t_complain?=
Message-ID: <20150218073817.13136.57041@psf.io>
https://hg.python.org/jython/rev/79ff91ded2aa
changeset: 7588:79ff91ded2aa
user: Jim Baker
date: Wed Feb 18 00:36:20 2015 -0700
summary:
Quiet compileall printing so regrtest doesn't complain
files:
Lib/test/test_compile_jy.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/Lib/test/test_compile_jy.py b/Lib/test/test_compile_jy.py
--- a/Lib/test/test_compile_jy.py
+++ b/Lib/test/test_compile_jy.py
@@ -85,7 +85,7 @@
# pretend we have a Python bytecode compiler by touching this file
open(PYC_GREETER, "a").close()
- compileall.compile_dir(PACKAGE)
+ compileall.compile_dir(PACKAGE, quiet=True)
self.assertTrue(os.path.exists(PYC_GREETER)) # still exists
self.assertTrue(os.path.exists(PYCLASS_TEST)) # along with these new compiled files
self.assertTrue(os.path.exists(PYCLASS_GREETER))
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 18 08:38:17 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 18 Feb 2015 07:38:17 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Adds_new_Jython_launcher_wr?=
=?utf-8?q?itten_in_Python=2C_bin/jython=2Epy?=
Message-ID: <20150218073817.13136.51191@psf.io>
https://hg.python.org/jython/rev/37db4c0d4c34
changeset: 7589:37db4c0d4c34
user: Jim Baker
date: Wed Feb 18 00:36:34 2015 -0700
summary:
Adds new Jython launcher written in Python, bin/jython.py
This new launcher is designed to be run via CPython 2.7, but there
could be scenarios where it makes sense for Jython to run it. Next
step is to create a bin/jython.exe using py2exe so as to fix
http://bugs.jython.org/issue1491
files:
Lib/readline.py | 12 +-
src/org/python/core/PlainConsole.java | 6 -
src/org/python/core/Py.java | 4 +
src/org/python/util/jython.java | 7 +-
src/shell/jython.py | 319 ++++++++++++++
5 files changed, 330 insertions(+), 18 deletions(-)
diff --git a/Lib/readline.py b/Lib/readline.py
--- a/Lib/readline.py
+++ b/Lib/readline.py
@@ -3,6 +3,12 @@
from warnings import warn
try:
+ _console = sys._jy_console
+ _reader = _console.reader
+except AttributeError:
+ raise ImportError("Cannot access JLine2 setup")
+
+try:
# jarjar-ed version
from org.python.jline.console.history import MemoryHistory
except ImportError:
@@ -19,12 +25,6 @@
'set_history_length', 'set_pre_input_hook', 'set_startup_hook',
'write_history_file']
-try:
- _console = sys._jy_console
- _reader = _console.reader
-except AttributeError:
- raise ImportError("Cannot access JLine2 setup")
-
_history_list = None
# The need for the following warnings should go away once we update
diff --git a/src/org/python/core/PlainConsole.java b/src/org/python/core/PlainConsole.java
--- a/src/org/python/core/PlainConsole.java
+++ b/src/org/python/core/PlainConsole.java
@@ -1,12 +1,6 @@
// Copyright (c) 2013 Jython Developers
package org.python.core;
-import java.io.BufferedReader;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
diff --git a/src/org/python/core/Py.java b/src/org/python/core/Py.java
--- a/src/org/python/core/Py.java
+++ b/src/org/python/core/Py.java
@@ -1521,6 +1521,10 @@
* @return true if (we think) we are in an interactive environment
*/
public static boolean isInteractive() {
+ String isTTY = System.getProperty("python.launcher.tty");
+ if (isTTY != null && isTTY.equals("true")) {
+ return true;
+ }
// Decide if System.in is interactive
POSIX posix = POSIXFactory.getPOSIX();
FileDescriptor in = FileDescriptor.in;
diff --git a/src/org/python/util/jython.java b/src/org/python/util/jython.java
--- a/src/org/python/util/jython.java
+++ b/src/org/python/util/jython.java
@@ -2,12 +2,10 @@
package org.python.util;
import java.io.File;
-import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -15,9 +13,6 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import jnr.posix.POSIX;
-import jnr.posix.POSIXFactory;
-
import org.python.Version;
import org.python.core.CodeFlag;
import org.python.core.CompileMode;
@@ -50,7 +45,7 @@
private static final String usage = usageHeader
+ "Options and arguments:\n"
// + "(and corresponding environment variables):\n"
- + "-B : don't write .py[co] files on import\n"
+ + "-B : don't write bytecode files on import\n"
// + "also PYTHONDONTWRITEBYTECODE=x\n" +
+ "-c cmd : program passed in as string (terminates option list)\n"
// + "-d : debug output from parser (also PYTHONDEBUG=x)\n"
diff --git a/src/shell/jython.py b/src/shell/jython.py
new file mode 100644
--- /dev/null
+++ b/src/shell/jython.py
@@ -0,0 +1,319 @@
+#!/usr/bin/env python2.7 -E
+# -*- coding: utf-8 -*-
+
+# bin/jython.py
+# Launch script for Jython, generally to be compiled by tools like py2exe.
+# However it is an alternative to the bin/jython bash script.
+#
+# Designed to be executed by CPython 2.7, although it may make sense for some
+# scenarios to be executed by Jython instead - inside a jar for example.
+#
+# FIXME consider how to use this to support standalone jython jar
+
+import argparse
+import glob
+import inspect
+import os
+import os.path
+import pipes
+import subprocess
+import sys
+from collections import OrderedDict
+
+
+is_windows = os.name == "nt" or (os.name == "java" and os._name == "nt")
+
+
+def make_parser():
+ parser = argparse.ArgumentParser(description="Jython", add_help=False)
+ parser.add_argument("-D", dest="properties", action="append")
+ parser.add_argument("-J", dest="java", action="append")
+ parser.add_argument("--boot", action="store_true")
+ parser.add_argument("--jdb", action="store_true")
+ parser.add_argument("--help", "-h", action="store_true")
+ parser.add_argument("--print", dest="print_requested", action="store_true")
+ parser.add_argument("--profile", action="store_true")
+ args, remainder = parser.parse_known_args()
+
+ items = args.java or []
+ args.java = []
+ for item in items:
+ if item.startswith("-Xmx"):
+ args.mem = item
+ elif item.startswith("-Xss"):
+ args.stack = item
+ else:
+ args.java.append(item)
+
+ # need to account for the fact that -c and -cp/-classpath are ambiguous options as far
+ # as argparse is concerned, so parse separately
+ args.classpath = []
+ r = iter(remainder)
+ r2 = []
+ while True:
+ try:
+ arg = next(r)
+ except StopIteration:
+ break
+ if arg == "-cp" or arg == "-classpath":
+ args.classpath = next(r)
+ else:
+ r2.append(arg)
+ remainder = r2
+
+ if args.properties is None:
+ args.properties = []
+ props = OrderedDict()
+ for kv in args.properties:
+ k, v = kv.split("=")
+ props[k] = v
+ args.properties = props
+ args.encoding = args.properties.get("file.encoding", None)
+
+ return parser, args, remainder
+
+
+class JythonCommand(object):
+
+ def __init__(self, parser, args, jython_args):
+ self.parser = parser
+ self.args = args
+ self.jython_args = jython_args
+
+ @property
+ def uname(self):
+ if hasattr(self, "_uname"):
+ return self._uname
+ if is_windows:
+ self._uname = "windows"
+ else:
+ uname = subprocess.check_output(["uname"]).strip().lower()
+ if uname.startswith("cygwin"):
+ self._uname = "cygwin"
+ else:
+ self._uname = uname
+ return self._uname
+
+ @property
+ def java_home(self):
+ if not hasattr(self, "_java_home"):
+ self.setup_java_command()
+ return self._java_home
+
+ @property
+ def java_command(self):
+ if not hasattr(self, "_java_command"):
+ self.setup_java_command()
+ return self._java_command
+
+ def setup_java_command(self):
+ if "JAVA_HOME" not in os.environ:
+ self._java_home = None
+ self._java_command = "jdb" if self.args.jdb else "java"
+ else:
+ self._java_home = os.environ["JAVA_HOME"]
+ #if self.uname == "cygwin":
+ # self._java_home = subprocess.check_output(["cygpath", "--windows", self._java_home]).strip()
+ if self.uname == "cygwin":
+ self._java_command = "jdb" if self.args.jdb else "java"
+ else:
+ self._java_command = os.path.join(
+ self.java_home, "bin",
+ "jdb" if self.args.jdb else "java")
+
+ @property
+ def executable(self):
+ """Path to executable"""
+ if hasattr(self, "_executable"):
+ return self._executable
+ # Modified from
+ # http://stackoverflow.com/questions/3718657/how-to-properly-determine-current-script-directory-in-python/22881871#22881871
+ if getattr(sys, "frozen", False): # py2exe, PyInstaller, cx_Freeze
+ path = os.path.abspath(sys.executable)
+ else:
+ def inspect_this(): pass
+ path = inspect.getabsfile(inspect_this)
+ self._executable = os.path.realpath(path)
+ return self._executable
+
+ @property
+ def jython_home(self):
+ if hasattr(self, "_jython_home"):
+ return self._jython_home
+ if "JYTHON_HOME" in os.environ:
+ self._jython_home = os.environ["JYTHON_HOME"]
+ else:
+ self._jython_home = os.path.dirname(os.path.dirname(self.executable))
+ if self.uname == "cygwin":
+ self._jython_home = subprocess.check_output(["cygpath", "--windows", self._jython_home]).strip()
+ return self._jython_home
+
+ @property
+ def jython_opts():
+ return os.environ.get("JYTHON_OPTS", "")
+
+ @property
+ def classpath_delimiter(self):
+ return ";" if (is_windows or self.uname == "cygwin") else ":"
+
+ @property
+ def classpath(self):
+ if hasattr(self, "_classpath"):
+ return self._classpath
+ if os.path.exists(os.path.join(self.jython_home, "jython-dev.jar")):
+ jars = [os.path.join(self.jython_home, "jython-dev.jar")]
+ for jar in glob.iglob(os.path.join(self.jython_home, "javalib", "*.jar")):
+ jars.append(jar)
+ elif not os.path.exists(os.path.join(self.jython_home, "jython.jar")):
+ self.parser.error(
+"""{executable}:
+{jython_home} contains neither jython-dev.jar nor jython.jar.
+Try running this script from the 'bin' directory of an installed Jython or
+setting {envvar_specifier}JYTHON_HOME.""".\
+ format(
+ executable=self.executable,
+ jython_home=self.jython_home,
+ envvar_specifier="%" if self.uname == "windows" else "$"))
+ else:
+ jars = [os.path.join(self.jython_home, "jython.jar")]
+ self._classpath = self.classpath_delimiter.join(jars)
+ if self.args.classpath and not self.args.boot:
+ self._classpath += self.classpath_delimiter + self.args.classpath
+ return self._classpath
+
+ @property
+ def java_mem(self):
+ if hasattr(self.args.java, "mem"):
+ return self.args.java.mem
+ else:
+ return os.environ.get("JAVA_MEM", "-Xmx512m")
+
+ @property
+ def java_stack(self):
+ if hasattr(self.args.java, "stack"):
+ return self.args.java.mem
+ else:
+ return os.environ.get("JAVA_STACK", "-Xss1024k")
+
+ @property
+ def java_opts(self):
+ if "JAVA_OPTS" in os.environ:
+ options = os.environ["JAVA_OPTS"].split()
+ else:
+ options = []
+ options.extend([self.java_mem, self.java_stack])
+ return options
+
+ @property
+ def java_profile_agent(self):
+ return os.path.join(self.jython_home, "javalib", "profile.jar")
+
+ def set_encoding(self):
+ if "JAVA_ENCODING" not in os.environ and self.uname == "darwin":
+ self.args.properties["file.encoding"] = "UTF-8"
+
+ def convert(self, arg):
+ if sys.stdout.encoding:
+ return arg.encode(sys.stdout.encoding)
+ else:
+ return arg
+
+ def convert_path(self, arg):
+ if self.uname == "cygwin":
+ if not arg.startswith("/cygdrive/"):
+ new_path = self.convert(arg).replace("/", "\\")
+ else:
+ new_path = subprocess.check_output(["cygpath", "-pw", self.convert(arg)]).strip()
+ return new_path
+ else:
+ return self.convert(arg)
+
+ @property
+ def command(self):
+ self.set_encoding()
+ args = [self.java_command]
+ args.extend(self.java_opts)
+ args.extend(self.args.java)
+ for k, v in self.args.properties.iteritems():
+ args.append("-D%s=%s" % (self.convert(k), self.convert(v)))
+ if self.args.boot:
+ args.append("-Xbootclasspath/a:%s" % self.convert_path(self.classpath))
+ if self.args.classpath:
+ args.extend(["-classpath", self.convert_path(self.args.classpath)])
+ else:
+ args.extend(["-classpath", self.convert_path(self.classpath)])
+ args.append("-Dpython.home=%s" % self.convert_path(self.jython_home))
+ args.append("-Dpython.executable=%s" % self.convert_path(self.executable))
+ args.append("-Dpython.launcher.uname=%s" % self.uname)
+ # determine if is-a-tty for the benefit of running on cygwin - mintty doesn't behave like
+ # a standard windows tty and so JNR posix doesn't detect it properly
+ args.append("-Dpython.launcher.tty=%s" % str(os.isatty(sys.stdin.fileno())).lower())
+ if self.uname == "cygwin":
+ args.append("-Dpython.console=org.python.core.PlainConsole")
+ if self.args.profile:
+ args.append("-XX:-UseSplitVerifier")
+ args.append("-javaagent:%s" % self.convert_path(self.java_profile_agent))
+ args.append("org.python.util.jython")
+ if self.args.help:
+ args.append("--help")
+ args.extend(self.jython_args)
+ return args
+
+
+def print_help():
+ print >> sys.stderr, """
+Jython launcher options:
+-Jarg : pass argument through to Java VM (e.g. -J-Xmx512m)
+--jdb : run under JDB
+--print : print the Java command instead of executing it
+--profile: run with the Java Interactive Profiler (http://jiprof.sf.net)
+--boot : put jython on the boot classpath (disables the bytecode verifier)
+-- : pass remaining arguments through to Jython
+Jython launcher environment variables:
+JAVA_HOME : Java installation directory
+JYTHON_HOME: Jython installation directory
+JYTHON_OPTS: default command line arguments
+"""
+
+
+def main():
+ if sys.stdout.encoding:
+ if sys.stdout.encoding.lower() == "cp65001":
+ sys.exit("""Jython does not support code page 65001 (CP_UTF8).
+Please try another code page by setting it with the chcp command.""")
+ sys.argv = [arg.decode(sys.stdout.encoding) for arg in sys.argv]
+ parser, args, jython_args = make_parser()
+ jython_command = JythonCommand(parser, args, jython_args)
+ command = jython_command.command
+
+ if args.profile:
+ try:
+ os.unlink("profile.txt")
+ except OSError:
+ pass
+ if args.print_requested:
+ if jython_command.uname == "windows":
+ print subprocess.list2cmdline(jython_command.command)
+ else:
+ print " ".join(pipes.quote(arg) for arg in jython_command.command)
+ else:
+ if not (is_windows or not hasattr(os, "execvp") or args.help or jython_command.uname == "cygwin"):
+ # Replace this process with the java process.
+ #
+ # NB such replacements actually do not work under Windows,
+ # but if tried, they also fail very badly by hanging.
+ # So don't even try!
+ os.execvp(command[0], command[1:])
+ else:
+ result = 1
+ try:
+ result = subprocess.call(command)
+ if args.help:
+ print_help()
+ except KeyboardInterrupt:
+ pass
+ sys.exit(result)
+
+
+if __name__ == "__main__":
+ main()
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Wed Feb 18 08:38:20 2015
From: jython-checkins at python.org (jim.baker)
Date: Wed, 18 Feb 2015 07:38:20 +0000
Subject: [Jython-checkins] =?utf-8?q?jython_=28merge_default_-=3E_default?=
=?utf-8?q?=29=3A_Merged_trunk?=
Message-ID: <20150218073817.62581.82303@psf.io>
https://hg.python.org/jython/rev/f01ff62e8931
changeset: 7587:f01ff62e8931
parent: 7586:a5e9aecc8d9c
parent: 7585:efda62bbec80
user: Jim Baker
date: Tue Feb 17 23:55:47 2015 -0700
summary:
Merged trunk
files:
.hgtags | 2 +
Lib/test/test_gc.py | 259 +++--
Lib/test/test_gc_jy.py | 209 ++--
src/org/python/core/JyAttribute.java | 74 +-
src/org/python/core/PyBytecode.java | 15 +-
src/org/python/core/PyObject.java | 23 +-
src/org/python/core/Traverseproc.java | 18 +-
src/org/python/core/TraverseprocDerived.java | 24 +-
src/org/python/core/Visitproc.java | 8 +-
src/org/python/core/finalization/FinalizeTrigger.java | 38 +-
src/org/python/modules/_weakref/AbstractReference.java | 27 +-
src/org/python/modules/_weakref/GlobalRef.java | 79 +-
src/org/python/modules/_weakref/ReferenceType.java | 6 +-
src/org/python/modules/gc.java | 425 +++++++++-
14 files changed, 816 insertions(+), 391 deletions(-)
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -84,3 +84,5 @@
6e650bf6302fbc142e9fa0397ac2e8f342e16aaf v2.7b4
6e650bf6302fbc142e9fa0397ac2e8f342e16aaf v2.7b4
e270e881c84cbe519035c215fde09677362a4953 v2.7b4
+e270e881c84cbe519035c215fde09677362a4953 v2.7b4
+17d18c6dde9693129d977affb8ca20406d6a6585 v2.7b4
diff --git a/Lib/test/test_gc.py b/Lib/test/test_gc.py
--- a/Lib/test/test_gc.py
+++ b/Lib/test/test_gc.py
@@ -1,9 +1,10 @@
import unittest
-#from test.test_support import verbose, run_unittest
+from test.test_support import verbose, run_unittest
from test import test_support
import sys
import gc
import weakref
+import time
try:
import threading
@@ -59,6 +60,7 @@
@classmethod
def tearDownClass(cls):
+ #Jython-specific block:
try:
gc.setJythonGCFlags(cls.savedJythonGCFlags)
gc.stopMonitoring()
@@ -203,17 +205,12 @@
self.assertEqual(gc.collect(), 2)
def test_frame(self):
- flg = gc.getJythonGCFlags()
- #gc.addJythonGCFlags(gc.VERBOSE)
- #sporadically fails in Jython, no idea why.
def f():
frame = sys._getframe()
gc.collect()
f()
col = gc.collect()
- gc.setJythonGCFlags(flg)
self.assertEqual(col, 1)
-
def test_saveall(self):
# Verify that cyclic garbage like lists show up in gc.garbage if the
@@ -316,104 +313,115 @@
gc.collect(2)
assertEqual(gc.get_count(), (0, 0, 0))
-# def test_trashcan(self):
-# class Ouch:
-# n = 0
-# def __del__(self):
-# Ouch.n = Ouch.n + 1
-# if Ouch.n % 17 == 0:
-# gc.collect()
-#
-# # "trashcan" is a hack to prevent stack overflow when deallocating
-# # very deeply nested tuples etc. It works in part by abusing the
-# # type pointer and refcount fields, and that can yield horrible
-# # problems when gc tries to traverse the structures.
-# # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
-# # most likely die via segfault.
-#
-# # Note: In 2.3 the possibility for compiling without cyclic gc was
-# # removed, and that in turn allows the trashcan mechanism to work
-# # via much simpler means (e.g., it never abuses the type pointer or
-# # refcount fields anymore). Since it's much less likely to cause a
-# # problem now, the various constants in this expensive (we force a lot
-# # of full collections) test are cut back from the 2.2 version.
-# gc.enable()
-# N = 150
-# for count in range(2):
-# t = []
-# for i in range(N):
-# t = [t, Ouch()]
-# u = []
-# for i in range(N):
-# u = [u, Ouch()]
-# v = {}
-# for i in range(N):
-# v = {1: v, 2: Ouch()}
-# gc.disable()
+ def test_trashcan(self):
+ class Ouch:
+ n = 0
+ def __del__(self):
+ Ouch.n = Ouch.n + 1
+ if Ouch.n % 17 == 0:
+ gc.collect()
+
+ # "trashcan" is a hack to prevent stack overflow when deallocating
+ # very deeply nested tuples etc. It works in part by abusing the
+ # type pointer and refcount fields, and that can yield horrible
+ # problems when gc tries to traverse the structures.
+ # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
+ # most likely die via segfault.
+
+ # Note: In 2.3 the possibility for compiling without cyclic gc was
+ # removed, and that in turn allows the trashcan mechanism to work
+ # via much simpler means (e.g., it never abuses the type pointer or
+ # refcount fields anymore). Since it's much less likely to cause a
+ # problem now, the various constants in this expensive (we force a lot
+ # of full collections) test are cut back from the 2.2 version.
+ gc.enable()
+ N = 150
+ for count in range(2):
+ t = []
+ for i in range(N):
+ t = [t, Ouch()]
+ u = []
+ for i in range(N):
+ u = [u, Ouch()]
+ v = {}
+ for i in range(N):
+ v = {1: v, 2: Ouch()}
+ try:
+ gc.disable()
+ except NotImplementedError:
+ #i.e. Jython is running (or other non-CPython interpreter without gc-disabling)
+ pass
-# @unittest.skipUnless(threading, "test meaningless on builds without threads")
-# def test_trashcan_threads(self):
-# # Issue #13992: trashcan mechanism should be thread-safe
-# NESTING = 60
-# N_THREADS = 2
-#
-# def sleeper_gen():
-# """A generator that releases the GIL when closed or dealloc'ed."""
-# try:
-# yield
-# finally:
-# time.sleep(0.000001)
-#
-# class C(list):
-# # Appending to a list is atomic, which avoids the use of a lock.
-# inits = []
-# dels = []
-# def __init__(self, alist):
-# self[:] = alist
-# C.inits.append(None)
-# def __del__(self):
-# # This __del__ is called by subtype_dealloc().
-# C.dels.append(None)
-# # `g` will release the GIL when garbage-collected. This
-# # helps assert subtype_dealloc's behaviour when threads
-# # switch in the middle of it.
-# g = sleeper_gen()
-# next(g)
-# # Now that __del__ is finished, subtype_dealloc will proceed
-# # to call list_dealloc, which also uses the trashcan mechanism.
-#
-# def make_nested():
-# """Create a sufficiently nested container object so that the
-# trashcan mechanism is invoked when deallocating it."""
-# x = C([])
-# for i in range(NESTING):
-# x = [C([x])]
-# del x
-#
-# def run_thread():
-# """Exercise make_nested() in a loop."""
-# while not exit:
-# make_nested()
-#
-# old_checkinterval = sys.getcheckinterval()
-# sys.setcheckinterval(3)
-# try:
-# exit = False
-# threads = []
-# for i in range(N_THREADS):
-# t = threading.Thread(target=run_thread)
-# threads.append(t)
-# for t in threads:
-# t.start()
-# time.sleep(1.0)
-# exit = True
-# for t in threads:
-# t.join()
-# finally:
-# pass
-# sys.setcheckinterval(old_checkinterval)
-# gc.collect()
-# self.assertEqual(len(C.inits), len(C.dels))
+ @unittest.skipIf(test_support.is_jython,
+ '''
+ Jython does not have a trashcan mechanism.
+ This test should still not fail but currently does.
+ This is because the massive referencing in this test brings
+ sync gc emulation to its limit. Making this more robust is
+ of no priority for now.
+ ''')
+ @unittest.skipUnless(threading, "test meaningless on builds without threads")
+ def test_trashcan_threads(self):
+ # Issue #13992: trashcan mechanism should be thread-safe
+ NESTING = 60
+ N_THREADS = 2
+
+ def sleeper_gen():
+ """A generator that releases the GIL when closed or dealloc'ed."""
+ try:
+ yield
+ finally:
+ time.sleep(0.000001)
+
+ class C(list):
+ # Appending to a list is atomic, which avoids the use of a lock.
+ inits = []
+ dels = []
+ def __init__(self, alist):
+ self[:] = alist
+ C.inits.append(None)
+ def __del__(self):
+ # This __del__ is called by subtype_dealloc().
+ C.dels.append(None)
+ # `g` will release the GIL when garbage-collected. This
+ # helps assert subtype_dealloc's behaviour when threads
+ # switch in the middle of it.
+ g = sleeper_gen()
+ next(g)
+ # Now that __del__ is finished, subtype_dealloc will proceed
+ # to call list_dealloc, which also uses the trashcan mechanism.
+
+ def make_nested():
+ """Create a sufficiently nested container object so that the
+ trashcan mechanism is invoked when deallocating it."""
+ x = C([])
+ for i in range(NESTING):
+ x = [C([x])]
+ del x
+
+ def run_thread():
+ """Exercise make_nested() in a loop."""
+ while not exit:
+ make_nested()
+
+ old_checkinterval = sys.getcheckinterval()
+ sys.setcheckinterval(3)
+ try:
+ exit = False
+ threads = []
+ for i in range(N_THREADS):
+ t = threading.Thread(target=run_thread)
+ threads.append(t)
+ for t in threads:
+ t.start()
+ time.sleep(0.01)
+ exit = True
+ for t in threads:
+ t.join()
+ finally:
+ pass
+ sys.setcheckinterval(old_checkinterval)
+ self.assertEqual(len(C.inits), len(C.dels))
def test_boom(self):
class Boom:
@@ -733,28 +741,29 @@
# empty __dict__.
self.assertEqual(x, None)
-# def test_main():
-# unittest.main()
-# enabled = gc.isenabled()
-# gc.disable()
-# assert not gc.isenabled()
-# debug = gc.get_debug()
-# gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
-#
-# try:
-# gc.collect() # Delete 2nd generation garbage
-# run_unittest(GCTests, GCTogglingTests)
-# finally:
-# gc.set_debug(debug)
-# # test gc.enable() even if GC is disabled by default
-# if verbose:
-# print "restoring automatic collection"
-# # make sure to always test gc.enable()
-# gc.enable()
-# assert gc.isenabled()
-# if not enabled:
-# gc.disable()
+def test_main():
+ enabled = gc.isenabled()
+ try:
+ gc.disable()
+ assert not gc.isenabled()
+ except NotImplementedError:
+ pass
+ debug = gc.get_debug()
+ gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
+
+ try:
+ gc.collect() # Delete 2nd generation garbage
+ run_unittest(GCTests, GCTogglingTests)
+ finally:
+ gc.set_debug(debug)
+ # test gc.enable() even if GC is disabled by default
+ if verbose:
+ print "restoring automatic collection"
+ # make sure to always test gc.enable()
+ gc.enable()
+ assert gc.isenabled()
+ if not enabled:
+ gc.disable()
if __name__ == "__main__":
- #test_main()
unittest.main()
diff --git a/Lib/test/test_gc_jy.py b/Lib/test/test_gc_jy.py
--- a/Lib/test/test_gc_jy.py
+++ b/Lib/test/test_gc_jy.py
@@ -1,31 +1,21 @@
"""
Tests some Jython-specific gc aspects and debugging
features.
+Skips and try-blocks assure that this test-script is
+still runnable with CPython and passes there as far
+as not skipped.
"""
import unittest
-#from test.test_support import verbose, run_unittest
-#import sys
+from test import test_support
import time
import gc
import weakref
-from java.lang import System, Runnable
-
-# class FinalizationDummy:
-# def __del__(self):
-# time.sleep(3.5)
-# print "FinalizationDummy.__del__"
-# time.sleep(3.5)
-#
-# class ResurrectionDummy:
-# def __del__(self):
-# print "ResurrectionDummy.__del__"
-# ResurrectionDummy.resurrected = self.toResurrect
-#
-# class SelfResurrectionDummy:
-# def __del__(self):
-# print "SelfResurrectionDummy.__del__"
-# SelfResurrectionDummy.resurrected = self
+try:
+ from java.lang import System, Runnable
+except ImportError:
+ #i.e. Jython is running
+ pass
class GCTests_Jy_CyclicGarbage(unittest.TestCase):
@@ -101,6 +91,8 @@
self.fail("didn't find obj in garbage (finalizer)")
gc.garbage.remove(obj)
+ @unittest.skipUnless(test_support.is_jython,
+ 'CPython has no monitor state')
def test_manual_monitoring(self):
# since tuples are immutable we close the loop with a list
l = []
@@ -117,6 +109,8 @@
self.assertEqual(gc.collect(), 1)
+ at unittest.skipUnless(test_support.is_jython,
+ 'CPython has no gc preprocess and postprocess features')
class GCTests_Jy_preprocess_and_postprocess(unittest.TestCase):
def test_finalization_preprocess_and_postprocess(self):
@@ -156,6 +150,82 @@
prePr = PreProcess()
postPr = PostProcess()
time.sleep(1) # <- to avoid that the newly registered processes
+ # become subject to previous run (remember: We
+ # are not in monitor-mode, i.e. gc runs async.
+ gc.registerPreFinalizationProcess(prePr)
+ gc.registerPostFinalizationProcess(postPr)
+ #Note that order matters here:
+ #If the flag gc.DONT_FINALIZE_RESURRECTED_OBJECTS is used,
+ #gc.registerPostFinalizationProcess(postPr, 0) would lead to failure,
+ #because postPr asserts that a's finalizer already ran. Since
+ #DONT_FINALIZE_RESURRECTED_OBJECTS also inserted a postprocess,
+ #to perform delayed finalization, the 0-index would prepend postPr
+ #before the process that actually runs the finalizers.
+ System.gc()
+ #we wait a bit longer here, since PostProcess runs asynchronous
+ #and must wait for the finalizer of A
+ time.sleep(2)
+ self.assertIn("run PostProcess", comments)
+ comments = []
+ gc.unregisterPreFinalizationProcess(prePr)
+ gc.unregisterPostFinalizationProcess(postPr)
+
+
+ at unittest.skipUnless(test_support.is_jython,
+ 'This class tests detailed Jython-specific behavior.')
+class GCTests_Jy_Delayed_Finalization(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ #the finalizer-related tests need this flag to pass in Jython:
+ gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ except Exception:
+ pass
+
+ def test_finalization_preprocess_and_postprocess(self):
+ #Note that this test is done here again (already was in another class
+ #in this module), to see that everything works as it should also with
+ #a different flag-context.
+ comments = []
+ self0 = self
+ class A:
+ def __del__(self):
+ self0.assertIn("run PreProcess", comments)
+ comments.append("A del")
+ #let's simulate a time-consuming finalizer
+ #to ensure that post finalization processing
+ #is sensitive to this
+ time.sleep(0.5)
+ comments.append("A del done")
+
+ class PreProcess(Runnable):
+ def run(self):
+ self0.assertEqual(comments, [])
+ comments.append("run PreProcess")
+
+ class PostProcess(Runnable):
+ def run(self):
+ self0.assertIn("run PreProcess", comments)
+ self0.assertIn("A del", comments)
+ self0.assertIn("A del done", comments)
+ comments.append("run PostProcess")
+
+ a = A()
+ a = None
+ prePr = PreProcess()
+ postPr = PostProcess()
+ time.sleep(1) # <- to avoid that the newly registered processes
# become subject to previous run
gc.registerPreFinalizationProcess(prePr)
gc.registerPostFinalizationProcess(postPr)
@@ -176,86 +246,7 @@
gc.unregisterPostFinalizationProcess(postPr)
-class GCTests_Jy_Delayed_Finalization(unittest.TestCase):
-
- @classmethod
- def setUpClass(cls):
- #Jython-specific block:
- try:
- cls.savedJythonGCFlags = gc.getJythonGCFlags()
- #the finalizer-related tests need this flag to pass in Jython:
- gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- gc.stopMonitoring()
- except Exception:
- pass
-
- @classmethod
- def tearDownClass(cls):
- try:
- gc.setJythonGCFlags(cls.savedJythonGCFlags)
- except Exception:
- pass
-
- def test_finalization_preprocess_and_postprocess(self):
- #print "test_finalization_preprocess_and_postprocess"
- #Note that this test is done here again (already was in another class
- #in this module), to see that everything works as it should also with
- #a different flag-context.
- #print "test_finalization_preprocess_and_postprocess"
- #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- comments = []
- self0 = self
- class A:
- def __del__(self):
- #print "del A"
- self0.assertIn("run PreProcess", comments)
- comments.append("A del")
- #let's simulate a time-consuming finalizer
- #to ensure that post finalization processing
- #is sensitive to this
- time.sleep(0.5)
- comments.append("A del done")
-
- class PreProcess(Runnable):
- def run(self):
- self0.assertEqual(comments, [])
- comments.append("run PreProcess")
-
- class PostProcess(Runnable):
- def run(self):
- self0.assertIn("run PreProcess", comments)
- self0.assertIn("A del", comments)
- self0.assertIn("A del done", comments)
- comments.append("run PostProcess")
-
- a = A()
- a = None
- prePr = PreProcess()
- postPr = PostProcess()
- time.sleep(1) # <- to avoid that the newly registered processes
- # become subject to previous run
- gc.registerPreFinalizationProcess(prePr)
- gc.registerPostFinalizationProcess(postPr)
- #Note that order matters here:
- #If the flag gc.DONT_FINALIZE_RESURRECTED_OBJECTS is used,
- #gc.registerPostFinalizationProcess(postPr, 0) would lead to failure,
- #because postPr asserts that a's finalizer already ran. Since
- #DONT_FINALIZE_RESURRECTED_OBJECTS also inserted a postprocess,
- #to perform delayed finalization, the 0-index would prepend postPr
- #before the process that actually runs the finalizers.
- System.gc()
- #we wait a bit longer here, since PostProcess runs asynchronous
- #and must wait for the finalizer of A
- time.sleep(2)
- self.assertIn("run PostProcess", comments)
- comments = []
- gc.unregisterPreFinalizationProcess(prePr)
- gc.unregisterPostFinalizationProcess(postPr)
-
-
def test_delayedFinalization(self):
- #gc.addJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- #gc.addJythonGCFlags(gc.VERBOSE)
resurrect = []
comments = []
@@ -291,15 +282,17 @@
del c
self.assertNotEqual(gc.collect(), 0)
time.sleep(1)
- #print comments
- #print resurrect
+ #Note that CPython would collect a, b and c in one run.
+ #With gc.DONT_FINALIZE_RESURRECTED_OBJECTS set, Jython
+ #Would not collect a and b in the same run with c
+ #because a and b might have been resurrected by c and
+ #Java allows not to detect such resurrection in any
+ #other way than waiting for the next gc-run.
self.assertIn('del c', comments)
self.assertEqual(1, len(comments))
comments = []
self.assertNotEqual(gc.collect(), 0)
time.sleep(1)
- #print comments
- #print resurrect
self.assertIn('del a', comments)
self.assertEqual(1, len(comments))
comments = []
@@ -307,8 +300,6 @@
time.sleep(1)
self.assertIn('del b', comments)
self.assertEqual(1, len(comments))
- #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
- #gc.removeJythonGCFlags(gc.VERBOSE)
class GCTests_Jy_Monitoring(unittest.TestCase):
@@ -336,6 +327,7 @@
except Exception:
pass
+ @unittest.skipUnless(test_support.is_jython, 'CPython has no monitor-state.')
def test_monitor_status_after_delayed_finalization(self):
resurrect = []
comments = []
@@ -409,8 +401,11 @@
a.b.lst = lst
del lst
del lst1
- self.assertTrue(gc.isMonitored(a))
- self.assertTrue(gc.isMonitored(a.b))
+ try:
+ self.assertTrue(gc.isMonitored(a))
+ self.assertTrue(gc.isMonitored(a.b))
+ except AttributeError:
+ pass
del a
self.assertEqual(gc.collect(), 2) # c is not cyclic and a, b are resurrected,
# the cycle of two lists is counted here
@@ -543,6 +538,7 @@
except Exception:
pass
+ @unittest.skipUnless(test_support.is_jython, '')
def test_weakref_after_resurrection_threadsafe(self):
resurrect = []
comments = []
@@ -587,7 +583,6 @@
# restored or the deletion is confirmed.
except Exception:
pass
- #self.assertEqual(str(wa()), '')
self.assertEqual(comments, [])
self.assertEqual(resurrect, [])
while comments == [] or resurrect == []:
diff --git a/src/org/python/core/JyAttribute.java b/src/org/python/core/JyAttribute.java
--- a/src/org/python/core/JyAttribute.java
+++ b/src/org/python/core/JyAttribute.java
@@ -4,16 +4,18 @@
/**
*
- * Manages a linked list of general purpose Object-attributes, which
- * can be attached to arbitrary {@code PyObject}s. This method replaces
- * the formerly used method of maintaining weak hash maps for such cases.
- * These weak hash maps were used to map {@code PyObject}s to such
- * attributes, for instance to attach
+ * Manages a linked list of general purpose Object-attributes that
+ * can be attached to arbitrary {@link org.python.core.PyObject}s.
+ * This method replaces the formerly used method of maintaining weak
+ * hash-maps ({@link java.util.WeakHashMap}) for such cases.
+ * These weak hash-maps were used to map
+ * {@code PyObject}s to such attributes, for instance
+ * to attach
* {@link org.python.modules._weakref.GlobalRef}-objects in the
* {@link org.python.modules._weakref.WeakrefModule}.
*
*
- * Attributes attached via the weak hash map method break, if the
+ * Attributes attached via the weak hash-map-method break, if the
* {@code PyObject} is resurrected in its finalizer. The
* {@code JyAttribute}-method is resurrection-safe.
*
@@ -35,18 +37,18 @@
public static final byte JAVA_PROXY_ATTR = Byte.MIN_VALUE;
/**
- * Stores list of weak references linking to this PyObject.
+ * Stores list of weak references linking to this {@code PyObject}.
* This list is weakref-based, so it does not keep the
* weakrefs alive. This is the only way to find out which
* weakrefs (i.e. org.python.modules._weakref.AbstractReference)
* linked to the object after a resurrection. A weak
- * hashmap-based approach for this purpose would break on
+ * hash-map-based approach for this purpose would break on
* resurrection.
*/
public static final byte WEAK_REF_ATTR = 0; //first transient
/**
- * Reserved for use by JyNI.
+ * Reserved for use by JyNI .
*/
public static final byte JYNI_HANDLE_ATTR = 1;
@@ -57,8 +59,9 @@
public static final byte PY_ID_ATTR = 2;
/**
- * Holds the current thread for an AbstractReference while
- * referent-retrieval is pending due to a potentially
+ * Holds the current thread for an
+ * {@link org.python.modules._weakref.AbstractReference}
+ * while referent-retrieval is pending due to a potentially
* restored-by-resurrection weak reference. After the
* restore has happened or the clear was confirmed, the
* thread is interrupted and the attribute is cleared.
@@ -66,34 +69,28 @@
public static final byte WEAKREF_PENDING_GET_ATTR = 3;
/**
- * Used by gc module to mark cyclic trash. Searching for cyclic
- * trash is usually not required by Jython. It is only done if
- * gc features are enabled that mimic CPython behavior.
+ * Used by {@link org.python.modules.gc}-module to mark cyclic
+ * trash. Searching for cyclic trash is usually not required
+ * by Jython. It is only done if gc-features are enabled that
+ * mimic CPython behavior.
*/
public static final byte GC_CYCLE_MARK_ATTR = 4;
/**
- * Used by gc module to mark monitored objects before they
- * become unmonitored for deletion. In case of a resurrection
- * this is the only way for gc to detect that the object was
- * to be monitored and should be monitored again.
+ * Used by {@link org.python.modules.gc}-module to mark
+ * finalizable objects that might have been resurrected
+ * during a delayed finalization process.
*/
- //public static final byte GC_MONITOR_MARK_ATTR = 5;
-
- /**
- * Used by gc module to mark finalizable objects that might have
- * been resurrected during a delayed finalization process.
- */
- public static final byte GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR = 6;
+ public static final byte GC_DELAYED_FINALIZE_CRITIC_MARK_ATTR = 5;
public static final byte FINALIZE_TRIGGER_ATTR = Byte.MAX_VALUE;
private static byte nonBuiltinAttrTypeOffset = Byte.MIN_VALUE+1;
- private static byte nonBuiltinTransientAttrTypeOffset = 7;
+ private static byte nonBuiltinTransientAttrTypeOffset = 6;
/**
- * Reserves and returns a new attr type for custom use.
+ * Reserves and returns a new non-transient attr type for custom use.
*
- * @return an attr type for custom use
+ * @return a non-transient attr type for custom use
*/
public static byte reserveCustomAttrType() {
if (nonBuiltinAttrTypeOffset == 0) {
@@ -178,8 +175,8 @@
protected abstract void setValue(Object value);
/**
- * Checks whether the given {@code PyObject} has an attribute
- * of the given type attached.
+ * Checks whether the given {@link org.python.core.PyObject}
+ * has an attribute of the given type attached.
*/
public static synchronized boolean hasAttr(PyObject ob, byte attr_type) {
if (ob.attributes == null) {
@@ -197,7 +194,7 @@
/**
* Retrieves the attribute of the given type from the given
- * {@code PyObject}.
+ * {@link org.python.core.PyObject}.
* If no attribute of the given type is attached, null is returned.
*/
public static synchronized Object getAttr(PyObject ob, byte attr_type) {
@@ -214,6 +211,11 @@
return att != null && att.attr_type == attr_type ? att.getValue() : null;
}
+ /**
+ * Prints the current state of the attribute-list of the
+ * given object to the given stream.
+ * (Intended for debugging)
+ */
public static synchronized void debugPrintAttributes(PyObject o, java.io.PrintStream out) {
out.println("debugPrintAttributes of "+System.identityHashCode(o)+":");
if (o.attributes == null) {
@@ -230,6 +232,11 @@
out.println("debugPrintAttributes done");
}
+ /**
+ * Sets the attribute of type {@code attr_type} in {@code ob} to {@code value}.
+ * If no corresponding attribute exists yet, one is created. If {@value == null},
+ * the attribute is removed (if it existed at all).
+ */
public static synchronized void setAttr(PyObject ob, byte attr_type, Object value) {
if (value == null) {
delAttr(ob, attr_type);
@@ -281,6 +288,11 @@
}
}
+ /**
+ * Removes the attribute of given type from the given object's attribute-list
+ * (if it existed at all). This is equivalent to calling
+ * {@code setAttr(ob, attr_type, null)}.
+ */
public static synchronized void delAttr(PyObject ob, byte attr_type) {
if (ob.attributes == null) {
return;
diff --git a/src/org/python/core/PyBytecode.java b/src/org/python/core/PyBytecode.java
--- a/src/org/python/core/PyBytecode.java
+++ b/src/org/python/core/PyBytecode.java
@@ -164,13 +164,13 @@
enum Why {
- NOT, /* No error */
+ NOT, /* No error */
EXCEPTION, /* Exception occurred */
- RERAISE, /* Exception re-raised by 'finally' */
- RETURN, /* 'return' statement */
- BREAK, /* 'break' statement */
- CONTINUE, /* 'continue' statement */
- YIELD /* 'yield' operator */
+ RERAISE, /* Exception re-raised by 'finally' */
+ RETURN, /* 'return' statement */
+ BREAK, /* 'break' statement */
+ CONTINUE, /* 'continue' statement */
+ YIELD /* 'yield' operator */
};
@@ -1054,7 +1054,8 @@
break;
case Opcode.WITH_CLEANUP: {
- /* TOP is the context.__exit__ bound method.
+ /*
+ TOP is the context.__exit__ bound method.
Below that are 1-3 values indicating how/why
we entered the finally clause:
- SECOND = None
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
@@ -20,7 +20,7 @@
/**
* All objects known to the Jython runtime system are represented by an instance
- * of the class PyObject
or one of its subclasses.
+ * of the class {@code PyObject} or one of its subclasses.
*/
@ExposedType(name = "object", doc = BuiltinDocs.object_doc)
public class PyObject implements Serializable {
@@ -28,10 +28,14 @@
public static final PyType TYPE = PyType.fromClass(PyObject.class);
/**
- * This should have been suited at org.python.modules.gc, but that would cause
- * a dependency cycle in the init-phases of gc.class and PyObject.class.
- * Now this boolean mirrors the presence of the MONITOR_GLOBAL-flag of
- * Jython's gc module.
+ * This should have been suited at {@link org.python.modules.gc},
+ * but that would cause a dependency cycle in the init-phases of
+ * {@code gc.class} and {@code PyObject.class}. Now this boolean
+ * mirrors the presence of the
+ * {@link org.python.modules.gc#MONITOR_GLOBAL}-flag in Jython's
+ * gc module.
+ *
+ * Do not change manually.
*/
public static boolean gcMonitorGlobal = false;
@@ -45,12 +49,15 @@
* objects can be accessed by the methods and keys in
* {@link org.python.core.JyAttribute}.
* A notable attribute is the javaProxy (accessible via
- * {@code JyAttribute.getAttr(this, JAVA_PROXY_ATTR)}),
+ * {@code JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR)}),
* an underlying Java instance that this object is wrapping or is a
* subclass of. Anything attempting to use the proxy should go through
* {@link #getJavaProxy()} which ensures that it's initialized.
+ *
+ * @see org.python.core.JyAttribute
+ * @see org.python.core.JyAttribute#JAVA_PROXY_ATTR
+ * @see #getJavaProxy()
*/
- //protected Object javaProxy;
protected Object attributes;
/** Primitives classes their wrapper classes. */
@@ -1708,7 +1715,6 @@
public PyObject _is(PyObject o) {
// Access javaProxy directly here as is is for object identity, and at best getJavaProxy
// will initialize a new object with a different identity
- //return this == o || (javaProxy != null && javaProxy == o.javaProxy) ? Py.True : Py.False;
return this == o || (JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) &&
JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) ==
JyAttribute.getAttr(o, JyAttribute.JAVA_PROXY_ATTR)) ? Py.True : Py.False;
@@ -1723,7 +1729,6 @@
public PyObject _isnot(PyObject o) {
// Access javaProxy directly here as is is for object identity, and at best getJavaProxy
// will initialize a new object with a different identity
- //return this != o && (javaProxy == null || javaProxy != o.javaProxy) ? Py.True : Py.False;
return this != o && (!JyAttribute.hasAttr(this, JyAttribute.JAVA_PROXY_ATTR) ||
JyAttribute.getAttr(this, JyAttribute.JAVA_PROXY_ATTR) !=
JyAttribute.getAttr(o, JyAttribute.JAVA_PROXY_ATTR)) ? Py.True : Py.False;
diff --git a/src/org/python/core/Traverseproc.java b/src/org/python/core/Traverseproc.java
--- a/src/org/python/core/Traverseproc.java
+++ b/src/org/python/core/Traverseproc.java
@@ -20,8 +20,7 @@
* tracefunc in {@link org.python.core.PyFrame}).
* PyObjects that don't own references to other PyObjects under any condition
* and neither inherit such references from a superclass are strictly recommended
- * to be annotated {@code {@literal @}Untraversable}
- * (see {@link org.python.core.Untraversable} for details).
+ * to be annotated {@link org.python.core.Untraversable}.
*
*
* Jython's traverse mechanism serves debugging purposes to ease finding memory
@@ -42,14 +41,16 @@
* Note that the slots-array and - if existent - the user-dict of fooDerived classes
* is traversed by {@link org.python.core.TraverseProcDerived}.
* The gc module takes care of exploiting both traverse methods in its static traverse
- * method. So for manual traversion one should always use {@code gc.traverse} rather
+ * method. So for manual traversion one should always use
+ * {@link org.python.modules.gc#traverse(PyObject, Visitproc, Object)} rather
* than directly calling methods in this interface.
*
*
- * Also note that {@code objtype} is not subject to {@code Traverseproc}s
- * by default. In CPython only objects with heap-types traverse their
- * ob_type field. In Jython, {@code fooDerived}-classes are the
- * equivalents of heapTypes. For such classes, objtype is actually
+ * Also note that {@link org.python.core.PyObject#objtype} is not subject to
+ * {@code Traverseproc}s by default. In CPython only objects with heap-types
+ * traverse their {@code ob_type}-field. In Jython, {@code fooDerived}-classes
+ * are the equivalents of heapTypes. For such classes
+ * {@link org.python.core.PyObject#objtype} is actually
* traversed (along with the user dict).
*
*
@@ -439,6 +440,9 @@
* UAdd - no refs, extends PythonTree
* USub - no refs, extends PythonTree
*
+ * @see org.python.core.Untraversable
+ * @see org.python.core.Visitproc
+ * @see org.python.modules.gc#traverse(PyObject, Visitproc, Object)
*/
public interface Traverseproc {
diff --git a/src/org/python/core/TraverseprocDerived.java b/src/org/python/core/TraverseprocDerived.java
--- a/src/org/python/core/TraverseprocDerived.java
+++ b/src/org/python/core/TraverseprocDerived.java
@@ -1,17 +1,19 @@
package org.python.core;
/**
- * This is used like Traverseproc, but traverses only the slots[] array
- * of fooDerived classes. This way it is avoided that the traverse
- * method of a traversable PyObject is overwritten by the derived
- * version. The gc module takes care of exploiting both traverse methods.
- *
+ * This is used like {@link org.python.core.Traverseproc},
+ * but traverses only the {@code slots[]}-array of
+ * {@code fooDerived}-classes. This way we avoid that the traverse
+ * method of a traversable {@link org.python.core.PyObject} is
+ * overwritten by the derived version.
+ * {@link org.python.modules.gc#traverse(PyObject, Visitproc, Object)} takes care of
+ * exploiting both traverse methods.
*/
public interface TraverseprocDerived {
- /**
- * Traverses all reachable {@code PyObject}s.
- * Like in CPython, {@code arg} must be passed
- * unmodified to {@code visit} as its second parameter.
- */
- public int traverseDerived(Visitproc visit, Object arg);
+ /**
+ * Traverses all reachable {@code PyObject}s.
+ * Like in CPython, {@code arg} must be passed
+ * unmodified to {@code visit} as its second parameter.
+ */
+ public int traverseDerived(Visitproc visit, Object arg);
}
diff --git a/src/org/python/core/Visitproc.java b/src/org/python/core/Visitproc.java
--- a/src/org/python/core/Visitproc.java
+++ b/src/org/python/core/Visitproc.java
@@ -1,7 +1,9 @@
package org.python.core;
public interface Visitproc {
- /**Must not be called with {@code null}.
- */
- public int visit(PyObject object, Object arg);
+
+ /**
+ * Must not be called with {@code object == null}.
+ */
+ public int visit(PyObject object, Object arg);
}
diff --git a/src/org/python/core/finalization/FinalizeTrigger.java b/src/org/python/core/finalization/FinalizeTrigger.java
--- a/src/org/python/core/finalization/FinalizeTrigger.java
+++ b/src/org/python/core/finalization/FinalizeTrigger.java
@@ -12,35 +12,15 @@
public class FinalizeTrigger {
/**
* This flag tells the finalize trigger to call
- * gc.notifyFinalize after it called the finalizer.
+ * {@link gc#notifyFinalize(PyObject)} after it called the finalizer.
*/
public static final byte NOTIFY_GC_FLAG = (1<<0);
-
- /**
- * This flag tells the finalize trigger to refrain from actually
- * running the PyObject's {@code __del__} method (or variants for
- * derived or builtins).
- * It can be used to have finalize triggers for debugging and
- * monitoring purposes. The actual purpose is for Jython gc's
- * {@code DONT_FINALIZE_CYCLIC_GARBAGE} flag that tells the gc to emulate
- * CPython's <3.4 policy never to finalize cyclic garbage.
- */
- //public static final byte INHIBIT_FINALIZER_FLAG = (1<<1);
-
- /**
- * Tells the finalizer to add the finalized PyObject to the gc's
- * garbage list. This allows gc to mimic CPython's way to deal
- * with cyclic finalizable objects prior 3.4
- * (c.f. CPython's gc's DEBUG_SAVEALL flag).
- */
- //public static final byte ADD_TO_GARBAGE_LIST_FLAG = (1<<2);
/**
- * Similar to {@code INHIBIT_FINALIZER_FLAG}, but indicates that the
- * underlying PyObject was never intended to be finalized, while
- * {@code INHIBIT_FINALIZER_FLAG} indicates that there actually *is* a
- * finalizer that is just not processed due to special
- * circumstances (i.e. inactive {@code DONT_FINALIZE_CYCLIC_GARBAGE} flag).
+ * Indicates that the underlying PyObject was never intended to be finalized.
+ * It is actually not finalizable and the trigger only exists to notify
+ * {@link org.python.modules.gc} that the underlying object was finalized.
+ * This is needed for some advanced gc-functionality.
*/
public static final byte NOT_FINALIZABLE_FLAG = (1<<3);
@@ -186,7 +166,7 @@
}
}
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", "finalization of "+toFinalize);
+ gc.writeDebug("gc", "finalization of "+toFinalize);
}
if (saveGarbage == 1 || (saveGarbage == 0 &&
(gc.get_debug() & gc.DEBUG_SAVEALL) != 0 && isCyclic())) {
@@ -199,13 +179,13 @@
}
gc.garbage.add(toFinalize);
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", toFinalize+" added to garbage.");
+ gc.writeDebug("gc", toFinalize+" added to garbage.");
}
}
}
if ((flags & NOTIFY_GC_FLAG) != 0) {
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", "notify finalization of "+toFinalize);
+ gc.writeDebug("gc", "notify finalization of "+toFinalize);
}
gc.notifyFinalize(toFinalize);
flags &= ~NOTIFY_GC_FLAG;
@@ -217,7 +197,7 @@
gc.notifyPreFinalization();
if (gc.delayedFinalizationEnabled() && toFinalize != null) {
if ((gc.getJythonGCFlags() & gc.VERBOSE_FINALIZE) != 0) {
- Py.writeDebug("gc", "delayed finalization for "+toFinalize);
+ gc.writeDebug("gc", "delayed finalization for "+toFinalize);
}
gc.registerForDelayedFinalization(toFinalize);
} else {
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
@@ -84,9 +84,9 @@
return null;
}
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
- Py.writeDebug("gc", "pending in get of abstract ref "+this+": "+
- Thread.currentThread().getId());
- }
+ gc.writeDebug("gc", "pending in get of abstract ref "+this+": "+
+ Thread.currentThread().getId());
+ }
JyAttribute.setAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR, Thread.currentThread());
while (!gref.cleared && result == null) {
try {
@@ -96,22 +96,23 @@
}
JyAttribute.delAttr(this, JyAttribute.WEAKREF_PENDING_GET_ATTR);
if ((gc.getJythonGCFlags() & gc.VERBOSE_WEAKREF) != 0) {
- Py.writeDebug("gc", "pending of "+this+" resolved: "+
- Thread.currentThread().getId());
- if (gref.cleared) {
- Py.writeDebug("gc", "reference was cleared.");
- } else if (result != null){
- Py.writeDebug("gc", "reference was restored.");
- } else {
- Py.writeDebug("gc", "something went very wrong.");
- }
- }
+ gc.writeDebug("gc", "pending of "+this+" resolved: "+
+ Thread.currentThread().getId());
+ if (gref.cleared) {
+ gc.writeDebug("gc", "reference was cleared.");
+ } else if (result != null){
+ gc.writeDebug("gc", "reference was restored.");
+ } else {
+ gc.writeDebug("gc", "something went very wrong.");
+ }
+ }
return result;
} else {
return result;
}
}
+
/* Traverseproc implementation */
@Override
public int traverse(Visitproc visit, Object arg) {
diff --git a/src/org/python/modules/_weakref/GlobalRef.java b/src/org/python/modules/_weakref/GlobalRef.java
--- a/src/org/python/modules/_weakref/GlobalRef.java
+++ b/src/org/python/modules/_weakref/GlobalRef.java
@@ -20,8 +20,8 @@
public class GlobalRef extends WeakReference {
/**
- * This reference's hashCode: the System.identityHashCode of the referent. Only used
- * internally.
+ * This reference's hashCode: The {@code System.identityHashCode} of the referent.
+ * Only used internally.
*/
private int hashCode;
@@ -31,16 +31,19 @@
*/
private int pythonHashCode;
- /** Whether pythonHashCode was already determined. */
+ /** Whether {@link #pythonHashCode} was already determined. */
private boolean havePythonHashCode;
/**
- * This boolean is set true when the callback is processed. If the reference is
- * cleared it might potentially be restored until this boolean is set true.
- * If weak reference restoring is activated (c.f.
- * gc.PRESERVE_WEAKREFS_ON_RESURRECTION), AbstractReference.get would block
- * until a consistent state is reached (i.e. referent is non-null or
- * cleared == true).
+ * This boolean is set {@code true} when the callback is processed.
+ * If the reference is cleared it might potentially be restored until
+ * this boolean is set true. If weak reference restoring is activated (c.f.
+ * {@link gc#PRESERVE_WEAKREFS_ON_RESURRECTION}), {@link AbstractReference#get()}
+ * would block until a consistent state is reached (i.e. referent is
+ * non-{@code null} or {@code cleared == true}).
+ *
+ * @see gc#PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see AbstractReference#get()
*/
protected boolean cleared = false;
@@ -73,7 +76,7 @@
* Search for a reusable reference. To be reused, it must be of the
* same class and it must not have a callback.
*/
- synchronized AbstractReference find(Class cls) {
+ synchronized AbstractReference find(Class> cls) {
for (int i = references.size() - 1; i >= 0; i--) {
AbstractReference r = getReferenceAt(i);
if (r == null) {
@@ -108,7 +111,10 @@
}
/**
- * Call all callbacks that were enqueued via delayedCallback method.
+ * Call all callbacks that were enqueued via
+ * {@link #delayedCallback(GlobalRef)} method.
+ *
+ * @see #delayedCallback(GlobalRef)
*/
public static void processDelayedCallbacks() {
if (delayedCallbacks != null) {
@@ -123,9 +129,12 @@
/**
* Stores the callback for later processing. This is needed if
- * weak reference restoration (c.f. gc.PRESERVE_WEAKREFS_ON_RESURRECTION)
- * is activated. In this case the callback is delayed until it was
+ * weak reference restoration (c.f.
+ * {@link gc#PRESERVE_WEAKREFS_ON_RESURRECTION})
+ * is active. In this case the callback is delayed until it was
* determined whether a resurrection restored the reference.
+ *
+ * @see gc#PRESERVE_WEAKREFS_ON_RESURRECTION
*/
private static void delayedCallback(GlobalRef cl) {
if (delayedCallbacks == null) {
@@ -164,10 +173,10 @@
}
/**
- * Create a new tracked GlobalRef.
+ * Create a new tracked {@code GlobalRef}.
*
- * @param object a PyObject to reference
- * @return a new tracked GlobalRef
+ * @param object a {@link org.python.core.PyObject} to reference
+ * @return a new tracked {@code GlobalRef}
*/
public static GlobalRef newInstance(PyObject object) {
createReaperThreadIfAbsent();
@@ -188,12 +197,16 @@
/**
* Restores this weak reference to its former referent.
- * This actually means that a fresh GlobalRef is created
- * and inserted into all adjacent AbstractRefs. The
- * current GlobalRef is disbanded.
- * If the given PyObject is not the former referent of
- * this weak reference, an IllegalArgumentException is
- * thrown.
+ * This actually means that a fresh {@code GlobalRef} is created
+ * and inserted into all adjacent
+ * {@link org.python.modules._weakref.AbstractReference}s. The
+ * current {@code GlobalRef} is disbanded.
+ * If the given {@link org.python.core.PyObject} is not the former
+ * referent of this weak reference, an
+ * {@link java.lang.IllegalArgumentException} is thrown.
+ *
+ * @throws java.lang.IllegalArgumentException if {@code formerReferent} is not
+ * the actual former referent.
*/
public void restore(PyObject formerReferent) {
if (JyAttribute.getAttr(formerReferent, JyAttribute.WEAK_REF_ATTR) != this) {
@@ -248,7 +261,8 @@
}
/**
- * Return the number of references to the specified PyObject.
+ * Return the number of references to the specified
+ * {@link org.python.core.PyObject}.
*
* @param object a PyObject
* @return an int reference count
@@ -259,10 +273,11 @@
}
/**
- * Return a list of references to the specified PyObject.
+ * Return a list of references to the specified
+ * {@link org.python.core.PyObject}.
*
- * @param object a PyObject
- * @return a PyList of references. may be empty
+ * @param object a {@link org.python.core.PyObject}
+ * @return a {@link org.python.core.PyList} of references. May be empty.
*/
public static PyList getRefs(PyObject object) {
GlobalRef ref = objects.get(new GlobalRef(object));
@@ -270,7 +285,7 @@
}
/**
- * Allow GlobalRef's to be used as hashtable keys.
+ * Allow {@code GlobalRef}s to be used as hashtable-keys.
*/
public boolean equals(Object o) {
if (this == o) {
@@ -292,18 +307,20 @@
}
/**
- * Allows GlobalRef to be used as hashtable keys.
+ * Allows {@code GlobalRef} to be used as hashtable-keys.
*
- * @return a hashCode int value
+ * @return a hashCode {@code int}-value
*/
public int hashCode() {
return hashCode;
}
/**
- * The publicly used hashCode, for the AbstractReference wrapper.
+ * The publicly used {@code hashCode}, for the
+ * {@link org.python.modules._weakref.AbstractReference}
+ * wrapper.
*
- * @return a hashCode int value
+ * @return a hashCode {@code int}-value
*/
public int pythonHashCode() {
if (havePythonHashCode) {
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
@@ -68,9 +68,9 @@
* to passthru).
*
* @param funcName the name of the caller
- * @param args PyObject array of args
- * @param keywords String array of keywords
- * @return an ArgParser instance
+ * @param args {@link or.python.core.PyObject} array of args
+ * @param keywords {@code String}-array of keywords
+ * @return an {@link or.python.core.ArgParser} instance
*/
private static ArgParser parseInitArgs(String funcName, PyObject[] args, String[] keywords) {
if (keywords.length > 0) {
diff --git a/src/org/python/modules/gc.java b/src/org/python/modules/gc.java
--- a/src/org/python/modules/gc.java
+++ b/src/org/python/modules/gc.java
@@ -18,6 +18,7 @@
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyInstance;
+import org.python.core.PyString;
import org.python.core.Traverseproc;
import org.python.core.TraverseprocDerived;
import org.python.core.Visitproc;
@@ -25,9 +26,11 @@
import org.python.core.finalization.FinalizeTrigger;
import org.python.modules._weakref.GlobalRef;
+import com.sun.management.GarbageCollectionNotificationInfo;
+
public class gc {
/**
- * A constant that can occur as result of {@code gc.collect} and
+ * A constant that can occur as result of {@link #collect()} and
* indicates an unknown number of collected cyclic trash.
* It is intentionally not valued -1 as that value is
* reserved to indicate an error.
@@ -36,9 +39,14 @@
/* Jython-specific gc-flags: */
/**
- * Tells every newly created PyObject to register for
- * gc-monitoring. This allows {@code gc.collect} to report the
+ * This flag tells every newly created PyObject to register for
+ * gc-monitoring. This allows {@link #collect()} to report the
* number of collected objects.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short MONITOR_GLOBAL = (1<<0);
@@ -47,6 +55,11 @@
* PyObjects, while Jython does this by default. This flag
* tells Jython's gc to mimic CPython <3.4 behavior (i.e.
* add such objects to {@code gc.garbage} list instead).
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short DONT_FINALIZE_CYCLIC_GARBAGE = (1<<1);
@@ -65,6 +78,11 @@
* delay garbage collection of some weak referenced objects
* for several gc cycles if activated. So we recommend to
* use it only for debugging.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short PRESERVE_WEAKREFS_ON_RESURRECTION = (1<<2);
@@ -80,6 +98,11 @@
* significant cost as it can delay collection of many objects
* for several gc-cycles. Its main intention is for debugging
* resurrection-sensitive code.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short DONT_FINALIZE_RESURRECTED_OBJECTS = (1<<3);
@@ -88,6 +111,11 @@
* is deactivated by default for now. This means that
* {@code DONT_TRAVERSE_BY_REFLECTION} is set by default.
* Once it is stable, reflection-based traversion will be active by default.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short DONT_TRAVERSE_BY_REFLECTION = (1<<4);
@@ -109,6 +137,11 @@
* This is because in an ideal implementation reflection-based traversion never
* occurs; it is only an inefficient fallback.
*
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING = (1<<5);
@@ -119,14 +152,65 @@
* gc-messages, no matter what overall verbose level is selected.
* This flag tells Jython to use {@code Py.writeDebug} for debugging output.
* If it is not set (default-case), gc-debugging output (if gc-{@code VERBOSE}
- * or -{@code DEBUG} flags are set) is directly written to {@code System.err}.
+ * or -{@code DEBUG} flags are set) is directly written to {@code System.err}.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static final short USE_PY_WRITE_DEBUG = (1<<6);
+ /**
+ * Enables collection-related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_COLLECT = (1<<7);
+
+ /**
+ * Enables weakref-related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_WEAKREF = (1<<8);
+
+ /**
+ * Enables delayed finalization related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_DELAYED = (1<<9);
+
+ /**
+ * Enables finalization-related verbose-output.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE_FINALIZE = (1<<10);
+
+ /**
+ * Bit-combination of the flags {@link #VERBOSE_COLLECT},
+ * {@link #VERBOSE_WEAKREF}, {@link #VERBOSE_DELAYED},
+ * {@link #VERBOSE_FINALIZE}.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static final short VERBOSE =
VERBOSE_COLLECT | VERBOSE_WEAKREF | VERBOSE_DELAYED | VERBOSE_FINALIZE;
@@ -134,38 +218,65 @@
/**
* print collection statistics
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_STATS = (1<<0);
/**
* print collectable objects
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_COLLECTABLE = (1<<1);
/**
* print uncollectable objects
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_UNCOLLECTABLE = (1<<2);
/**
* print instances
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_INSTANCES = (1<<3);
/**
* print other objects
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_OBJECTS = (1<<4);
/**
* save all garbage in gc.garbage
* (in Jython scoped on monitored objects)
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
*/
public static final int DEBUG_SAVEALL = (1<<5);
+
+ /**
+ * Bit-combination of the flags {@link #DEBUG_COLLECTABLE},
+ * {@link #DEBUG_UNCOLLECTABLE}, {@link #DEBUG_INSTANCES},
+ * {@link #DEBUG_OBJECTS}, {@link #DEBUG_SAVEALL}.
+ *
+ * @see #set_debug(int)
+ * @see #get_debug()
+ */
public static final int DEBUG_LEAK = DEBUG_COLLECTABLE |
DEBUG_UNCOLLECTABLE |
DEBUG_INSTANCES |
@@ -184,6 +295,10 @@
private static long lastRemoveTimeStamp = -1, maxWaitTime = initWaitTime;
private static int gcMonitoredRunCount = 0;
public static long gcRecallTime = 4000;
+
+ /**
+ * list of uncollectable objects
+ */
public static PyList garbage = new PyList();
//Finalization preprocess/postprocess-related declarations:
@@ -207,17 +322,113 @@
private static boolean notifyRerun = false;
public static final String __doc__ =
- "This module provides access to the garbage collector.\n" +
+ "This module provides access to the garbage collector for reference cycles.\n" +
"\n" +
- "enable() -- Enable automatic garbage collection (does nothing).\n" +
+ "enable() -- Enable automatic garbage collection (does nothing in Jython).\n" +
+ "disable() -- Disable automatic garbage collection (raises NotImplementedError in Jython).\n" +
"isenabled() -- Returns True because Java garbage collection cannot be disabled.\n" +
- "collect() -- Trigger a Java garbage collection (potentially expensive).\n" +
- "get_debug() -- Get debugging flags (returns 0).\n" +
- "\n" +
- "Other functions raise NotImplementedError because they do not apply to Java.\n";
+ "collect() -- Do a full collection right now (potentially expensive).\n" +
+ "get_count() -- Return the current collection counts (raises NotImplementedError in Jython).\n" +
+ "set_debug() -- Set debugging flags.\n" +
+ "get_debug() -- Get debugging flags.\n" +
+ "set_threshold() -- Set the collection thresholds (raise NotImplementedError in Jython).\n" +
+ "get_threshold() -- Return the current the collection thresholds (raise NotImplementedError in Jython).\n" +
+ "get_objects() -- Return a list of all objects tracked by the collector (raises NotImplementedError in Jython).\n" +
+ "is_tracked() -- Returns true if a given object is tracked (i.e. monitored in Jython).\n" +
+ "get_referrers() -- Return the list of objects that refer to an object (only finds monitored referrers in Jython).\n" +
+ "get_referents() -- Return the list of objects that an object refers to.\n";
public static final String __name__ = "gc";
+ public static final PyString __doc__enable = new PyString(
+ "enable() -> None\n" +
+ "\n" +
+ "Enable automatic garbage collection.\n" +
+ "(does nothing in Jython)\n");
+
+ public static final PyString __doc__disable = new PyString(
+ "disable() -> None\n" +
+ "\n" +
+ "Disable automatic garbage collection.\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__isenabled = new PyString(
+ "isenabled() -> status\n" +
+ "\n" +
+ "Returns true if automatic garbage collection is enabled.\n");
+
+ public static final PyString __doc__collect = new PyString(
+ "collect([generation]) -> n\n" +
+ "\n" +
+ "With no arguments, run a full collection. The optional argument\n" +
+ "may be an integer specifying which generation to collect. A ValueError\n" +
+ "is raised if the generation number is invalid.\n\n" +
+ "The number of unreachable objects is returned.\n" +
+ "(Jython emulates CPython cyclic trash counting if objects are monitored.\n" +
+ "If no objects are monitored, returns -2\n");
+
+ public static final PyString __doc__get_count = new PyString(
+ "get_count() -> (count0, count1, count2)\n" +
+ "\n" +
+ "Return the current collection counts\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__set_debug = new PyString(
+ "set_debug(flags) -> None\n" +
+ "\n" +
+ "Set the garbage collection debugging flags. Debugging information is\n" +
+ "written to sys.stderr.\n" +
+ "\n" +
+ "flags is an integer and can have the following bits turned on:\n" +
+ "\n" +
+ " DEBUG_STATS - Print statistics during collection.\n" +
+ " DEBUG_COLLECTABLE - Print collectable objects found.\n" +
+ " DEBUG_UNCOLLECTABLE - Print unreachable but uncollectable objects found.\n" +
+ " DEBUG_INSTANCES - Print instance objects.\n" +
+ " DEBUG_OBJECTS - Print objects other than instances.\n" +
+ " DEBUG_SAVEALL - Save objects to gc.garbage rather than freeing them.\n" +
+ " DEBUG_LEAK - Debug leaking programs (everything but STATS).\n");
+
+ public static final PyString __doc__get_debug = new PyString(
+ "get_debug() -> flags\n" +
+ "\n" +
+ "Get the garbage collection debugging flags.\n");
+
+ public static final PyString __doc__set_thresh = new PyString(
+ "set_threshold(threshold0, [threshold1, threshold2]) -> None\n" +
+ "\n" +
+ "Sets the collection thresholds. Setting threshold0 to zero disables\n" +
+ "collection.\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__get_thresh = new PyString(
+ "get_threshold() -> (threshold0, threshold1, threshold2)\n" +
+ "\n" +
+ "Return the current collection thresholds\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__get_objects = new PyString(
+ "get_objects() -> [...]\n" +
+ "\n" +
+ "Return a list of objects tracked by the collector (excluding the list\n" +
+ "returned).\n" +
+ "(raises NotImplementedError in Jython)\n");
+
+ public static final PyString __doc__is_tracked = new PyString(
+ "is_tracked(obj) -> bool\n" +
+ "\n" +
+ "Returns true if the object is tracked by the garbage collector.\n" +
+ "(i.e. monitored in Jython)\n");
+
+ public static final PyString __doc__get_referrers = new PyString(
+ "get_referrers(*objs) -> list\n" +
+ "Return the list of objects that directly refer to any of objs.\n" +
+ "(only finds monitored referrers in Jython)");
+
+ public static final PyString __doc__get_referents = new PyString(
+ "get_referents(*objs) -> list\n" +
+ "Return the list of objects that are directly referred to by objs.");
+
public static class CycleMarkAttr {
private boolean cyclic = false;
@@ -382,7 +593,17 @@
}
}
- private static void writeDebug(String type, String msg) {
+ /**
+ * Works like {@link org.python.core.Py#writeDebug(String, String)},
+ * but prints to {@link org.python.core.Py#writeDebug(String, String)}
+ * (i.e. subject to Jython's verbose level)
+ * or directly to {@code System.err}, according to
+ * {@link #USE_PY_WRITE_DEBUG}.
+ *
+ * @see #USE_PY_WRITE_DEBUG
+ * @see org.python.core.Py#writeDebug(String, String)
+ */
+ public static void writeDebug(String type, String msg) {
if ((gcFlags & USE_PY_WRITE_DEBUG) != 0) {
Py.writeDebug(type, msg);
} else {
@@ -1102,6 +1323,25 @@
//----------end of Monitoring section--------------------------------------
+ /**
+ * Gets the current Jython-specific gc-flags.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #setJythonGCFlags(short)
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static short getJythonGCFlags() {
if (((gcFlags & MONITOR_GLOBAL) != 0) != PyObject.gcMonitorGlobal) {
if (PyObject.gcMonitorGlobal) {
@@ -1113,6 +1353,44 @@
return gcFlags;
}
+ /**
+ * Sets the current Jython-specific gc-flags.
+ *
+ * {@code flags} is a {@code short} and can have the following bits turned on:
+ *
+ * {@link #MONITOR_GLOBAL} - Automatically monitors all PyObjects created from now on.
+ * {@link #DONT_FINALIZE_CYCLIC_GARBAGE} - Adds cyclic finalizable PyObjects to {@link #garbage}.
+ * {@link #PRESERVE_WEAKREFS_ON_RESURRECTION} - Keeps weak references alive if the referent is resurrected.
+ * {@link #DONT_FINALIZE_RESURRECTED_OBJECTS} -
+ * Emulates CPython behavior regarding resurrected objects and finalization.
+ * {@link #DONT_TRAVERSE_BY_REFLECTION} - Inhibits reflection-based traversion.
+ * {@link #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING} -
+ * Suppress warnings for PyObjects that neither implement {@link org.python.core.Traverseproc} nor
+ * are marked as {@link org.python.core.Untraversable}.
+ * {@link #USE_PY_WRITE_DEBUG} - uses {@link org.python.core.Py#writeDebug(String, String)} for
+ * debugging output instead of directly writing to {@link java.lang.System#err}.
+ * {@link #VERBOSE_COLLECT} - Enable collection-related verbose output.
+ * {@link #VERBOSE_WEAKREF} - Enable weakref-related verbose output.
+ * {@link #VERBOSE_DELAYED} - Enable delayed finalization-related verbose output.
+ * {@link #VERBOSE_FINALIZE} - Enable finalization-related verbose output.
+ * {@link #VERBOSE} - All previous verbose-flags combined.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ */
public static void setJythonGCFlags(short flags) {
gcFlags = flags;
PyObject.gcMonitorGlobal = (gcFlags & MONITOR_GLOBAL) != 0;
@@ -1121,6 +1399,22 @@
/**
* This is a convenience method to add flags via bitwise or.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #getJythonGCFlags()
+ * @see #setJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
*/
public static void addJythonGCFlags(short flags) {
gcFlags |= flags;
@@ -1130,6 +1424,22 @@
/**
* This is a convenience method to remove flags via bitwise and-not.
+ *
+ * @see #MONITOR_GLOBAL
+ * @see #DONT_FINALIZE_CYCLIC_GARBAGE
+ * @see #PRESERVE_WEAKREFS_ON_RESURRECTION
+ * @see #DONT_FINALIZE_RESURRECTED_OBJECTS
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #USE_PY_WRITE_DEBUG
+ * @see #VERBOSE_COLLECT
+ * @see #VERBOSE_WEAKREF
+ * @see #VERBOSE_DELAYED
+ * @see #VERBOSE_FINALIZE
+ * @see #VERBOSE
+ * @see #getJythonGCFlags()
+ * @see #setJythonGCFlags(short)
+ * @see #addJythonGCFlags(short)
*/
public static void removeJythonGCFlags(short flags) {
gcFlags &= ~flags;
@@ -1161,21 +1471,34 @@
notifyFinalize(abort);
}
+ /**
+ * Does nothing in Jython as Java-gc is always enabled.
+ */
public static void enable() {}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static void disable() {
throw Py.NotImplementedError("can't disable Java GC");
}
+ /**
+ * Always returns {@code true} in Jython.
+ */
public static boolean isenabled() { return true; }
/**
* The generation parameter is only for compatibility with
- * CPython {@code gc.collect} and is ignored.
+ * CPython {@link gc.collect()} and is ignored.
* @param generation (ignored)
* @return Collected monitored cyclic trash-objects or
* {@code gc.UNKNOWN_COUNT} if nothing is monitored or -1 if
* an error occurred and collection did not complete.
+ * @see #collect()
*/
public static int collect(int generation) {
return collect();
@@ -1194,14 +1517,16 @@
/**
* If no objects are monitored, this just delegates to
- * {@code System.gc} and returns {@code gc.UNKNOWN_COUNT} as a
+ * {@code System.gc()} and returns {@link #UNKNOWN_COUNT} as a
* non-erroneous default value. If objects are monitored,
- * it emulates a synchronous gc run in the sense that it waits
+ * it emulates a synchronous gc-run in the sense that it waits
* until all collected monitored objects were finalized.
*
* @return Number of collected monitored cyclic trash-objects
- * or {@code gc.UNKNOWN_COUNT} if nothing is monitored or -1
+ * or {@link #UNKNOWN_COUNT} if nothing is monitored or -1
* if an error occurred and collection did not complete.
+ * @see #UNKNOWN_COUNT
+ * @see #collect(int)
*/
public static int collect() {
try {
@@ -1619,26 +1944,79 @@
stat[1] -= abortedCyclicFinalizers;
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static PyObject get_count() {
throw Py.NotImplementedError("not applicable to Java GC");
}
+ /**
+ * Copied from CPython-doc:
+ *
+ * Set the garbage collection debugging flags. Debugging information is
+ * written to {@code System.err}.
+ *
+ * {@flags} flags is an {@code int}eger and can have the following bits turned on:
+ *
+ * {@link #DEBUG_STATS} - Print statistics during collection.
+ * {@link #DEBUG_COLLECTABLE} - Print collectable objects found.
+ * {@link #DEBUG_UNCOLLECTABLE} - Print unreachable but uncollectable objects found.
+ * {@link #DEBUG_INSTANCES} - Print instance objects.
+ * {@link #DEBUG_OBJECTS} - Print objects other than instances.
+ * {@link #DEBUG_SAVEALL} - Save objects to gc.garbage rather than freeing them.
+ * {@link #DEBUG_LEAK} - Debug leaking programs (everything but STATS).
+ *
+ * @see #DEBUG_STATS
+ * @see #DEBUG_COLLECTABLE
+ * @see #DEBUG_UNCOLLECTABLE
+ * @see #DEBUG_INSTANCES
+ * @see #DEBUG_OBJECTS
+ * @see #DEBUG_SAVEALL
+ * @see #DEBUG_LEAK
+ */
public static void set_debug(int flags) {
debugFlags = flags;
}
+ /**
+ * Copied from CPython-doc:
+ *
+ * Get the garbage collection debugging flags.
+ */
public static int get_debug() {
return debugFlags;
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static void set_threshold(PyObject[] args, String[] kwargs) {
throw Py.NotImplementedError("not applicable to Java GC");
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static PyObject get_threshold() {
throw Py.NotImplementedError("not applicable to Java GC");
}
+ /**
+ * Not supported by Jython.
+ * Throws {@link org.python.core.Py#NotImplementedError}.
+ *
+ * @throws org.python.core.Py#NotImplementedError
+ */
public static PyObject get_objects() {
throw Py.NotImplementedError("not applicable to Java GC");
}
@@ -2007,6 +2385,23 @@
return search;
}
+ /**
+ * Does its best to traverse the given {@link org.python.core.PyObject}
+ * {@code ob}. It exploits both
+ * {@link org.python.core.Traverseproc#traverse(Visitproc, Object)} and
+ * {@link org.python.core.TraverseprocDerived#traverseDerived(Visitproc, Object)}.
+ * If {@code ob} neither implements {@link org.python.core.Traverseproc} nor
+ * {@link org.python.core.Traverseproc} and is not annotated with
+ * {@link org.python.core.Untraversable}, reflection-based traversion via
+ * {@link #traverseByReflection(Object, Visitproc, Object)} may be attempted
+ * according to {@link #DONT_TRAVERSE_BY_REFLECTION}.
+ *
+ * @see org.python.core.Traverseproc#traverse(Visitproc, Object)
+ * @see org.python.core.TraverseprocDerived#traverseDerived(Visitproc, Object)
+ * @see #DONT_TRAVERSE_BY_REFLECTION
+ * @see org.python.core.Untraversable
+ * @see #traverseByReflection(Object, Visitproc, Object)
+ */
public static int traverse(PyObject ob, Visitproc visit, Object arg) {
int retVal;
boolean traversed = false;
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Fri Feb 20 00:43:19 2015
From: jython-checkins at python.org (jim.baker)
Date: Thu, 19 Feb 2015 23:43:19 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Use_classpath_wildcard?=
Message-ID: <20150219234319.99237.30434@psf.io>
https://hg.python.org/jython/rev/2c38c3c3b49a
changeset: 7590:2c38c3c3b49a
user: Jim Baker
date: Thu Feb 19 16:41:54 2015 -0700
summary:
Use classpath wildcard
files:
src/shell/jython.py | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/src/shell/jython.py b/src/shell/jython.py
--- a/src/shell/jython.py
+++ b/src/shell/jython.py
@@ -162,8 +162,7 @@
return self._classpath
if os.path.exists(os.path.join(self.jython_home, "jython-dev.jar")):
jars = [os.path.join(self.jython_home, "jython-dev.jar")]
- for jar in glob.iglob(os.path.join(self.jython_home, "javalib", "*.jar")):
- jars.append(jar)
+ jars.append(os.path.join(self.jython_home, "javalib", "*"))
elif not os.path.exists(os.path.join(self.jython_home, "jython.jar")):
self.parser.error(
"""{executable}:
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sat Feb 21 16:57:08 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Sat, 21 Feb 2015 15:57:08 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fixed_reflection-based_gc-t?=
=?utf-8?q?raversion_and_added_corresponding_tests=2E?=
Message-ID: <20150221155708.14033.68787@psf.io>
https://hg.python.org/jython/rev/e687ace9c391
changeset: 7591:e687ace9c391
user: Stefan Richthofer
date: Sat Feb 21 16:56:59 2015 +0100
summary:
Fixed reflection-based gc-traversion and added corresponding tests. Reflection-based traversion is now activated by default, i.e. as a fallback for ordinary traversion.
files:
Lib/test/test_gc_jy.py | 53 ++++-
src/org/python/modules/gc.java | 150 +++++++++---
tests/java/javatests/GCTestHelper.java | 93 ++++++++
3 files changed, 258 insertions(+), 38 deletions(-)
diff --git a/Lib/test/test_gc_jy.py b/Lib/test/test_gc_jy.py
--- a/Lib/test/test_gc_jy.py
+++ b/Lib/test/test_gc_jy.py
@@ -13,8 +13,9 @@
import weakref
try:
from java.lang import System, Runnable
+ from javatests import GCTestHelper
except ImportError:
- #i.e. Jython is running
+ #i.e. not Jython is running
pass
class GCTests_Jy_CyclicGarbage(unittest.TestCase):
@@ -592,5 +593,55 @@
self.assertEqual(wc(), None)
+ at unittest.skipUnless(test_support.is_jython,
+ '''
+ The test involves Java-classes and is thus not supported by
+ non-Jython interpreters.
+ ''')
+class GCTests_Jy_TraverseByReflection(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ #Jython-specific block:
+ try:
+ cls.savedJythonGCFlags = gc.getJythonGCFlags()
+ gc.addJythonGCFlags(gc.SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING)
+ gc.setMonitorGlobal(True)
+ except Exception:
+ pass
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ gc.setJythonGCFlags(cls.savedJythonGCFlags)
+ gc.stopMonitoring()
+ except Exception:
+ pass
+
+
+ def test_weakref_after_resurrection_threadsafe(self):
+ gc.collect()
+
+ prt = GCTestHelper.reflectionTraverseTestField()
+ del prt
+ self.assertEqual(gc.collect(), 1)
+
+ prt = GCTestHelper.reflectionTraverseTestList()
+ del prt
+ self.assertEqual(gc.collect(), 1)
+
+ prt = GCTestHelper.reflectionTraverseTestArray()
+ del prt
+ self.assertEqual(gc.collect(), 1)
+
+ prt = GCTestHelper.reflectionTraverseTestPyList()
+ del prt
+ self.assertEqual(gc.collect(), 2)
+
+ prt = GCTestHelper.reflectionTraverseTestCycle()
+ del prt
+ self.assertEqual(gc.collect(), 0)
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/src/org/python/modules/gc.java b/src/org/python/modules/gc.java
--- a/src/org/python/modules/gc.java
+++ b/src/org/python/modules/gc.java
@@ -107,15 +107,40 @@
public static final short DONT_FINALIZE_RESURRECTED_OBJECTS = (1<<3);
/**
- * Reflection-based traversion is currently an experimental feature and
- * is deactivated by default for now. This means that
- * {@code DONT_TRAVERSE_BY_REFLECTION} is set by default.
- * Once it is stable, reflection-based traversion will be active by default.
+ *
+ * Reflection-based traversion is an inefficient fallback-method to
+ * traverse PyObject-subtypes that don't implement
+ * {@link org.python.core.Traverseproc} and
+ * are not marked as {@link org.python.core.Untraversable}.
+ * Such a situation indicates that the programmer was not aware of
+ * Jython's traverseproc-mechanism and reflection is used to
+ * compensate this.
+ *
+ *
+ * This flag allows to inhibit reflection-based traversion. If it is
+ * activated, objects that don't implement
+ * {@link org.python.core.Traverseproc}
+ * are always treated as if they were marked as
+ * {@link org.python.core.Untraversable}.
+ *
+ *
+ * Note that reflection-based traversion fallback is performed by
+ * default. Further note that Jython emits warning-messages if
+ * reflection-based traversion occurs or if an object is encountered
+ * that neither implements {@link org.python.core.Traverseproc}
+ * nor is marked as {@link org.python.core.Untraversable} (even if
+ * reflection-based traversion is inhibited). See
+ * {@link #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING} and
+ * {@link #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING} to control
+ * these warning-messages.
+ *
*
* @see #setJythonGCFlags(short)
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
*/
public static final short DONT_TRAVERSE_BY_REFLECTION = (1<<4);
@@ -142,10 +167,26 @@
* @see #getJythonGCFlags()
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
+ * @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
*/
public static final short SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING = (1<<5);
/**
+ * Makes gc emit reflection-based traversion warning for every traversed
+ * object instead of only once per class.
+ * A potential reflection-based traversion occurs whenever an object is
+ * traversed that neither implements {@link org.python.core.Traverseproc},
+ * nor is annotated with the {@link org.python.core.Untraversable}-annotation.
+ *
+ * @see #setJythonGCFlags(short)
+ * @see #getJythonGCFlags()
+ * @see #addJythonGCFlags(short)
+ * @see #removeJythonGCFlags(short)
+ * @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ */
+ public static final short INSTANCE_TRAVERSE_BY_REFLECTION_WARNING = (1<<6);
+
+ /**
* In Jython one usually uses {@code Py.writeDebug} for debugging output.
* However that method is only verbose if an appropriate verbose-level
* was set. In CPython it is enough to set gc-{@code DEBUG} flags to get
@@ -159,7 +200,7 @@
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
- public static final short USE_PY_WRITE_DEBUG = (1<<6);
+ public static final short USE_PY_WRITE_DEBUG = (1<<7);
/**
* Enables collection-related verbose-output.
@@ -169,7 +210,7 @@
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
- public static final short VERBOSE_COLLECT = (1<<7);
+ public static final short VERBOSE_COLLECT = (1<<8);
/**
* Enables weakref-related verbose-output.
@@ -179,7 +220,7 @@
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
- public static final short VERBOSE_WEAKREF = (1<<8);
+ public static final short VERBOSE_WEAKREF = (1<<9);
/**
* Enables delayed finalization related verbose-output.
@@ -189,7 +230,7 @@
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
- public static final short VERBOSE_DELAYED = (1<<9);
+ public static final short VERBOSE_DELAYED = (1<<10);
/**
* Enables finalization-related verbose-output.
@@ -199,7 +240,7 @@
* @see #addJythonGCFlags(short)
* @see #removeJythonGCFlags(short)
*/
- public static final short VERBOSE_FINALIZE = (1<<10);
+ public static final short VERBOSE_FINALIZE = (1<<11);
/**
* Bit-combination of the flags {@link #VERBOSE_COLLECT},
@@ -283,19 +324,20 @@
DEBUG_OBJECTS |
DEBUG_SAVEALL;
- private static short gcFlags = DONT_TRAVERSE_BY_REFLECTION;
+ private static short gcFlags = 0;
private static int debugFlags = 0;
private static boolean monitorNonTraversable = false;
private static boolean waitingForFinalizers = false;
private static AtomicBoolean gcRunning = new AtomicBoolean(false);
private static HashSet monitoredObjects;
+ private static HashSet> reflectionWarnedClasses;
private static ReferenceQueue gcTrash;
private static int finalizeWaitCount = 0;
private static int initWaitTime = 10, defaultWaitFactor = 2;
private static long lastRemoveTimeStamp = -1, maxWaitTime = initWaitTime;
private static int gcMonitoredRunCount = 0;
public static long gcRecallTime = 4000;
-
+
/**
* list of uncollectable objects
*/
@@ -611,7 +653,7 @@
}
}
- //----------delayed finalization section-----------------------------------
+//--------------delayed finalization section-----------------------------------
private static class DelayedFinalizationProcess implements Runnable {
static DelayedFinalizationProcess defaultInstance =
@@ -849,12 +891,12 @@
delayedFinalizables.put(ob, ob);
}
}
- //----------end of delayed finalization section----------------------------
+//--------------end of delayed finalization section----------------------------
- //----------Finalization preprocess/postprocess section--------------------
+//--------------Finalization preprocess/postprocess section--------------------
protected static class PostFinalizationProcessor implements Runnable {
protected static PostFinalizationProcessor defaultInstance =
@@ -1166,12 +1208,12 @@
postFinalizationProcessRemove = null;
}
}
- //----------end of Finalization preprocess/postprocess section-------------
+//--------------end of Finalization preprocess/postprocess section-------------
- //----------Monitoring section---------------------------------------------
+//--------------Monitoring section---------------------------------------------
public static void monitorObject(PyObject ob) {
monitorObject(ob, false);
@@ -1180,11 +1222,17 @@
public static void monitorObject(PyObject ob, boolean initString) {
//Already collected garbage should not be monitored,
//thus also not the garbage list:
- if (ob == garbage) {
+ if (ob == null || ob == garbage) {
return;
}
- if (!monitorNonTraversable && !isTraversable(ob)) {
- if (!JyAttribute.hasAttr(ob, JyAttribute.FINALIZE_TRIGGER_ATTR)) {
+
+ //In contrast to isTraversable(ob) we don't look for DONT_TRAVERSE_BY_REFLECTION
+ //here. Objects not explicitly marked as Untraversable get monitored so we are
+ //able to print out warnings in Traverseproc.
+ if (!monitorNonTraversable &&
+ !(ob instanceof Traverseproc || ob instanceof TraverseprocDerived)) {
+ if (ob.getClass().isAnnotationPresent(Untraversable.class) &&
+ !JyAttribute.hasAttr(ob, JyAttribute.FINALIZE_TRIGGER_ATTR)) {
return;
}
}
@@ -1320,7 +1368,7 @@
}
PyObject.gcMonitorGlobal = flag;
}
- //----------end of Monitoring section--------------------------------------
+//--------------end of Monitoring section--------------------------------------
/**
@@ -1332,6 +1380,7 @@
* @see #DONT_FINALIZE_RESURRECTED_OBJECTS
* @see #DONT_TRAVERSE_BY_REFLECTION
* @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
* @see #USE_PY_WRITE_DEBUG
* @see #VERBOSE_COLLECT
* @see #VERBOSE_WEAKREF
@@ -1381,6 +1430,7 @@
* @see #DONT_FINALIZE_RESURRECTED_OBJECTS
* @see #DONT_TRAVERSE_BY_REFLECTION
* @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
* @see #USE_PY_WRITE_DEBUG
* @see #VERBOSE_COLLECT
* @see #VERBOSE_WEAKREF
@@ -1406,6 +1456,7 @@
* @see #DONT_FINALIZE_RESURRECTED_OBJECTS
* @see #DONT_TRAVERSE_BY_REFLECTION
* @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
* @see #USE_PY_WRITE_DEBUG
* @see #VERBOSE_COLLECT
* @see #VERBOSE_WEAKREF
@@ -1431,6 +1482,7 @@
* @see #DONT_FINALIZE_RESURRECTED_OBJECTS
* @see #DONT_TRAVERSE_BY_REFLECTION
* @see #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING
+ * @see #INSTANCE_TRAVERSE_BY_REFLECTION_WARNING
* @see #USE_PY_WRITE_DEBUG
* @see #VERBOSE_COLLECT
* @see #VERBOSE_WEAKREF
@@ -2415,14 +2467,26 @@
traversed = true;
if (retVal != 0) return retVal;
}
+ boolean justAddedWarning = false;
if ((gcFlags & SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING) == 0) {
if (! (ob instanceof Traverseproc || ob instanceof TraverseprocDerived ||
ob.getClass() == PyObject.class ||
ob.getClass().isAnnotationPresent(Untraversable.class)) ) {
- Py.writeWarning("gc", "The PyObject-subclass "+ob.getClass().getName()+"\n" +
- "should either implement Traverseproc or be marked with the\n" +
- "@Untraversable annotation. See the instructions\n" +
- "in javadoc of org.python.core.Traverseproc.java.");
+ if (((gcFlags & INSTANCE_TRAVERSE_BY_REFLECTION_WARNING) != 0) ||
+ reflectionWarnedClasses == null ||
+ !reflectionWarnedClasses.contains(ob.getClass())) {
+ if ((gcFlags & INSTANCE_TRAVERSE_BY_REFLECTION_WARNING) == 0) {
+ if (reflectionWarnedClasses == null) {
+ reflectionWarnedClasses = new HashSet<>();
+ }
+ reflectionWarnedClasses.add(ob.getClass());
+ justAddedWarning = true;
+ }
+ Py.writeWarning("gc", "The PyObject-subclass "+ob.getClass().getName()+"\n" +
+ "should either implement Traverseproc or be marked with the\n" +
+ "@Untraversable annotation. See instructions in\n" +
+ "javadoc of org.python.core.Traverseproc.java.");
+ }
}
}
if ((gcFlags & DONT_TRAVERSE_BY_REFLECTION) != 0) {
@@ -2434,9 +2498,21 @@
return 0;
}
if ((gcFlags & SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING) == 0) {
- Py.writeWarning("gc", "Traverse by reflection: "+ob.getClass().getName()+"\n" +
- "This is an inefficient procedure. It is recommended to\n" +
- "implement the traverseproc mechanism properly.");
+ if (((gcFlags & INSTANCE_TRAVERSE_BY_REFLECTION_WARNING) != 0) ||
+ justAddedWarning ||
+ reflectionWarnedClasses == null ||
+ !reflectionWarnedClasses.contains(ob.getClass())) {
+ if ((gcFlags & INSTANCE_TRAVERSE_BY_REFLECTION_WARNING) == 0 &&
+ !justAddedWarning) {
+ if (reflectionWarnedClasses == null) {
+ reflectionWarnedClasses = new HashSet<>();
+ }
+ reflectionWarnedClasses.add(ob.getClass());
+ }
+ Py.writeWarning("gc", "Traverse by reflection: "+ob.getClass().getName()+"\n" +
+ "This is an inefficient procedure. It is recommended to\n" +
+ "implement the traverseproc mechanism properly.");
+ }
}
return traverseByReflection(ob, visit, arg);
}
@@ -2478,16 +2554,16 @@
private static int traverseByReflectionIntern(Object ob,
IdentityHashMap alreadyTraversed, Visitproc visit, Object arg) {
Class extends Object> cls = ob.getClass();
- int result;
+ int result = 0;
Object element;
if (cls.isArray() && canLinkToPyObject(cls.getComponentType(), false)) {
for (int i = 0; i < Array.getLength(ob); ++i) {
element = Array.get(ob, i);
- if (element != null && !alreadyTraversed.containsKey(element)) {
- alreadyTraversed.put(element, element);
+ if (element != null) {
if (element instanceof PyObject) {
result = visit.visit((PyObject) element, arg);
- } else {
+ } else if (!alreadyTraversed.containsKey(element)) {
+ alreadyTraversed.put(element, element);
result = traverseByReflectionIntern(element,
alreadyTraversed, visit, arg);
}
@@ -2508,11 +2584,11 @@
if (canLinkToPyObject(declFields[i].getType(), false)) {
try {
element = declFields[i].get(ob);
- if (!alreadyTraversed.containsKey(element)) {
- alreadyTraversed.put(element, element);
+ if (element != null) {
if (element instanceof PyObject) {
result = visit.visit((PyObject) element, arg);
- } else {
+ } else if (!alreadyTraversed.containsKey(element)) {
+ alreadyTraversed.put(element, element);
result = traverseByReflectionIntern(element,
alreadyTraversed, visit, arg);
}
@@ -2551,7 +2627,7 @@
*
*/
public static boolean canLinkToPyObject(Class> cls, boolean actual) {
- // At first some quick fail fast/succeed fast-checks:
+ // At first some quick (fail-fast/succeed-fast)-checks:
if (quickCheckCannotLinkToPyObject(cls)) {
return false;
}
@@ -2674,7 +2750,7 @@
cls.isAnnotationPresent(Untraversable.class));
}
- //----------Visitproc section----------------------------------------------
+//--------------Visitproc section----------------------------------------------
static class ReferentsFinder implements Visitproc {
public static ReferentsFinder defaultInstance = new ReferentsFinder();
@@ -2823,5 +2899,5 @@
return 0;
}
}
- //----------end of Visitproc section---------------------------------------
+//--------------end of Visitproc section---------------------------------------
}
diff --git a/tests/java/javatests/GCTestHelper.java b/tests/java/javatests/GCTestHelper.java
new file mode 100644
--- /dev/null
+++ b/tests/java/javatests/GCTestHelper.java
@@ -0,0 +1,93 @@
+package javatests;
+
+import org.python.core.*;
+import java.util.*;
+
+public class GCTestHelper {
+
+ public static class NastyFinalizer {
+
+ public void finalize() {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ /**
+ * PyReflectionTraversed is a test-PyObject that intentionally
+ * violates the Traverseproc-mechanism, i.e. holds a non-static
+ * reference to another PyObject, but neither implements Traverseproc
+ * nor is marked as untraversable.
+ */
+ public static class PyReflectionTraversed extends PyObject {
+ PyObject referent1;
+ PyObject[] refArray = new PyObject[3];
+ List refList = new ArrayList<>();
+ PyObject referent2;
+
+ public PyReflectionTraversed() {
+ super();
+ }
+ }
+
+ public static PyObject reflectionTraverseTestField() {
+ PyReflectionTraversed result = new PyReflectionTraversed();
+ result.referent1 = new PyList();
+ result.refArray[0] = new PyInteger(244);
+ result.refArray[1] = new PyString("test1");
+ result.refArray[2] = new PyInteger(333);
+ result.refList.add(new PyFloat(0.7));
+ result.referent2 = result;
+ return result;
+ }
+
+ public static PyObject reflectionTraverseTestList() {
+ PyReflectionTraversed result = new PyReflectionTraversed();
+ result.referent1 = new PyList();
+ result.refArray[0] = new PyInteger(245);
+ result.refArray[1] = new PyString("test2");
+ result.refArray[2] = new PyInteger(334);
+ result.refList.add(new PyFloat(0.71));
+ result.refList.add(result);
+ return result;
+ }
+
+ public static PyObject reflectionTraverseTestArray() {
+ PyReflectionTraversed result = new PyReflectionTraversed();
+ result.referent1 = new PyList();
+ result.refArray[0] = new PyInteger(246);
+ result.refArray[1] = new PyString("test3");
+ result.refArray[2] = result;
+ result.refList.add(new PyFloat(0.72));
+ return result;
+ }
+
+ public static PyObject reflectionTraverseTestPyList() {
+ PyReflectionTraversed result = new PyReflectionTraversed();
+ result.referent1 = new PyList();
+ result.refArray[0] = new PyInteger(247);
+ result.refArray[1] = new PyString("test4");
+ result.refArray[2] = new PyInteger(335);
+ result.refList.add(new PyFloat(0.73));
+ result.refList.add(new PyFloat(-0.73));
+ PyList pl = new PyList();
+ result.referent2 = pl;
+ pl.add(Py.True);
+ pl.add(new PyString("test5"));
+ pl.add(result);
+ return result;
+ }
+
+ public static PyObject reflectionTraverseTestCycle() {
+ PyReflectionTraversed result = new PyReflectionTraversed();
+ result.referent1 = new PyList();
+ result.refArray[0] = new PyInteger(248);
+ result.refArray[1] = new PyString("test6");
+ result.refArray[2] = new PyInteger(336);
+ result.refList.add(new PyFloat(0.74));
+ result.refList.add(result.refList);
+ return result;
+ }
+}
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sat Feb 21 22:33:15 2015
From: jython-checkins at python.org (jim.baker)
Date: Sat, 21 Feb 2015 21:33:15 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Fix_Popen=2E=5Finternal=5Fp?=
=?utf-8?q?oll_so_that_it_does_not_race_with_j=2El=2EProcess_finalization?=
Message-ID: <20150221213315.14033.58460@psf.io>
https://hg.python.org/jython/rev/b66aa40e00f5
changeset: 7592:b66aa40e00f5
user: Jim Baker
date: Sat Feb 21 14:33:12 2015 -0700
summary:
Fix Popen._internal_poll so that it does not race with j.l.Process finalization
The _internal_poll method is called by __del__, but it was racing with
the finalization of java.lang.Process objects when accessing
exitValue(). It's too late at this point, so just ignore.
files:
Lib/subprocess.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -1406,7 +1406,7 @@
except java.lang.IllegalThreadStateException:
# The child process is not ready to return status, so None os still right.
pass
- except java.io.IOException:
+ except (java.io.IOException, AttributeError) as e:
# Child has exited but returncode lost?
self.returncode = _deadstate
return self.returncode
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sun Feb 22 07:11:24 2015
From: jython-checkins at python.org (jim.baker)
Date: Sun, 22 Feb 2015 06:11:24 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Remove_jython=2Ebat_in_favo?=
=?utf-8?q?r_of_jython=2Eexe_wrapper_script?=
Message-ID: <20150222061124.6722.97287@psf.io>
https://hg.python.org/jython/rev/5cda1a50ecf0
changeset: 7593:5cda1a50ecf0
user: Jim Baker
date: Sat Feb 21 23:10:12 2015 -0700
summary:
Remove jython.bat in favor of jython.exe wrapper script
Built with PyInstaller (https://github.com/pyinstaller/pyinstaller)
Fixes http://bugs.jython.org/issue1491
files:
src/shell/jython.bat | 282 -----------------------------
src/shell/jython.exe | Bin
src/shell/python27.dll | Bin
3 files changed, 0 insertions(+), 282 deletions(-)
diff --git a/src/shell/jython.bat b/src/shell/jython.bat
deleted file mode 100644
--- a/src/shell/jython.bat
+++ /dev/null
@@ -1,282 +0,0 @@
- at echo off
-rem ---------------------------------------------------------------------------
-rem jython.bat - start script for Jython (adapted from jruby.bat)
-rem
-rem Environment variables (optional)
-rem
-rem JAVA_HOME Java installation directory
-rem
-rem JYTHON_HOME Jython installation directory
-rem
-rem JYTHON_OPTS Default Jython command line arguments
-rem
-rem ---------------------------------------------------------------------------
-
-rem If running in Take Command (4NT), force to run in cmd.exe
-if not "%@eval[2+2]" == "4" goto normalstart
-cmd /C %0 %*
-goto finish
-
-:normalstart
-set _PERCENT=%%
-set _EXCLAMATION=!
-setlocal enabledelayedexpansion
-
-rem ----- Verify and set required environment variables -----------------------
-
-rem make sure to clear the internal variables, to prevent leaking into subprocess calls
-set _JAVA_CMD=java
-if defined JAVA_HOME set _JAVA_CMD="%JAVA_HOME:"=%\bin\java"
-set _JYTHON_OPTS=
-if defined JYTHON_OPTS set _JYTHON_OPTS="%JYTHON_OPTS:"=%"
-set _JYTHON_HOME=
-if defined JYTHON_HOME set _JYTHON_HOME="%JYTHON_HOME:"=%"
-if defined _JYTHON_HOME goto gotHome
-
-rem try to dynamically determine jython home
-rem (this script typically resides in jython home, or in the /bin subdirectory)
-pushd "%~dp0%"
-set _JYTHON_HOME="%CD%"
-popd
-if exist %_JYTHON_HOME%\jython-dev.jar goto gotHome
-if exist %_JYTHON_HOME%\jython.jar goto gotHome
-pushd "%~dp0%\.."
-set _JYTHON_HOME="%CD%"
-popd
-if exist %_JYTHON_HOME%\jython-dev.jar goto gotHome
-if exist %_JYTHON_HOME%\jython.jar goto gotHome
-rem jython home fallback (if all else fails)
-rem if present, %JYTHON_HOME_FALLBACK% is already quoted
-set _JYTHON_HOME=%JYTHON_HOME_FALLBACK%
-
-:gotHome
-if not exist %_JYTHON_HOME%\jython-dev.jar goto tryComplete
-rem prefer built version
-set _CP=%_JYTHON_HOME%\jython-dev.jar
-for %%j in (%_JYTHON_HOME%\javalib\*.jar) do (
- set _CP=!_CP!;"%%j"
-)
-goto run
-
-:tryComplete
-set _CP=%_JYTHON_HOME%\jython.jar
-if exist %_JYTHON_HOME%/jython.jar goto run
-
-echo Cannot find jython-dev.jar or jython.jar in %_JYTHON_HOME%
-echo Try running this batch file from the 'bin' directory of an installed Jython,
-echo or setting JYTHON_HOME.
-goto cleanup
-
-rem ----- Execute the requested command ----------------------------------------
-
-:run
-set _JAVA_MEM=-Xmx512m
-rem 1152k is the minimum for test_marshal to pass. Windows' default is
-rem apparently 1M, anyway
-set _JAVA_STACK=-Xss1152k
-
-rem Escape any quotes. Use _S for ', _D for ", and _U to escape _ itself.
-rem We have to escape _ itself, otherwise file names with _S and _D
-rem will be converted to to wrong ones, when we un-escape. See JRUBY-2821.
-set _ARGS=%*
-if not defined _ARGS goto argsDone
-set _ARGS=%_ARGS:_=_U%
-set _ARGS=%_ARGS:'=_S%
-set _ARGS=%_ARGS:"=_D%
-rem also escape % signs
-set _replaceVal=%_ARGS%
-call :escape
-set _ARGS=%_replaceVal%
-
-set _ARGS="%_ARGS%"
-set _JYTHON_ARGS=
-
-:scanArgs
-rem split args by spaces into first and rest
-for /f "tokens=1,*" %%i in (%_ARGS%) do call :getArg "%%i" "%%j"
-goto procArg
-
-:getArg
-rem remove quotes around first arg
-set _CMP=%~1
-set _ARGS=%2
-goto :EOF
-
-:procArg
-if ["%_CMP%"] == [""] (
- set _ARGS=
- goto argsDone
-)
-
-REM NOTE: If you'd like to use a parameter with underscore in its name,
-REM NOTE: use the quoted value: --do_stuff -> --do_Ustuff
-
-if ["%_CMP%"] == ["--"] goto argsDone
-
-if ["%_CMP%"] == ["--jdb"] (
- if defined JAVA_HOME (
- set _JAVA_CMD="%JAVA_HOME:"=%\bin\jdb"
- ) else (
- set _JAVA_CMD=jdb
- )
- goto :nextArg
-)
-
-if ["%_CMP%"] == ["--boot"] (
- set _BOOT_CP=-Xbootclasspath/a:%_CP%
- goto :nextArg
-)
-
-if ["%_CMP%"] == ["--print"] (
- set _PRINT=print
- goto :nextArg
-)
-
-rem now unescape everything
-set _replaceVal=%_CMP%
-call :escape
-set _CMP=%_replaceVal%
-set _CMP=%_CMP:_D="%
-set _CMP=%_CMP:_S='%
-set _CMP=%_CMP:_U=_%
-set _CMP1=%_CMP:~0,1%
-set _CMP2=%_CMP:~0,2%
-
-rem detect first character is a quote; skip directly to jythonArg
-rem this avoids a batch syntax error
-if "%_CMP1:"=\\%" == "\\" goto jythonArg
-
-rem removing quote avoids a batch syntax error
-if "%_CMP2:"=\\%" == "-J" goto jvmArg
-
-:jythonArg
-set _JYTHON_ARGS=%_JYTHON_ARGS% %_CMP%
-goto nextArg
-
-:jvmArg
-set _VAL=%_CMP:~2%
-
-if "%_VAL:~0,4%" == "-Xmx" (
- set _JAVA_MEM=%_VAL%
-) else if "%_VAL:~0,4%" == "-Xss" (
- set _JAVA_STACK=%_VAL%
-) else (
- set _JAVA_OPTS=%_JAVA_OPTS% %_VAL%
-)
-
-:nextArg
-set _CMP=
-goto scanArgs
-
-:argsDone
-rem do not use 'if () else ()': this does not work with CLASSPATH containing '(x86)'
-if defined _BOOT_CP goto fullCmd
-if defined CLASSPATH goto classpathDefined
-set CLASSPATH=%_CP:"=%
-goto fullCmd
-:classpathDefined
-set CLASSPATH=%_CP:"=%;%CLASSPATH:"=%
-
-:fullCmd
-set _FULL_CMD=%_JAVA_CMD% %_JAVA_OPTS% %_JAVA_MEM% %_JAVA_STACK% -Dpython.home=%_JYTHON_HOME% -Dpython.executable="%~f0" %_BOOT_CP% -classpath "%CLASSPATH%" org.python.util.jython %_JYTHON_OPTS% %_JYTHON_ARGS% %_ARGS%
-if defined _PRINT (
- echo %_FULL_CMD%
-) else (
- %_FULL_CMD%
-)
-set E=%ERRORLEVEL%
-
-:cleanup
-set _ARGS=
-set _CMP=
-set _CMP1=
-set _CMP2=
-set _CP=
-set _BOOT_CP=
-set _FULL_CMD=
-set _JAVA_CMD=
-set _JAVA_OPTS=
-set _JAVA_MEM=
-set _JAVA_STACK=
-set _JYTHON_HOME=
-set _JYTHON_OPTS=
-set _JYTHON_ARGS=
-set _PRINT=
-goto finish
-
-
-
-REM escapes or unescapes % with @@P@@, and ! with @@E@@
-REM input: a text variable named _replaceVal
-REM result: _replaceVal has the new value
-:escape
-if not defined _replaceVal goto :EOF
-set /a _index=-1
-set _replaced=
-
-:escapeNext
-set /a _index=%_index% + 1
-call set _escapeChar=%%_replaceVal:~%_index%,1%%
-if ^"==^%_escapeChar% goto noEscape
-if ''=='%_escapeChar%' goto escapeEnd
-if "%_escapeChar%"==" " goto noEscape
-if "%_escapeChar%"=="@" goto unescapeCheck
-if "%_escapeChar%"=="%_EXCLAMATION%" goto escapeExclamation
-if "%_escapeChar%"=="%_PERCENT%" goto escapePercent
-:noEscape
-set _replaced=%_replaced%%_escapeChar%
-goto escapeNext
-
-:escapeExclamation
-set _replaced=%_replaced%@@E@@
-goto escapeNext
-
-:escapePercent
-set _replaced=%_replaced%@@P@@
-goto escapeNext
-
-:unescapeCheck
-set _isExclamation=
-set _isPercent=
-set _isUnrecognized=true
-set /a _aheadIndex=%_index% + 1
-call set _aheadChar=%%_replaceVal:~%_aheadIndex%,1%%
-if ^"==^%_aheadChar% goto noEscape
-if "%_aheadChar%"=="@" set /a _aheadIndex=%_aheadIndex% + 1
-call set _aheadChar=%%_replaceVal:~%_aheadIndex%,1%%
-if ^"==^%_aheadChar% goto noEscape
-if "%_aheadChar%"=="E" set _isExclamation=true & set _isUnrecognized=
-if "%_aheadChar%"=="P" set _isPercent=true & set _isUnrecognized=
-if defined _isUnrecognized goto noEscape
-set _isUnrecognized=true
-set /a _aheadIndex=%_aheadIndex% + 1
-call set _aheadChar=%%_replaceVal:~%_aheadIndex%,1%%
-if ^"==^%_aheadChar% goto noEscape
-if "%_aheadChar%"=="@" set /a _aheadIndex=%_aheadIndex% + 1
-call set _aheadChar=%%_replaceVal:~%_aheadIndex%,1%%
-if ^"==^%_aheadChar% goto noEscape
-if "%_aheadChar%"=="@" set _isUnrecognized=
-if defined _isUnrecognized goto noEscape
-if defined _isExclamation goto unescapeExclamation
-if defined _isPercent goto unescapePercent
-goto noEscape
-
-:unescapeExclamation
-set _replaced=%_replaced%%_EXCLAMATION%
-set /a _index=%_index% + 4
-goto escapeNext
-
-:unescapePercent
-set _replaced=%_replaced%%_PERCENT%
-set /a _index=%_index% + 4
-goto escapeNext
-
-:escapeEnd
-set _replaceVal=%_replaced%
-goto :EOF
-
-
-
-:finish
-set _UNQUOTED_COMSPEC=%COMSPEC:"=%
-"%_UNQUOTED_COMSPEC%" /c exit /b %E%
diff --git a/src/shell/jython.exe b/src/shell/jython.exe
new file mode 100755
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0e68e65aded720b141b9b4e2daf0d0dca4d46dc9
GIT binary patch
[stripped]
diff --git a/src/shell/python27.dll b/src/shell/python27.dll
new file mode 100755
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8d99ae24798acc720af4dca97140e0f591f39eb7
GIT binary patch
[stripped]
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sun Feb 22 07:13:29 2015
From: jython-checkins at python.org (jim.baker)
Date: Sun, 22 Feb 2015 06:13:29 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Update_bundled_pip_wheel_to?=
=?utf-8?q?_latest_jythontools/pip?=
Message-ID: <20150222061124.109567.24370@psf.io>
https://hg.python.org/jython/rev/e16c22b9568b
changeset: 7594:e16c22b9568b
user: Jim Baker
date: Sat Feb 21 23:10:52 2015 -0700
summary:
Update bundled pip wheel to latest jythontools/pip
https://github.com/jythontools/pip/commit/abd15e14ddab4b3aa56735d4aef69da5afca8512
files:
Lib/ensurepip/_bundled/pip-1.6-py2.py3-none-any.whl | Bin
1 files changed, 0 insertions(+), 0 deletions(-)
diff --git a/Lib/ensurepip/_bundled/pip-1.6-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-1.6-py2.py3-none-any.whl
index 8433b71cbec1bdf41dc7d8b41c77b80bf59ae296..b5e29a60e34ef796748bf2c3179973235254e8c8
GIT binary patch
[stripped]
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sun Feb 22 07:13:29 2015
From: jython-checkins at python.org (jim.baker)
Date: Sun, 22 Feb 2015 06:13:29 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Directly_execute_zip_files_?=
=?utf-8?q?or_dirs_with_top_level_=5F=5Fmain=5F=5F=2Epy?=
Message-ID: <20150222061124.3869.10089@psf.io>
https://hg.python.org/jython/rev/5d62f637a8d1
changeset: 7595:5d62f637a8d1
user: Jim Baker
date: Sat Feb 21 23:11:14 2015 -0700
summary:
Directly execute zip files or dirs with top level __main__.py
Necessary to support wrapper scripts generated by distlib, part of pip
(and used to build pip.exe wrapper script).
Corresponds to http://bugs.python.org/issue1739468
files:
src/org/python/core/PyModule.java | 1 +
src/org/python/core/PyNullImporter.java | 71 +++++++++++++
src/org/python/core/PyObject.java | 2 +-
src/org/python/core/imp.java | 22 +++-
src/org/python/util/jython.java | 55 +++++++--
5 files changed, 135 insertions(+), 16 deletions(-)
diff --git a/src/org/python/core/PyModule.java b/src/org/python/core/PyModule.java
--- a/src/org/python/core/PyModule.java
+++ b/src/org/python/core/PyModule.java
@@ -62,6 +62,7 @@
__dict__.__setitem__("__doc__", doc);
if (name.equals(new PyString("__main__"))) {
__dict__.__setitem__("__builtins__", Py.getSystemState().modules.__finditem__("__builtin__"));
+ __dict__.__setitem__("__package__", Py.None);
}
}
diff --git a/src/org/python/core/PyNullImporter.java b/src/org/python/core/PyNullImporter.java
new file mode 100644
--- /dev/null
+++ b/src/org/python/core/PyNullImporter.java
@@ -0,0 +1,71 @@
+/* Maintains semantics of NullImporter objects in CPython - unique objects,
+ but does not retain the path used. */
+
+package org.python.core;
+
+import java.nio.file.FileSystems;
+import java.nio.file.Path;
+
+import org.python.expose.ExposedMethod;
+import org.python.expose.ExposedType;
+
+ at Untraversable
+ at ExposedType(name = "NullImporter", isBaseType = false)
+public class PyNullImporter extends PyObject {
+
+ public static final PyType TYPE = PyType.fromClass(PyNullImporter.class);
+ static {
+ TYPE.setName("imp.NullImporter");
+ }
+
+ public PyNullImporter(PyObject pathObj) {
+ super();
+ String pathStr = asPath(pathObj);
+ if (pathStr.equals("")) {
+ throw Py.ImportError("empty pathname");
+ }
+ if (isDir(pathStr)) {
+ throw Py.ImportError("existing directory: " + pathStr);
+ }
+ }
+
+ public PyObject find_module(String fullname) {
+ return Py.None;
+ }
+
+ public PyObject find_module(String fullname, String path) {
+ return Py.None;
+ }
+
+ @ExposedMethod(defaults = "null")
+ final PyObject NullImporter_find_module(String fullname, String path) {
+ return Py.None;
+ }
+
+ // FIXME Refactoring move helper function to a central util library
+ // FIXME Also can take in account working in zip file systems
+
+ private static String asPath(PyObject pathObj) {
+ if (!(pathObj instanceof PyString)) {
+ throw Py.TypeError(String.format("coercing to Unicode: need string, %s type found",
+ pathObj.getType().fastGetName()));
+ }
+ return pathObj.toString();
+ }
+
+ private static boolean isDir(String pathStr) {
+ if (pathStr.equals("")) {
+ return false;
+ }
+ try {
+ Path path = FileSystems.getDefault().getPath(pathStr);
+ if (!path.isAbsolute()) {
+ path = FileSystems.getDefault().getPath(Py.getSystemState().getCurrentWorkingDir(), pathStr);
+ }
+ return path.toFile().isDirectory();
+ } catch (java.nio.file.InvalidPathException ex) {
+ return false;
+ }
+ }
+
+}
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
@@ -228,7 +228,7 @@
* Equivalent to the standard Python __repr__ method. This method
* should not typically need to be overrriden. The easiest way to
* configure the string representation of a PyObject
is to
- * override the standard Java toString
method.
+ * override the standard Java toString
method.
**/
// counter-intuitively exposing this as __str__, otherwise stack overflow
// occurs during regression testing. XXX: more detail for this comment
diff --git a/src/org/python/core/imp.java b/src/org/python/core/imp.java
--- a/src/org/python/core/imp.java
+++ b/src/org/python/core/imp.java
@@ -456,6 +456,11 @@
return PyType.fromClass(c, false); // xxx?
}
+ public static PyObject getImporter(PyObject p) {
+ PySystemState sys = Py.getSystemState();
+ return getPathImporter(sys.path_importer_cache, sys.path_hooks, p);
+ }
+
static PyObject getPathImporter(PyObject cache, PyList hooks, PyObject p) {
// attempt to get an importer for the path
@@ -480,8 +485,21 @@
}
}
- importer = (importer == null ? Py.None : importer);
- cache.__setitem__(p, importer);
+ if (importer == null) {
+ try {
+ importer = new PyNullImporter(p);
+ } catch (PyException e) {
+ if (!e.match(Py.ImportError)) {
+ throw e;
+ }
+ }
+ }
+
+ if (importer != null) {
+ cache.__setitem__(p, importer);
+ } else {
+ importer = Py.None;
+ }
return importer;
}
diff --git a/src/org/python/util/jython.java b/src/org/python/util/jython.java
--- a/src/org/python/util/jython.java
+++ b/src/org/python/util/jython.java
@@ -22,6 +22,8 @@
import org.python.core.PyException;
import org.python.core.PyFile;
import org.python.core.PyList;
+import org.python.core.PyNullImporter;
+import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyStringMap;
import org.python.core.PySystemState;
@@ -185,6 +187,37 @@
}
}
+ private static void runModule(InteractiveConsole interp, String moduleName) {
+ runModule(interp, moduleName, false);
+ }
+
+ private static void runModule(InteractiveConsole interp, String moduleName, boolean set_argv0) {
+ // PEP 338 - Execute module as a script
+ try {
+ PyObject runpy = imp.importName("runpy", true);
+ PyObject runmodule = runpy.__findattr__("_run_module_as_main");
+ runmodule.__call__(Py.newStringOrUnicode(moduleName), Py.newBoolean(set_argv0));
+ } catch (Throwable t) {
+ Py.printException(t);
+ interp.cleanup();
+ System.exit(-1);
+ }
+ }
+
+ private static boolean runMainFromImporter(InteractiveConsole interp, String filename) {
+ // Support http://bugs.python.org/issue1739468 - Allow interpreter to execute a zip file or directory
+ PyString argv0 = Py.newStringOrUnicode(filename);
+ PyObject importer = imp.getImporter(argv0);
+ if (!(importer instanceof PyNullImporter)) {
+ /* argv0 is usable as an import source, so
+ put it in sys.path[0] and import __main__ */
+ Py.getSystemState().path.insert(0, argv0);
+ runModule(interp, "__main__", true);
+ return true;
+ }
+ return false;
+ }
+
public static void run(String[] args) {
// Parse the command line options
CommandLineOptions opts = new CommandLineOptions();
@@ -285,6 +318,11 @@
// was there a filename on the command line?
if (opts.filename != null) {
+ if (runMainFromImporter(interp, opts.filename)) {
+ interp.cleanup();
+ return;
+ }
+
String path;
try {
path = new File(opts.filename).getCanonicalFile().getParent();
@@ -294,7 +332,7 @@
if (path == null) {
path = "";
}
- Py.getSystemState().path.insert(0, new PyString(path));
+ Py.getSystemState().path.insert(0, Py.newStringOrUnicode(path));
if (opts.jar) {
try {
runJar(opts.filename);
@@ -366,18 +404,9 @@
}
if (opts.moduleName != null) {
- // PEP 338 - Execute module as a script
- try {
- interp.exec("import runpy");
- interp.set("name", Py.newString(opts.moduleName));
- interp.exec("runpy.run_module(name, run_name='__main__', alter_sys=True)");
- interp.cleanup();
- return;
- } catch (Throwable t) {
- Py.printException(t);
- interp.cleanup();
- System.exit(-1);
- }
+ runModule(interp, opts.moduleName);
+ interp.cleanup();
+ return;
}
}
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sun Feb 22 14:03:34 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Sun, 22 Feb 2015 13:03:34 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Minor_doc-cosmetics?=
Message-ID: <20150222130317.29684.84696@psf.io>
https://hg.python.org/jython/rev/cf9ad15fca87
changeset: 7596:cf9ad15fca87
user: Stefan Richthofer
date: Sun Feb 22 14:00:36 2015 +0100
summary:
Minor doc-cosmetics
files:
src/org/python/core/PyObject.java | 2 +-
src/org/python/modules/gc.java | 56 +++++++++---------
2 files changed, 29 insertions(+), 29 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
@@ -228,7 +228,7 @@
* Equivalent to the standard Python __repr__ method. This method
* should not typically need to be overrriden. The easiest way to
* configure the string representation of a PyObject
is to
- * override the standard Java toString
method.
+ * override the standard Java toString
method.
**/
// counter-intuitively exposing this as __str__, otherwise stack overflow
// occurs during regression testing. XXX: more detail for this comment
diff --git a/src/org/python/modules/gc.java b/src/org/python/modules/gc.java
--- a/src/org/python/modules/gc.java
+++ b/src/org/python/modules/gc.java
@@ -942,7 +942,7 @@
* must not terminate by throwing an exception). (Note: Using this for extern
* finalizers is currently experimental and needs more testing.)
* This works independently from monitoring, which is mainly needed to allow
- * counting of cyclic garbage in {@code gc.collect}.
+ * counting of cyclic garbage in {@link #collect()}.
*
*
* This feature compensates that Java's gc does not provide any guarantees about
@@ -973,7 +973,7 @@
}
/**
- * See doc of {@core registerPreFinalizationProcess(Runnable process)}.
+ * See doc of {@link #registerPreFinalizationProcess(Runnable)}.
*/
public static void registerPreFinalizationProcess(Runnable process, int index) {
while (true) {
@@ -1042,7 +1042,7 @@
* must not terminate by throwing an exception). (Note: Using this for extern
* finalizers is currently experimental and needs more testing.)
* This works independently from monitoring (which is mainly needed to allow
- * garbage counting in {@code gc.collect}).
+ * garbage counting in {@link #collect()}).
*
*
* This feature compensates that Java's gc does not provide any guarantees about
@@ -1059,7 +1059,7 @@
* already during finalization phase. This can happen if external frameworks use
* their own finalizers. This can be cured by letting these finalizers call
* {@code gc.notifyPreFinalization()} before anything else is done and
- * {@code notifyPostFinalization()} right before the finalization method returns.
+ * {@code gc.notifyPostFinalization()} right before the finalization method returns.
* Between these calls the finalizer must not terminate by throwing an exception.
* (Note: Using this for extern finalizers is currently experimental and needs more testing.)
*
@@ -1074,7 +1074,7 @@
}
/**
- * See doc of {@code registerPostFinalizationProcess(Runnable process)}.
+ * See doc of {@link #registerPostFinalizationProcess(Runnable)}.
*/
public static void registerPostFinalizationProcess(Runnable process, int index) {
while (true) {
@@ -1407,22 +1407,22 @@
*
* {@code flags} is a {@code short} and can have the following bits turned on:
*
- * {@link #MONITOR_GLOBAL} - Automatically monitors all PyObjects created from now on.
- * {@link #DONT_FINALIZE_CYCLIC_GARBAGE} - Adds cyclic finalizable PyObjects to {@link #garbage}.
- * {@link #PRESERVE_WEAKREFS_ON_RESURRECTION} - Keeps weak references alive if the referent is resurrected.
- * {@link #DONT_FINALIZE_RESURRECTED_OBJECTS} -
- * Emulates CPython behavior regarding resurrected objects and finalization.
- * {@link #DONT_TRAVERSE_BY_REFLECTION} - Inhibits reflection-based traversion.
- * {@link #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING} -
- * Suppress warnings for PyObjects that neither implement {@link org.python.core.Traverseproc} nor
- * are marked as {@link org.python.core.Untraversable}.
- * {@link #USE_PY_WRITE_DEBUG} - uses {@link org.python.core.Py#writeDebug(String, String)} for
- * debugging output instead of directly writing to {@link java.lang.System#err}.
- * {@link #VERBOSE_COLLECT} - Enable collection-related verbose output.
- * {@link #VERBOSE_WEAKREF} - Enable weakref-related verbose output.
- * {@link #VERBOSE_DELAYED} - Enable delayed finalization-related verbose output.
- * {@link #VERBOSE_FINALIZE} - Enable finalization-related verbose output.
- * {@link #VERBOSE} - All previous verbose-flags combined.
+ * {@link #MONITOR_GLOBAL} - Automatically monitors all PyObjects created from now on.
+ * {@link #DONT_FINALIZE_CYCLIC_GARBAGE} - Adds cyclic finalizable PyObjects to {@link #garbage}.
+ * {@link #PRESERVE_WEAKREFS_ON_RESURRECTION} - Keeps weak references alive if the referent is resurrected.
+ * {@link #DONT_FINALIZE_RESURRECTED_OBJECTS} -
+ * Emulates CPython behavior regarding resurrected objects and finalization.
+ * {@link #DONT_TRAVERSE_BY_REFLECTION} - Inhibits reflection-based traversion.
+ * {@link #SUPPRESS_TRAVERSE_BY_REFLECTION_WARNING} -
+ * Suppress warnings for PyObjects that neither implement {@link org.python.core.Traverseproc} nor
+ * are marked as {@link org.python.core.Untraversable}.
+ * {@link #USE_PY_WRITE_DEBUG} - uses {@link org.python.core.Py#writeDebug(String, String)} for
+ * debugging output instead of directly writing to {@link java.lang.System#err}.
+ * {@link #VERBOSE_COLLECT} - Enable collection-related verbose output.
+ * {@link #VERBOSE_WEAKREF} - Enable weakref-related verbose output.
+ * {@link #VERBOSE_DELAYED} - Enable delayed finalization-related verbose output.
+ * {@link #VERBOSE_FINALIZE} - Enable finalization-related verbose output.
+ * {@link #VERBOSE} - All previous verbose-flags combined.
*
* @see #MONITOR_GLOBAL
* @see #DONT_FINALIZE_CYCLIC_GARBAGE
@@ -2014,13 +2014,13 @@
*
* {@flags} flags is an {@code int}eger and can have the following bits turned on:
*
- * {@link #DEBUG_STATS} - Print statistics during collection.
- * {@link #DEBUG_COLLECTABLE} - Print collectable objects found.
- * {@link #DEBUG_UNCOLLECTABLE} - Print unreachable but uncollectable objects found.
- * {@link #DEBUG_INSTANCES} - Print instance objects.
- * {@link #DEBUG_OBJECTS} - Print objects other than instances.
- * {@link #DEBUG_SAVEALL} - Save objects to gc.garbage rather than freeing them.
- * {@link #DEBUG_LEAK} - Debug leaking programs (everything but STATS).
+ * {@link #DEBUG_STATS} - Print statistics during collection.
+ * {@link #DEBUG_COLLECTABLE} - Print collectable objects found.
+ * {@link #DEBUG_UNCOLLECTABLE} - Print unreachable but uncollectable objects found.
+ * {@link #DEBUG_INSTANCES} - Print instance objects.
+ * {@link #DEBUG_OBJECTS} - Print objects other than instances.
+ * {@link #DEBUG_SAVEALL} - Save objects to gc.garbage rather than freeing them.
+ * {@link #DEBUG_LEAK} - Debug leaking programs (everything but STATS).
*
* @see #DEBUG_STATS
* @see #DEBUG_COLLECTABLE
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Sun Feb 22 18:40:13 2015
From: jython-checkins at python.org (jim.baker)
Date: Sun, 22 Feb 2015 17:40:13 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_More_robust_registering/rai?=
=?utf-8?q?sing_of_signals_on_JVMs_other_than_OpenJDK?=
Message-ID: <20150222174013.109589.95535@psf.io>
https://hg.python.org/jython/rev/e909a4641731
changeset: 7597:e909a4641731
user: Pekka Kl?rck
date: Sun Feb 22 10:40:10 2015 -0700
summary:
More robust registering/raising of signals on JVMs other than OpenJDK
Certain platforms, such as IBM's J9, raise
java.lang.IllegalArgumentException when attempting to register or
raise signals, even when present as determined by Jython's signal
probe. Raise ValueError in these cases.
Fixes http://bugs.jython.org/issue1729
files:
Lib/signal.py | 20 +++++++++++++++-----
1 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/Lib/signal.py b/Lib/signal.py
--- a/Lib/signal.py
+++ b/Lib/signal.py
@@ -132,9 +132,9 @@
raise ValueError("signal number out of range")
if callable(action):
- prev = sun.misc.Signal.handle(signal, JythonSignalHandler(action))
+ prev = _register_signal(signal, JythonSignalHandler(action))
elif action in (SIG_IGN, SIG_DFL) or isinstance(action, sun.misc.SignalHandler):
- prev = sun.misc.Signal.handle(signal, action)
+ prev = _register_signal(signal, action)
else:
raise TypeError("signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object")
@@ -144,6 +144,13 @@
return prev
+def _register_signal(signal, action):
+ try:
+ return sun.misc.Signal.handle(signal, action)
+ except IllegalArgumentException, err:
+ raise ValueError(err.getMessage())
+
+
# dangerous! don't use!
def getsignal(sig):
"""getsignal(sig) -> action
@@ -162,8 +169,8 @@
signal = _signals[sig]
except KeyError:
raise ValueError("signal number out of range")
- current = sun.misc.Signal.handle(signal, SIG_DFL)
- sun.misc.Signal.handle(signal, current) # and reinstall
+ current = _register_signal(signal, SIG_DFL)
+ _register_signal(signal, current) # and reinstall
if isinstance(current, JythonSignalHandler):
return current.action
@@ -222,7 +229,10 @@
raise NotImplementedError("alarm not implemented on this platform")
def raise_alarm():
- sun.misc.Signal.raise(_signals[SIGALRM])
+ try:
+ sun.misc.Signal.raise(_signals[SIGALRM])
+ except IllegalArgumentException, err:
+ raise ValueError(err.getMessage())
if time > 0:
new_alarm_timer = _Alarm(time, raise_alarm)
--
Repository URL: https://hg.python.org/jython
From jython-checkins at python.org Tue Feb 24 05:27:51 2015
From: jython-checkins at python.org (stefan.richthofer)
Date: Tue, 24 Feb 2015 04:27:51 +0000
Subject: [Jython-checkins] =?utf-8?q?jython=3A_Improved_traverse_method_of?=
=?utf-8?q?_derived_classes=2E?=
Message-ID: <20150224042751.29680.79512@psf.io>
https://hg.python.org/jython/rev/9f045cc6d5b0
changeset: 7598:9f045cc6d5b0
user: Stefan Richthofer
date: Tue Feb 24 05:27:15 2015 +0100
summary:
Improved traverse method of derived classes.
Further added test for (time-consuming) non-PyObject finalizers
notifying gc and improved various gc-related docstrings.
files:
Lib/test/test_gc_jy.py | 59 ++++++++-
src/org/python/antlr/ast/AssertDerived.java | 11 +-
src/org/python/antlr/ast/AssignDerived.java | 11 +-
src/org/python/antlr/ast/AttributeDerived.java | 11 +-
src/org/python/antlr/ast/AugAssignDerived.java | 11 +-
src/org/python/antlr/ast/BinOpDerived.java | 11 +-
src/org/python/antlr/ast/BoolOpDerived.java | 11 +-
src/org/python/antlr/ast/BreakDerived.java | 11 +-
src/org/python/antlr/ast/CallDerived.java | 11 +-
src/org/python/antlr/ast/ClassDefDerived.java | 11 +-
src/org/python/antlr/ast/CompareDerived.java | 11 +-
src/org/python/antlr/ast/ContinueDerived.java | 11 +-
src/org/python/antlr/ast/DeleteDerived.java | 11 +-
src/org/python/antlr/ast/DictDerived.java | 11 +-
src/org/python/antlr/ast/EllipsisDerived.java | 11 +-
src/org/python/antlr/ast/ExceptHandlerDerived.java | 11 +-
src/org/python/antlr/ast/ExecDerived.java | 11 +-
src/org/python/antlr/ast/ExprDerived.java | 11 +-
src/org/python/antlr/ast/ExpressionDerived.java | 11 +-
src/org/python/antlr/ast/ExtSliceDerived.java | 11 +-
src/org/python/antlr/ast/ForDerived.java | 11 +-
src/org/python/antlr/ast/FunctionDefDerived.java | 11 +-
src/org/python/antlr/ast/GeneratorExpDerived.java | 11 +-
src/org/python/antlr/ast/GlobalDerived.java | 11 +-
src/org/python/antlr/ast/IfDerived.java | 11 +-
src/org/python/antlr/ast/IfExpDerived.java | 11 +-
src/org/python/antlr/ast/ImportDerived.java | 11 +-
src/org/python/antlr/ast/ImportFromDerived.java | 11 +-
src/org/python/antlr/ast/IndexDerived.java | 11 +-
src/org/python/antlr/ast/InteractiveDerived.java | 11 +-
src/org/python/antlr/ast/LambdaDerived.java | 11 +-
src/org/python/antlr/ast/ListCompDerived.java | 11 +-
src/org/python/antlr/ast/ListDerived.java | 11 +-
src/org/python/antlr/ast/ModuleDerived.java | 11 +-
src/org/python/antlr/ast/NameDerived.java | 11 +-
src/org/python/antlr/ast/NumDerived.java | 11 +-
src/org/python/antlr/ast/PassDerived.java | 11 +-
src/org/python/antlr/ast/PrintDerived.java | 11 +-
src/org/python/antlr/ast/RaiseDerived.java | 11 +-
src/org/python/antlr/ast/ReprDerived.java | 11 +-
src/org/python/antlr/ast/ReturnDerived.java | 11 +-
src/org/python/antlr/ast/SliceDerived.java | 11 +-
src/org/python/antlr/ast/StrDerived.java | 11 +-
src/org/python/antlr/ast/SubscriptDerived.java | 11 +-
src/org/python/antlr/ast/SuiteDerived.java | 11 +-
src/org/python/antlr/ast/TryExceptDerived.java | 11 +-
src/org/python/antlr/ast/TryFinallyDerived.java | 11 +-
src/org/python/antlr/ast/TupleDerived.java | 11 +-
src/org/python/antlr/ast/UnaryOpDerived.java | 11 +-
src/org/python/antlr/ast/WhileDerived.java | 11 +-
src/org/python/antlr/ast/WithDerived.java | 11 +-
src/org/python/antlr/ast/YieldDerived.java | 11 +-
src/org/python/antlr/ast/aliasDerived.java | 11 +-
src/org/python/antlr/ast/argumentsDerived.java | 11 +-
src/org/python/antlr/ast/comprehensionDerived.java | 11 +-
src/org/python/antlr/ast/keywordDerived.java | 11 +-
src/org/python/antlr/op/AddDerived.java | 11 +-
src/org/python/antlr/op/AndDerived.java | 11 +-
src/org/python/antlr/op/AugLoadDerived.java | 11 +-
src/org/python/antlr/op/AugStoreDerived.java | 11 +-
src/org/python/antlr/op/BitAndDerived.java | 11 +-
src/org/python/antlr/op/BitOrDerived.java | 11 +-
src/org/python/antlr/op/BitXorDerived.java | 11 +-
src/org/python/antlr/op/DelDerived.java | 11 +-
src/org/python/antlr/op/DivDerived.java | 11 +-
src/org/python/antlr/op/EqDerived.java | 11 +-
src/org/python/antlr/op/FloorDivDerived.java | 11 +-
src/org/python/antlr/op/GtDerived.java | 11 +-
src/org/python/antlr/op/GtEDerived.java | 11 +-
src/org/python/antlr/op/InDerived.java | 11 +-
src/org/python/antlr/op/InvertDerived.java | 11 +-
src/org/python/antlr/op/IsDerived.java | 11 +-
src/org/python/antlr/op/IsNotDerived.java | 11 +-
src/org/python/antlr/op/LShiftDerived.java | 11 +-
src/org/python/antlr/op/LoadDerived.java | 11 +-
src/org/python/antlr/op/LtDerived.java | 11 +-
src/org/python/antlr/op/LtEDerived.java | 11 +-
src/org/python/antlr/op/ModDerived.java | 11 +-
src/org/python/antlr/op/MultDerived.java | 11 +-
src/org/python/antlr/op/NotDerived.java | 11 +-
src/org/python/antlr/op/NotEqDerived.java | 11 +-
src/org/python/antlr/op/NotInDerived.java | 11 +-
src/org/python/antlr/op/OrDerived.java | 11 +-
src/org/python/antlr/op/ParamDerived.java | 11 +-
src/org/python/antlr/op/PowDerived.java | 11 +-
src/org/python/antlr/op/RShiftDerived.java | 11 +-
src/org/python/antlr/op/StoreDerived.java | 11 +-
src/org/python/antlr/op/SubDerived.java | 11 +-
src/org/python/antlr/op/UAddDerived.java | 11 +-
src/org/python/antlr/op/USubDerived.java | 11 +-
src/org/python/core/ClasspathPyImporterDerived.java | 11 +-
src/org/python/core/JyAttribute.java | 4 +-
src/org/python/core/PyArrayDerived.java | 11 +-
src/org/python/core/PyBaseExceptionDerived.java | 11 +-
src/org/python/core/PyByteArrayDerived.java | 11 +-
src/org/python/core/PyClassMethodDerived.java | 11 +-
src/org/python/core/PyComplexDerived.java | 11 +-
src/org/python/core/PyDictionaryDerived.java | 11 +-
src/org/python/core/PyEnumerateDerived.java | 11 +-
src/org/python/core/PyFileDerived.java | 11 +-
src/org/python/core/PyFloatDerived.java | 11 +-
src/org/python/core/PyFrozenSetDerived.java | 11 +-
src/org/python/core/PyInstance.java | 3 -
src/org/python/core/PyIntegerDerived.java | 11 +-
src/org/python/core/PyListDerived.java | 11 +-
src/org/python/core/PyLongDerived.java | 11 +-
src/org/python/core/PyModuleDerived.java | 11 +-
src/org/python/core/PyObjectDerived.java | 11 +-
src/org/python/core/PyPropertyDerived.java | 11 +-
src/org/python/core/PySetDerived.java | 11 +-
src/org/python/core/PyStringDerived.java | 11 +-
src/org/python/core/PySuperDerived.java | 11 +-
src/org/python/core/PyTupleDerived.java | 11 +-
src/org/python/core/PyTypeDerived.java | 11 +-
src/org/python/core/PyUnicodeDerived.java | 11 +-
src/org/python/core/Traverseproc.java | 25 ++-
src/org/python/core/finalization/FinalizableBuiltin.java | 17 +-
src/org/python/core/finalization/FinalizablePyObject.java | 53 ++++----
src/org/python/modules/PyStructDerived.java | 11 +-
src/org/python/modules/_collections/PyDefaultDictDerived.java | 11 +-
src/org/python/modules/_collections/PyDequeDerived.java | 11 +-
src/org/python/modules/_csv/PyDialectDerived.java | 11 +-
src/org/python/modules/_functools/PyPartialDerived.java | 11 +-
src/org/python/modules/_io/PyFileIODerived.java | 11 +-
src/org/python/modules/_io/PyIOBaseDerived.java | 11 +-
src/org/python/modules/_io/PyRawIOBaseDerived.java | 11 +-
src/org/python/modules/_weakref/ReferenceTypeDerived.java | 11 +-
src/org/python/modules/bz2/PyBZ2CompressorDerived.java | 11 +-
src/org/python/modules/bz2/PyBZ2DecompressorDerived.java | 11 +-
src/org/python/modules/bz2/PyBZ2FileDerived.java | 11 +-
src/org/python/modules/gc.java | 18 +-
src/org/python/modules/itertools/PyTeeIteratorDerived.java | 11 +-
src/org/python/modules/itertools/chainDerived.java | 11 +-
src/org/python/modules/itertools/combinationsDerived.java | 11 +-
src/org/python/modules/itertools/combinationsWithReplacementDerived.java | 11 +-
src/org/python/modules/itertools/compressDerived.java | 11 +-
src/org/python/modules/itertools/countDerived.java | 11 +-
src/org/python/modules/itertools/cycleDerived.java | 11 +-
src/org/python/modules/itertools/dropwhileDerived.java | 11 +-
src/org/python/modules/itertools/groupbyDerived.java | 11 +-
src/org/python/modules/itertools/ifilterDerived.java | 11 +-
src/org/python/modules/itertools/ifilterfalseDerived.java | 11 +-
src/org/python/modules/itertools/isliceDerived.java | 11 +-
src/org/python/modules/itertools/izipDerived.java | 11 +-
src/org/python/modules/itertools/izipLongestDerived.java | 11 +-
src/org/python/modules/itertools/permutationsDerived.java | 11 +-
src/org/python/modules/itertools/productDerived.java | 11 +-
src/org/python/modules/itertools/repeatDerived.java | 11 +-
src/org/python/modules/itertools/starmapDerived.java | 11 +-
src/org/python/modules/itertools/takewhileDerived.java | 11 +-
src/org/python/modules/random/PyRandomDerived.java | 11 +-
src/org/python/modules/thread/PyLocalDerived.java | 11 +-
src/org/python/modules/zipimport/zipimporterDerived.java | 11 +-
src/templates/gderived-defs | 11 +-
tests/java/javatests/GCTestHelper.java | 25 ++++-
155 files changed, 1167 insertions(+), 654 deletions(-)
diff --git a/Lib/test/test_gc_jy.py b/Lib/test/test_gc_jy.py
--- a/Lib/test/test_gc_jy.py
+++ b/Lib/test/test_gc_jy.py
@@ -18,6 +18,7 @@
#i.e. not Jython is running
pass
+
class GCTests_Jy_CyclicGarbage(unittest.TestCase):
@classmethod
@@ -38,7 +39,6 @@
except Exception:
pass
-
# In contrast to the tests in test_gc, these finalizer tests shall work
# even if gc-monitoring is disabled.
def test_finalizer(self):
@@ -66,7 +66,6 @@
self.fail("didn't find obj in garbage (finalizer)")
gc.garbage.remove(obj)
-
def test_finalizer_newclass(self):
# A() is uncollectable if it is part of a cycle, make sure it shows up
# in gc.garbage.
@@ -115,17 +114,13 @@
class GCTests_Jy_preprocess_and_postprocess(unittest.TestCase):
def test_finalization_preprocess_and_postprocess(self):
- #print "test_finalization_preprocess_and_postprocess"
#Note that this test is done here again (already was in another class
#in this module), to see that everything works as it should also with
#a different flag-context.
- #print "test_finalization_preprocess_and_postprocess"
- #gc.removeJythonGCFlags(gc.DONT_FINALIZE_RESURRECTED_OBJECTS)
comments = []
self0 = self
class A:
def __del__(self):
- #print "del A"
self0.assertIn("run PreProcess", comments)
comments.append("A del")
#let's simulate a time-consuming finalizer
@@ -171,6 +166,55 @@
gc.unregisterPreFinalizationProcess(prePr)
gc.unregisterPostFinalizationProcess(postPr)
+ def test_with_extern_NonPyObjectFinalizer_that_notifies_gc(self):
+ #Note that this test is done here again (already was in another class
+ #in this module), to see that everything works as it should also with
+ #a different flag-context.
+ comments = []
+ class A:
+ def __init__(self, index):
+ self.index = index
+
+ def __del__(self):
+ comments.append("A_del_"+str(self.index))
+
+ class PreProcess(Runnable):
+ preCount = 0
+ def run(self):
+ PreProcess.preCount += 1
+
+ class PostProcess(Runnable):
+ postCount = 0
+ def run(self):
+ PostProcess.postCount += 1
+
+ prePr = PreProcess()
+ postPr = PostProcess()
+ time.sleep(1) # <- to avoid that the newly registered processes
+ # become subject to previous run (remember: We
+ # are not in monitor-mode, i.e. gc runs async.
+ gc.registerPreFinalizationProcess(prePr)
+ gc.registerPostFinalizationProcess(postPr)
+ for i in range(4):
+ f = A(i)
+ del f
+ #NastyFinalizer would cause this test occasionally to fail
+ externFinalizer = GCTestHelper.NotSoNastyFinalizer()
+ del externFinalizer
+ for i in range(4, 8):
+ f = A(i)
+ del f
+ System.gc()
+ #we wait a bit longer here, since PostProcess runs asynchronous
+ #and must wait for the finalizer of A
+ time.sleep(4)
+ self.assertEqual(len(comments), 8)
+ self.assertEqual(PreProcess.preCount, 1)
+ self.assertEqual(PostProcess.postCount, 1)
+ comments = []
+ gc.unregisterPreFinalizationProcess(prePr)
+ gc.unregisterPostFinalizationProcess(postPr)
+
@unittest.skipUnless(test_support.is_jython,
'This class tests detailed Jython-specific behavior.')
@@ -367,7 +411,6 @@
del a
del c
#gc.set_debug(gc.DEBUG_SAVEALL)
- #gc.collect()
self.assertEqual(gc.collect(), 0) #c is not cyclic and a, b are resurrected,
#so nothing to count here
#self.asserEqual(len(gc.garbage), 0)
@@ -381,7 +424,6 @@
def test_notifyRerun_for_delayed_finalization(self):
gc.collect()
- resurrect = []
comments = []
class Test_Finalizable(object):
@@ -618,7 +660,6 @@
except Exception:
pass
-
def test_weakref_after_resurrection_threadsafe(self):
gc.collect()
diff --git a/src/org/python/antlr/ast/AssertDerived.java b/src/org/python/antlr/ast/AssertDerived.java
--- a/src/org/python/antlr/ast/AssertDerived.java
+++ b/src/org/python/antlr/ast/AssertDerived.java
@@ -34,12 +34,15 @@
public int traverseDerived(Visitproc visit,Object arg) {
int retVal;
for(int i=0;iJyNI .
+ * Reserved for use by JyNI .
*/
public static final byte JYNI_HANDLE_ATTR = 1;
diff --git a/src/org/python/core/PyArrayDerived.java b/src/org/python/core/PyArrayDerived.java
--- a/src/org/python/core/PyArrayDerived.java
+++ b/src/org/python/core/PyArrayDerived.java
@@ -33,12 +33,15 @@
public int traverseDerived(Visitproc visit,Object arg) {
int retVal;
for(int i=0;i
- * This interface defines a CPython equivalent traverse mechanism allowing
- * to detect reference cycles. While this is crucial for cyclic gc support
- * in CPython, it only serves debugging purposes in Jython. As a side effect
- * it allows a more complete implementation of the gc module.
+ * This interface defines a
+ *
+ * CPython equivalent traverse mechanism
+ * allowing to detect reference cycles. While this is crucial for cyclic
+ * gc support in CPython, it only serves debugging purposes in Jython. As a side
+ * effect it allows a more complete implementation of the gc module.
*
*
* Note that implementing this interface is only OPTIONAL. Gc will work fine
@@ -32,7 +34,8 @@
* gc behaviors.
*
*
- * Further this mechanism is crucial for some aspects of gc-support of the JyNI
+ * Further this mechanism is crucial for some aspects of gc-support of the
+ * JyNI
* project. JyNI does not strictly depend on it to emulate CPython's gc
* for extensions, but would have to perform inefficient reflection-based
* traversal in some edge-cases (which might also conflict security managers).
@@ -447,19 +450,23 @@
public interface Traverseproc {
/**
- * Traverses all reachable {@code PyObject}s.
+ * Traverses all directly contained {@code PyObject}s.
* Like in CPython, {@code arg} must be passed
* unmodified to {@code visit} as its second parameter.
- * If {@code visit.visit} returns nonzero, this return value
+ * If {@link Visitproc#visit(PyObject, Object)} returns
+ * nonzero, this return value
* must be returned immediately by traverse.
+ *
+ * {@link Visitproc#visit(PyObject, Object)} must not be
+ * called with a {@code null} PyObject-argument.
*/
public int traverse(Visitproc visit, Object arg);
/**
* Optional operation.
* Should only be implemented if it is more efficient
- * than calling {@code traverse} with a visitproc
- * that just watches out for {@code ob}.
+ * than calling {@link #traverse(Visitproc, Object)} with
+ * a visitproc that just watches out for {@code ob}.
* Must return {@code false} if {@code ob} is {@code null}.
*/
public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException;
diff --git a/src/org/python/core/finalization/FinalizableBuiltin.java b/src/org/python/core/finalization/FinalizableBuiltin.java
--- a/src/org/python/core/finalization/FinalizableBuiltin.java
+++ b/src/org/python/core/finalization/FinalizableBuiltin.java
@@ -6,13 +6,16 @@
public interface FinalizableBuiltin {
/**
- * {@code __del_builtin__} is the built-in's own finalizer, while
- * {@code __del_derived__} refers to an instance's in-dict {@code __del__}.
- * A FinalizeTrigger calls {@code __del_derived__} first and
- * - if existent - {@code __del_builtin__} after that. A plain {@code __del__}
- * would behave as overwritten by {@code __del__Derived}, i.e. won't be called
- * if the type implements {@code FinalizablePyObjectDerived} while
- * {@code __del_builtin__} is called in any case.
+ * {@link #__del_builtin__()} is the built-in's own finalizer, while
+ * {@link FinalizablePyObjectDerived#__del_derived__()} refers to an
+ * instance's in-dict {@code __del__}.
+ * A FinalizeTrigger calls {@link FinalizablePyObjectDerived#__del_derived__()}
+ * first and - if existent - {@link #__del_builtin__()} after that. A plain
+ * {@link FinalizablePyObject#__del__()}
+ * would behave as overwritten by
+ * {@link FinalizablePyObjectDerived#__del_derived__()}, i.e. won't be called
+ * if the type implements {@link FinalizablePyObjectDerived}, while
+ * {@link #__del_builtin__()} is called in any case.
*/
public void __del_builtin__();
}
diff --git a/src/org/python/core/finalization/FinalizablePyObject.java b/src/org/python/core/finalization/FinalizablePyObject.java
--- a/src/org/python/core/finalization/FinalizablePyObject.java
+++ b/src/org/python/core/finalization/FinalizablePyObject.java
@@ -9,20 +9,22 @@
* {@link org.python.core.finalization.FinalizableBuiltin}.
*
*
- * The difference is that {@code __del__} can be overwritten by a
+ * The difference is that {@link #__del__()} can be overwritten by a
* new-style subclass's {@code __del__}-method on Python-side, while
- * {@code __del_builtin__} is always called. If a Python-side
- * finalizer exists, {@code __del_builtin__} will be called after the
+ * {@link FinalizableBuiltin#__del_builtin__()} is always called.
+ * If a Python-side finalizer exists,
+ * {@link FinalizableBuiltin#__del_builtin__()} will be called after the
* Python-side finalizer has been processed.
*
*
* One can even implement both interfaces.
- * If both interfaces are implemented, the {@code FinalizeTrigger} will
- * call {@code __del__} first and then {@code __del_builtin__}. If a
+ * If both interfaces are implemented, the {@link FinalizeTrigger} will
+ * call {@link #__del__()} first and then
+ * {@link FinalizableBuiltin#__del_builtin__()}. If a
* new-style subclass has an own, Python-side {@code __del__}-method, this
- * overwrites the Java-implemented {@code __del__}, but not
- * {@code __del_builtin__}, which will be called after the Python-side
- * finalizer.
+ * overwrites the Java-implemented {@link #__del__()}, but not
+ * {@link FinalizableBuiltin#__del_builtin__()}, which will be called after
+ * the Python-side finalizer.
*
*
* If you are writing a custom built-in that shall directly
@@ -32,38 +34,38 @@
*
*
*
- * Let your subclass implement {@code FinalizablePyObject}
- * (or {@link org.python.core.finalization.FinalizableBuiltin}).
+ * Let your subclass implement {@link FinalizablePyObject}
+ * (or {@link FinalizableBuiltin}).
*
*
* In every constructor call
* {@code FinalizeTrigger.ensureFinalizer(this);}
*
*
- * Write your {@code __del__}-method however you intend it.
- * (or {@code __del__Builtin} if
- * {@link org.python.core.finalization.FinalizableBuiltin} was used)
+ * Write your {@link #__del__()}-method however you intend it.
+ * (or {@link FinalizableBuiltin#__del_builtin__()} if
+ * {@link FinalizableBuiltin} was used)
*
*
* (optional)
* If your finalizer resurrects the object (Python allows this) and you wish the
* finalizer to run again on next collection of the object:
- * In the block where the resurrection occurs, let your {@code __del__}- or
- * {@code __del_builtin__}-method call
+ * In the block where the resurrection occurs, let your {@link #__del__()}- or
+ * {@link FinalizableBuiltin#__del_builtin__()}-method call
* {@code FinalizeTrigger.ensureFinalizer(this);}.
* If you implement {@code __del__} in Python and need this functionality, you can
* simply call {@code someObject.__ensure_finalizer__()}
- * Note that this is Jython specific and should be surrounded by a {@code try/except}
- * block to ensure compatibility with other Python implementations.
+ * Note that this is Jython-specific and should be surrounded by a
+ * {@code try/except}-block to ensure compatibility with other Python implementations.
*
*
*
*
* Note: Regarding to object resurrection, Jython currently behaves like CPython >= 3.4.
- * That means the finalizer {@code __del__} or {@code __del_builtin__} is called only the
- * first time an object gets gc'ed. If pre-3.4.-behavior is required for some reason (i.e.
- * have the finalizer called repeatedly on every collection after a resurrection), one can
- * achieve this manually via step 5).
+ * That means the finalizer {@link #__del__()} or {@link FinalizableBuiltin#__del_builtin__()}
+ * is called only the first time an object gets gc'ed. If pre-3.4.-behavior is required for
+ * some reason (i.e. have the finalizer called repeatedly on every collection after a
+ * resurrection), one can achieve this manually via step 5).
*
*
* The built-in function {@code __ensure_finalizer__} is also useful if a class acquires a
@@ -72,7 +74,7 @@
* CPython).
* However, one can manually tell earlier created instances to become finalizable by
* calling {@code __ensure_finalizer__()} on them. As mentioned above, it is recommended to
- * surround this with a {@code try/except} block to ensure compatibility with other Python
+ * surround this with a {@code try/except}-block to ensure compatibility with other Python
* implementations.
*
*
@@ -82,8 +84,8 @@
*
*
* It is possible to switch finalization on and off at any desired time for a certain object.
- * This can be helpful if it is only necessary to have {@code __del__} or
- * {@code __del_builtin__} called for certain configurations of an object.
+ * This can be helpful if it is only necessary to have {@link #__del__()} or
+ * {@link FinalizableBuiltin#__del_builtin__()} called for certain configurations of an object.
*
*
* To turn off the finalizer, call
@@ -91,6 +93,9 @@
* To turn it on again, call
* {@code ((FinalizeTrigger) JyAttribute.getAttr(this, JyAttribute.FINALIZE_TRIGGER_ATTR)).trigger(this);}
*
+ *
+ * @see org.python.core.JyAttribute#FINALIZE_TRIGGER_ATTR
+ * @see FinalizableBuiltin#__del_builtin__()
*/
public interface FinalizablePyObject {
diff --git a/src/org/python/modules/PyStructDerived.java b/src/org/python/modules/PyStructDerived.java
--- a/src/org/python/modules/PyStructDerived.java
+++ b/src/org/python/modules/PyStructDerived.java
@@ -34,12 +34,15 @@
public int traverseDerived(Visitproc visit,Object arg) {
int retVal;
for(int i=0;i
@@ -960,7 +959,6 @@
* {@code gc.notifyPreFinalization()} before anything else is done and
* {@code gc.notifyPostFinalization()} right before the finalization method returns.
* Between these calls the finalizer must not terminate by throwing an exception.
- * (Note: Using this for extern finalizers is currently experimental and needs more testing.)
*
*
* We recommend to use this feature in a way such that false-positive runs are
@@ -1039,8 +1037,7 @@
* to care for other finalizers these must call
* {@code gc.notifyPreFinalization()} before anything else is done and
* {@code gc.notifyPostFinalization()} afterwards; between these calls the finalizer
- * must not terminate by throwing an exception). (Note: Using this for extern
- * finalizers is currently experimental and needs more testing.)
+ * must not terminate by throwing an exception).
* This works independently from monitoring (which is mainly needed to allow
* garbage counting in {@link #collect()}).
*
@@ -1061,7 +1058,6 @@
* {@code gc.notifyPreFinalization()} before anything else is done and
* {@code gc.notifyPostFinalization()} right before the finalization method returns.
* Between these calls the finalizer must not terminate by throwing an exception.
- * (Note: Using this for extern finalizers is currently experimental and needs more testing.)
*
*
* If it runs too early, we can at least guarantee that it will run again after
@@ -1532,7 +1528,7 @@
* Not supported by Jython.
* Throws {@link org.python.core.Py#NotImplementedError}.
*
- * @throws org.python.core.Py#NotImplementedError
+ * @throws org.python.core.Py.NotImplementedError
*/
public static void disable() {
throw Py.NotImplementedError("can't disable Java GC");
@@ -2000,7 +1996,7 @@
* Not supported by Jython.
* Throws {@link org.python.core.Py#NotImplementedError}.
*
- * @throws org.python.core.Py#NotImplementedError
+ * @throws org.python.core.Py.NotImplementedError
*/
public static PyObject get_count() {
throw Py.NotImplementedError("not applicable to Java GC");
@@ -2047,7 +2043,7 @@
* Not supported by Jython.
* Throws {@link org.python.core.Py#NotImplementedError}.
*
- * @throws org.python.core.Py#NotImplementedError
+ * @throws org.python.core.Py.NotImplementedError
*/
public static void set_threshold(PyObject[] args, String[] kwargs) {
throw Py.NotImplementedError("not applicable to Java GC");
@@ -2057,7 +2053,7 @@
* Not supported by Jython.
* Throws {@link org.python.core.Py#NotImplementedError}.
*
- * @throws org.python.core.Py#NotImplementedError
+ * @throws org.python.core.Py.NotImplementedError
*/
public static PyObject get_threshold() {
throw Py.NotImplementedError("not applicable to Java GC");
@@ -2067,7 +2063,7 @@
* Not supported by Jython.
* Throws {@link org.python.core.Py#NotImplementedError}.
*
- * @throws org.python.core.Py#NotImplementedError
+ * @throws org.python.core.Py.NotImplementedError
*/
public static PyObject get_objects() {
throw Py.NotImplementedError("not applicable to Java GC");
diff --git a/src/org/python/modules/itertools/PyTeeIteratorDerived.java b/src/org/python/modules/itertools/PyTeeIteratorDerived.java
--- a/src/org/python/modules/itertools/PyTeeIteratorDerived.java
+++ b/src/org/python/modules/itertools/PyTeeIteratorDerived.java
@@ -34,12 +34,15 @@
public int traverseDerived(Visitproc visit,Object arg) {
int retVal;
for(int i=0;i