From: <pj...@us...> - 2009-04-08 01:27:08
|
Revision: 6189 http://jython.svn.sourceforge.net/jython/?rev=6189&view=rev Author: pjenvey Date: 2009-04-08 01:27:07 +0000 (Wed, 08 Apr 2009) Log Message: ----------- relax the Windows platform matching, don't bother special casing ce fixes #1162 Modified Paths: -------------- trunk/jython/Lib/os.py trunk/jython/Lib/subprocess.py Modified: trunk/jython/Lib/os.py =================================================================== --- trunk/jython/Lib/os.py 2009-04-08 01:23:13 UTC (rev 6188) +++ trunk/jython/Lib/os.py 2009-04-08 01:27:07 UTC (rev 6189) @@ -57,17 +57,9 @@ # Mapping of: os._name: [name list, shell command list] _os_map = dict(nt=[ - ['Windows 95', 'Windows 98', 'Windows ME', 'Windows NT', - 'Windows NT 4.0', 'WindowsNT', 'Windows 2000', 'Windows 2003', - 'Windows XP', 'Windows Vista'], + ['Windows'], [['cmd.exe', '/c'], ['command.com', '/c']] ], - - ce=[ - ['Windows CE'], - [['cmd.exe', '/c']] - ], - posix=[ [], # posix is a fallback, instead of matching names [['/bin/sh', '-c']] @@ -133,7 +125,7 @@ _posix = POSIXFactory.getPOSIX(PythonPOSIXHandler(), True) _native_posix = not isinstance(_posix, JavaPOSIX) -if _name in ('nt', 'ce'): +if _name == 'nt': import ntpath as path else: import posixpath as path Modified: trunk/jython/Lib/subprocess.py =================================================================== --- trunk/jython/Lib/subprocess.py 2009-04-08 01:23:13 UTC (rev 6188) +++ trunk/jython/Lib/subprocess.py 2009-04-08 01:27:07 UTC (rev 6189) @@ -545,7 +545,7 @@ if jython: # Escape the command line arguments with list2cmdline on Windows - escape_args_oses = ['nt', 'ce'] + escape_args_oses = ['nt'] escape_args = None shell_command = None This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-05 02:14:42
|
Revision: 6295 http://jython.svn.sourceforge.net/jython/?rev=6295&view=rev Author: pjenvey Date: 2009-05-05 01:31:30 +0000 (Tue, 05 May 2009) Log Message: ----------- small cleanup Modified Paths: -------------- trunk/jython/Lib/subprocess.py trunk/jython/Lib/test/test_subprocess_jy.py Modified: trunk/jython/Lib/subprocess.py =================================================================== --- trunk/jython/Lib/subprocess.py 2009-05-04 12:09:44 UTC (rev 6294) +++ trunk/jython/Lib/subprocess.py 2009-05-05 01:31:30 UTC (rev 6295) @@ -1107,8 +1107,8 @@ pass elif stderr == PIPE: errread = PIPE - elif stderr == STDOUT or \ - isinstance(stderr, org.python.core.io.RawIOBase): + elif (stderr == STDOUT or + isinstance(stderr, org.python.core.io.RawIOBase)): errwrite = stderr else: # Assuming file-like object @@ -1123,8 +1123,8 @@ """Determine if the subprocess' stderr should be redirected to stdout """ - return errwrite == STDOUT or c2pwrite not in (None, PIPE) and \ - c2pwrite is errwrite + return (errwrite == STDOUT or c2pwrite not in (None, PIPE) and + c2pwrite is errwrite) def _coupler_thread(self, *args, **kwargs): @@ -1164,9 +1164,9 @@ raise OSError(iae.getMessage() or iae) if env is None: - # This is for compatibility with the CPython implementation, - # that ends up calling os.execvp(). So os.environ is "inherited" - # there if env is not explicitly set. + # For compatibility with CPython which calls + # os.execvp(). os.environ is "inherited" there if env is + # not explicitly set env = os.environ builder_env = builder.environment() @@ -1563,9 +1563,9 @@ # print "Running a jython subprocess to return the number of processors..." p = Popen([sys.executable, "-c", - 'import sys;' \ - 'from java.lang import Runtime;' \ - 'sys.exit(Runtime.getRuntime().availableProcessors())']) + ('import sys;' + 'from java.lang import Runtime;' + 'sys.exit(Runtime.getRuntime().availableProcessors())')]) print p.wait() # @@ -1573,15 +1573,15 @@ # print "Connecting two jython subprocesses..." p1 = Popen([sys.executable, "-c", - 'import os;' \ - 'print os.environ["foo"]'], env=dict(foo='bar'), + ('import os;' + 'print os.environ["foo"]')], env=dict(foo='bar'), stdout=PIPE) p2 = Popen([sys.executable, "-c", - 'import os, sys;' \ - 'their_foo = sys.stdin.read().strip();' \ - 'my_foo = os.environ["foo"];' \ - 'msg = "Their env\'s foo: %r, My env\'s foo: %r";' \ - 'print msg % (their_foo, my_foo)'], + ('import os, sys;' + 'their_foo = sys.stdin.read().strip();' + 'my_foo = os.environ["foo"];' + 'msg = "Their env\'s foo: %r, My env\'s foo: %r";' + 'print msg % (their_foo, my_foo)')], env=dict(foo='baz'), stdin=p1.stdout, stdout=PIPE) print p2.communicate()[0] Modified: trunk/jython/Lib/test/test_subprocess_jy.py =================================================================== --- trunk/jython/Lib/test/test_subprocess_jy.py 2009-05-04 12:09:44 UTC (rev 6294) +++ trunk/jython/Lib/test/test_subprocess_jy.py 2009-05-05 01:31:30 UTC (rev 6295) @@ -1,4 +1,4 @@ -"Tests for cmp() compatibility with CPython" +"""Misc subprocess tests""" import unittest import os import sys @@ -6,6 +6,7 @@ from subprocess import Popen, PIPE class EnvironmentInheritanceTest(unittest.TestCase): + def testDefaultEnvIsInherited(self): # Test for issue #1104 os.environ['foo'] = 'something' @@ -15,8 +16,12 @@ self.assertEquals('something', p1.stdout.read()) -# tests for (some parts of) issue #1187: JYTHON_OPTS should not be enriched by arguments class JythonOptsTest(unittest.TestCase): + + """ Tests for (some parts of) issue #1187: JYTHON_OPTS should not be + enriched by arguments + """ + def testNoJythonOpts(self): os.environ['JYTHON_OPTS'] = '' p1 = Popen([sys.executable, "-c", @@ -32,12 +37,11 @@ stdout=PIPE) self.assertEquals(options, p1.stdout.read()) + def test_main(): - test_classes = ( - EnvironmentInheritanceTest, - JythonOptsTest, - ) - test_support.run_unittest(*test_classes) + test_support.run_unittest(EnvironmentInheritanceTest, + JythonOptsTest) + if __name__ == '__main__': test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-13 02:49:13
|
Revision: 6345 http://jython.svn.sourceforge.net/jython/?rev=6345&view=rev Author: pjenvey Date: 2009-05-13 02:48:53 +0000 (Wed, 13 May 2009) Log Message: ----------- from: http://svn.python.org/projects/python/branches/release25-maint/Lib/tempfile.py@60728 http://svn.python.org/projects/python/branches/release25-maint/Lib/test/test_tempfile.py@60728 Modified Paths: -------------- trunk/jython/Lib/tempfile.py trunk/jython/Lib/test/test_tempfile.py Modified: trunk/jython/Lib/tempfile.py =================================================================== --- trunk/jython/Lib/tempfile.py 2009-05-12 01:51:08 UTC (rev 6344) +++ trunk/jython/Lib/tempfile.py 2009-05-13 02:48:53 UTC (rev 6345) @@ -377,30 +377,32 @@ self.name = name self.close_called = False - # XXX: CPython assigns unlink as a class var but this would - # rebind Jython's os.unlink (to be a classmethod) because it's - # not a built-in function (unfortunately built-in functions act - # differently when binding: - # http://mail.python.org/pipermail/python-dev/2003-April/034749.html) - - # Cache the unlinker so we don't get spurious errors at - # shutdown when the module-level "os" is None'd out. Note - # that this must be referenced as self.unlink, because the - # name TemporaryFileWrapper may also get None'd out before - # __del__ is called. - self.unlink = _os.unlink - def __getattr__(self, name): + # Attribute lookups are delegated to the underlying file + # and cached for non-numeric results + # (i.e. methods are cached, closed and friends are not) file = self.__dict__['file'] a = getattr(file, name) if type(a) != type(0): setattr(self, name, a) return a + # The underlying __enter__ method returns the wrong object + # (self.file) so override it to return the wrapper + def __enter__(self): + self.file.__enter__() + return self + # NT provides delete-on-close as a primitive, so we don't need # the wrapper to do anything special. We still use it so that # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile. if _os.name != 'nt': + # Cache the unlinker so we don't get spurious errors at + # shutdown when the module-level "os" is None'd out. Note + # that this must be referenced as self.unlink, because the + # name TemporaryFileWrapper may also get None'd out before + # __del__ is called. + unlink = _os.unlink def close(self): if not self.close_called: @@ -411,6 +413,14 @@ def __del__(self): self.close() + # Need to trap __exit__ as well to ensure the file gets + # deleted when used in a with statement + def __exit__(self, exc, value, tb): + result = self.file.__exit__(exc, value, tb) + self.close() + return result + + def NamedTemporaryFile(mode='w+b', bufsize=-1, suffix="", prefix=template, dir=None): """Create and return a temporary file. Modified: trunk/jython/Lib/test/test_tempfile.py =================================================================== --- trunk/jython/Lib/test/test_tempfile.py 2009-05-12 01:51:08 UTC (rev 6344) +++ trunk/jython/Lib/test/test_tempfile.py 2009-05-13 02:48:53 UTC (rev 6345) @@ -1,6 +1,5 @@ -# From Python 2.5.1 # tempfile.py unit tests. - +from __future__ import with_statement import tempfile import os import sys @@ -208,32 +207,21 @@ class mkstemped: _bflags = tempfile._bin_openflags _tflags = tempfile._text_openflags + _close = os.close + _unlink = os.unlink def __init__(self, dir, pre, suf, bin): if bin: flags = self._bflags else: flags = self._tflags - # XXX: CPython assigns _close/_unlink as class vars but this - # would rebind Jython's close/unlink (to be classmethods) - # because they're not built-in functions (unfortunately - # built-in functions act differently when binding: - # http://mail.python.org/pipermail/python-dev/2003-April/034749.html) - self._close = os.close - self._unlink = os.unlink (self.fd, self.name) = tempfile._mkstemp_inner(dir, pre, suf, flags) def write(self, str): os.write(self.fd, str) - # XXX: self.test_choose_directory expects the file to have been deleted - # (via __del__) by the time it's called, which is CPython specific - # garbage collection behavior. We need to delete it now in Jython - self._close(self.fd) - self._unlink(self.name) def __del__(self): self._close(self.fd) - if os.path.exists(self.name): - self._unlink(self.name) + self._unlink(self.name) def do_create(self, dir=None, pre="", suf="", bin=1): if dir is None: @@ -259,10 +247,6 @@ extant = range(TEST_FILES) for i in extant: extant[i] = self.do_create(pre="aa") - # XXX: Ensure mkstemped files are deleted (can't rely on Java's - # GC) - for i in extant: - i.__del__() def test_choose_directory(self): # _mkstemp_inner can create files in a user-selected directory @@ -272,8 +256,7 @@ finally: os.rmdir(dir) - # XXX: Jython can't set the write mode yet - def _test_file_mode(self): + def test_file_mode(self): # _mkstemp_inner creates files with the proper mode if not has_stat: return # ugh, can't use TestSkipped. @@ -315,7 +298,7 @@ # On Windows a spawn* /path/ with embedded spaces shouldn't be quoted, # but an arg with embedded spaces should be decorated with double # quotes on each end - if sys.platform in ('win32'): + if sys.platform in ('win32',): decorated = '"%s"' % sys.executable tester = '"%s"' % tester else: @@ -494,9 +477,6 @@ # mkdtemp creates directories with the proper mode if not has_stat: return # ugh, can't use TestSkipped. - if os.name == 'java': - # Java doesn't support stating files for permissions - return dir = self.do_create() try: @@ -529,25 +509,18 @@ self.dir = None class mktemped: + _unlink = os.unlink _bflags = tempfile._bin_openflags def __init__(self, dir, pre, suf): - # XXX: Assign _unlink here, instead of as a class var. See - # mkstemped.__init__ for an explanation - self._unlink = os.unlink - self.name = tempfile.mktemp(dir=dir, prefix=pre, suffix=suf) # Create the file. This will raise an exception if it's # mysteriously appeared in the meanwhile. os.close(os.open(self.name, self._bflags, 0600)) - # XXX: test_mktemp.tearDown expects the file to have been deleted - # (via __del__) by the time it's called, which is CPython specific - # garbage collection behavior. We need to delete it now in Jython + + def __del__(self): self._unlink(self.name) - #def __del__(self): - # self._unlink(self.name) - def do_create(self, pre="", suf=""): try: file = self.mktemped(self.dir, pre, suf) @@ -628,7 +601,6 @@ def test_multiple_close(self): # A NamedTemporaryFile can be closed many times without error - f = tempfile.NamedTemporaryFile() f.write('abc\n') f.close() @@ -638,6 +610,16 @@ except: self.failOnException("close") + def test_context_manager(self): + # A NamedTemporaryFile can be used as a context manager + with tempfile.NamedTemporaryFile() as f: + self.failUnless(os.path.exists(f.name)) + self.failIf(os.path.exists(f.name)) + def use_closed(): + with f: + pass + self.failUnlessRaises(ValueError, use_closed) + # How to test the mode and bufsize parameters? test_classes.append(test_NamedTemporaryFile) @@ -693,7 +675,3 @@ if __name__ == "__main__": test_main() - # XXX: Nudge Java's GC in an attempt to trigger any temp file's - # __del__ (cause them to be deleted) that hasn't been called - from java.lang import System - System.gc() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-13 02:50:54
|
Revision: 6346 http://jython.svn.sourceforge.net/jython/?rev=6346&view=rev Author: pjenvey Date: 2009-05-13 02:50:33 +0000 (Wed, 13 May 2009) Log Message: ----------- apply tempfile workarounds from r3712. with r6345 makes _TemporaryFileWrapper a context manager Modified Paths: -------------- trunk/jython/Lib/tempfile.py trunk/jython/Lib/test/test_tempfile.py Modified: trunk/jython/Lib/tempfile.py =================================================================== --- trunk/jython/Lib/tempfile.py 2009-05-13 02:48:53 UTC (rev 6345) +++ trunk/jython/Lib/tempfile.py 2009-05-13 02:50:33 UTC (rev 6346) @@ -377,6 +377,19 @@ self.name = name self.close_called = False + # XXX: CPython assigns unlink as a class var but this would + # rebind Jython's os.unlink (to be a classmethod) because it's + # not a built-in function (unfortunately built-in functions act + # differently when binding: + # http://mail.python.org/pipermail/python-dev/2003-April/034749.html) + + # Cache the unlinker so we don't get spurious errors at + # shutdown when the module-level "os" is None'd out. Note + # that this must be referenced as self.unlink, because the + # name TemporaryFileWrapper may also get None'd out before + # __del__ is called. + self.unlink = _os.unlink + def __getattr__(self, name): # Attribute lookups are delegated to the underlying file # and cached for non-numeric results @@ -397,13 +410,6 @@ # the wrapper to do anything special. We still use it so that # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile. if _os.name != 'nt': - # Cache the unlinker so we don't get spurious errors at - # shutdown when the module-level "os" is None'd out. Note - # that this must be referenced as self.unlink, because the - # name TemporaryFileWrapper may also get None'd out before - # __del__ is called. - unlink = _os.unlink - def close(self): if not self.close_called: self.close_called = True Modified: trunk/jython/Lib/test/test_tempfile.py =================================================================== --- trunk/jython/Lib/test/test_tempfile.py 2009-05-13 02:48:53 UTC (rev 6345) +++ trunk/jython/Lib/test/test_tempfile.py 2009-05-13 02:50:33 UTC (rev 6346) @@ -1,3 +1,4 @@ +# From Python 2.5.1 # tempfile.py unit tests. from __future__ import with_statement import tempfile @@ -207,21 +208,32 @@ class mkstemped: _bflags = tempfile._bin_openflags _tflags = tempfile._text_openflags - _close = os.close - _unlink = os.unlink def __init__(self, dir, pre, suf, bin): if bin: flags = self._bflags else: flags = self._tflags + # XXX: CPython assigns _close/_unlink as class vars but this + # would rebind Jython's close/unlink (to be classmethods) + # because they're not built-in functions (unfortunately + # built-in functions act differently when binding: + # http://mail.python.org/pipermail/python-dev/2003-April/034749.html) + self._close = os.close + self._unlink = os.unlink (self.fd, self.name) = tempfile._mkstemp_inner(dir, pre, suf, flags) def write(self, str): os.write(self.fd, str) + # XXX: self.test_choose_directory expects the file to have been deleted + # (via __del__) by the time it's called, which is CPython specific + # garbage collection behavior. We need to delete it now in Jython + self._close(self.fd) + self._unlink(self.name) def __del__(self): self._close(self.fd) - self._unlink(self.name) + if os.path.exists(self.name): + self._unlink(self.name) def do_create(self, dir=None, pre="", suf="", bin=1): if dir is None: @@ -247,6 +259,10 @@ extant = range(TEST_FILES) for i in extant: extant[i] = self.do_create(pre="aa") + # XXX: Ensure mkstemped files are deleted (can't rely on Java's + # GC) + for i in extant: + i.__del__() def test_choose_directory(self): # _mkstemp_inner can create files in a user-selected directory @@ -256,7 +272,8 @@ finally: os.rmdir(dir) - def test_file_mode(self): + # XXX: Jython can't set the write mode yet + def _test_file_mode(self): # _mkstemp_inner creates files with the proper mode if not has_stat: return # ugh, can't use TestSkipped. @@ -477,6 +494,9 @@ # mkdtemp creates directories with the proper mode if not has_stat: return # ugh, can't use TestSkipped. + if os.name == 'java': + # Java doesn't support stating files for permissions + return dir = self.do_create() try: @@ -509,18 +529,25 @@ self.dir = None class mktemped: - _unlink = os.unlink _bflags = tempfile._bin_openflags def __init__(self, dir, pre, suf): + # XXX: Assign _unlink here, instead of as a class var. See + # mkstemped.__init__ for an explanation + self._unlink = os.unlink + self.name = tempfile.mktemp(dir=dir, prefix=pre, suffix=suf) # Create the file. This will raise an exception if it's # mysteriously appeared in the meanwhile. os.close(os.open(self.name, self._bflags, 0600)) - - def __del__(self): + # XXX: test_mktemp.tearDown expects the file to have been deleted + # (via __del__) by the time it's called, which is CPython specific + # garbage collection behavior. We need to delete it now in Jython self._unlink(self.name) + #def __del__(self): + # self._unlink(self.name) + def do_create(self, pre="", suf=""): try: file = self.mktemped(self.dir, pre, suf) @@ -675,3 +702,7 @@ if __name__ == "__main__": test_main() + # XXX: Nudge Java's GC in an attempt to trigger any temp file's + # __del__ (cause them to be deleted) that hasn't been called + from java.lang import System + System.gc() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-13 03:00:05
|
Revision: 6347 http://jython.svn.sourceforge.net/jython/?rev=6347&view=rev Author: pjenvey Date: 2009-05-13 02:59:46 +0000 (Wed, 13 May 2009) Log Message: ----------- strify the thread name fixes #1344 Modified Paths: -------------- trunk/jython/Lib/threading.py Added Paths: ----------- trunk/jython/Lib/test/test_threading_jy.py Added: trunk/jython/Lib/test/test_threading_jy.py =================================================================== --- trunk/jython/Lib/test/test_threading_jy.py (rev 0) +++ trunk/jython/Lib/test/test_threading_jy.py 2009-05-13 02:59:46 UTC (rev 6347) @@ -0,0 +1,23 @@ +"""Misc threading module tests + +Made for Jython. +""" +import unittest +from test import test_support +from threading import Thread + +class ThreadingTestCase(unittest.TestCase): + + def test_str_name(self): + t = Thread(name=1) + self.assertEqual(t.getName(), '1') + t.setName(2) + self.assertEqual(t.getName(), '2') + + +def test_main(): + test_support.run_unittest(ThreadingTestCase) + + +if __name__ == "__main__": + test_main() Property changes on: trunk/jython/Lib/test/test_threading_jy.py ___________________________________________________________________ Added: svn:keywords + Id Modified: trunk/jython/Lib/threading.py =================================================================== --- trunk/jython/Lib/threading.py 2009-05-13 02:50:33 UTC (rev 6346) +++ trunk/jython/Lib/threading.py 2009-05-13 02:59:46 UTC (rev 6347) @@ -200,7 +200,7 @@ return self._thread.getName() def setName(self, name): - self._thread.setName(name) + self._thread.setName(str(name)) def isAlive(self): return self._thread.isAlive() @@ -228,7 +228,7 @@ self._args = args self._kwargs = kwargs if name: - self._thread.setName(name) + self._thread.setName(str(name)) def _create_thread(self): return _newFunctionThread(self.__bootstrap, ()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <otm...@us...> - 2009-05-20 05:45:11
|
Revision: 6358 http://jython.svn.sourceforge.net/jython/?rev=6358&view=rev Author: otmarhumbel Date: 2009-05-20 05:44:58 +0000 (Wed, 20 May 2009) Log Message: ----------- on termination, delete the thread from the global _threads list fixes issue #1348 Modified Paths: -------------- trunk/jython/Lib/test/test_threading_jy.py trunk/jython/Lib/threading.py Modified: trunk/jython/Lib/test/test_threading_jy.py =================================================================== --- trunk/jython/Lib/test/test_threading_jy.py 2009-05-18 21:52:55 UTC (rev 6357) +++ trunk/jython/Lib/test/test_threading_jy.py 2009-05-20 05:44:58 UTC (rev 6358) @@ -4,6 +4,9 @@ """ import unittest from test import test_support +import threading +import time +import random from threading import Thread class ThreadingTestCase(unittest.TestCase): @@ -13,8 +16,26 @@ self.assertEqual(t.getName(), '1') t.setName(2) self.assertEqual(t.getName(), '2') + + # make sure activeCount() gets decremented (see issue 1348) + def test_activeCount(self): + activeBefore = threading.activeCount() + activeCount = 10 + for i in range(activeCount): + t = Thread(target=self._sleep, args=(i,)) + t.setDaemon(0) + t.start() + polls = activeCount + while activeCount > activeBefore and polls > 0: + time.sleep(1) + activeCount = threading.activeCount() + polls -= 1 + self.assertTrue(activeCount <= activeBefore, 'activeCount should to be <= %s, instead of %s' % (activeBefore, activeCount)) + def _sleep(self, n): + time.sleep(random.random()) + def test_main(): test_support.run_unittest(ThreadingTestCase) Modified: trunk/jython/Lib/threading.py =================================================================== --- trunk/jython/Lib/threading.py 2009-05-18 21:52:55 UTC (rev 6357) +++ trunk/jython/Lib/threading.py 2009-05-20 05:44:58 UTC (rev 6358) @@ -294,7 +294,7 @@ pass def __delete(self): - pass + del _threads[self._thread.getId()] class _MainThread(Thread): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-26 00:03:42
|
Revision: 6376 http://jython.svn.sourceforge.net/jython/?rev=6376&view=rev Author: pjenvey Date: 2009-05-26 00:03:26 +0000 (Tue, 26 May 2009) Log Message: ----------- fix quoted/escaped non-sequence str cmdlines on Windows by first parsing them into an escaped argv. painful but necessary as Java takes a List<String> cmdline. cases like these are ruined otherwise as Java rejoins it with a list2cmdline-like routine for passing to Windows' CreateProcess Modified Paths: -------------- trunk/jython/Lib/subprocess.py trunk/jython/Lib/test/test_subprocess_jy.py Modified: trunk/jython/Lib/subprocess.py =================================================================== --- trunk/jython/Lib/subprocess.py 2009-05-25 06:07:22 UTC (rev 6375) +++ trunk/jython/Lib/subprocess.py 2009-05-26 00:03:26 UTC (rev 6376) @@ -544,21 +544,80 @@ if jython: - # Escape the command line arguments with list2cmdline on Windows - _escape_args_oses = ['nt'] + # Parse command line arguments for Windows + _win_oses = ['nt'] + _cmdline2list = None _escape_args = None _shell_command = None + def cmdline2list(cmdline): + """Build an argv list from a Microsoft shell style cmdline str + + The reverse of list2cmdline that follows the same MS C runtime + rules. + + Java's ProcessBuilder takes a List<String> cmdline that's joined + with a list2cmdline-like routine for Windows CreateProcess + (which takes a String cmdline). This process ruins String + cmdlines from the user with escapes or quotes. To avoid this we + first parse these cmdlines into an argv. + + Runtime.exec(String) is too naive and useless for this case. + """ + whitespace = ' \t' + # count of preceding '\' + bs_count = 0 + in_quotes = False + arg = [] + argv = [] + + for ch in cmdline: + if ch in whitespace and not in_quotes: + if arg: + # finalize arg and reset + argv.append(''.join(arg)) + arg = [] + bs_count = 0 + elif ch == '\\': + arg.append(ch) + bs_count += 1 + elif ch == '"': + if not bs_count % 2: + # Even number of '\' followed by a '"'. Place one + # '\' for every pair and treat '"' as a delimiter + if bs_count: + del arg[-(bs_count / 2):] + in_quotes = not in_quotes + else: + # Odd number of '\' followed by a '"'. Place one '\' + # for every pair and treat '"' as an escape sequence + # by the remaining '\' + del arg[-(bs_count / 2 + 1):] + arg.append(ch) + bs_count = 0 + else: + # regular char + arg.append(ch) + bs_count = 0 + + # A single trailing '"' delimiter yields an empty arg + if arg or in_quotes: + argv.append(''.join(arg)) + + return argv + def _setup_platform(): """Setup the shell command and the command line argument escape function depending on the underlying platform """ - global _escape_args, _shell_command + global _cmdline2list, _escape_args, _shell_command - if os._name in _escape_args_oses: + if os._name in _win_oses: + _cmdline2list = cmdline2list _escape_args = lambda args: [list2cmdline([arg]) for arg in args] else: + _cmdline2list = lambda args: [args] _escape_args = lambda args: args os_info = os._os_map.get(os._name) @@ -1167,7 +1226,7 @@ """Execute program (Java version)""" if isinstance(args, types.StringTypes): - args = [args] + args = _cmdline2list(args) else: args = list(args) # NOTE: CPython posix (execv) will str() any unicode @@ -1175,7 +1234,7 @@ # posix. Windows passes unicode through, however if any(not isinstance(arg, (str, unicode)) for arg in args): raise TypeError('args must contain only strings') - args = _escape_args(args) + args = _escape_args(args) if shell: args = _shell_command + args Modified: trunk/jython/Lib/test/test_subprocess_jy.py =================================================================== --- trunk/jython/Lib/test/test_subprocess_jy.py 2009-05-25 06:07:22 UTC (rev 6375) +++ trunk/jython/Lib/test/test_subprocess_jy.py 2009-05-26 00:03:26 UTC (rev 6376) @@ -3,7 +3,7 @@ import os import sys from test import test_support -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen, cmdline2list class EnvironmentInheritanceTest(unittest.TestCase): @@ -38,9 +38,41 @@ self.assertEquals(options, p1.stdout.read()) +class Cmdline2ListTestCase(unittest.TestCase): + + cmdlines = { + # From "Parsing C Command-Line Arguments" + # http://msdn.microsoft.com/en-us/library/a1y7w461(VS.80).aspx + '"a b c" d e': ['a b c', 'd', 'e'], + r'"ab\"c" "\\" d': ['ab"c', '\\', 'd'], + r'a\\\b d"e f"g h': [r'a\\\b', 'de fg', 'h'], + r'a\\\"b c d': [r'a\"b', 'c', 'd'], + r'a\\\\"b c" d e': [r'a\\b c', 'd', 'e'], + + r'C:\\foo\bar\baz jy thon': [r'C:\\foo\bar\baz', 'jy', 'thon'], + r'C:\\Program Files\Foo\Bar qu \\ ux': + [r'C:\\Program', 'Files\Foo\Bar', 'qu', '\\\\', 'ux'], + r'"C:\\Program Files\Foo\Bar" qu \\ ux': + [r'C:\\Program Files\Foo\Bar', 'qu', '\\\\', 'ux'], + r'dir "C:\\Program Files\Foo\\" bar': + ['dir', 'C:\\\\Program Files\\Foo\\', 'bar'], + + r'echo "\"I hate Windows!\""': ['echo', '"I hate Windows!"'], + r'print "jython" "': ['print', 'jython', ''], + r'print \"jython\" \"': ['print', '"jython"', '"'], + r'print \"jython\" \\"': ['print', '"jython"', '\\'] + } + + def test_cmdline2list(self): + for cmdline, argv in self.cmdlines.iteritems(): + self.assertEqual(cmdline2list(cmdline), argv) + + def test_main(): - test_support.run_unittest(EnvironmentInheritanceTest, - JythonOptsTest) + test_support.run_unittest( + EnvironmentInheritanceTest, + JythonOptsTest, + Cmdline2ListTestCase) if __name__ == '__main__': This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-28 02:58:25
|
Revision: 6409 http://jython.svn.sourceforge.net/jython/?rev=6409&view=rev Author: pjenvey Date: 2009-05-28 02:58:09 +0000 (Thu, 28 May 2009) Log Message: ----------- plug more leaky file handles in tarfile and test_pkgimport Modified Paths: -------------- trunk/jython/Lib/tarfile.py trunk/jython/Lib/test/test_pkgimport.py trunk/jython/Lib/test/test_tarfile.py Modified: trunk/jython/Lib/tarfile.py =================================================================== --- trunk/jython/Lib/tarfile.py 2009-05-28 01:46:11 UTC (rev 6408) +++ trunk/jython/Lib/tarfile.py 2009-05-28 02:58:09 UTC (rev 6409) @@ -1210,6 +1210,7 @@ try: t = cls.taropen(name, mode, fileobj) except IOError: + fileobj.close() raise ReadError("not a gzip file") t._extfileobj = False return t @@ -1235,6 +1236,7 @@ try: t = cls.taropen(name, mode, fileobj) except IOError: + fileobj.close() raise ReadError("not a bzip2 file") t._extfileobj = False return t Modified: trunk/jython/Lib/test/test_pkgimport.py =================================================================== --- trunk/jython/Lib/test/test_pkgimport.py 2009-05-28 01:46:11 UTC (rev 6408) +++ trunk/jython/Lib/test/test_pkgimport.py 2009-05-28 02:58:09 UTC (rev 6409) @@ -22,7 +22,8 @@ self.package_dir = os.path.join(self.test_dir, self.package_name) os.mkdir(self.package_dir) - open(os.path.join(self.package_dir, '__init__'+os.extsep+'py'), 'w') + open(os.path.join(self.package_dir, '__init__'+os.extsep+'py'), + 'w').close() self.module_path = os.path.join(self.package_dir, 'foo'+os.extsep+'py') def tearDown(self): Modified: trunk/jython/Lib/test/test_tarfile.py =================================================================== --- trunk/jython/Lib/test/test_tarfile.py 2009-05-28 01:46:11 UTC (rev 6408) +++ trunk/jython/Lib/test/test_tarfile.py 2009-05-28 02:58:09 UTC (rev 6409) @@ -727,6 +727,7 @@ def test_no_name_argument(self): fobj = open(testtar, "rb") + self.tar.close() self.tar = tarfile.open(fileobj=fobj, mode="r") self.assertEqual(self.tar.name, os.path.abspath(fobj.name)) fobj.close() @@ -737,6 +738,7 @@ fp.close() fobj = StringIO.StringIO(data) self.assertRaises(AttributeError, getattr, fobj, "name") + self.tar.close() self.tar = tarfile.open(fileobj=fobj, mode="r") self.assertEqual(self.tar.name, None) @@ -746,6 +748,7 @@ fp.close() fobj = StringIO.StringIO(data) fobj.name = "" + self.tar.close() self.tar = tarfile.open(fileobj=fobj, mode="r") self.assertEqual(self.tar.name, None) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-28 04:02:33
|
Revision: 6410 http://jython.svn.sourceforge.net/jython/?rev=6410&view=rev Author: pjenvey Date: 2009-05-28 04:02:18 +0000 (Thu, 28 May 2009) Log Message: ----------- from: http://svn.python.org/projects/python/trunk/Lib/netrc.py@72972 http://svn.python.org/projects/python/branches/release25-maint/Lib/test/test_urllib2.py@62464 Added Paths: ----------- trunk/jython/Lib/netrc.py trunk/jython/Lib/test/test_urllib2.py Added: trunk/jython/Lib/netrc.py =================================================================== --- trunk/jython/Lib/netrc.py (rev 0) +++ trunk/jython/Lib/netrc.py 2009-05-28 04:02:18 UTC (rev 6410) @@ -0,0 +1,114 @@ +"""An object-oriented interface to .netrc files.""" + +# Module and documentation by Eric S. Raymond, 21 Dec 1998 + +import os, shlex + +__all__ = ["netrc", "NetrcParseError"] + + +class NetrcParseError(Exception): + """Exception raised on syntax errors in the .netrc file.""" + def __init__(self, msg, filename=None, lineno=None): + self.filename = filename + self.lineno = lineno + self.msg = msg + Exception.__init__(self, msg) + + def __str__(self): + return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno) + + +class netrc: + def __init__(self, file=None): + if file is None: + try: + file = os.path.join(os.environ['HOME'], ".netrc") + except KeyError: + raise IOError("Could not find .netrc: $HOME is not set") + self.hosts = {} + self.macros = {} + with open(file) as fp: + self._parse(file, fp) + + def _parse(self, file, fp): + lexer = shlex.shlex(fp) + lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" + while 1: + # Look for a machine, default, or macdef top-level keyword + toplevel = tt = lexer.get_token() + if not tt: + break + elif tt == 'machine': + entryname = lexer.get_token() + elif tt == 'default': + entryname = 'default' + elif tt == 'macdef': # Just skip to end of macdefs + entryname = lexer.get_token() + self.macros[entryname] = [] + lexer.whitespace = ' \t' + while 1: + line = lexer.instream.readline() + if not line or line == '\012': + lexer.whitespace = ' \t\r\n' + break + self.macros[entryname].append(line) + continue + else: + raise NetrcParseError( + "bad toplevel token %r" % tt, file, lexer.lineno) + + # We're looking at start of an entry for a named machine or default. + login = '' + account = password = None + self.hosts[entryname] = {} + while 1: + tt = lexer.get_token() + if (tt=='' or tt == 'machine' or + tt == 'default' or tt =='macdef'): + if password: + self.hosts[entryname] = (login, account, password) + lexer.push_token(tt) + break + else: + raise NetrcParseError( + "malformed %s entry %s terminated by %s" + % (toplevel, entryname, repr(tt)), + file, lexer.lineno) + elif tt == 'login' or tt == 'user': + login = lexer.get_token() + elif tt == 'account': + account = lexer.get_token() + elif tt == 'password': + password = lexer.get_token() + else: + raise NetrcParseError("bad follower token %r" % tt, + file, lexer.lineno) + + def authenticators(self, host): + """Return a (user, account, password) tuple for given host.""" + if host in self.hosts: + return self.hosts[host] + elif 'default' in self.hosts: + return self.hosts['default'] + else: + return None + + def __repr__(self): + """Dump the class data in the format of a .netrc file.""" + rep = "" + for host in self.hosts.keys(): + attrs = self.hosts[host] + rep = rep + "machine "+ host + "\n\tlogin " + repr(attrs[0]) + "\n" + if attrs[1]: + rep = rep + "account " + repr(attrs[1]) + rep = rep + "\tpassword " + repr(attrs[2]) + "\n" + for macro in self.macros.keys(): + rep = rep + "macdef " + macro + "\n" + for line in self.macros[macro]: + rep = rep + line + rep = rep + "\n" + return rep + +if __name__ == '__main__': + print netrc() Added: trunk/jython/Lib/test/test_urllib2.py =================================================================== --- trunk/jython/Lib/test/test_urllib2.py (rev 0) +++ trunk/jython/Lib/test/test_urllib2.py 2009-05-28 04:02:18 UTC (rev 6410) @@ -0,0 +1,1066 @@ +import unittest +from test import test_support + +import os, socket +import StringIO + +import urllib2 +from urllib2 import Request, OpenerDirector + +# XXX +# Request +# CacheFTPHandler (hard to write) +# parse_keqv_list, parse_http_list, HTTPDigestAuthHandler + +class TrivialTests(unittest.TestCase): + def test_trivial(self): + # A couple trivial tests + + self.assertRaises(ValueError, urllib2.urlopen, 'bogus url') + + # XXX Name hacking to get this to work on Windows. + fname = os.path.abspath(urllib2.__file__).replace('\\', '/') + if fname[1:2] == ":": + fname = fname[2:] + # And more hacking to get it to work on MacOS. This assumes + # urllib.pathname2url works, unfortunately... + if os.name == 'mac': + fname = '/' + fname.replace(':', '/') + elif os.name == 'riscos': + import string + fname = os.expand(fname) + fname = fname.translate(string.maketrans("/.", "./")) + + file_url = "file://%s" % fname + f = urllib2.urlopen(file_url) + + buf = f.read() + f.close() + + def test_parse_http_list(self): + tests = [('a,b,c', ['a', 'b', 'c']), + ('path"o,l"og"i"cal, example', ['path"o,l"og"i"cal', 'example']), + ('a, b, "c", "d", "e,f", g, h', ['a', 'b', '"c"', '"d"', '"e,f"', 'g', 'h']), + ('a="b\\"c", d="e\\,f", g="h\\\\i"', ['a="b"c"', 'd="e,f"', 'g="h\\i"'])] + for string, list in tests: + self.assertEquals(urllib2.parse_http_list(string), list) + + +def test_request_headers_dict(): + """ + The Request.headers dictionary is not a documented interface. It should + stay that way, because the complete set of headers are only accessible + through the .get_header(), .has_header(), .header_items() interface. + However, .headers pre-dates those methods, and so real code will be using + the dictionary. + + The introduction in 2.4 of those methods was a mistake for the same reason: + code that previously saw all (urllib2 user)-provided headers in .headers + now sees only a subset (and the function interface is ugly and incomplete). + A better change would have been to replace .headers dict with a dict + subclass (or UserDict.DictMixin instance?) that preserved the .headers + interface and also provided access to the "unredirected" headers. It's + probably too late to fix that, though. + + + Check .capitalize() case normalization: + + >>> url = "http://example.com" + >>> Request(url, headers={"Spam-eggs": "blah"}).headers["Spam-eggs"] + 'blah' + >>> Request(url, headers={"spam-EggS": "blah"}).headers["Spam-eggs"] + 'blah' + + Currently, Request(url, "Spam-eggs").headers["Spam-Eggs"] raises KeyError, + but that could be changed in future. + + """ + +def test_request_headers_methods(): + """ + Note the case normalization of header names here, to .capitalize()-case. + This should be preserved for backwards-compatibility. (In the HTTP case, + normalization to .title()-case is done by urllib2 before sending headers to + httplib). + + >>> url = "http://example.com" + >>> r = Request(url, headers={"Spam-eggs": "blah"}) + >>> r.has_header("Spam-eggs") + True + >>> r.header_items() + [('Spam-eggs', 'blah')] + >>> r.add_header("Foo-Bar", "baz") + >>> items = r.header_items() + >>> items.sort() + >>> items + [('Foo-bar', 'baz'), ('Spam-eggs', 'blah')] + + Note that e.g. r.has_header("spam-EggS") is currently False, and + r.get_header("spam-EggS") returns None, but that could be changed in + future. + + >>> r.has_header("Not-there") + False + >>> print r.get_header("Not-there") + None + >>> r.get_header("Not-there", "default") + 'default' + + """ + + +def test_password_manager(self): + """ + >>> mgr = urllib2.HTTPPasswordMgr() + >>> add = mgr.add_password + >>> add("Some Realm", "http://example.com/", "joe", "password") + >>> add("Some Realm", "http://example.com/ni", "ni", "ni") + >>> add("c", "http://example.com/foo", "foo", "ni") + >>> add("c", "http://example.com/bar", "bar", "nini") + >>> add("b", "http://example.com/", "first", "blah") + >>> add("b", "http://example.com/", "second", "spam") + >>> add("a", "http://example.com", "1", "a") + >>> add("Some Realm", "http://c.example.com:3128", "3", "c") + >>> add("Some Realm", "d.example.com", "4", "d") + >>> add("Some Realm", "e.example.com:3128", "5", "e") + + >>> mgr.find_user_password("Some Realm", "example.com") + ('joe', 'password') + >>> mgr.find_user_password("Some Realm", "http://example.com") + ('joe', 'password') + >>> mgr.find_user_password("Some Realm", "http://example.com/") + ('joe', 'password') + >>> mgr.find_user_password("Some Realm", "http://example.com/spam") + ('joe', 'password') + >>> mgr.find_user_password("Some Realm", "http://example.com/spam/spam") + ('joe', 'password') + >>> mgr.find_user_password("c", "http://example.com/foo") + ('foo', 'ni') + >>> mgr.find_user_password("c", "http://example.com/bar") + ('bar', 'nini') + + Actually, this is really undefined ATM +## Currently, we use the highest-level path where more than one match: + +## >>> mgr.find_user_password("Some Realm", "http://example.com/ni") +## ('joe', 'password') + + Use latest add_password() in case of conflict: + + >>> mgr.find_user_password("b", "http://example.com/") + ('second', 'spam') + + No special relationship between a.example.com and example.com: + + >>> mgr.find_user_password("a", "http://example.com/") + ('1', 'a') + >>> mgr.find_user_password("a", "http://a.example.com/") + (None, None) + + Ports: + + >>> mgr.find_user_password("Some Realm", "c.example.com") + (None, None) + >>> mgr.find_user_password("Some Realm", "c.example.com:3128") + ('3', 'c') + >>> mgr.find_user_password("Some Realm", "http://c.example.com:3128") + ('3', 'c') + >>> mgr.find_user_password("Some Realm", "d.example.com") + ('4', 'd') + >>> mgr.find_user_password("Some Realm", "e.example.com:3128") + ('5', 'e') + + """ + pass + + +def test_password_manager_default_port(self): + """ + >>> mgr = urllib2.HTTPPasswordMgr() + >>> add = mgr.add_password + + The point to note here is that we can't guess the default port if there's + no scheme. This applies to both add_password and find_user_password. + + >>> add("f", "http://g.example.com:80", "10", "j") + >>> add("g", "http://h.example.com", "11", "k") + >>> add("h", "i.example.com:80", "12", "l") + >>> add("i", "j.example.com", "13", "m") + >>> mgr.find_user_password("f", "g.example.com:100") + (None, None) + >>> mgr.find_user_password("f", "g.example.com:80") + ('10', 'j') + >>> mgr.find_user_password("f", "g.example.com") + (None, None) + >>> mgr.find_user_password("f", "http://g.example.com:100") + (None, None) + >>> mgr.find_user_password("f", "http://g.example.com:80") + ('10', 'j') + >>> mgr.find_user_password("f", "http://g.example.com") + ('10', 'j') + >>> mgr.find_user_password("g", "h.example.com") + ('11', 'k') + >>> mgr.find_user_password("g", "h.example.com:80") + ('11', 'k') + >>> mgr.find_user_password("g", "http://h.example.com:80") + ('11', 'k') + >>> mgr.find_user_password("h", "i.example.com") + (None, None) + >>> mgr.find_user_password("h", "i.example.com:80") + ('12', 'l') + >>> mgr.find_user_password("h", "http://i.example.com:80") + ('12', 'l') + >>> mgr.find_user_password("i", "j.example.com") + ('13', 'm') + >>> mgr.find_user_password("i", "j.example.com:80") + (None, None) + >>> mgr.find_user_password("i", "http://j.example.com") + ('13', 'm') + >>> mgr.find_user_password("i", "http://j.example.com:80") + (None, None) + + """ + +class MockOpener: + addheaders = [] + def open(self, req, data=None): + self.req, self.data = req, data + def error(self, proto, *args): + self.proto, self.args = proto, args + +class MockFile: + def read(self, count=None): pass + def readline(self, count=None): pass + def close(self): pass + +class MockHeaders(dict): + def getheaders(self, name): + return self.values() + +class MockResponse(StringIO.StringIO): + def __init__(self, code, msg, headers, data, url=None): + StringIO.StringIO.__init__(self, data) + self.code, self.msg, self.headers, self.url = code, msg, headers, url + def info(self): + return self.headers + def geturl(self): + return self.url + +class MockCookieJar: + def add_cookie_header(self, request): + self.ach_req = request + def extract_cookies(self, response, request): + self.ec_req, self.ec_r = request, response + +class FakeMethod: + def __init__(self, meth_name, action, handle): + self.meth_name = meth_name + self.handle = handle + self.action = action + def __call__(self, *args): + return self.handle(self.meth_name, self.action, *args) + +class MockHandler: + # useful for testing handler machinery + # see add_ordered_mock_handlers() docstring + handler_order = 500 + def __init__(self, methods): + self._define_methods(methods) + def _define_methods(self, methods): + for spec in methods: + if len(spec) == 2: name, action = spec + else: name, action = spec, None + meth = FakeMethod(name, action, self.handle) + setattr(self.__class__, name, meth) + def handle(self, fn_name, action, *args, **kwds): + self.parent.calls.append((self, fn_name, args, kwds)) + if action is None: + return None + elif action == "return self": + return self + elif action == "return response": + res = MockResponse(200, "OK", {}, "") + return res + elif action == "return request": + return Request("http://blah/") + elif action.startswith("error"): + code = action[action.rfind(" ")+1:] + try: + code = int(code) + except ValueError: + pass + res = MockResponse(200, "OK", {}, "") + return self.parent.error("http", args[0], res, code, "", {}) + elif action == "raise": + raise urllib2.URLError("blah") + assert False + def close(self): pass + def add_parent(self, parent): + self.parent = parent + self.parent.calls = [] + def __lt__(self, other): + if not hasattr(other, "handler_order"): + # No handler_order, leave in original order. Yuck. + return True + return self.handler_order < other.handler_order + +def add_ordered_mock_handlers(opener, meth_spec): + """Create MockHandlers and add them to an OpenerDirector. + + meth_spec: list of lists of tuples and strings defining methods to define + on handlers. eg: + + [["http_error", "ftp_open"], ["http_open"]] + + defines methods .http_error() and .ftp_open() on one handler, and + .http_open() on another. These methods just record their arguments and + return None. Using a tuple instead of a string causes the method to + perform some action (see MockHandler.handle()), eg: + + [["http_error"], [("http_open", "return request")]] + + defines .http_error() on one handler (which simply returns None), and + .http_open() on another handler, which returns a Request object. + + """ + handlers = [] + count = 0 + for meths in meth_spec: + class MockHandlerSubclass(MockHandler): pass + h = MockHandlerSubclass(meths) + h.handler_order += count + h.add_parent(opener) + count = count + 1 + handlers.append(h) + opener.add_handler(h) + return handlers + +def build_test_opener(*handler_instances): + opener = OpenerDirector() + for h in handler_instances: + opener.add_handler(h) + return opener + +class MockHTTPHandler(urllib2.BaseHandler): + # useful for testing redirections and auth + # sends supplied headers and code as first response + # sends 200 OK as second response + def __init__(self, code, headers): + self.code = code + self.headers = headers + self.reset() + def reset(self): + self._count = 0 + self.requests = [] + def http_open(self, req): + import mimetools, httplib, copy + from StringIO import StringIO + self.requests.append(copy.deepcopy(req)) + if self._count == 0: + self._count = self._count + 1 + name = httplib.responses[self.code] + msg = mimetools.Message(StringIO(self.headers)) + return self.parent.error( + "http", req, MockFile(), self.code, name, msg) + else: + self.req = req + msg = mimetools.Message(StringIO("\r\n\r\n")) + return MockResponse(200, "OK", msg, "", req.get_full_url()) + +class MockPasswordManager: + def add_password(self, realm, uri, user, password): + self.realm = realm + self.url = uri + self.user = user + self.password = password + def find_user_password(self, realm, authuri): + self.target_realm = realm + self.target_url = authuri + return self.user, self.password + + +class OpenerDirectorTests(unittest.TestCase): + + def test_add_non_handler(self): + class NonHandler(object): + pass + self.assertRaises(TypeError, + OpenerDirector().add_handler, NonHandler()) + + def test_badly_named_methods(self): + # test work-around for three methods that accidentally follow the + # naming conventions for handler methods + # (*_open() / *_request() / *_response()) + + # These used to call the accidentally-named methods, causing a + # TypeError in real code; here, returning self from these mock + # methods would either cause no exception, or AttributeError. + + from urllib2 import URLError + + o = OpenerDirector() + meth_spec = [ + [("do_open", "return self"), ("proxy_open", "return self")], + [("redirect_request", "return self")], + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + o.add_handler(urllib2.UnknownHandler()) + for scheme in "do", "proxy", "redirect": + self.assertRaises(URLError, o.open, scheme+"://example.com/") + + def test_handled(self): + # handler returning non-None means no more handlers will be called + o = OpenerDirector() + meth_spec = [ + ["http_open", "ftp_open", "http_error_302"], + ["ftp_open"], + [("http_open", "return self")], + [("http_open", "return self")], + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + req = Request("http://example.com/") + r = o.open(req) + # Second .http_open() gets called, third doesn't, since second returned + # non-None. Handlers without .http_open() never get any methods called + # on them. + # In fact, second mock handler defining .http_open() returns self + # (instead of response), which becomes the OpenerDirector's return + # value. + self.assertEqual(r, handlers[2]) + calls = [(handlers[0], "http_open"), (handlers[2], "http_open")] + for expected, got in zip(calls, o.calls): + handler, name, args, kwds = got + self.assertEqual((handler, name), expected) + self.assertEqual(args, (req,)) + + def test_handler_order(self): + o = OpenerDirector() + handlers = [] + for meths, handler_order in [ + ([("http_open", "return self")], 500), + (["http_open"], 0), + ]: + class MockHandlerSubclass(MockHandler): pass + h = MockHandlerSubclass(meths) + h.handler_order = handler_order + handlers.append(h) + o.add_handler(h) + + r = o.open("http://example.com/") + # handlers called in reverse order, thanks to their sort order + self.assertEqual(o.calls[0][0], handlers[1]) + self.assertEqual(o.calls[1][0], handlers[0]) + + def test_raise(self): + # raising URLError stops processing of request + o = OpenerDirector() + meth_spec = [ + [("http_open", "raise")], + [("http_open", "return self")], + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + req = Request("http://example.com/") + self.assertRaises(urllib2.URLError, o.open, req) + self.assertEqual(o.calls, [(handlers[0], "http_open", (req,), {})]) + +## def test_error(self): +## # XXX this doesn't actually seem to be used in standard library, +## # but should really be tested anyway... + + def test_http_error(self): + # XXX http_error_default + # http errors are a special case + o = OpenerDirector() + meth_spec = [ + [("http_open", "error 302")], + [("http_error_400", "raise"), "http_open"], + [("http_error_302", "return response"), "http_error_303", + "http_error"], + [("http_error_302")], + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + class Unknown: + def __eq__(self, other): return True + + req = Request("http://example.com/") + r = o.open(req) + assert len(o.calls) == 2 + calls = [(handlers[0], "http_open", (req,)), + (handlers[2], "http_error_302", + (req, Unknown(), 302, "", {}))] + for expected, got in zip(calls, o.calls): + handler, method_name, args = expected + self.assertEqual((handler, method_name), got[:2]) + self.assertEqual(args, got[2]) + + def test_processors(self): + # *_request / *_response methods get called appropriately + o = OpenerDirector() + meth_spec = [ + [("http_request", "return request"), + ("http_response", "return response")], + [("http_request", "return request"), + ("http_response", "return response")], + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + req = Request("http://example.com/") + r = o.open(req) + # processor methods are called on *all* handlers that define them, + # not just the first handler that handles the request + calls = [ + (handlers[0], "http_request"), (handlers[1], "http_request"), + (handlers[0], "http_response"), (handlers[1], "http_response")] + + for i, (handler, name, args, kwds) in enumerate(o.calls): + if i < 2: + # *_request + self.assertEqual((handler, name), calls[i]) + self.assertEqual(len(args), 1) + self.assert_(isinstance(args[0], Request)) + else: + # *_response + self.assertEqual((handler, name), calls[i]) + self.assertEqual(len(args), 2) + self.assert_(isinstance(args[0], Request)) + # response from opener.open is None, because there's no + # handler that defines http_open to handle it + self.assert_(args[1] is None or + isinstance(args[1], MockResponse)) + + +def sanepathname2url(path): + import urllib + urlpath = urllib.pathname2url(path) + if os.name == "nt" and urlpath.startswith("///"): + urlpath = urlpath[2:] + # XXX don't ask me about the mac... + return urlpath + +class HandlerTests(unittest.TestCase): + + def test_ftp(self): + class MockFTPWrapper: + def __init__(self, data): self.data = data + def retrfile(self, filename, filetype): + self.filename, self.filetype = filename, filetype + return StringIO.StringIO(self.data), len(self.data) + + class NullFTPHandler(urllib2.FTPHandler): + def __init__(self, data): self.data = data + def connect_ftp(self, user, passwd, host, port, dirs): + self.user, self.passwd = user, passwd + self.host, self.port = host, port + self.dirs = dirs + self.ftpwrapper = MockFTPWrapper(self.data) + return self.ftpwrapper + + import ftplib, socket + data = "rheum rhaponicum" + h = NullFTPHandler(data) + o = h.parent = MockOpener() + + for url, host, port, type_, dirs, filename, mimetype in [ + ("ftp://localhost/foo/bar/baz.html", + "localhost", ftplib.FTP_PORT, "I", + ["foo", "bar"], "baz.html", "text/html"), + ("ftp://localhost:80/foo/bar/", + "localhost", 80, "D", + ["foo", "bar"], "", None), + ("ftp://localhost/baz.gif;type=a", + "localhost", ftplib.FTP_PORT, "A", + [], "baz.gif", None), # XXX really this should guess image/gif + ]: + r = h.ftp_open(Request(url)) + # ftp authentication not yet implemented by FTPHandler + self.assert_(h.user == h.passwd == "") + self.assertEqual(h.host, socket.gethostbyname(host)) + self.assertEqual(h.port, port) + self.assertEqual(h.dirs, dirs) + self.assertEqual(h.ftpwrapper.filename, filename) + self.assertEqual(h.ftpwrapper.filetype, type_) + headers = r.info() + self.assertEqual(headers.get("Content-type"), mimetype) + self.assertEqual(int(headers["Content-length"]), len(data)) + + def test_file(self): + import time, rfc822, socket + h = urllib2.FileHandler() + o = h.parent = MockOpener() + + TESTFN = test_support.TESTFN + urlpath = sanepathname2url(os.path.abspath(TESTFN)) + towrite = "hello, world\n" + urls = [ + "file://localhost%s" % urlpath, + "file://%s" % urlpath, + "file://%s%s" % (socket.gethostbyname('localhost'), urlpath), + ] + try: + localaddr = socket.gethostbyname(socket.gethostname()) + except socket.gaierror: + localaddr = '' + if localaddr: + urls.append("file://%s%s" % (localaddr, urlpath)) + + for url in urls: + f = open(TESTFN, "wb") + try: + try: + f.write(towrite) + finally: + f.close() + + r = h.file_open(Request(url)) + try: + data = r.read() + headers = r.info() + newurl = r.geturl() + finally: + r.close() + stats = os.stat(TESTFN) + modified = rfc822.formatdate(stats.st_mtime) + finally: + os.remove(TESTFN) + self.assertEqual(data, towrite) + self.assertEqual(headers["Content-type"], "text/plain") + self.assertEqual(headers["Content-length"], "13") + self.assertEqual(headers["Last-modified"], modified) + + for url in [ + "file://localhost:80%s" % urlpath, +# XXXX bug: these fail with socket.gaierror, should be URLError +## "file://%s:80%s/%s" % (socket.gethostbyname('localhost'), +## os.getcwd(), TESTFN), +## "file://somerandomhost.ontheinternet.com%s/%s" % +## (os.getcwd(), TESTFN), + ]: + try: + f = open(TESTFN, "wb") + try: + f.write(towrite) + finally: + f.close() + + self.assertRaises(urllib2.URLError, + h.file_open, Request(url)) + finally: + os.remove(TESTFN) + + h = urllib2.FileHandler() + o = h.parent = MockOpener() + # XXXX why does // mean ftp (and /// mean not ftp!), and where + # is file: scheme specified? I think this is really a bug, and + # what was intended was to distinguish between URLs like: + # file:/blah.txt (a file) + # file://localhost/blah.txt (a file) + # file:///blah.txt (a file) + # file://ftp.example.com/blah.txt (an ftp URL) + for url, ftp in [ + ("file://ftp.example.com//foo.txt", True), + ("file://ftp.example.com///foo.txt", False), +# XXXX bug: fails with OSError, should be URLError + ("file://ftp.example.com/foo.txt", False), + ]: + req = Request(url) + try: + h.file_open(req) + # XXXX remove OSError when bug fixed + except (urllib2.URLError, OSError): + self.assert_(not ftp) + else: + self.assert_(o.req is req) + self.assertEqual(req.type, "ftp") + + def test_http(self): + class MockHTTPResponse: + def __init__(self, fp, msg, status, reason): + self.fp = fp + self.msg = msg + self.status = status + self.reason = reason + def read(self): + return '' + class MockHTTPClass: + def __init__(self): + self.req_headers = [] + self.data = None + self.raise_on_endheaders = False + def __call__(self, host): + self.host = host + return self + def set_debuglevel(self, level): + self.level = level + def request(self, method, url, body=None, headers={}): + self.method = method + self.selector = url + self.req_headers += headers.items() + self.req_headers.sort() + if body: + self.data = body + if self.raise_on_endheaders: + import socket + raise socket.error() + def getresponse(self): + return MockHTTPResponse(MockFile(), {}, 200, "OK") + + h = urllib2.AbstractHTTPHandler() + o = h.parent = MockOpener() + + url = "http://example.com/" + for method, data in [("GET", None), ("POST", "blah")]: + req = Request(url, data, {"Foo": "bar"}) + req.add_unredirected_header("Spam", "eggs") + http = MockHTTPClass() + r = h.do_open(http, req) + + # result attributes + r.read; r.readline # wrapped MockFile methods + r.info; r.geturl # addinfourl methods + r.code, r.msg == 200, "OK" # added from MockHTTPClass.getreply() + hdrs = r.info() + hdrs.get; hdrs.has_key # r.info() gives dict from .getreply() + self.assertEqual(r.geturl(), url) + + self.assertEqual(http.host, "example.com") + self.assertEqual(http.level, 0) + self.assertEqual(http.method, method) + self.assertEqual(http.selector, "/") + self.assertEqual(http.req_headers, + [("Connection", "close"), + ("Foo", "bar"), ("Spam", "eggs")]) + self.assertEqual(http.data, data) + + # check socket.error converted to URLError + http.raise_on_endheaders = True + self.assertRaises(urllib2.URLError, h.do_open, http, req) + + # check adding of standard headers + o.addheaders = [("Spam", "eggs")] + for data in "", None: # POST, GET + req = Request("http://example.com/", data) + r = MockResponse(200, "OK", {}, "") + newreq = h.do_request_(req) + if data is None: # GET + self.assert_("Content-length" not in req.unredirected_hdrs) + self.assert_("Content-type" not in req.unredirected_hdrs) + else: # POST + self.assertEqual(req.unredirected_hdrs["Content-length"], "0") + self.assertEqual(req.unredirected_hdrs["Content-type"], + "application/x-www-form-urlencoded") + # XXX the details of Host could be better tested + self.assertEqual(req.unredirected_hdrs["Host"], "example.com") + self.assertEqual(req.unredirected_hdrs["Spam"], "eggs") + + # don't clobber existing headers + req.add_unredirected_header("Content-length", "foo") + req.add_unredirected_header("Content-type", "bar") + req.add_unredirected_header("Host", "baz") + req.add_unredirected_header("Spam", "foo") + newreq = h.do_request_(req) + self.assertEqual(req.unredirected_hdrs["Content-length"], "foo") + self.assertEqual(req.unredirected_hdrs["Content-type"], "bar") + self.assertEqual(req.unredirected_hdrs["Host"], "baz") + self.assertEqual(req.unredirected_hdrs["Spam"], "foo") + + def test_errors(self): + h = urllib2.HTTPErrorProcessor() + o = h.parent = MockOpener() + + url = "http://example.com/" + req = Request(url) + # 200 OK is passed through + r = MockResponse(200, "OK", {}, "", url) + newr = h.http_response(req, r) + self.assert_(r is newr) + self.assert_(not hasattr(o, "proto")) # o.error not called + # anything else calls o.error (and MockOpener returns None, here) + r = MockResponse(201, "Created", {}, "", url) + self.assert_(h.http_response(req, r) is None) + self.assertEqual(o.proto, "http") # o.error called + self.assertEqual(o.args, (req, r, 201, "Created", {})) + + def test_cookies(self): + cj = MockCookieJar() + h = urllib2.HTTPCookieProcessor(cj) + o = h.parent = MockOpener() + + req = Request("http://example.com/") + r = MockResponse(200, "OK", {}, "") + newreq = h.http_request(req) + self.assert_(cj.ach_req is req is newreq) + self.assertEquals(req.get_origin_req_host(), "example.com") + self.assert_(not req.is_unverifiable()) + newr = h.http_response(req, r) + self.assert_(cj.ec_req is req) + self.assert_(cj.ec_r is r is newr) + + def test_redirect(self): + from_url = "http://example.com/a.html" + to_url = "http://example.com/b.html" + h = urllib2.HTTPRedirectHandler() + o = h.parent = MockOpener() + + # ordinary redirect behaviour + for code in 301, 302, 303, 307: + for data in None, "blah\nblah\n": + method = getattr(h, "http_error_%s" % code) + req = Request(from_url, data) + req.add_header("Nonsense", "viking=withhold") + req.add_unredirected_header("Spam", "spam") + try: + method(req, MockFile(), code, "Blah", + MockHeaders({"location": to_url})) + except urllib2.HTTPError: + # 307 in response to POST requires user OK + self.assert_(code == 307 and data is not None) + self.assertEqual(o.req.get_full_url(), to_url) + try: + self.assertEqual(o.req.get_method(), "GET") + except AttributeError: + self.assert_(not o.req.has_data()) + self.assertEqual(o.req.headers["Nonsense"], + "viking=withhold") + self.assert_("Spam" not in o.req.headers) + self.assert_("Spam" not in o.req.unredirected_hdrs) + + # loop detection + req = Request(from_url) + def redirect(h, req, url=to_url): + h.http_error_302(req, MockFile(), 302, "Blah", + MockHeaders({"location": url})) + # Note that the *original* request shares the same record of + # redirections with the sub-requests caused by the redirections. + + # detect infinite loop redirect of a URL to itself + req = Request(from_url, origin_req_host="example.com") + count = 0 + try: + while 1: + redirect(h, req, "http://example.com/") + count = count + 1 + except urllib2.HTTPError: + # don't stop until max_repeats, because cookies may introduce state + self.assertEqual(count, urllib2.HTTPRedirectHandler.max_repeats) + + # detect endless non-repeating chain of redirects + req = Request(from_url, origin_req_host="example.com") + count = 0 + try: + while 1: + redirect(h, req, "http://example.com/%d" % count) + count = count + 1 + except urllib2.HTTPError: + self.assertEqual(count, + urllib2.HTTPRedirectHandler.max_redirections) + + def test_cookie_redirect(self): + # cookies shouldn't leak into redirected requests + from cookielib import CookieJar + + from test.test_cookielib import interact_netscape + + cj = CookieJar() + interact_netscape(cj, "http://www.example.com/", "spam=eggs") + hh = MockHTTPHandler(302, "Location: http://www.cracker.com/\r\n\r\n") + hdeh = urllib2.HTTPDefaultErrorHandler() + hrh = urllib2.HTTPRedirectHandler() + cp = urllib2.HTTPCookieProcessor(cj) + o = build_test_opener(hh, hdeh, hrh, cp) + o.open("http://www.example.com/") + self.assert_(not hh.req.has_header("Cookie")) + + def test_proxy(self): + o = OpenerDirector() + ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128")) + o.add_handler(ph) + meth_spec = [ + [("http_open", "return response")] + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + + req = Request("http://acme.example.com/") + self.assertEqual(req.get_host(), "acme.example.com") + r = o.open(req) + self.assertEqual(req.get_host(), "proxy.example.com:3128") + + self.assertEqual([(handlers[0], "http_open")], + [tup[0:2] for tup in o.calls]) + + def test_basic_auth(self): + opener = OpenerDirector() + password_manager = MockPasswordManager() + auth_handler = urllib2.HTTPBasicAuthHandler(password_manager) + realm = "ACME Widget Store" + http_handler = MockHTTPHandler( + 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + opener.add_handler(auth_handler) + opener.add_handler(http_handler) + self._test_basic_auth(opener, auth_handler, "Authorization", + realm, http_handler, password_manager, + "http://acme.example.com/protected", + "http://acme.example.com/protected", + ) + + def test_proxy_basic_auth(self): + opener = OpenerDirector() + ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128")) + opener.add_handler(ph) + password_manager = MockPasswordManager() + auth_handler = urllib2.ProxyBasicAuthHandler(password_manager) + realm = "ACME Networks" + http_handler = MockHTTPHandler( + 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + opener.add_handler(auth_handler) + opener.add_handler(http_handler) + self._test_basic_auth(opener, auth_handler, "Proxy-authorization", + realm, http_handler, password_manager, + "http://acme.example.com:3128/protected", + "proxy.example.com:3128", + ) + + def test_basic_and_digest_auth_handlers(self): + # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40* + # response (http://python.org/sf/1479302), where it should instead + # return None to allow another handler (especially + # HTTPBasicAuthHandler) to handle the response. + + # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must + # try digest first (since it's the strongest auth scheme), so we record + # order of calls here to check digest comes first: + class RecordingOpenerDirector(OpenerDirector): + def __init__(self): + OpenerDirector.__init__(self) + self.recorded = [] + def record(self, info): + self.recorded.append(info) + class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler): + def http_error_401(self, *args, **kwds): + self.parent.record("digest") + urllib2.HTTPDigestAuthHandler.http_error_401(self, + *args, **kwds) + class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler): + def http_error_401(self, *args, **kwds): + self.parent.record("basic") + urllib2.HTTPBasicAuthHandler.http_error_401(self, + *args, **kwds) + + opener = RecordingOpenerDirector() + password_manager = MockPasswordManager() + digest_handler = TestDigestAuthHandler(password_manager) + basic_handler = TestBasicAuthHandler(password_manager) + realm = "ACME Networks" + http_handler = MockHTTPHandler( + 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + opener.add_handler(basic_handler) + opener.add_handler(digest_handler) + opener.add_handler(http_handler) + + # check basic auth isn't blocked by digest handler failing + self._test_basic_auth(opener, basic_handler, "Authorization", + realm, http_handler, password_manager, + "http://acme.example.com/protected", + "http://acme.example.com/protected", + ) + # check digest was tried before basic (twice, because + # _test_basic_auth called .open() twice) + self.assertEqual(opener.recorded, ["digest", "basic"]*2) + + def _test_basic_auth(self, opener, auth_handler, auth_header, + realm, http_handler, password_manager, + request_url, protected_url): + import base64, httplib + user, password = "wile", "coyote" + + # .add_password() fed through to password manager + auth_handler.add_password(realm, request_url, user, password) + self.assertEqual(realm, password_manager.realm) + self.assertEqual(request_url, password_manager.url) + self.assertEqual(user, password_manager.user) + self.assertEqual(password, password_manager.password) + + r = opener.open(request_url) + + # should have asked the password manager for the username/password + self.assertEqual(password_manager.target_realm, realm) + self.assertEqual(password_manager.target_url, protected_url) + + # expect one request without authorization, then one with + self.assertEqual(len(http_handler.requests), 2) + self.assertFalse(http_handler.requests[0].has_header(auth_header)) + userpass = '%s:%s' % (user, password) + auth_hdr_value = 'Basic '+base64.encodestring(userpass).strip() + self.assertEqual(http_handler.requests[1].get_header(auth_header), + auth_hdr_value) + + # if the password manager can't find a password, the handler won't + # handle the HTTP auth error + password_manager.user = password_manager.password = None + http_handler.reset() + r = opener.open(request_url) + self.assertEqual(len(http_handler.requests), 1) + self.assertFalse(http_handler.requests[0].has_header(auth_header)) + + +class MiscTests(unittest.TestCase): + + def test_build_opener(self): + class MyHTTPHandler(urllib2.HTTPHandler): pass + class FooHandler(urllib2.BaseHandler): + def foo_open(self): pass + class BarHandler(urllib2.BaseHandler): + def bar_open(self): pass + + build_opener = urllib2.build_opener + + o = build_opener(FooHandler, BarHandler) + self.opener_has_handler(o, FooHandler) + self.opener_has_handler(o, BarHandler) + + # can take a mix of classes and instances + o = build_opener(FooHandler, BarHandler()) + self.opener_has_handler(o, FooHandler) + self.opener_has_handler(o, BarHandler) + + # subclasses of default handlers override default handlers + o = build_opener(MyHTTPHandler) + self.opener_has_handler(o, MyHTTPHandler) + + # a particular case of overriding: default handlers can be passed + # in explicitly + o = build_opener() + self.opener_has_handler(o, urllib2.HTTPHandler) + o = build_opener(urllib2.HTTPHandler) + self.opener_has_handler(o, urllib2.HTTPHandler) + o = build_opener(urllib2.HTTPHandler()) + self.opener_has_handler(o, urllib2.HTTPHandler) + + # Issue2670: multiple handlers sharing the same base class + class MyOtherHTTPHandler(urllib2.HTTPHandler): pass + o = build_opener(MyHTTPHandler, MyOtherHTTPHandler) + self.opener_has_handler(o, MyHTTPHandler) + self.opener_has_handler(o, MyOtherHTTPHandler) + + def opener_has_handler(self, opener, handler_class): + for h in opener.handlers: + if h.__class__ == handler_class: + break + else: + self.assert_(False) + + +def test_main(verbose=None): + from test import test_urllib2 + test_support.run_doctest(test_urllib2, verbose) + test_support.run_doctest(urllib2, verbose) + tests = (TrivialTests, + OpenerDirectorTests, + HandlerTests, + MiscTests) + test_support.run_unittest(*tests) + +if __name__ == "__main__": + test_main(verbose=True) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-28 04:03:36
|
Revision: 6411 http://jython.svn.sourceforge.net/jython/?rev=6411&view=rev Author: pjenvey Date: 2009-05-28 04:03:31 +0000 (Thu, 28 May 2009) Log Message: ----------- o enable with statement for netrc to fix test_netrc on Windows o add jython windows check to test_urllib2 Modified Paths: -------------- trunk/jython/Lib/netrc.py trunk/jython/Lib/test/test_urllib2.py Modified: trunk/jython/Lib/netrc.py =================================================================== --- trunk/jython/Lib/netrc.py 2009-05-28 04:02:18 UTC (rev 6410) +++ trunk/jython/Lib/netrc.py 2009-05-28 04:03:31 UTC (rev 6411) @@ -2,6 +2,7 @@ # Module and documentation by Eric S. Raymond, 21 Dec 1998 +from __future__ import with_statement import os, shlex __all__ = ["netrc", "NetrcParseError"] Modified: trunk/jython/Lib/test/test_urllib2.py =================================================================== --- trunk/jython/Lib/test/test_urllib2.py 2009-05-28 04:02:18 UTC (rev 6410) +++ trunk/jython/Lib/test/test_urllib2.py 2009-05-28 04:03:31 UTC (rev 6411) @@ -535,7 +535,8 @@ def sanepathname2url(path): import urllib urlpath = urllib.pathname2url(path) - if os.name == "nt" and urlpath.startswith("///"): + if ((os._name if test_support.is_jython else os.name) == 'nt' + and urlpath.startswith("///")): urlpath = urlpath[2:] # XXX don't ask me about the mac... return urlpath This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-05-30 06:41:44
|
Revision: 6426 http://jython.svn.sourceforge.net/jython/?rev=6426&view=rev Author: pjenvey Date: 2009-05-30 06:40:55 +0000 (Sat, 30 May 2009) Log Message: ----------- from: http://svn.python.org/projects/python/branches/release25-maint/Lib/mailbox.py@60096 http://svn.python.org/projects/python/branches/release25-maint/Lib/test/test_old_mailbox.py@54954 http://svn.python.org/projects/python/branches/release25-maint/Lib/test/test_shutil.py@51187 Added Paths: ----------- trunk/jython/Lib/mailbox.py trunk/jython/Lib/test/test_old_mailbox.py trunk/jython/Lib/test/test_shutil.py Added: trunk/jython/Lib/mailbox.py =================================================================== --- trunk/jython/Lib/mailbox.py (rev 0) +++ trunk/jython/Lib/mailbox.py 2009-05-30 06:40:55 UTC (rev 6426) @@ -0,0 +1,2097 @@ +#! /usr/bin/env python + +"""Read/write support for Maildir, mbox, MH, Babyl, and MMDF mailboxes.""" + +# Notes for authors of new mailbox subclasses: +# +# Remember to fsync() changes to disk before closing a modified file +# or returning from a flush() method. See functions _sync_flush() and +# _sync_close(). + +import sys +import os +import time +import calendar +import socket +import errno +import copy +import email +import email.Message +import email.Generator +import rfc822 +import StringIO +try: + if sys.platform == 'os2emx': + # OS/2 EMX fcntl() not adequate + raise ImportError + import fcntl +except ImportError: + fcntl = None + +__all__ = [ 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', + 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', + 'BabylMessage', 'MMDFMessage', 'UnixMailbox', + 'PortableUnixMailbox', 'MmdfMailbox', 'MHMailbox', 'BabylMailbox' ] + +class Mailbox: + """A group of messages in a particular place.""" + + def __init__(self, path, factory=None, create=True): + """Initialize a Mailbox instance.""" + self._path = os.path.abspath(os.path.expanduser(path)) + self._factory = factory + + def add(self, message): + """Add message and return assigned key.""" + raise NotImplementedError('Method must be implemented by subclass') + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + raise NotImplementedError('Method must be implemented by subclass') + + def __delitem__(self, key): + self.remove(key) + + def discard(self, key): + """If the keyed message exists, remove it.""" + try: + self.remove(key) + except KeyError: + pass + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get(self, key, default=None): + """Return the keyed message, or default if it doesn't exist.""" + try: + return self.__getitem__(key) + except KeyError: + return default + + def __getitem__(self, key): + """Return the keyed message; raise KeyError if it doesn't exist.""" + if not self._factory: + return self.get_message(key) + else: + return self._factory(self.get_file(key)) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def iterkeys(self): + """Return an iterator over keys.""" + raise NotImplementedError('Method must be implemented by subclass') + + def keys(self): + """Return a list of keys.""" + return list(self.iterkeys()) + + def itervalues(self): + """Return an iterator over all messages.""" + for key in self.iterkeys(): + try: + value = self[key] + except KeyError: + continue + yield value + + def __iter__(self): + return self.itervalues() + + def values(self): + """Return a list of messages. Memory intensive.""" + return list(self.itervalues()) + + def iteritems(self): + """Return an iterator over (key, message) tuples.""" + for key in self.iterkeys(): + try: + value = self[key] + except KeyError: + continue + yield (key, value) + + def items(self): + """Return a list of (key, message) tuples. Memory intensive.""" + return list(self.iteritems()) + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + raise NotImplementedError('Method must be implemented by subclass') + + def __contains__(self, key): + return self.has_key(key) + + def __len__(self): + """Return a count of messages in the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def clear(self): + """Delete all messages.""" + for key in self.iterkeys(): + self.discard(key) + + def pop(self, key, default=None): + """Delete the keyed message and return it, or default.""" + try: + result = self[key] + except KeyError: + return default + self.discard(key) + return result + + def popitem(self): + """Delete an arbitrary (key, message) pair and return it.""" + for key in self.iterkeys(): + return (key, self.pop(key)) # This is only run once. + else: + raise KeyError('No messages in mailbox') + + def update(self, arg=None): + """Change the messages that correspond to certain keys.""" + if hasattr(arg, 'iteritems'): + source = arg.iteritems() + elif hasattr(arg, 'items'): + source = arg.items() + else: + source = arg + bad_key = False + for key, message in source: + try: + self[key] = message + except KeyError: + bad_key = True + if bad_key: + raise KeyError('No message with key(s)') + + def flush(self): + """Write any pending changes to the disk.""" + raise NotImplementedError('Method must be implemented by subclass') + + def lock(self): + """Lock the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def unlock(self): + """Unlock the mailbox if it is locked.""" + raise NotImplementedError('Method must be implemented by subclass') + + def close(self): + """Flush and close the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def _dump_message(self, message, target, mangle_from_=False): + # Most files are opened in binary mode to allow predictable seeking. + # To get native line endings on disk, the user-friendly \n line endings + # used in strings and by email.Message are translated here. + """Dump message contents to target file.""" + if isinstance(message, email.Message.Message): + buffer = StringIO.StringIO() + gen = email.Generator.Generator(buffer, mangle_from_, 0) + gen.flatten(message) + buffer.seek(0) + target.write(buffer.read().replace('\n', os.linesep)) + elif isinstance(message, str): + if mangle_from_: + message = message.replace('\nFrom ', '\n>From ') + message = message.replace('\n', os.linesep) + target.write(message) + elif hasattr(message, 'read'): + while True: + line = message.readline() + if line == '': + break + if mangle_from_ and line.startswith('From '): + line = '>From ' + line[5:] + line = line.replace('\n', os.linesep) + target.write(line) + else: + raise TypeError('Invalid message type: %s' % type(message)) + + +class Maildir(Mailbox): + """A qmail-style Maildir mailbox.""" + + colon = ':' + + def __init__(self, dirname, factory=rfc822.Message, create=True): + """Initialize a Maildir instance.""" + Mailbox.__init__(self, dirname, factory, create) + if not os.path.exists(self._path): + if create: + os.mkdir(self._path, 0700) + os.mkdir(os.path.join(self._path, 'tmp'), 0700) + os.mkdir(os.path.join(self._path, 'new'), 0700) + os.mkdir(os.path.join(self._path, 'cur'), 0700) + else: + raise NoSuchMailboxError(self._path) + self._toc = {} + + def add(self, message): + """Add message and return assigned key.""" + tmp_file = self._create_tmp() + try: + self._dump_message(message, tmp_file) + finally: + _sync_close(tmp_file) + if isinstance(message, MaildirMessage): + subdir = message.get_subdir() + suffix = self.colon + message.get_info() + if suffix == self.colon: + suffix = '' + else: + subdir = 'new' + suffix = '' + uniq = os.path.basename(tmp_file.name).split(self.colon)[0] + dest = os.path.join(self._path, subdir, uniq + suffix) + try: + if hasattr(os, 'link'): + os.link(tmp_file.name, dest) + os.remove(tmp_file.name) + else: + os.rename(tmp_file.name, dest) + except OSError, e: + os.remove(tmp_file.name) + if e.errno == errno.EEXIST: + raise ExternalClashError('Name clash with existing message: %s' + % dest) + else: + raise + if isinstance(message, MaildirMessage): + os.utime(dest, (os.path.getatime(dest), message.get_date())) + return uniq + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + os.remove(os.path.join(self._path, self._lookup(key))) + + def discard(self, key): + """If the keyed message exists, remove it.""" + # This overrides an inapplicable implementation in the superclass. + try: + self.remove(key) + except KeyError: + pass + except OSError, e: + if e.errno != errno.ENOENT: + raise + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + old_subpath = self._lookup(key) + temp_key = self.add(message) + temp_subpath = self._lookup(temp_key) + if isinstance(message, MaildirMessage): + # temp's subdir and suffix were specified by message. + dominant_subpath = temp_subpath + else: + # temp's subdir and suffix were defaults from add(). + dominant_subpath = old_subpath + subdir = os.path.dirname(dominant_subpath) + if self.colon in dominant_subpath: + suffix = self.colon + dominant_subpath.split(self.colon)[-1] + else: + suffix = '' + self.discard(key) + new_path = os.path.join(self._path, subdir, key + suffix) + os.rename(os.path.join(self._path, temp_subpath), new_path) + if isinstance(message, MaildirMessage): + os.utime(new_path, (os.path.getatime(new_path), + message.get_date())) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + subpath = self._lookup(key) + f = open(os.path.join(self._path, subpath), 'r') + try: + if self._factory: + msg = self._factory(f) + else: + msg = MaildirMessage(f) + finally: + f.close() + subdir, name = os.path.split(subpath) + msg.set_subdir(subdir) + if self.colon in name: + msg.set_info(name.split(self.colon)[-1]) + msg.set_date(os.path.getmtime(os.path.join(self._path, subpath))) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + f = open(os.path.join(self._path, self._lookup(key)), 'r') + try: + return f.read() + finally: + f.close() + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + f = open(os.path.join(self._path, self._lookup(key)), 'rb') + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + self._refresh() + for key in self._toc: + try: + self._lookup(key) + except KeyError: + continue + yield key + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._refresh() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._refresh() + return len(self._toc) + + def flush(self): + """Write any pending changes to disk.""" + return # Maildir changes are always written immediately. + + def lock(self): + """Lock the mailbox.""" + return + + def unlock(self): + """Unlock the mailbox if it is locked.""" + return + + def close(self): + """Flush and close the mailbox.""" + return + + def list_folders(self): + """Return a list of folder names.""" + result = [] + for entry in os.listdir(self._path): + if len(entry) > 1 and entry[0] == '.' and \ + os.path.isdir(os.path.join(self._path, entry)): + result.append(entry[1:]) + return result + + def get_folder(self, folder): + """Return a Maildir instance for the named folder.""" + return Maildir(os.path.join(self._path, '.' + folder), + factory=self._factory, + create=False) + + def add_folder(self, folder): + """Create a folder and return a Maildir instance representing it.""" + path = os.path.join(self._path, '.' + folder) + result = Maildir(path, factory=self._factory) + maildirfolder_path = os.path.join(path, 'maildirfolder') + if not os.path.exists(maildirfolder_path): + os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY)) + return result + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, '.' + folder) + for entry in os.listdir(os.path.join(path, 'new')) + \ + os.listdir(os.path.join(path, 'cur')): + if len(entry) < 1 or entry[0] != '.': + raise NotEmptyError('Folder contains message(s): %s' % folder) + for entry in os.listdir(path): + if entry != 'new' and entry != 'cur' and entry != 'tmp' and \ + os.path.isdir(os.path.join(path, entry)): + raise NotEmptyError("Folder contains subdirectory '%s': %s" % + (folder, entry)) + for root, dirs, files in os.walk(path, topdown=False): + for entry in files: + os.remove(os.path.join(root, entry)) + for entry in dirs: + os.rmdir(os.path.join(root, entry)) + os.rmdir(path) + + def clean(self): + """Delete old files in "tmp".""" + now = time.time() + for entry in os.listdir(os.path.join(self._path, 'tmp')): + path = os.path.join(self._path, 'tmp', entry) + if now - os.path.getatime(path) > 129600: # 60 * 60 * 36 + os.remove(path) + + _count = 1 # This is used to generate unique file names. + + def _create_tmp(self): + """Create a file in the tmp subdirectory and open and return it.""" + now = time.time() + hostname = socket.gethostname() + if '/' in hostname: + hostname = hostname.replace('/', r'\057') + if ':' in hostname: + hostname = hostname.replace(':', r'\072') + uniq = "%s.M%sP%sQ%s.%s" % (int(now), int(now % 1 * 1e6), os.getpid(), + Maildir._count, hostname) + path = os.path.join(self._path, 'tmp', uniq) + try: + os.stat(path) + except OSError, e: + if e.errno == errno.ENOENT: + Maildir._count += 1 + try: + return _create_carefully(path) + except OSError, e: + if e.errno != errno.EEXIST: + raise + else: + raise + + # Fall through to here if stat succeeded or open raised EEXIST. + raise ExternalClashError('Name clash prevented file creation: %s' % + path) + + def _refresh(self): + """Update table of contents mapping.""" + self._toc = {} + for subdir in ('new', 'cur'): + subdir_path = os.path.join(self._path, subdir) + for entry in os.listdir(subdir_path): + p = os.path.join(subdir_path, entry) + if os.path.isdir(p): + continue + uniq = entry.split(self.colon)[0] + self._toc[uniq] = os.path.join(subdir, entry) + + def _lookup(self, key): + """Use TOC to return subpath for given key, or raise a KeyError.""" + try: + if os.path.exists(os.path.join(self._path, self._toc[key])): + return self._toc[key] + except KeyError: + pass + self._refresh() + try: + return self._toc[key] + except KeyError: + raise KeyError('No message with key: %s' % key) + + # This method is for backward compatibility only. + def next(self): + """Return the next message in a one-time iteration.""" + if not hasattr(self, '_onetime_keys'): + self._onetime_keys = self.iterkeys() + while True: + try: + return self[self._onetime_keys.next()] + except StopIteration: + return None + except KeyError: + continue + + +class _singlefileMailbox(Mailbox): + """A single-file mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize a single-file mailbox.""" + Mailbox.__init__(self, path, factory, create) + try: + f = open(self._path, 'rb+') + except IOError, e: + if e.errno == errno.ENOENT: + if create: + f = open(self._path, 'wb+') + else: + raise NoSuchMailboxError(self._path) + elif e.errno == errno.EACCES: + f = open(self._path, 'rb') + else: + raise + self._file = f + self._toc = None + self._next_key = 0 + self._pending = False # No changes require rewriting the file. + self._locked = False + + def add(self, message): + """Add message and return assigned key.""" + self._lookup() + self._toc[self._next_key] = self._append_message(message) + self._next_key += 1 + self._pending = True + return self._next_key - 1 + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + del self._toc[key] + self._pending = True + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + self._toc[key] = self._append_message(message) + self._pending = True + + def iterkeys(self): + """Return an iterator over keys.""" + self._lookup() + for key in self._toc.keys(): + yield key + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._lookup() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._lookup() + return len(self._toc) + + def lock(self): + """Lock the mailbox.""" + if not self._locked: + _lock_file(self._file) + self._locked = True + + def unlock(self): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + self._locked = False + + def flush(self): + """Write any pending changes to disk.""" + if not self._pending: + return + self._lookup() + new_file = _create_temporary(self._path) + try: + new_toc = {} + self._pre_mailbox_hook(new_file) + for key in sorted(self._toc.keys()): + start, stop = self._toc[key] + self._file.seek(start) + self._pre_message_hook(new_file) + new_start = new_file.tell() + while True: + buffer = self._file.read(min(4096, + stop - self._file.tell())) + if buffer == '': + break + new_file.write(buffer) + new_toc[key] = (new_start, new_file.tell()) + self._post_message_hook(new_file) + except: + new_file.close() + os.remove(new_file.name) + raise + _sync_close(new_file) + # self._file is about to get replaced, so no need to sync. + self._file.close() + try: + os.rename(new_file.name, self._path) + except OSError, e: + if e.errno == errno.EEXIST or \ + (os.name == 'os2' and e.errno == errno.EACCES): + os.remove(self._path) + os.rename(new_file.name, self._path) + else: + raise + self._file = open(self._path, 'rb+') + self._toc = new_toc + self._pending = False + if self._locked: + _lock_file(self._file, dotlock=False) + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + return + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + return + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + return + + def close(self): + """Flush and close the mailbox.""" + self.flush() + if self._locked: + self.unlock() + self._file.close() # Sync has been done by self.flush() above. + + def _lookup(self, key=None): + """Return (start, stop) or raise KeyError.""" + if self._toc is None: + self._generate_toc() + if key is not None: + try: + return self._toc[key] + except KeyError: + raise KeyError('No message with key: %s' % key) + + def _append_message(self, message): + """Append message to mailbox and return (start, stop) offsets.""" + self._file.seek(0, 2) + self._pre_message_hook(self._file) + offsets = self._install_message(message) + self._post_message_hook(self._file) + self._file.flush() + return offsets + + + +class _mboxMMDF(_singlefileMailbox): + """An mbox or MMDF mailbox.""" + + _mangle_from_ = True + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + from_line = self._file.readline().replace(os.linesep, '') + string = self._file.read(stop - self._file.tell()) + msg = self._message_factory(string.replace(os.linesep, '\n')) + msg.set_from(from_line[5:]) + return msg + + def get_string(self, key, from_=False): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + string = self._file.read(stop - self._file.tell()) + return string.replace(os.linesep, '\n') + + def get_file(self, key, from_=False): + """Return a file-like representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + return _PartialFile(self._file, self._file.tell(), stop) + + def _install_message(self, message): + """Format a message and blindly write to self._file.""" + from_line = None + if isinstance(message, str) and message.startswith('From '): + newline = message.find('\n') + if newline != -1: + from_line = message[:newline] + message = message[newline + 1:] + else: + from_line = message + message = '' + elif isinstance(message, _mboxMMDFMessage): + from_line = 'From ' + message.get_from() + elif isinstance(message, email.Message.Message): + from_line = message.get_unixfrom() # May be None. + if from_line is None: + from_line = 'From MAILER-DAEMON %s' % time.asctime(time.gmtime()) + start = self._file.tell() + self._file.write(from_line + os.linesep) + self._dump_message(message, self._file, self._mangle_from_) + stop = self._file.tell() + return (start, stop) + + +class mbox(_mboxMMDF): + """A classic mbox mailbox.""" + + _mangle_from_ = True + + def __init__(self, path, factory=None, create=True): + """Initialize an mbox mailbox.""" + self._message_factory = mboxMessage + _mboxMMDF.__init__(self, path, factory, create) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + if f.tell() != 0: + f.write(os.linesep) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + while True: + line_pos = self._file.tell() + line = self._file.readline() + if line.startswith('From '): + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + starts.append(line_pos) + elif line == '': + stops.append(line_pos) + break + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + + +class MMDF(_mboxMMDF): + """An MMDF mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize an MMDF mailbox.""" + self._message_factory = MMDFMessage + _mboxMMDF.__init__(self, path, factory, create) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write('\001\001\001\001' + os.linesep) + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(os.linesep + '\001\001\001\001' + os.linesep) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line.startswith('\001\001\001\001' + os.linesep): + starts.append(next_pos) + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == '\001\001\001\001' + os.linesep: + stops.append(line_pos - len(os.linesep)) + break + elif line == '': + stops.append(line_pos) + break + elif line == '': + break + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + + +class MH(Mailbox): + """An MH mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize an MH instance.""" + Mailbox.__init__(self, path, factory, create) + if not os.path.exists(self._path): + if create: + os.mkdir(self._path, 0700) + os.close(os.open(os.path.join(self._path, '.mh_sequences'), + os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0600)) + else: + raise NoSuchMailboxError(self._path) + self._locked = False + + def add(self, message): + """Add message and return assigned key.""" + keys = self.keys() + if len(keys) == 0: + new_key = 1 + else: + new_key = max(keys) + 1 + new_path = os.path.join(self._path, str(new_key)) + f = _create_carefully(new_path) + try: + if self._locked: + _lock_file(f) + try: + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, new_key) + finally: + if self._locked: + _unlock_file(f) + finally: + _sync_close(f) + return new_key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) + try: + f = open(path, 'rb+') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + f.close() + os.remove(os.path.join(self._path, str(key))) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) + try: + f = open(path, 'rb+') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + os.close(os.open(path, os.O_WRONLY | os.O_TRUNC)) + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, key) + finally: + if self._locked: + _unlock_file(f) + finally: + _sync_close(f) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + try: + if self._locked: + f = open(os.path.join(self._path, str(key)), 'r+') + else: + f = open(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + msg = MHMessage(f) + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + for name, key_list in self.get_sequences(): + if key in key_list: + msg.add_sequence(name) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + try: + if self._locked: + f = open(os.path.join(self._path, str(key)), 'r+') + else: + f = open(os.path.join(self._path, str(key)), 'r') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + return f.read() + finally: + if self._locked: + _unlock_file(f) + finally: + f.close() + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + try: + f = open(os.path.join(self._path, str(key)), 'rb') + except IOError, e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + return iter(sorted(int(entry) for entry in os.listdir(self._path) + if entry.isdigit())) + + def has_key(self, key): + """Return True if the keyed message exists, False otherwise.""" + return os.path.exists(os.path.join(self._path, str(key))) + + def __len__(self): + """Return a count of messages in the mailbox.""" + return len(list(self.iterkeys())) + + def lock(self): + """Lock the mailbox.""" + if not self._locked: + self._file = open(os.path.join(self._path, '.mh_sequences'), 'rb+') + _lock_file(self._file) + self._locked = True + + def unlock(self): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + _sync_close(self._file) + del self._file + self._locked = False + + def flush(self): + """Write any pending changes to the disk.""" + return + + def close(self): + """Flush and close the mailbox.""" + if self._locked: + self.unlock() + + def list_folders(self): + """Return a list of folder names.""" + result = [] + for entry in os.listdir(self._path): + if os.path.isdir(os.path.join(self._path, entry)): + result.append(entry) + return result + + def get_folder(self, folder): + """Return an MH instance for the named folder.""" + return MH(os.path.join(self._path, folder), + factory=self._factory, create=False) + + def add_folder(self, folder): + """Create a folder and return an MH instance representing it.""" + return MH(os.path.join(self._path, folder), + factory=self._factory) + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, folder) + entries = os.listdir(path) + if entries == ['.mh_sequences']: + os.remove(os.path.join(path, '.mh_sequences')) + elif entries == []: + pass + else: + raise NotEmptyError('Folder not empty: %s' % self._path) + os.rmdir(path) + + def get_sequences(self): + """Return a name-to-key-list dictionary to define each sequence.""" + results = {} + f = open(os.path.join(self._path, '.mh_sequences'), 'r') + try: + all_keys = set(self.keys()) + for line in f: + try: + name, contents = line.split(':') + keys = set() + for spec in contents.split(): + if spec.isdigit(): + keys.add(int(spec)) + else: + start, stop = (int(x) for x in spec.split('-')) + keys.update(range(start, stop + 1)) + results[name] = [key for key in sorted(keys) \ + if key in all_keys] + if len(results[name]) == 0: + del results[name] + except ValueError: + raise FormatError('Invalid sequence specification: %s' % + line.rstrip()) + finally: + f.close() + return results + + def set_sequences(self, sequences): + """Set sequences using the given name-to-key-list dictionary.""" + f = open(os.path.join(self._path, '.mh_sequences'), 'r+') + try: + os.close(os.open(f.name, os.O_WRONLY | os.O_TRUNC)) + for name, keys in sequences.iteritems(): + if len(keys) == 0: + continue + f.write('%s:' % name) + prev = None + completing = False + for key in sorted(set(keys)): + if key - 1 == prev: + if not completing: + completing = True + f.write('-') + elif completing: + completing = False + f.write('%s %s' % (prev, key)) + else: + f.write(' %s' % key) + prev = key + if completing: + f.write(str(prev) + '\n') + else: + f.write('\n') + finally: + _sync_close(f) + + def pack(self): + """Re-name messages to eliminate numbering gaps. Invalidates keys.""" + sequences = self.get_sequences() + prev = 0 + changes = [] + for key in self.iterkeys(): + if key - 1 != prev: + changes.append((key, prev + 1)) + if hasattr(os, 'link'): + os.link(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + os.unlink(os.path.join(self._path, str(key))) + else: + os.rename(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + prev += 1 + self._next_key = prev + 1 + if len(changes) == 0: + return + for name, key_list in sequences.items(): + for old, new in changes: + if old in key_list: + key_list[key_list.index(old)] = new + self.set_sequences(sequences) + + def _dump_sequences(self, message, key): + """Inspect a new MHMessage and update sequences appropriately.""" + pending_sequences = message.get_sequences() + all_sequences = self.get_sequences() + for name, key_list in all_sequences.iteritems(): + if name in pending_sequences: + key_list.append(key) + elif key in key_list: + del key_list[key_list.index(key)] + for sequence in pending_sequences: + if sequence not in all_sequences: + all_sequences[sequence] = [key] + self.set_sequences(all_sequences) + + +class Babyl(_singlefileMailbox): + """An Rmail-style Babyl mailbox.""" + + _special_labels = frozenset(('unseen', 'deleted', 'filed', 'answered', + 'forwarded', 'edited', 'resent')) + + def __init__(self, path, factory=None, create=True): + """Initialize a Babyl mailbox.""" + _singlefileMailbox.__init__(self, path, factory, create) + self._labels = {} + + def add(self, message): + """Add message and return assigned key.""" + key = _singlefileMailbox.add(self, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + return key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.remove(self, key) + if key in self._labels: + del self._labels[key] + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.__setitem__(self, key, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + self._file.readline() # Skip '1,' line specifying labels. + original_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == '*** EOOH ***' + os.linesep or line == '': + break + original_headers.write(line.replace(os.linesep, '\n')) + visible_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == os.linesep or line == '': + break + visible_headers.write(line.replace(os.linesep, '\n')) + body = self._file.read(stop - self._file.tell()).replace(os.linesep, + '\n') + msg = BabylMessage(original_headers.getvalue() + body) + msg.set_visible(visible_headers.getvalue()) + if key in self._labels: + msg.set_labels(self._labels[key]) + return msg + + def get_string(self, key): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + self._file.readline() # Skip '1,' line specifying labels. + original_headers = StringIO.StringIO() + while True: + line = self._file.readline() + if line == '*** EOOH ***' + os.linesep or line == '': + break + original_headers.write(line.replace(os.linesep, '\n')) + while True: + line = self._file.readline() + if line == os.linesep or line == '': + break + return original_headers.getvalue() + \ + self._file.read(stop - self._file.tell()).replace(os.linesep, + '\n') + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + return StringIO.StringIO(self.get_string(key).replace('\n', + os.linesep)) + + def get_labels(self): + """Return a list of user-defined labels in the mailbox.""" + self._lookup() + labels = set() + for label_list in self._labels.values(): + labels.update(label_list) + labels.difference_update(self._special_labels) + return list(labels) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + label_lists = [] + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == '\037\014' + os.linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + starts.append(next_pos) + labels = [label.strip() for label + in self._file.readline()[1:].split(',') + if label.strip() != ''] + label_lists.append(labels) + elif line == '\037' or line == '\037' + os.linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(os.linesep)) + elif line == '': + stops.append(line_pos - len(os.linesep)) + break + self._toc = dict(enumerate(zip(starts, stops))) + self._labels = dict(enumerate(label_lists)) + self._next_key = len(self._toc) + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + f.write('BABYL OPTIONS:%sVersion: 5%sLabels:%s%s\037' % + (os.linesep, os.linesep, ','.join(self.get_labels()), + os.linesep)) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write('\014' + os.linesep) + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(os.linesep + '\037') + + def _install_message(self, message): + """Write message contents and return (start, stop).""" + start = self._file.tell() + if isinstance(message, BabylMessage): + special_labels = [] + labels = [] + for label in message.get_labels(): + if label in self._special_labels: + special_labels.append(label) + else: + labels.append(label) + self._file.write('1') + for label in special_labels: + self._file.write(', ' + label) + self._file.write(',,') + for label in labels: + self._file.write(' ' + label + ',') + self._file.write(os.linesep) + else: + self._file.write('1,,' + os.linesep) + if isinstance(message, email.Message.Message): + orig_buffer = StringIO.StringIO() + orig_generator = email.Generator.Generator(orig_buffer, False, 0) + orig_generator.flatten(message) + orig_buffer.seek(0) + while True: + line = orig_buffer.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + break + self._file.write('*** EOOH ***' + os.linesep) + if isinstance(message, BabylMessage): + vis_buffer = StringIO.StringIO() + vis_generator = email.Generator.Generator(vis_buffer, False, 0) + vis_generator.flatten(message.get_visible()) + while True: + line = vis_buffer.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + break + else: + orig_buffer.seek(0) + while True: + line = orig_buffer.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + break + while True: + buffer = orig_buffer.read(4096) # Buffer size is arbitrary. + if buffer == '': + break + self._file.write(buffer.replace('\n', os.linesep)) + elif isinstance(message, str): + body_start = message.find('\n\n') + 2 + if body_start - 2 != -1: + self._file.write(message[:body_start].replace('\n', + os.linesep)) + self._file.write('*** EOOH ***' + os.linesep) + self._file.write(message[:body_start].replace('\n', + os.linesep)) + self._file.write(message[body_start:].replace('\n', + os.linesep)) + else: + self._file.write('*** EOOH ***' + os.linesep + os.linesep) + self._file.write(message.replace('\n', os.linesep)) + elif hasattr(message, 'readline'): + original_pos = message.tell() + first_pass = True + while True: + line = message.readline() + self._file.write(line.replace('\n', os.linesep)) + if line == '\n' or line == '': + self._file.write('*** EOOH ***' + os.linesep) + if first_pass: + first_pass = False + message.seek(original_pos) + else: + break + while True: + buffer = message.read(4096) # Buffer size is arbitrary. + if buffer == '': + break + self._file.write(buffer.replace('\n', os.linesep)) + else: + raise TypeError('Invalid message type: %s' % type(message)) + stop = self._file.tell() + return (start, stop) + + +class Message(email.Message.Message): + """Message with mailbox-format-specific properties.""" + + def __init__(self, message=None): + """Initialize a Message instance.""" + if isinstance(message, email.Message.Message): + self._become_message(copy.deepcopy(message)) + if isinstance(message, Message): + message._explain_to(self) + elif isinstance(message, str): + self._become_message(email.message_from_string(message)) + elif hasattr(message, "read"): + self._become_message(email.message_from_file(message)) + elif message is None: + email.Message.Message.__init__(self) + else: + raise TypeError('Invalid message type: %s' % type(message)) + + def _become_message(self, message): + """Assume the non-format-specific state of message.""" + for name in ('_headers', '_unixfrom', '_payload', '_charset', + 'preamble', 'epilogue', 'defects', '_default_type'): + self.__dict__[name] = message.__dict__[name] + + def _explain_to(self, message): + """Copy format-specific state to message insofar as possible.""" + if isinstance(message, Message): + return # There's nothing format-specific to explain. + else: + raise TypeError('Cannot convert to specified type') + + +class MaildirMessage(Message): + """Message with Maildir-specific properties.""" + + def __init__(self, message=None): + """Initialize a MaildirMessage instance.""" + self._subdir = 'new' + self._info = '' + self._date = time.time() + Message.__init__(self, message) + + def get_subdir(self): + """Return 'new' or 'cur'.""" + return self._subdir + + def set_subdir(self, subdir): + """Set subdir to 'new' or 'cur'.""" + if subdir == 'new' or subdir == 'cur': + self._subdir = subdir + else: + raise ValueError("subdir must be 'new' or 'cur': %s" % subdir) + + def get_flags(self): + """Return as a string the flags that are set.""" + if self._info.startswith('2,'): + return self._info[2:] + else: + return '' + + def set_flags(self, flags): + """Set the given flags and unset all others.""" + self._info = '2,' + ''.join(sorted(flags)) + + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) + + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" + if self.get_flags() != '': + self.set_flags(''.join(set(self.get_flags()) - set(flag))) + + def get_date(self): + """Return delivery date of message, in seconds since the epoch.""" + return self._date + + def set_date(self, date): + """Set delivery date of message, in seconds since the epoch.""" + try: + self._date = float(date) + except ValueError: + raise TypeError("can't convert to float: %s" % date) + + def get_info(self): + """Get the message's "info" as a string.""" + return self._info + + def set_info(self, info): + """Set the message's "info" string.""" + if isinstance(info, str): + self._info = info + else: + raise TypeError('info must be a string: %s' % type(info)) + + def _explain_to(self, message): + """Copy Maildir-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + message.set_flags(self.get_flags()) + message.set_subdir(self.get_subdir()) + message.set_date(self.get_date()) + elif isinstance(message, _mboxMMDFMessage): + flags = set(self.get_flags()) + if 'S' in flags: + message.add_flag('R') + if self.get_subdir() == 'cur': + message.add_flag('O') + if 'T' in flags: + message.add_flag('D') + if 'F' in flags: + message.add_flag('F') + if 'R' in flags: + message.add_flag('A') + message.set_from('MAILER-DAEMON', time.gmtime(self.get_date())) + elif isinstance(message, MHMessage): + flags = set(self.get_flags()) + if 'S' not in flags: + message.add_sequence('unseen') + if 'R' in flags: + message.add_sequence('replied') + if 'F' in flags: + message.add_sequence('flagged') + elif isinstance(message, BabylMessage): + flags = set(self.get_flags()) + if 'S' not in flags: + message.add_label('unseen') + if 'T' in flags: + message.add_label('deleted') + if 'R' in flags: + message.add_label('answered') + if 'P' in flags: + message.add_label('forwarded') + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class _mboxMMDFMessage(Message): + """Message with mbox- or MMDF-specific properties.""" + + def __init__(self, message=None): + """Initialize an mboxMMDFMessage instance.""" + self.set_from('MAILER-DAEMON', True) + if isinstance(message, email.Message.Message): + unixfrom = message.get_unixfrom() + if unixfrom is not None and unixfrom.startswith('From '): + self.set_from(unixfrom[5:]) + Message.__init__(self, message) + + def get_from(self): + """Return contents of "From " line.""" + return self._from + + def set_from(self, from_, time_=None): + """Set "From " line, formatting and appending time_ if specified.""" + if time_ is not None: + if time_ is True: + time_ = time.gmtime() + from_ += ' ' + time.asctime(time_) + self._from = from_ + + def get_flags(self): + """Return as a string the flags that are set.""" + return self.get('Status', '') + self.get('X-Status', '') + + def set_flags(self, flags): + """Set the given flags and unset all others.""" + flags = set(flags) + status_flags, xstatus_flags = '', '' + for flag in ('R', 'O'): + if flag in flags: + status_flags += flag + flags.remove(flag) + for flag in ('D', 'F', 'A'): + if flag in flags: + xstatus_flags += flag + flags.remove(flag) + xstatus_flags += ''.join(sorted(flags)) + try: + self.replace_header('Status', status_flags) + except KeyError: + self.add_header('Status', status_flags) + try: + self.replace_header('X-Status', xstatus_flags) + except KeyError: + self.add_header('X-Status', xstatus_flags) + + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) + + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" + if 'Status' in self or 'X-Status' in self: + self.set_flags(''.join(set(self.get_flags()) - set(flag))) + + def _explain_to(self, message): + """Copy mbox- or MMDF-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + flags = set(self.get_flags()) + if 'O' in flags: + message.set_subdir('cur') + if 'F' in flags: + message.add_flag('F') + if 'A' in flags: + message.add_flag('R') + if 'R' in flags: + message.add_flag('S') + if 'D' in flags: + message.add_flag('T') + del message['status'] + del message['x-status'] + maybe_date = ' '.join(self.get_from().split()[-5:]) + try: + message.set_date(calendar.timegm(time.strptime(maybe_date, + '%a %b %d %H:%M:%S %Y'))) + except (ValueError, OverflowError): + pass + elif isinstance(message, _mboxMMDFMessage): + message.set_flags(self.get_flags()) + message.set_from(self.get_from()) + elif isinstance(message, MHMessage): + flags = set(self.get_flags()) + if 'R' not in flags: + message.add_sequence('unseen') + if 'A' in flags: + message.add_sequence('replied') + if 'F' in flags: + message.add_sequence('flagged') + del message['status'] + del message['x-status'] + elif isinstance(message, BabylMessage): + flags = set(self.get_flags()) + if 'R' not in flags: + message.add_label('unseen') + if 'D' in flags: + message.add_label('deleted') + if 'A' in flags: + message.add_label('answered') + del message['status'] + del message['x-status'] + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class mboxMessage(_mboxMMDFMessage): + """Message with mbox-specific properties.""" + + +class MHMessage(Message): + """Message with MH-specific properties.""" + + def __init__(self, message=None): + """Initialize an MHMessage instance.""" + self._sequences = [] + Message.__init__(self, message) + + def get_sequences(self): + """Return a list of sequences that include the message.""" + return self._sequences[:] + + def set_sequences(self, sequences): + """Set the list of sequences that include the message.""" + self._sequences = list(sequences) + + def add_sequence(self, sequence): + """Add sequence to list of sequences including the message.""" + if isinstance(sequence, str): + if not sequence in self._sequences: + self._sequences.append(sequence) + else: + raise TypeError('sequence must be a string: %s' % type(sequence)) + + def remove_sequence(self, sequence): + """Remove sequence from the list of sequences including the message.""" + try: + self._sequences.remove(sequence) + except ValueError: + pass + + def _explain_to(self, message): + """Copy MH-specific state to message insofar as possible.""" + i... [truncated message content] |
From: <pj...@us...> - 2009-05-30 06:42:35
|
Revision: 6427 http://jython.svn.sourceforge.net/jython/?rev=6427&view=rev Author: pjenvey Date: 2009-05-30 06:41:33 +0000 (Sat, 30 May 2009) Log Message: ----------- o close some mailbox file handles o skip test_shutil.test_on_error which is questionable for Jython Modified Paths: -------------- trunk/jython/Lib/mailbox.py trunk/jython/Lib/test/test_old_mailbox.py trunk/jython/Lib/test/test_shutil.py Modified: trunk/jython/Lib/mailbox.py =================================================================== --- trunk/jython/Lib/mailbox.py 2009-05-30 06:40:55 UTC (rev 6426) +++ trunk/jython/Lib/mailbox.py 2009-05-30 06:41:33 UTC (rev 6427) @@ -955,6 +955,7 @@ if self._locked: _unlock_file(self._file) _sync_close(self._file) + self._file.close() del self._file self._locked = False @@ -1780,6 +1781,7 @@ def close(self): """Close the file.""" + self._file.close() del self._file def _read(self, size, read_method): Modified: trunk/jython/Lib/test/test_old_mailbox.py =================================================================== --- trunk/jython/Lib/test/test_old_mailbox.py 2009-05-30 06:40:55 UTC (rev 6426) +++ trunk/jython/Lib/test/test_old_mailbox.py 2009-05-30 06:41:33 UTC (rev 6427) @@ -1,6 +1,7 @@ # This set of tests exercises the backward-compatibility class # in mailbox.py (the ones without write support). +from __future__ import with_statement import mailbox import os import time @@ -63,6 +64,10 @@ self._msgfiles.append(newname) return tmpname + def assert_and_close(self, message): + self.assert_(message is not None) + message.fp.close() + def test_empty_maildir(self): """Test an empty maildir mailbox""" # Test for regression on bug #117490: @@ -75,7 +80,7 @@ self.createMessage("cur") self.mbox = mailbox.Maildir(test_support.TESTFN) self.assert_(len(self.mbox) == 1) - self.assert_(self.mbox.next() is not None) + self.assert_and_close(self.mbox.next()) self.assert_(self.mbox.next() is None) self.assert_(self.mbox.next() is None) @@ -83,7 +88,7 @@ self.createMessage("new") self.mbox = mailbox.Maildir(test_support.TESTFN) self.assert_(len(self.mbox) == 1) - self.assert_(self.mbox.next() is not None) + self.assert_and_close(self.mbox.next()) self.assert_(self.mbox.next() is None) self.assert_(self.mbox.next() is None) @@ -92,8 +97,8 @@ self.createMessage("new") self.mbox = mailbox.Maildir(test_support.TESTFN) self.assert_(len(self.mbox) == 2) - self.assert_(self.mbox.next() is not None) - self.assert_(self.mbox.next() is not None) + self.assert_and_close(self.mbox.next()) + self.assert_and_close(self.mbox.next()) self.assert_(self.mbox.next() is None) self.assert_(self.mbox.next() is None) @@ -102,11 +107,12 @@ import email.Parser fname = self.createMessage("cur", True) n = 0 - for msg in mailbox.PortableUnixMailbox(open(fname), - email.Parser.Parser().parse): - n += 1 - self.assertEqual(msg["subject"], "Simple Test") - self.assertEqual(len(str(msg)), len(FROM_)+len(DUMMY_MESSAGE)) + with open(fname) as fp: + for msg in mailbox.PortableUnixMailbox(fp, + email.Parser.Parser().parse): + n += 1 + self.assertEqual(msg["subject"], "Simple Test") + self.assertEqual(len(str(msg)), len(FROM_)+len(DUMMY_MESSAGE)) self.assertEqual(n, 1) class MboxTestCase(unittest.TestCase): @@ -139,7 +145,10 @@ """) f.close() box = mailbox.UnixMailbox(open(self._path, 'r')) - self.assert_(len(list(iter(box))) == 4) + messages = list(iter(box)) + self.assert_(len(messages) == 4) + for message in messages: + message.fp.close() # XXX We still need more tests! Modified: trunk/jython/Lib/test/test_shutil.py =================================================================== --- trunk/jython/Lib/test/test_shutil.py 2009-05-30 06:40:55 UTC (rev 6426) +++ trunk/jython/Lib/test/test_shutil.py 2009-05-30 06:41:33 UTC (rev 6427) @@ -18,8 +18,11 @@ # See bug #1071513 for why we don't run this on cygwin # and bug #1076467 for why we don't run this as root. + # XXX: Fails on Jython because Java resets the S_IREAD permission + # when removing the file if (hasattr(os, 'chmod') and sys.platform[:6] != 'cygwin' - and not (hasattr(os, 'geteuid') and os.geteuid() == 0)): + and not (hasattr(os, 'geteuid') and os.geteuid() == 0) + and (not test_support.is_jython or os._name != 'nt')): def test_on_error(self): self.errorState = 0 os.mkdir(TESTFN) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-06-04 02:50:55
|
Revision: 6446 http://jython.svn.sourceforge.net/jython/?rev=6446&view=rev Author: pjenvey Date: 2009-06-04 02:50:50 +0000 (Thu, 04 Jun 2009) Log Message: ----------- workaround lack of os.fstat Modified Paths: -------------- trunk/jython/Lib/SimpleHTTPServer.py trunk/jython/Lib/posixpath.py trunk/jython/Lib/tarfile.py trunk/jython/Lib/test/test_largefile.py Modified: trunk/jython/Lib/SimpleHTTPServer.py =================================================================== --- trunk/jython/Lib/SimpleHTTPServer.py 2009-06-04 02:38:34 UTC (rev 6445) +++ trunk/jython/Lib/SimpleHTTPServer.py 2009-06-04 02:50:50 UTC (rev 6446) @@ -91,7 +91,7 @@ return None self.send_response(200) self.send_header("Content-type", ctype) - fs = os.fstat(f.fileno()) + fs = os.fstat(f.fileno()) if hasattr(os, 'fstat') else os.stat(path) self.send_header("Content-Length", str(fs[6])) self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) self.end_headers() Modified: trunk/jython/Lib/posixpath.py =================================================================== --- trunk/jython/Lib/posixpath.py 2009-06-04 02:38:34 UTC (rev 6445) +++ trunk/jython/Lib/posixpath.py 2009-06-04 02:50:50 UTC (rev 6446) @@ -230,8 +230,8 @@ return samestat(s1, s2) -# XXX: Plain Jython lacks fstat and st_ino/st_dev -if os.name != 'java': +# XXX: Jython currently lacks fstat +if hasattr(os, 'fstat'): # Are two open files really referencing the same file? # (Not necessarily the same file descriptor!) @@ -244,6 +244,7 @@ __all__.append("sameopenfile") +# XXX: Pure Java stat lacks st_ino/st_dev if os._native_posix: # Are two stat buffers (obtained from stat, fstat or lstat) # describing the same file? Modified: trunk/jython/Lib/tarfile.py =================================================================== --- trunk/jython/Lib/tarfile.py 2009-06-04 02:38:34 UTC (rev 6445) +++ trunk/jython/Lib/tarfile.py 2009-06-04 02:50:50 UTC (rev 6446) @@ -1336,8 +1336,11 @@ statres = os.lstat(name) else: statres = os.stat(name) + elif hasattr(os, 'fstat'): + statres = os.fstat(fileobj.fileno()) else: - statres = os.fstat(fileobj.fileno()) + raise NotImplementedError('fileobj argument not supported on this ' + 'platform (no os.fstat)') linkname = "" stmd = statres.st_mode Modified: trunk/jython/Lib/test/test_largefile.py =================================================================== --- trunk/jython/Lib/test/test_largefile.py 2009-06-04 02:38:34 UTC (rev 6445) +++ trunk/jython/Lib/test/test_largefile.py 2009-06-04 02:50:50 UTC (rev 6446) @@ -75,7 +75,7 @@ f.seek(size) f.write('a') f.flush() - if not test_support.is_jython: + if hasattr(os, 'fstat'): if test_support.verbose: print 'check file size with os.fstat' expect(os.fstat(f.fileno())[stat.ST_SIZE], size+1) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-06-07 00:11:08
|
Revision: 6464 http://jython.svn.sourceforge.net/jython/?rev=6464&view=rev Author: pjenvey Date: 2009-06-07 00:10:28 +0000 (Sun, 07 Jun 2009) Log Message: ----------- make cmdline2list private Modified Paths: -------------- trunk/jython/Lib/subprocess.py trunk/jython/Lib/test/test_subprocess_jy.py Modified: trunk/jython/Lib/subprocess.py =================================================================== --- trunk/jython/Lib/subprocess.py 2009-06-06 21:41:26 UTC (rev 6463) +++ trunk/jython/Lib/subprocess.py 2009-06-07 00:10:28 UTC (rev 6464) @@ -547,11 +547,11 @@ # Parse command line arguments for Windows _win_oses = ['nt'] - _cmdline2list = None + _cmdline2listimpl = None _escape_args = None _shell_command = None - def cmdline2list(cmdline): + def _cmdline2list(cmdline): """Build an argv list from a Microsoft shell style cmdline str The reverse of list2cmdline that follows the same MS C runtime @@ -611,13 +611,13 @@ """Setup the shell command and the command line argument escape function depending on the underlying platform """ - global _cmdline2list, _escape_args, _shell_command + global _cmdline2listimpl, _escape_args, _shell_command if os._name in _win_oses: - _cmdline2list = cmdline2list + _cmdline2listimpl = _cmdline2list _escape_args = lambda args: [list2cmdline([arg]) for arg in args] else: - _cmdline2list = lambda args: [args] + _cmdline2listimpl = lambda args: [args] _escape_args = lambda args: args os_info = os._os_map.get(os._name) @@ -1226,7 +1226,7 @@ """Execute program (Java version)""" if isinstance(args, types.StringTypes): - args = _cmdline2list(args) + args = _cmdline2listimpl(args) else: args = list(args) # NOTE: CPython posix (execv) will str() any unicode Modified: trunk/jython/Lib/test/test_subprocess_jy.py =================================================================== --- trunk/jython/Lib/test/test_subprocess_jy.py 2009-06-06 21:41:26 UTC (rev 6463) +++ trunk/jython/Lib/test/test_subprocess_jy.py 2009-06-07 00:10:28 UTC (rev 6464) @@ -3,7 +3,7 @@ import os import sys from test import test_support -from subprocess import PIPE, Popen, cmdline2list +from subprocess import PIPE, Popen, _cmdline2list class EnvironmentInheritanceTest(unittest.TestCase): @@ -65,7 +65,7 @@ def test_cmdline2list(self): for cmdline, argv in self.cmdlines.iteritems(): - self.assertEqual(cmdline2list(cmdline), argv) + self.assertEqual(_cmdline2list(cmdline), argv) def test_main(): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2009-07-03 15:45:09
|
Revision: 6506 http://jython.svn.sourceforge.net/jython/?rev=6506&view=rev Author: amak Date: 2009-07-03 15:45:08 +0000 (Fri, 03 Jul 2009) Log Message: ----------- 1. Added support for the AI_PASSIVE flag 2. Added support for the AI_CANONNAME flag 3. Added unit tests for same 4. Split out getaddrinfo unit tests into their own test case class Modified Paths: -------------- trunk/jython/Lib/socket.py trunk/jython/Lib/test/test_socket.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2009-06-30 06:11:20 UTC (rev 6505) +++ trunk/jython/Lib/socket.py 2009-07-03 15:45:08 UTC (rev 6506) @@ -76,12 +76,12 @@ # Javax.net.ssl classes import javax.net.ssl.SSLSocketFactory -# Javax.net.ssl exceptions -javax.net.ssl.SSLException -javax.net.ssl.SSLHandshakeException -javax.net.ssl.SSLKeyException -javax.net.ssl.SSLPeerUnverifiedException -javax.net.ssl.SSLProtocolException +# Javax.net.ssl exceptions +javax.net.ssl.SSLException +javax.net.ssl.SSLHandshakeException +javax.net.ssl.SSLKeyException +javax.net.ssl.SSLPeerUnverifiedException +javax.net.ssl.SSLProtocolException import org.python.core.io.DatagramSocketIO import org.python.core.io.ServerSocketIO @@ -128,16 +128,16 @@ (java.nio.channels.NotYetConnectedException, ALL) : None, (java.nio.channels.UnresolvedAddressException, ALL) : lambda: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), (java.nio.channels.UnsupportedAddressTypeException, ALL) : None, - -# These error codes are currently wrong: getting them correct is going to require -# some investigation. Cpython 2.6 introduced extensive SSL support. - -(javax.net.ssl.SSLException, ALL) : lambda: sslerror(-1, 'SSL exception'), -(javax.net.ssl.SSLHandshakeException, ALL) : lambda: sslerror(-1, 'SSL handshake exception'), -(javax.net.ssl.SSLKeyException, ALL) : lambda: sslerror(-1, 'SSL key exception'), -(javax.net.ssl.SSLPeerUnverifiedException, ALL) : lambda: sslerror(-1, 'SSL peer unverified exception'), -(javax.net.ssl.SSLProtocolException, ALL) : lambda: sslerror(-1, 'SSL protocol exception'), +# These error codes are currently wrong: getting them correct is going to require +# some investigation. Cpython 2.6 introduced extensive SSL support. + +(javax.net.ssl.SSLException, ALL) : lambda: sslerror(-1, 'SSL exception'), +(javax.net.ssl.SSLHandshakeException, ALL) : lambda: sslerror(-1, 'SSL handshake exception'), +(javax.net.ssl.SSLKeyException, ALL) : lambda: sslerror(-1, 'SSL key exception'), +(javax.net.ssl.SSLPeerUnverifiedException, ALL) : lambda: sslerror(-1, 'SSL peer unverified exception'), +(javax.net.ssl.SSLProtocolException, ALL) : lambda: sslerror(-1, 'SSL protocol exception'), + } def would_block_error(exc=None): @@ -168,7 +168,14 @@ AF_INET6 = 23 AI_PASSIVE=1 +AI_CANONNAME=2 +# For some reason, probably historical, SOCK_DGRAM and SOCK_STREAM are opposite values of what they are on cpython. +# I.E. The following is the way they are on cpython +# SOCK_STREAM = 1 +# SOCK_DGRAM = 2 +# At some point, we should probably switch them around, which *should* not affect anybody + SOCK_DGRAM = 1 SOCK_STREAM = 2 SOCK_RAW = 3 # not supported @@ -189,10 +196,10 @@ SO_TIMEOUT = 128 TCP_NODELAY = 256 - -INADDR_ANY = "0.0.0.0" -INADDR_BROADCAST = "255.255.255.255" +INADDR_ANY = "0.0.0.0" +INADDR_BROADCAST = "255.255.255.255" + # Options with negative constants are not supported # They are being added here so that code that refers to them # will not break with an AttributeError @@ -209,11 +216,11 @@ SO_SNDTIMEO = -512 SO_TYPE = -1024 SO_USELOOPBACK = -2048 - + __all__ = ['AF_UNSPEC', 'AF_INET', 'AF_INET6', 'AI_PASSIVE', 'SOCK_DGRAM', 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', 'SO_BROADCAST', 'SO_ERROR', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', - 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', + 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', 'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP', 'SocketType', 'error', 'herror', 'gaierror', 'timeout', 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', @@ -222,15 +229,15 @@ 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', ] -def _constant_to_name(const_value): - sock_module = sys.modules['socket'] - try: - for name in dir(sock_module): - if getattr(sock_module, name) is const_value: - return name - return "Unknown" - finally: - sock_module = None +def _constant_to_name(const_value): + sock_module = sys.modules['socket'] + try: + for name in dir(sock_module): + if getattr(sock_module, name) is const_value: + return name + return "Unknown" + finally: + sock_module = None class _nio_impl: @@ -285,15 +292,15 @@ class _client_socket_impl(_nio_impl): - options = { + options = { (SOL_SOCKET, SO_KEEPALIVE): 'KeepAlive', (SOL_SOCKET, SO_LINGER): 'SoLinger', (SOL_SOCKET, SO_OOBINLINE): 'OOBInline', (SOL_SOCKET, SO_RCVBUF): 'ReceiveBufferSize', (SOL_SOCKET, SO_REUSEADDR): 'ReuseAddress', (SOL_SOCKET, SO_SNDBUF): 'SendBufferSize', - (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', - (IPPROTO_TCP, TCP_NODELAY): 'TcpNoDelay', + (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', + (IPPROTO_TCP, TCP_NODELAY): 'TcpNoDelay', } def __init__(self, socket=None): @@ -361,10 +368,10 @@ class _server_socket_impl(_nio_impl): - options = { + options = { (SOL_SOCKET, SO_RCVBUF): 'ReceiveBufferSize', (SOL_SOCKET, SO_REUSEADDR): 'ReuseAddress', - (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', + (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', } def __init__(self, host, port, backlog, reuse_addr): @@ -399,12 +406,12 @@ class _datagram_socket_impl(_nio_impl): - options = { + options = { (SOL_SOCKET, SO_BROADCAST): 'Broadcast', (SOL_SOCKET, SO_RCVBUF): 'ReceiveBufferSize', (SOL_SOCKET, SO_REUSEADDR): 'ReuseAddress', (SOL_SOCKET, SO_SNDBUF): 'SendBufferSize', - (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', + (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', } def __init__(self, port=None, address=None, reuse_addr=0): @@ -515,7 +522,7 @@ has_ipv6 = False # Name and address functions - + def _gethostbyaddr(name): # This is as close as I can get; at least the types are correct... addresses = java.net.InetAddress.getAllByName(gethostbyname(name)) @@ -576,12 +583,12 @@ assert family == AF_INET, "Only AF_INET sockets are currently supported on jython" assert type in (SOCK_DGRAM, SOCK_STREAM), "Only SOCK_STREAM and SOCK_DGRAM sockets are currently supported on jython" if type == SOCK_STREAM: - if protocol != 0: - assert protocol == IPPROTO_TCP, "Only IPPROTO_TCP supported on SOCK_STREAM sockets" + if protocol != 0: + assert protocol == IPPROTO_TCP, "Only IPPROTO_TCP supported on SOCK_STREAM sockets" return _tcpsocket() else: - if protocol != 0: - assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" + if protocol != 0: + assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" return _udpsocket() def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None): @@ -594,16 +601,23 @@ AF_INET6: lambda x: isinstance(x, java.net.Inet6Address), AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress), }[family]) - # Cant see a way to support AI_PASSIVE right now. - # if flags and flags & AI_PASSIVE: - # pass + if host == "": + host = java.net.InetAddress.getLocalHost().getHostName() + passive_mode = flags is not None and flags & AI_PASSIVE + canonname_mode = flags is not None and flags & AI_CANONNAME results = [] for a in java.net.InetAddress.getAllByName(host): if len([f for f in filter_fns if f(a)]): family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] + if passive_mode and not canonname_mode: + canonname = "" + else: + canonname = asPyString(a.getCanonicalHostName()) + if host is None and passive_mode and not canonname_mode: + sockname = INADDR_ANY + else: + sockname = asPyString(a.getHostAddress()) # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses - canonname = asPyString(a.getCanonicalHostName()) - sockname = asPyString(a.getHostAddress()) results.append((family, socktype, proto, canonname, (sockname, port))) return results except java.lang.Exception, jlx: @@ -674,9 +688,9 @@ close_lock = threading.Lock() def __init__(self): - self.timeout = _defaulttimeout - if self.timeout is not None: - self.mode = MODE_TIMEOUT + self.timeout = _defaulttimeout + if self.timeout is not None: + self.mode = MODE_TIMEOUT self.pending_options = { (SOL_SOCKET, SO_REUSEADDR): 0, } @@ -844,7 +858,7 @@ self.sock_impl.bind(bind_host, bind_port, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) self._config() # Configure timeouts, etc, now that the socket exists self.sock_impl.connect(host, port) - except java.lang.Exception, jlx: + except java.lang.Exception, jlx: raise _map_exception(jlx) def connect(self, addr): @@ -952,8 +966,8 @@ def bind(self, addr): try: assert not self.sock_impl - host, port = _unpack_address_tuple(addr) - if host == "": + host, port = _unpack_address_tuple(addr) + if host == "": host = INADDR_ANY host_address = java.net.InetAddress.getByName(host) self.sock_impl = _datagram_socket_impl(port, host_address, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) @@ -994,8 +1008,8 @@ if not self.sock_impl: self.sock_impl = _datagram_socket_impl() self._config() - host, port = _unpack_address_tuple(addr) - if host == "<broadcast>": + host, port = _unpack_address_tuple(addr) + if host == "<broadcast>": host = INADDR_BROADCAST byte_array = java.lang.String(data).getBytes('iso-8859-1') result = self.sock_impl.sendto(byte_array, host, port, flags) @@ -1409,11 +1423,11 @@ class ssl: - def __init__(self, plain_sock, keyfile=None, certfile=None): + def __init__(self, plain_sock, keyfile=None, certfile=None): try: self.ssl_sock = self._make_ssl_socket(plain_sock) self._in_buf = java.io.BufferedInputStream(self.ssl_sock.getInputStream()) - self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream()) + self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream()) except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -1429,7 +1443,7 @@ return ssl_socket def read(self, n=4096): - try: + try: data = jarray.zeros(n, 'b') m = self._in_buf.read(data, 0, n) if m <= 0: @@ -1441,7 +1455,7 @@ raise _map_exception(jlx) def write(self, s): - try: + try: self._out_buf.write(s) self._out_buf.flush() return len(s) @@ -1449,7 +1463,7 @@ raise _map_exception(jlx) def _get_server_cert(self): - try: + try: return self.ssl_sock.getSession().getPeerCertificates()[0] except java.lang.Exception, jlx: raise _map_exception(jlx) Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2009-06-30 06:11:20 UTC (rev 6505) +++ trunk/jython/Lib/test/test_socket.py 2009-07-03 15:45:08 UTC (rev 6506) @@ -2,6 +2,8 @@ AMAK: 20050515: This module is the test_socket.py from cpython 2.4, ported to jython. """ +import java + import unittest from test import test_support @@ -308,22 +310,6 @@ if not fqhn in all_host_names: self.fail("Error testing host resolution mechanisms.") - def testGetAddrInfo(self): - try: - socket.getaddrinfo(HOST, PORT, 9999) - except socket.gaierror, gaix: - self.failUnlessEqual(gaix[0], errno.EIO) - except Exception, x: - self.fail("getaddrinfo with bad family raised wrong exception: %s" % x) - else: - self.fail("getaddrinfo with bad family should have raised exception") - - addrinfos = socket.getaddrinfo(HOST, PORT) - for addrinfo in addrinfos: - family, socktype, proto, canonname, sockaddr = addrinfo - self.assert_(isinstance(canonname, str)) - self.assert_(isinstance(sockaddr[0], str)) - def testRefCountGetNameInfo(self): # Testing reference count for getnameinfo import sys @@ -529,7 +515,7 @@ class TestSocketOptions(unittest.TestCase): - def setUp(self): + def setUp(self): self.test_udp = self.test_tcp_client = self.test_tcp_server = 0 def _testSetAndGetOption(self, sock, level, option, values): @@ -621,7 +607,7 @@ class TestSupportedOptions(TestSocketOptions): - def testSO_BROADCAST(self): + def testSO_BROADCAST(self): self.test_udp = 1 self._testOption(socket.SOL_SOCKET, socket.SO_BROADCAST, [0, 1]) @@ -821,32 +807,32 @@ def _testDup(self): self.serv_conn.send(MSG) self.serv_conn.send('and ' + MSG) - -class UDPBindTest(unittest.TestCase): - + +class UDPBindTest(unittest.TestCase): + HOST = HOST PORT = PORT - def setUp(self): + def setUp(self): self.sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - + def testBindSpecific(self): self.sock.bind( (self.HOST, self.PORT) ) # Use a specific port actual_port = self.sock.getsockname()[1] self.failUnless(actual_port == self.PORT, "Binding to specific port number should have returned same number: %d != %d" % (actual_port, self.PORT)) - def testBindEphemeral(self): + def testBindEphemeral(self): self.sock.bind( (self.HOST, 0) ) # let system choose a free port - self.failUnless(self.sock.getsockname()[1] != 0, "Binding to port zero should have allocated an ephemeral port number") + self.failUnless(self.sock.getsockname()[1] != 0, "Binding to port zero should have allocated an ephemeral port number") def testShutdown(self): self.sock.bind( (self.HOST, self.PORT) ) self.sock.shutdown(socket.SHUT_RDWR) - def tearDown(self): - self.sock.close() - + def tearDown(self): + self.sock.close() + class BasicUDPTest(ThreadedUDPSocketTest): def __init__(self, methodName='runTest'): @@ -1354,7 +1340,7 @@ used, but if it is on your network this failure is bogus.''' % host) def testConnectDefaultTimeout(self): - _saved_timeout = socket.getdefaulttimeout() + _saved_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(0.1) cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = '192.168.192.168' @@ -1437,6 +1423,49 @@ if not ok: self.fail("recv() returned success when we did not expect it") +class TestGetAddrInfo(unittest.TestCase): + + def testBadFamily(self): + try: + socket.getaddrinfo(HOST, PORT, 9999) + except socket.gaierror, gaix: + self.failUnlessEqual(gaix[0], errno.EIO) + except Exception, x: + self.fail("getaddrinfo with bad family raised wrong exception: %s" % x) + else: + self.fail("getaddrinfo with bad family should have raised exception") + + def testReturnsAreStrings(self): + addrinfos = socket.getaddrinfo(HOST, PORT) + for addrinfo in addrinfos: + family, socktype, proto, canonname, sockaddr = addrinfo + self.assert_(isinstance(canonname, str)) + self.assert_(isinstance(sockaddr[0], str)) + + def testAI_PASSIVE(self): + IPV4_LOOPBACK = "127.0.0.1" + local_hostname = java.net.InetAddress.getLocalHost().getHostName() + local_ip_address = java.net.InetAddress.getLocalHost().getHostAddress() + for flags, host_param, expected_canonname, expected_sockaddr in [ + # First passive flag + (socket.AI_PASSIVE, None, "", socket.INADDR_ANY), + (socket.AI_PASSIVE, "", "", local_ip_address), + (socket.AI_PASSIVE, "localhost", "", IPV4_LOOPBACK), + (socket.AI_PASSIVE, local_hostname, "", local_ip_address), + # Now passive flag AND canonname flag + (socket.AI_PASSIVE|socket.AI_CANONNAME, None, "127.0.0.1", "127.0.0.1"), + (socket.AI_PASSIVE|socket.AI_CANONNAME, "", local_hostname, local_ip_address), + # The following result seems peculiar to me, and may be to do with my local machine setup + # Also may be caused by a security permission failure. + # If you have problems with the following result, just comment it out. + (socket.AI_PASSIVE|socket.AI_CANONNAME, "localhost", IPV4_LOOPBACK, IPV4_LOOPBACK), + (socket.AI_PASSIVE|socket.AI_CANONNAME, local_hostname, local_hostname, local_ip_address), + ]: + addrinfos = socket.getaddrinfo(host_param, 0, socket.AF_INET, socket.SOCK_STREAM, 0, flags) + for family, socktype, proto, canonname, sockaddr in addrinfos: + self.failUnlessEqual(expected_canonname, canonname, "For hostname '%s' and flags %d, canonname '%s' != '%s'" % (host_param, flags, expected_canonname, canonname) ) + self.failUnlessEqual(expected_sockaddr, sockaddr[0], "For hostname '%s' and flags %d, sockaddr '%s' != '%s'" % (host_param, flags, expected_sockaddr, sockaddr[0]) ) + class TestExceptions(unittest.TestCase): def testExceptionTree(self): @@ -1640,6 +1669,7 @@ TCPClientTimeoutTest, TestExceptions, TestInvalidUsage, + TestGetAddrInfo, TestTCPAddressParameters, TestUDPAddressParameters, UDPBindTest, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Philip J. <pj...@un...> - 2009-07-07 02:57:11
|
On Jul 3, 2009, at 8:45 AM, am...@us... wrote: > Revision: 6506 > http://jython.svn.sourceforge.net/jython/?rev=6506&view=rev > Author: amak > Date: 2009-07-03 15:45:08 +0000 (Fri, 03 Jul 2009) > > Log Message: > ----------- > 1. Added support for the AI_PASSIVE flag > 2. Added support for the AI_CANONNAME flag > 3. Added unit tests for same > 4. Split out getaddrinfo unit tests into their own test case class > > Modified Paths: > -------------- > trunk/jython/Lib/socket.py > trunk/jython/Lib/test/test_socket.py FYI this broke all the buildbots: http://bob.underboss.org:8080/job/jython/922/ http://freya.cs.uiuc.edu:8009/waterfall -- Philip Jenvey |
From: <am...@us...> - 2009-07-21 23:06:22
|
Revision: 6557 http://jython.svn.sourceforge.net/jython/?rev=6557&view=rev Author: amak Date: 2009-07-21 23:06:14 +0000 (Tue, 21 Jul 2009) Log Message: ----------- 1. Fixed a bug where send in UDP sockets whereby a send() on a connected UDP socket raised an NPE. 2. Added tests for same for both timeout and non-timeout code paths. 3. Dropped the "finishConnect" method for UDP sockets: DatagramChannels and DatagramSockts don't have finishConnect() methods, since UDP "connections" are not like TCP stateful connections: UDP connections are more akin to address filters, and cannot be "finished". Modified Paths: -------------- trunk/jython/Lib/socket.py trunk/jython/Lib/test/test_socket.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2009-07-21 21:53:56 UTC (rev 6556) +++ trunk/jython/Lib/socket.py 2009-07-21 23:06:14 UTC (rev 6557) @@ -429,9 +429,6 @@ def connect(self, host, port): self.jchannel.connect(java.net.InetSocketAddress(host, port)) - def finish_connect(self): - return self.jchannel.finishConnect() - def disconnect(self): """ Disconnect the datagram socket. @@ -449,16 +446,19 @@ def _do_send_net(self, byte_array, socket_address, flags): # Need two separate implementations because the java.nio APIs do not support timeouts num_bytes = len(byte_array) - if socket_address: - packet = java.net.DatagramPacket(byte_array, num_bytes, socket_address) - else: + if self.jsocket.isConnected() and socket_address is None: packet = java.net.DatagramPacket(byte_array, num_bytes) + else: + packet = java.net.DatagramPacket(byte_array, num_bytes, socket_address) self.jsocket.send(packet) return num_bytes def _do_send_nio(self, byte_array, socket_address, flags): - byte_buf = java.nio.ByteBuffer.wrap(byte_array) - bytes_sent = self.jchannel.send(byte_buf, socket_address) + byte_buf = java.nio.ByteBuffer.wrap(byte_array) + if self.jchannel.isConnected() and socket_address is None: + bytes_sent = self.jchannel.write(byte_buf) + else: + bytes_sent = self.jchannel.send(byte_buf, socket_address) return bytes_sent def sendto(self, byte_array, host, port, flags): @@ -993,11 +993,7 @@ def connect_ex(self, addr): if not self.sock_impl: self._do_connect(addr) - if self.sock_impl.finish_connect(): - if self.mode == MODE_NONBLOCKING: - return errno.EISCONN - return 0 - return errno.EINPROGRESS + return 0 def sendto(self, data, p1, p2=None): try: Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2009-07-21 21:53:56 UTC (rev 6556) +++ trunk/jython/Lib/test/test_socket.py 2009-07-21 23:06:14 UTC (rev 6557) @@ -857,6 +857,28 @@ self.cli.settimeout(10) self.cli.sendto(MSG, 0, (self.HOST, self.PORT)) + def testSendAndRecv(self): + # Testing send() and recv() over connect'ed UDP + msg = self.serv.recv(len(MSG)) + self.assertEqual(msg, MSG) + + def _testSendAndRecv(self): + self.cli.connect( (self.HOST, self.PORT) ) + self.cli.send(MSG, 0) + + def testSendAndRecvTimeoutMode(self): + # Need to test again in timeout mode, which follows + # a different code path + self.serv.settimeout(10) + # Testing send() and recv() over connect'ed UDP + msg = self.serv.recv(len(MSG)) + self.assertEqual(msg, MSG) + + def _testSendAndRecvTimeoutMode(self): + self.cli.connect( (self.HOST, self.PORT) ) + self.cli.settimeout(10) + self.cli.send(MSG, 0) + def testRecvFrom(self): # Testing recvfrom() over UDP msg, addr = self.serv.recvfrom(len(MSG)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <fwi...@us...> - 2009-08-10 16:48:29
|
Revision: 6636 http://jython.svn.sourceforge.net/jython/?rev=6636&view=rev Author: fwierzbicki Date: 2009-08-10 16:48:17 +0000 (Mon, 10 Aug 2009) Log Message: ----------- Ran these through reindent.py. These are the files that resulted in fairly trivial changes. The files os.py, dbexts.py, and isql.py resulted in large changes, so I'll look at them one at a time. Modified Paths: -------------- trunk/jython/Lib/_rawffi.py trunk/jython/Lib/codeop.py trunk/jython/Lib/datetime.py trunk/jython/Lib/fileinput.py trunk/jython/Lib/javapath.py trunk/jython/Lib/javashell.py trunk/jython/Lib/pkgutil.py trunk/jython/Lib/posixpath.py trunk/jython/Lib/readline.py trunk/jython/Lib/signal.py trunk/jython/Lib/socket.py trunk/jython/Lib/subprocess.py trunk/jython/Lib/telnetlib.py trunk/jython/Lib/threading.py trunk/jython/Lib/unicodedata.py trunk/jython/Lib/zlib.py Modified: trunk/jython/Lib/_rawffi.py =================================================================== --- trunk/jython/Lib/_rawffi.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/_rawffi.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -52,6 +52,3 @@ fnp = FuncPtr(fn, name, argtypes, restype) self.cache[key] = fnp return fnp - - - Modified: trunk/jython/Lib/codeop.py =================================================================== --- trunk/jython/Lib/codeop.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/codeop.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -132,4 +132,3 @@ raise ValueError,"symbol arg must be either single or eval" symbol = CompileMode.getMode(symbol) return Py.compile_command_flags(source,filename,symbol,self._cflags,0) - Modified: trunk/jython/Lib/datetime.py =================================================================== --- trunk/jython/Lib/datetime.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/datetime.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -13,7 +13,7 @@ Sources for time zone and DST data: http://www.twinsun.com/tz/tz-link.htm This was originally copied from the sandbox of the CPython CVS repository. -Thanks to Tim Peters for suggesting using it. +Thanks to Tim Peters for suggesting using it. """ import time as _time @@ -599,9 +599,9 @@ 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) + return timedelta(-self.__days, + -self.__seconds, + -self.__microseconds) def __pos__(self): return self @@ -1622,7 +1622,7 @@ if L[-1] == 0: del L[-1] if L[-1] == 0: - del L[-1] + del L[-1] s = ", ".join(map(str, L)) s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s) if self._tzinfo is not None: @@ -2076,4 +2076,3 @@ perverse time zone returns a negative dst()). So a breaking case must be pretty bizarre, and a tzinfo subclass can override fromutc() if it is. """ - Modified: trunk/jython/Lib/fileinput.py =================================================================== --- trunk/jython/Lib/fileinput.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/fileinput.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -324,8 +324,8 @@ try: perm = os.fstat(self._file.fileno()).st_mode except (AttributeError, OSError): - # AttributeError occurs in Jython, where there's no - # os.fstat. + # AttributeError occurs in Jython, where there's no + # os.fstat. self._output = open(self._filename, "w") else: fd = os.open(self._filename, Modified: trunk/jython/Lib/javapath.py =================================================================== --- trunk/jython/Lib/javapath.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/javapath.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -38,7 +38,7 @@ type_name = '%s.' % obj_type.__module__ type_name += obj_type.__name__ return type_name - + def dirname(path): """Return the directory component of a pathname""" path = _tostr(path, "dirname") @@ -83,7 +83,7 @@ return (path[:n], path[n:]) def splitdrive(path): - """Split a pathname into drive and path specifiers. + """Split a pathname into drive and path specifiers. Returns a 2-tuple "(drive,path)"; either part may be empty. """ @@ -260,7 +260,7 @@ """Return an absolute path normalized and symbolic links eliminated""" path = _tostr(path, "realpath") return _realpath(path) - + def _realpath(path): try: return asPyString(File(sys.getPath(path)).getCanonicalPath()) @@ -358,6 +358,3 @@ res = res + c index = index + 1 return res - - - Modified: trunk/jython/Lib/javashell.py =================================================================== --- trunk/jython/Lib/javashell.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/javashell.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -28,7 +28,7 @@ def __warn( *args ): print " ".join( [str( arg ) for arg in args ]) - + class _ShellEnv: """Provide environment derived by spawning a subshell and parsing its environment. Also supports subshell execution functions and provides @@ -69,14 +69,14 @@ if self.cmd is None: msgFmt = "Unable to execute commands in subshell because shell" \ " functionality not implemented for OS %s" \ - " Failed command=%s" + " Failed command=%s" raise OSError( 0, msgFmt % ( os._name, cmd )) - + if isinstance(cmd, basestring): shellCmd = self.cmd + [cmd] else: shellCmd = cmd - + return shellCmd def _formatEnvironment( self, env ): Modified: trunk/jython/Lib/pkgutil.py =================================================================== --- trunk/jython/Lib/pkgutil.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/pkgutil.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -246,7 +246,7 @@ return f.read() finally: f.close() - + def _reopen(self): if self.file and self.file.closed: mod_type = self.etc[2] @@ -454,7 +454,7 @@ if loader is not None: return loader fullname = module.__name__ - elif module_or_name == sys: + elif module_or_name == sys: # Jython sys is not a real module; fake it here for now since # making it a module requires a fair amount of decoupling from # PySystemState Modified: trunk/jython/Lib/posixpath.py =================================================================== --- trunk/jython/Lib/posixpath.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/posixpath.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -227,7 +227,7 @@ """Test whether two pathnames reference the same actual file""" s1 = os.stat(f1) s2 = os.stat(f2) - return samestat(s1, s2) + return samestat(s1, s2) # XXX: Jython currently lacks fstat @@ -244,7 +244,7 @@ __all__.append("sameopenfile") -# XXX: Pure Java stat lacks st_ino/st_dev +# XXX: Pure Java stat lacks st_ino/st_dev if os._native_posix: # Are two stat buffers (obtained from stat, fstat or lstat) # describing the same file? Modified: trunk/jython/Lib/readline.py =================================================================== --- trunk/jython/Lib/readline.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/readline.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -6,9 +6,9 @@ """ try: - from org.gnu.readline import Readline, ReadlineCompleter + from org.gnu.readline import Readline, ReadlineCompleter except ImportError, msg: - raise ImportError, '%s. The readline module requires that java-readline from http://java-readline.sourceforge.net/ be on the classpath' % msg + raise ImportError, '%s. The readline module requires that java-readline from http://java-readline.sourceforge.net/ be on the classpath' % msg __all__ = ["readline"] @@ -55,10 +55,10 @@ """ class DerivedCompleter (ReadlineCompleter): def __init__ (self, method): - self.method = method + self.method = method def completer (self, text, state): - return self.method(text, state) + return self.method(text, state) Readline.setCompleter(DerivedCompleter(completionfunction)) Modified: trunk/jython/Lib/signal.py =================================================================== --- trunk/jython/Lib/signal.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/signal.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -1,22 +1,22 @@ """ This module provides mechanisms to use signal handlers in Python. - + Functions: - + signal(sig,action) -- set the action for a given signal (done) pause(sig) -- wait until a signal arrives [Unix only] alarm(seconds) -- cause SIGALRM after a specified time [Unix only] getsignal(sig) -- get the signal action for a given signal default_int_handler(action) -- default SIGINT handler (done, but acts string) - + Constants: - + SIG_DFL -- used to refer to the system default handler SIG_IGN -- used to ignore the signal NSIG -- number of defined signals - + SIGINT, SIGTERM, etc. -- signal numbers - + *** IMPORTANT NOTICES *** A signal handler function is called with two arguments: the first is the signal number, the second is the interrupted stack frame. @@ -173,7 +173,7 @@ def default_int_handler(sig, frame): """ default_int_handler(...) - + The default handler for SIGINT installed by Python. It raises KeyboardInterrupt. """ @@ -214,7 +214,7 @@ return self.scheduled - now else: return 0 - + def alarm(time): try: SIGALRM Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/socket.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -24,7 +24,7 @@ import time import types -# Java.io classes +# Java.io classes import java.io.BufferedInputStream import java.io.BufferedOutputStream # Java.io exceptions @@ -221,7 +221,7 @@ 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', 'SO_BROADCAST', 'SO_ERROR', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', - 'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP', + 'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP', 'SocketType', 'error', 'herror', 'gaierror', 'timeout', 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', 'socket', 'getaddrinfo', 'getdefaulttimeout', 'setdefaulttimeout', @@ -529,8 +529,8 @@ names = [] addrs = [] for addr in addresses: - names.append(asPyString(addr.getHostName())) - addrs.append(asPyString(addr.getHostAddress())) + names.append(asPyString(addr.getHostName())) + addrs.append(asPyString(addr.getHostAddress())) return (names, addrs) def getfqdn(name=None): @@ -741,7 +741,7 @@ def shutdown(self, how): assert how in (SHUT_RD, SHUT_WR, SHUT_RDWR) if not self.sock_impl: - raise error(errno.ENOTCONN, "Transport endpoint is not connected") + raise error(errno.ENOTCONN, "Transport endpoint is not connected") try: self.sock_impl.shutdown(how) except java.lang.Exception, jlx: @@ -953,8 +953,8 @@ self.sock_impl.close() except java.lang.Exception, jlx: raise _map_exception(jlx) - + class _udpsocket(_nonblocking_api_mixin): sock_impl = None @@ -1027,7 +1027,7 @@ http://bugs.sun.com/view_bug.do?bug_id=6621689 """ try: - # This is the old 2.1 behaviour + # This is the old 2.1 behaviour #assert self.sock_impl # This is amak's preferred interpretation #raise error(errno.ENOTCONN, "Recvfrom on unbound udp socket meaningless operation") @@ -1124,7 +1124,7 @@ if isinstance(_sock, _nonblocking_api_mixin): _sock.close_lock.acquire() try: - _sock.reference_count -=1 + _sock.reference_count -=1 if not _sock.reference_count: _sock.close() self._sock = _closedsocket() Modified: trunk/jython/Lib/subprocess.py =================================================================== --- trunk/jython/Lib/subprocess.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/subprocess.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -562,7 +562,7 @@ (which takes a String cmdline). This process ruins String cmdlines from the user with escapes or quotes. To avoid this we first parse these cmdlines into an argv. - + Runtime.exec(String) is too naive and useless for this case. """ whitespace = ' \t' @@ -623,7 +623,7 @@ os_info = os._os_map.get(os._name) if os_info is None: os_info = os._os_map.get('posix') - + for shell_command in os_info[1]: executable = shell_command[0] if not os.path.isabs(executable): @@ -1174,7 +1174,7 @@ else: # Assuming file-like object errwrite = stderr.fileno() - + return (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) @@ -1184,7 +1184,7 @@ """Determine if the subprocess' stderr should be redirected to stdout """ - return (errwrite == STDOUT or c2pwrite not in (None, PIPE) and + return (errwrite == STDOUT or c2pwrite not in (None, PIPE) and c2pwrite is errwrite) @@ -1196,7 +1196,7 @@ def _setup_env(self, env, builder_env): """Carefully merge env with ProcessBuilder's only overwriting key/values that differ - + System.getenv (Map<String, String>) may be backed by <byte[], byte[]> on UNIX platforms where these are really bytes. ProcessBuilder's env inherits its contents and will Modified: trunk/jython/Lib/telnetlib.py =================================================================== --- trunk/jython/Lib/telnetlib.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/telnetlib.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -38,9 +38,9 @@ import socket import os if os.name == 'java': - from select import cpython_compatible_select as select + from select import cpython_compatible_select as select else: - from select import select + from select import select del os Modified: trunk/jython/Lib/threading.py =================================================================== --- trunk/jython/Lib/threading.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/threading.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -125,10 +125,10 @@ self._thread.join(millis_int, nanos) else: self._thread.join() - + def getName(self): return self._thread.getName() - + def setName(self, name): self._thread.setName(str(name)) Modified: trunk/jython/Lib/unicodedata.py =================================================================== --- trunk/jython/Lib/unicodedata.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/unicodedata.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -91,7 +91,7 @@ if v is None: v = check_segments(codepoint, _segments) if v is not None: - return "%s-%X" % (v[8], codepoint) + return "%s-%X" % (v[8], codepoint) if v is None: if default is not Nonesuch: @@ -126,13 +126,13 @@ v = _eaw.get(codepoint, None) if v is None: v = check_segments(codepoint, _eaw_segments) - + if v is None: if default is not Nonesuch: return default raise ValueError() return v - + def get(unichr, default, fn, getter): codepoint = get_codepoint(unichr, fn) data = _codepoints.get(codepoint, None) @@ -192,24 +192,24 @@ try: from java.text import Normalizer - _forms = { - 'NFC': Normalizer.Form.NFC, - 'NFKC': Normalizer.Form.NFKC, - 'NFD': Normalizer.Form.NFD, - 'NFKD': Normalizer.Form.NFKD - } + _forms = { + 'NFC': Normalizer.Form.NFC, + 'NFKC': Normalizer.Form.NFKC, + 'NFD': Normalizer.Form.NFD, + 'NFKD': Normalizer.Form.NFKD + } - def normalize(form, unistr): - """ - Return the normal form 'form' for the Unicode string unistr. Valid - values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'. - """ - + def normalize(form, unistr): + """ + Return the normal form 'form' for the Unicode string unistr. Valid + values for form are 'NFC', 'NFKC', 'NFD', and 'NFKD'. + """ + try: normalizer_form = _forms[form] except KeyError: raise ValueError('invalid normalization form') - return Normalizer.normalize(unistr, normalizer_form) + return Normalizer.normalize(unistr, normalizer_form) except ImportError: pass Modified: trunk/jython/Lib/zlib.py =================================================================== --- trunk/jython/Lib/zlib.py 2009-08-09 00:05:01 UTC (rev 6635) +++ trunk/jython/Lib/zlib.py 2009-08-10 16:48:17 UTC (rev 6636) @@ -45,7 +45,7 @@ _valid_flush_modes = (Z_FINISH,) def adler32(s, value=1): - if value != 1: + if value != 1: raise ValueError, "adler32 only support start value of 1" checksum = Adler32() checksum.update(String.getBytes(s, 'iso-8859-1')) @@ -86,7 +86,7 @@ raise error("compressobj may not be used after flush(Z_FINISH)") self.deflater.setInput(string, 0, len(string)) return _get_deflate_data(self.deflater) - + def flush(self, mode=Z_FINISH): if self._ended: raise error("compressobj may not be used after flush(Z_FINISH)") @@ -111,7 +111,7 @@ def decompress(self, string, max_length=0): if self._ended: raise error("decompressobj may not be used after flush()") - + # unused_data is always "" until inflation is finished; then it is # the unused bytes of the input; # unconsumed_tail is whatever input was not used because max_length @@ -132,7 +132,7 @@ self.unconsumed_tail = string[-r:] else: self.unused_data = string[-r:] - + return inflated def flush(self, length=None): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <fwi...@us...> - 2009-10-01 01:25:52
|
Revision: 6818 http://jython.svn.sourceforge.net/jython/?rev=6818&view=rev Author: fwierzbicki Date: 2009-10-01 01:25:40 +0000 (Thu, 01 Oct 2009) Log Message: ----------- svn:ignore *$py.class in these. Property Changed: ---------------- trunk/jython/Lib/collections/ trunk/jython/Lib/compiler/ trunk/jython/Lib/distutils/ trunk/jython/Lib/distutils/command/ trunk/jython/Lib/distutils/tests/ trunk/jython/Lib/modjy/ trunk/jython/Lib/test/bugs/ trunk/jython/Lib/test/bugs/pr133/ trunk/jython/Lib/test/pbcvm/test/ trunk/jython/Lib/test/pyservlet/ trunk/jython/Lib/test/test_metaclass_support/ trunk/jython/Lib/test/zxjdbc/ trunk/jython/Lib/xml/etree/ trunk/jython/Lib/xml/parsers/ Property changes on: trunk/jython/Lib/collections ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/compiler ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/distutils ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/distutils/command ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/distutils/tests ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/modjy ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/test/bugs ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/test/bugs/pr133 ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/test/pbcvm/test ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/test/pyservlet ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/test/test_metaclass_support ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/test/zxjdbc ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/xml/etree ___________________________________________________________________ Added: svn:ignore + *$py.class Property changes on: trunk/jython/Lib/xml/parsers ___________________________________________________________________ Added: svn:ignore + *$py.class This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-10-19 21:56:29
|
Revision: 6873 http://jython.svn.sourceforge.net/jython/?rev=6873&view=rev Author: pjenvey Date: 2009-10-19 21:56:07 +0000 (Mon, 19 Oct 2009) Log Message: ----------- move os.py -> posix.py, in prep. for separating the posix bits from it os.py from: http://svn.python.org/projects/python/branches/release26-maint/Lib/os.py@75144 Added Paths: ----------- trunk/jython/Lib/os.py trunk/jython/Lib/posix.py Removed Paths: ------------- trunk/jython/Lib/os.py trunk/jython/Lib/posix.py Deleted: trunk/jython/Lib/os.py =================================================================== --- trunk/jython/Lib/os.py 2009-10-19 21:26:09 UTC (rev 6872) +++ trunk/jython/Lib/os.py 2009-10-19 21:56:07 UTC (rev 6873) @@ -1,1166 +0,0 @@ -r"""OS routines for Java, with some attempts to support NT, and Posix -functionality. - -This exports: - - all functions from posix, nt, dos, os2, mac, or ce, e.g. unlink, stat, etc. - - os.path is one of the modules posixpath, ntpath, macpath, or dospath - - os.name is 'posix', 'nt', 'dos', 'os2', 'mac', 'ce' or 'riscos' - - os.curdir is a string representing the current directory ('.' or ':') - - os.pardir is a string representing the parent directory ('..' or '::') - - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') - - os.altsep is the alternate pathname separator (None or '/') - - os.pathsep is the component separator used in $PATH etc - - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n') - - os.defpath is the default search path for executables - -Programs that import and use 'os' stand a better chance of being -portable between different platforms. Of course, they must then -only use functions that are defined by all platforms (e.g., unlink -and opendir), and leave all pathname manipulation to os.path -(e.g., split and join). -""" - -# CPython os.py __all__ -__all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep", - "defpath", "name", "path", - "SEEK_SET", "SEEK_CUR", "SEEK_END"] - -# Would come from the posix/nt/etc. modules on CPython -__all__.extend(['EX_OK', 'F_OK', 'O_APPEND', 'O_CREAT', 'O_EXCL', 'O_RDONLY', - 'O_RDWR', 'O_SYNC', 'O_TRUNC', 'O_WRONLY', 'R_OK', 'SEEK_CUR', - 'SEEK_END', 'SEEK_SET', 'W_OK', 'X_OK', '_exit', 'access', - 'altsep', 'chdir', 'chmod', 'close', 'curdir', 'defpath', - 'environ', 'error', 'fdopen', 'fsync', 'getcwd', 'getcwdu', - 'getenv', 'getpid', 'isatty', 'linesep', 'listdir', 'lseek', - 'lstat', 'makedirs', 'mkdir', 'name', 'open', 'pardir', 'path', - 'pathsep', 'popen', 'popen2', 'popen3', 'popen4', 'putenv', - 'read', 'remove', 'removedirs', 'rename', 'renames', 'rmdir', - 'sep', 'stat', 'stat_result', 'strerror', 'system', 'unlink', - 'unsetenv', 'utime', 'walk', 'write']) - -import errno -import jarray -import java.lang.System -import time -import stat as _stat -import sys -from java.io import File -from org.python.core.io import FileDescriptors, FileIO, IOBase -from org.python.core.Py import newString as asPyString - -try: - from org.python.constantine.platform import Errno -except ImportError: - from com.kenai.constantine.platform import Errno - -# Mapping of: os._name: [name list, shell command list] -_os_map = dict(nt=[ - ['Windows'], - [['cmd.exe', '/c'], ['command.com', '/c']] - ], - posix=[ - [], # posix is a fallback, instead of matching names - [['/bin/sh', '-c']] - ] - ) - -def get_os_type(): - """Return the name of the type of the underlying OS. - - Returns a value suitable for the os.name variable (though not - necessarily intended to be for os.name Jython). This value may be - overwritten in the Jython registry. - """ - os_name = sys.registry.getProperty('python.os') - if os_name: - return asPyString(os_name) - - os_name = asPyString(java.lang.System.getProperty('os.name')) - os_type = None - for type, (patterns, shell_commands) in _os_map.iteritems(): - for pattern in patterns: - if os_name.startswith(pattern): - # determine the shell_command later, when it's needed: - # it requires os.path (which isn't setup yet) - return type - return 'posix' - -name = 'java' -# WARNING: _name is private: for Jython internal usage only! user code -# should *NOT* use it -_name = get_os_type() - -try: - from org.python.posix import JavaPOSIX, POSIXHandler, POSIXFactory -except ImportError: - from org.jruby.ext.posix import JavaPOSIX, POSIXHandler, POSIXFactory - -class PythonPOSIXHandler(POSIXHandler): - def error(self, error, msg): - err = getattr(errno, error.name(), None) - if err is None: - raise OSError('%s: %s' % (error, asPyString(msg))) - raise OSError(err, strerror(err), asPyString(msg)) - def unimplementedError(self, method_name): - raise NotImplementedError(method_name) - def warn(self, warning_id, msg, rest): - pass # XXX implement - def isVerbose(self): - return False - def getCurrentWorkingDirectory(self): - return File(getcwdu()) - def getEnv(self): - return ['%s=%s' % (key, val) for key, val in environ.iteritems()] - def getInputStream(self): - return getattr(java.lang.System, 'in') # XXX handle resetting - def getOutputStream(self): - return java.lang.System.out # XXX handle resetting - def getPID(self): - return 0 - def getErrorStream(self): - return java.lang.System.err # XXX handle resetting - -_posix = POSIXFactory.getPOSIX(PythonPOSIXHandler(), True) -_native_posix = not isinstance(_posix, JavaPOSIX) - -if _name == 'nt': - import ntpath as path -else: - import posixpath as path - -sys.modules['os.path'] = _path = path -from os.path import curdir, pardir, sep, pathsep, defpath, extsep, altsep, devnull -linesep = java.lang.System.getProperty('line.separator') - -# open for reading only -O_RDONLY = 0x0 -# open for writing only -O_WRONLY = 0x1 -# open for reading and writing -O_RDWR = 0x2 - -# set append mode -O_APPEND = 0x8 -# synchronous writes -O_SYNC = 0x80 - -# create if nonexistant -O_CREAT = 0x200 -# truncate to zero length -O_TRUNC = 0x400 -# error if already exists -O_EXCL = 0x800 - -# seek variables -SEEK_SET = 0 -SEEK_CUR = 1 -SEEK_END = 2 - -# test for existence of file -F_OK = 0 -# test for execute or search permission -X_OK = 1<<0 -# test for write permission -W_OK = 1<<1 -# test for read permission -R_OK = 1<<2 - -# successful termination -EX_OK = 0 - -# Java class representing the size of a time_t. internal use, lazily set -_time_t = None - -class stat_result: - - _stat_members = ( - ('st_mode', _stat.ST_MODE), - ('st_ino', _stat.ST_INO), - ('st_dev', _stat.ST_DEV), - ('st_nlink', _stat.ST_NLINK), - ('st_uid', _stat.ST_UID), - ('st_gid', _stat.ST_GID), - ('st_size', _stat.ST_SIZE), - ('st_atime', _stat.ST_ATIME), - ('st_mtime', _stat.ST_MTIME), - ('st_ctime', _stat.ST_CTIME), - ) - - def __init__(self, results): - if len(results) != 10: - raise TypeError("stat_result() takes an a 10-sequence") - for (name, index) in stat_result._stat_members: - self.__dict__[name] = results[index] - - @classmethod - def from_jnastat(cls, s): - results = [] - for meth in (s.mode, s.ino, s.dev, s.nlink, s.uid, s.gid, s.st_size, - s.atime, s.mtime, s.ctime): - try: - results.append(meth()) - except NotImplementedError: - results.append(0) - return cls(results) - - def __getitem__(self, i): - if i < 0 or i > 9: - raise IndexError(i) - return getattr(self, stat_result._stat_members[i][0]) - - def __setitem__(self, x, value): - raise TypeError("object doesn't support item assignment") - - def __setattr__(self, name, value): - if name in [x[0] for x in stat_result._stat_members]: - raise TypeError(name) - raise AttributeError("readonly attribute") - - def __len__(self): - return 10 - - def __cmp__(self, other): - if not isinstance(other, stat_result): - return 1 - return cmp(self.__dict__, other.__dict__) - - def __repr__(self): - return repr(tuple(self.__dict__[member[0]] for member - in stat_result._stat_members)) - -error = OSError - -def _exit(n=0): - """_exit(status) - - Exit to the system with specified status, without normal exit - processing. - """ - java.lang.System.exit(n) - -def getcwd(): - """getcwd() -> path - - Return a string representing the current working directory. - """ - return asPyString(sys.getCurrentWorkingDir()) - -def getcwdu(): - """getcwd() -> path - - Return a unicode string representing the current working directory. - """ - return sys.getCurrentWorkingDir() - -def chdir(path): - """chdir(path) - - Change the current working directory to the specified path. - """ - realpath = _path.realpath(path) - if not _path.exists(realpath): - raise OSError(errno.ENOENT, strerror(errno.ENOENT), path) - if not _path.isdir(realpath): - raise OSError(errno.ENOTDIR, strerror(errno.ENOTDIR), path) - sys.setCurrentWorkingDir(realpath) - -def listdir(path): - """listdir(path) -> list_of_strings - - Return a list containing the names of the entries in the directory. - - path: path of directory to list - - The list is in arbitrary order. It does not include the special - entries '.' and '..' even if they are present in the directory. - """ - l = File(sys.getPath(path)).list() - if l is None: - raise OSError(0, 'No such directory', path) - return [asPyString(entry) for entry in l] - -def chmod(path, mode): - """chmod(path, mode) - - Change the access permissions of a file. - """ - # XXX no error handling for chmod in jna-posix - # catch not found errors explicitly here, for now - abs_path = sys.getPath(path) - if not File(abs_path).exists(): - raise OSError(errno.ENOENT, strerror(errno.ENOENT), path) - _posix.chmod(abs_path, mode) - -def mkdir(path, mode='ignored'): - """mkdir(path [, mode=0777]) - - Create a directory. - - The optional parameter is currently ignored. - """ - # XXX: use _posix.mkdir when we can get the real errno upon failure - fp = File(sys.getPath(path)) - if not fp.mkdir(): - if fp.isDirectory() or fp.isFile(): - err = errno.EEXIST - else: - err = 0 - msg = strerror(err) if err else "couldn't make directory" - raise OSError(err, msg, path) - -def makedirs(path, mode='ignored'): - """makedirs(path [, mode=0777]) - - Super-mkdir; create a leaf directory and all intermediate ones. - - Works like mkdir, except that any intermediate path segment (not - just the rightmost) will be created if it does not exist. - The optional parameter is currently ignored. - """ - sys_path = sys.getPath(path) - if File(sys_path).mkdirs(): - return - - # if making a /x/y/z/., java.io.File#mkdirs inexplicably fails. So we need - # to force it - - # need to use _path instead of path, because param is hiding - # os.path module in namespace! - head, tail = _path.split(sys_path) - if tail == curdir: - if File(_path.join(head)).mkdirs(): - return - - raise OSError(0, "couldn't make directories", path) - -def remove(path): - """remove(path) - - Remove a file (same as unlink(path)). - """ - if not File(sys.getPath(path)).delete(): - raise OSError(0, "couldn't delete file", path) - -unlink = remove - -def rename(path, newpath): - """rename(old, new) - - Rename a file or directory. - """ - if not File(sys.getPath(path)).renameTo(File(sys.getPath(newpath))): - raise OSError(0, "couldn't rename file", path) - -#XXX: copied from CPython 2.5.1 -def renames(old, new): - """renames(old, new) - - Super-rename; create directories as necessary and delete any left - empty. Works like rename, except creation of any intermediate - directories needed to make the new pathname good is attempted - first. After the rename, directories corresponding to rightmost - path segments of the old name will be pruned way until either the - whole path is consumed or a nonempty directory is found. - - Note: this function can fail with the new directory structure made - if you lack permissions needed to unlink the leaf directory or - file. - - """ - head, tail = path.split(new) - if head and tail and not path.exists(head): - makedirs(head) - rename(old, new) - head, tail = path.split(old) - if head and tail: - try: - removedirs(head) - except error: - pass - -def rmdir(path): - """rmdir(path) - - Remove a directory.""" - f = File(sys.getPath(path)) - if not f.exists(): - raise OSError(errno.ENOENT, strerror(errno.ENOENT), path) - elif not f.isDirectory(): - raise OSError(errno.ENOTDIR, strerror(errno.ENOTDIR), path) - elif not f.delete(): - raise OSError(0, "couldn't delete directory", path) - -#XXX: copied from CPython 2.5.1 -def removedirs(name): - """removedirs(path) - - Super-rmdir; remove a leaf directory and empty all intermediate - ones. Works like rmdir except that, if the leaf directory is - successfully removed, directories corresponding to rightmost path - segments will be pruned away until either the whole path is - consumed or an error occurs. Errors during this latter phase are - ignored -- they generally mean that a directory was not empty. - - """ - rmdir(name) - head, tail = path.split(name) - if not tail: - head, tail = path.split(head) - while head and tail: - try: - rmdir(head) - except error: - break - head, tail = path.split(head) - -__all__.extend(['makedirs', 'renames', 'removedirs']) - -def strerror(code): - """strerror(code) -> string - - Translate an error code to a message string. - """ - if not isinstance(code, (int, long)): - raise TypeError('an integer is required') - constant = Errno.valueOf(code) - if constant is Errno.__UNKNOWN_CONSTANT__: - return 'Unknown error: %d' % code - if constant.name() == constant.description(): - # XXX: have constantine handle this fallback - # Fake constant or just lacks a description, fallback to Linux's - try: - from org.python.constantine.platform.linux import Errno as LinuxErrno - except ImportError: - from com.kenai.constantine.platform.linux import Errno as LinuxErrno - constant = getattr(LinuxErrno, constant.name(), None) - if not constant: - return 'Unknown error: %d' % code - return asPyString(constant.toString()) - -def access(path, mode): - """access(path, mode) -> True if granted, False otherwise - - Use the real uid/gid to test for access to a path. Note that most - operations will use the effective uid/gid, therefore this routine can - be used in a suid/sgid environment to test if the invoking user has the - specified access to the path. The mode argument can be F_OK to test - existence, or the inclusive-OR of R_OK, W_OK, and X_OK. - """ - if not isinstance(mode, (int, long)): - raise TypeError('an integer is required') - - f = File(sys.getPath(path)) - result = True - if not f.exists(): - result = False - if mode & R_OK and not f.canRead(): - result = False - if mode & W_OK and not f.canWrite(): - result = False - if mode & X_OK: - # NOTE: always False without jna-posix stat - try: - result = (stat(path).st_mode & _stat.S_IEXEC) != 0 - except OSError: - result = False - return result - -def stat(path): - """stat(path) -> stat result - - Perform a stat system call on the given path. - - The Java stat implementation only returns a small subset of - the standard fields: size, modification time and change time. - """ - abs_path = sys.getPath(path) - try: - return stat_result.from_jnastat(_posix.stat(abs_path)) - except NotImplementedError: - pass - f = File(abs_path) - if not f.exists(): - raise OSError(errno.ENOENT, strerror(errno.ENOENT), path) - size = f.length() - mtime = f.lastModified() / 1000.0 - mode = 0 - if f.isDirectory(): - mode = _stat.S_IFDIR - elif f.isFile(): - mode = _stat.S_IFREG - if f.canRead(): - mode = mode | _stat.S_IREAD - if f.canWrite(): - mode = mode | _stat.S_IWRITE - return stat_result((mode, 0, 0, 0, 0, 0, size, mtime, mtime, 0)) - -def lstat(path): - """lstat(path) -> stat result - - Like stat(path), but do not follow symbolic links. - """ - abs_path = sys.getPath(path) - try: - return stat_result.from_jnastat(_posix.lstat(abs_path)) - except NotImplementedError: - pass - f = File(sys.getPath(path)) - # XXX: jna-posix implements similar link detection in - # JavaFileStat.calculateSymlink, fallback to that instead when not - # native - abs_parent = f.getAbsoluteFile().getParentFile() - if not abs_parent: - # root isn't a link - return stat(path) - can_parent = abs_parent.getCanonicalFile() - - if can_parent.getAbsolutePath() == abs_parent.getAbsolutePath(): - # The parent directory's absolute path is canonical.. - if f.getAbsolutePath() != f.getCanonicalPath(): - # but the file's absolute and canonical paths differ (a - # link) - return stat_result((_stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0)) - - # The parent directory's path is not canonical (one of the parent - # directories is a symlink). Build a new path with the parent's - # canonical path and compare the files - f = File(_path.join(can_parent.getAbsolutePath(), f.getName())) - if f.getAbsolutePath() != f.getCanonicalPath(): - return stat_result((_stat.S_IFLNK, 0, 0, 0, 0, 0, 0, 0, 0, 0)) - - # Not a link, only now can we determine if it exists (because - # File.exists() returns False for dead links) - if not f.exists(): - raise OSError(errno.ENOENT, strerror(errno.ENOENT), path) - return stat(path) - -def utime(path, times): - """utime(path, (atime, mtime)) - utime(path, None) - - Set the access and modification time of the file to the given values. - If the second form is used, set the access and modification times to the - current time. - - Due to Java limitations, on some platforms only the modification time - may be changed. - """ - if path is None: - raise TypeError('path must be specified, not None') - - if times is None: - atimeval = mtimeval = None - elif isinstance(times, tuple) and len(times) == 2: - atimeval = _to_timeval(times[0]) - mtimeval = _to_timeval(times[1]) - else: - raise TypeError('utime() arg 2 must be a tuple (atime, mtime)') - - _posix.utimes(path, atimeval, mtimeval) - -def _to_timeval(seconds): - """Convert seconds (with a fraction) from epoch to a 2 item tuple of - seconds, microseconds from epoch as longs - """ - global _time_t - if _time_t is None: - from java.lang import Integer, Long - try: - from org.python.posix.util import Platform - except ImportError: - from org.jruby.ext.posix.util import Platform - _time_t = Integer if Platform.IS_32_BIT else Long - - try: - floor = long(seconds) - except TypeError: - raise TypeError('an integer is required') - if not _time_t.MIN_VALUE <= floor <= _time_t.MAX_VALUE: - raise OverflowError('long int too large to convert to int') - - # usec can't exceed 1000000 - usec = long((seconds - floor) * 1e6) - if usec < 0: - # If rounding gave us a negative number, truncate - usec = 0 - return floor, usec - -def close(fd): - """close(fd) - - Close a file descriptor (for low level IO). - """ - rawio = FileDescriptors.get(fd) - _handle_oserror(rawio.close) - -def fdopen(fd, mode='r', bufsize=-1): - """fdopen(fd [, mode='r' [, bufsize]]) -> file_object - - Return an open file object connected to a file descriptor. - """ - rawio = FileDescriptors.get(fd) - if (len(mode) and mode[0] or '') not in 'rwa': - raise ValueError("invalid file mode '%s'" % mode) - if rawio.closed(): - raise OSError(errno.EBADF, strerror(errno.EBADF)) - - try: - fp = FileDescriptors.wrap(rawio, mode, bufsize) - except IOError: - raise OSError(errno.EINVAL, strerror(errno.EINVAL)) - return fp - -def ftruncate(fd, length): - """ftruncate(fd, length) - - Truncate a file to a specified length. - """ - rawio = FileDescriptors.get(fd) - try: - rawio.truncate(length) - except Exception, e: - raise IOError(errno.EBADF, strerror(errno.EBADF)) - -def lseek(fd, pos, how): - """lseek(fd, pos, how) -> newpos - - Set the current position of a file descriptor. - """ - rawio = FileDescriptors.get(fd) - return _handle_oserror(rawio.seek, pos, how) - -def open(filename, flag, mode=0777): - """open(filename, flag [, mode=0777]) -> fd - - Open a file (for low level IO). - """ - reading = flag & O_RDONLY - writing = flag & O_WRONLY - updating = flag & O_RDWR - creating = flag & O_CREAT - - truncating = flag & O_TRUNC - exclusive = flag & O_EXCL - sync = flag & O_SYNC - appending = flag & O_APPEND - - if updating and writing: - raise OSError(errno.EINVAL, strerror(errno.EINVAL), filename) - - if not creating and not path.exists(filename): - raise OSError(errno.ENOENT, strerror(errno.ENOENT), filename) - - if not writing: - if updating: - writing = True - else: - reading = True - - if truncating and not writing: - # Explicitly truncate, writing will truncate anyway - FileIO(filename, 'w').close() - - if exclusive and creating: - try: - if not File(sys.getPath(filename)).createNewFile(): - raise OSError(errno.EEXIST, strerror(errno.EEXIST), - filename) - except java.io.IOException, ioe: - raise OSError(ioe) - - mode = '%s%s%s%s' % (reading and 'r' or '', - (not appending and writing) and 'w' or '', - (appending and (writing or updating)) and 'a' or '', - updating and '+' or '') - - if sync and (writing or updating): - from java.io import FileNotFoundException, RandomAccessFile - try: - fchannel = RandomAccessFile(sys.getPath(filename), 'rws').getChannel() - except FileNotFoundException, fnfe: - if path.isdir(filename): - raise OSError(errno.EISDIR, strerror(errno.EISDIR)) - raise OSError(errno.ENOENT, strerror(errno.ENOENT), filename) - return FileIO(fchannel, mode) - - return FileIO(filename, mode) - -def read(fd, buffersize): - """read(fd, buffersize) -> string - - Read a file descriptor. - """ - from org.python.core.util import StringUtil - rawio = FileDescriptors.get(fd) - buf = _handle_oserror(rawio.read, buffersize) - return asPyString(StringUtil.fromBytes(buf)) - -def write(fd, string): - """write(fd, string) -> byteswritten - - Write a string to a file descriptor. - """ - from java.nio import ByteBuffer - from org.python.core.util import StringUtil - rawio = FileDescriptors.get(fd) - return _handle_oserror(rawio.write, - ByteBuffer.wrap(StringUtil.toBytes(string))) - -def _handle_oserror(func, *args, **kwargs): - """Translate exceptions into OSErrors""" - try: - return func(*args, **kwargs) - except: - raise OSError(errno.EBADF, strerror(errno.EBADF)) - -def system(command): - """system(command) -> exit_status - - Execute the command (a string) in a subshell. - """ - import subprocess - return subprocess.call(command, shell=True) - -def popen(command, mode='r', bufsize=-1): - """popen(command [, mode='r' [, bufsize]]) -> pipe - - Open a pipe to/from a command returning a file object. - """ - import subprocess - if mode == 'r': - proc = subprocess.Popen(command, bufsize=bufsize, shell=True, - stdout=subprocess.PIPE) - return _wrap_close(proc.stdout, proc) - elif mode == 'w': - proc = subprocess.Popen(command, bufsize=bufsize, shell=True, - stdin=subprocess.PIPE) - return _wrap_close(proc.stdin, proc) - else: - raise OSError(errno.EINVAL, strerror(errno.EINVAL)) - -# Helper for popen() -- a proxy for a file whose close waits for the process -class _wrap_close(object): - def __init__(self, stream, proc): - self._stream = stream - self._proc = proc - def close(self): - self._stream.close() - returncode = self._proc.wait() - if returncode == 0: - return None - if _name == 'nt': - return returncode - else: - return returncode - def __getattr__(self, name): - return getattr(self._stream, name) - def __iter__(self): - return iter(self._stream) - -# os module versions of the popen# methods have different return value -# order than popen2 functions - -def popen2(cmd, mode="t", bufsize=-1): - """Execute the shell command cmd in a sub-process. - - On UNIX, 'cmd' may be a sequence, in which case arguments will be - passed directly to the program without shell intervention (as with - os.spawnv()). If 'cmd' is a string it will be passed to the shell - (as with os.system()). If 'bufsize' is specified, it sets the - buffer size for the I/O pipes. The file objects (child_stdin, - child_stdout) are returned. - """ - import popen2 - stdout, stdin = popen2.popen2(cmd, bufsize) - return stdin, stdout - -def popen3(cmd, mode="t", bufsize=-1): - """Execute the shell command 'cmd' in a sub-process. - - On UNIX, 'cmd' may be a sequence, in which case arguments will be - passed directly to the program without shell intervention - (as with os.spawnv()). If 'cmd' is a string it will be passed - to the shell (as with os.system()). If 'bufsize' is specified, - it sets the buffer size for the I/O pipes. The file objects - (child_stdin, child_stdout, child_stderr) are returned. - """ - import popen2 - stdout, stdin, stderr = popen2.popen3(cmd, bufsize) - return stdin, stdout, stderr - -def popen4(cmd, mode="t", bufsize=-1): - """Execute the shell command 'cmd' in a sub-process. - - On UNIX, 'cmd' may be a sequence, in which case arguments will be - passed directly to the program without shell intervention - (as with os.spawnv()). If 'cmd' is a string it will be passed - to the shell (as with os.system()). If 'bufsize' is specified, - it sets the buffer size for the I/O pipes. The file objects - (child_stdin, child_stdout_stderr) are returned. - """ - import popen2 - stdout, stdin = popen2.popen4(cmd, bufsize) - return stdin, stdout - -def getlogin(): - """getlogin() -> string - - Return the actual login name. - """ - return java.lang.System.getProperty("user.name") - -#XXX: copied from CPython's release23-maint branch revision 56502 -def walk(top, topdown=True, onerror=None): - """Directory tree generator. - - For each directory in the directory tree rooted at top (including top - itself, but excluding '.' and '..'), yields a 3-tuple - - dirpath, dirnames, filenames - - dirpath is a string, the path to the directory. dirnames is a list of - the names of the subdirectories in dirpath (excluding '.' and '..'). - filenames is a list of the names of the non-directory files in dirpath. - Note that the names in the lists are just names, with no path components. - To get a full path (which begins with top) to a file or directory in - dirpath, do os.path.join(dirpath, name). - - If optional arg 'topdown' is true or not specified, the triple for a - directory is generated before the triples for any of its subdirectories - (directories are generated top down). If topdown is false, the triple - for a directory is generated after the triples for all of its - subdirectories (directories are generated bottom up). - - When topdown is true, the caller can modify the dirnames list in-place - (e.g., via del or slice assignment), and walk will only recurse into the - subdirectories whose names remain in dirnames; this can be used to prune - the search, or to impose a specific order of visiting. Modifying - dirnames when topdown is false is ineffective, since the directories in - dirnames have already been generated by the time dirnames itself is - generated. - - By default errors from the os.listdir() call are ignored. If - optional arg 'onerror' is specified, it should be a function; it - will be called with one argument, an os.error instance. It can - report the error to continue with the walk, or raise the exception - to abort the walk. Note that the filename is available as the - filename attribute of the exception object. - - Caution: if you pass a relative pathname for top, don't change the - current working directory between resumptions of walk. walk never - changes the current directory, and assumes that the client doesn't - either. - - Example: - - from os.path import join, getsize - for root, dirs, files in walk('python/Lib/email'): - print root, "consumes", - print sum([getsize(join(root, name)) for name in files]), - print "bytes in", len(files), "non-directory files" - if 'CVS' in dirs: - dirs.remove('CVS') # don't visit CVS directories - """ - - from os.path import join, isdir, islink - - # We may not have read permission for top, in which case we can't - # get a list of the files the directory contains. os.path.walk - # always suppressed the exception then, rather than blow up for a - # minor reason when (say) a thousand readable directories are still - # left to visit. That logic is copied here. - try: - # Note that listdir and error are globals in this module due - # to earlier import-*. - names = listdir(top) - except error, err: - if onerror is not None: - onerror(err) - return - - dirs, nondirs = [], [] - for name in names: - if isdir(join(top, name)): - dirs.append(name) - else: - nondirs.append(name) - - if topdown: - yield top, dirs, nondirs - for name in dirs: - path = join(top, name) - if not islink(path): - for x in walk(path, topdown, onerror): - yield x - if not topdown: - yield top, dirs, nondirs - -__all__.append("walk") - -environ = sys.getEnviron() - -if _name in ('os2', 'nt'): # Where Env Var Names Must Be UPPERCASE - import UserDict - - # But we store them as upper case - class _Environ(UserDict.IterableUserDict): - def __init__(self, environ): - UserDict.UserDict.__init__(self) - data = self.data - for k, v in environ.items(): - data[k.upper()] = v - def __setitem__(self, key, item): - self.data[key.upper()] = item - def __getitem__(self, key): - return self.data[key.upper()] - def __delitem__(self, key): - del self.data[key.upper()] - def has_key(self, key): - return key.upper() in self.data - def __contains__(self, key): - return key.upper() in self.data - def get(self, key, failobj=None): - return self.data.get(key.upper(), failobj) - def update(self, dict=None, **kwargs): - if dict: - try: - keys = dict.keys() - except AttributeError: - # List of (key, value) - for k, v in dict: - self[k] = v - else: - # got keys - # cannot use items(), since mappings - # may not have them. - for k in keys: - self[k] = dict[k] - if kwargs: - self.update(kwargs) - def copy(self): - return dict(self) - - environ = _Environ(environ) - -def putenv(key, value): - """putenv(key, value) - - Change or add an environment variable. - """ - environ[key] = value - -def unsetenv(key): - """unsetenv(key) - - Delete an environment variable. - """ - if key in environ: - del environ[key] - -def getenv(key, default=None): - """Get an environment variable, return None if it doesn't exist. - The optional second argument can specify an alternate default.""" - return environ.get(key, default) - -if _name == 'posix': - def link(src, dst): - """link(src, dst) - - Create a hard link to a file. - """ - _posix.link(sys.getPath(src), sys.getPath(dst)) - - def symlink(src, dst): - """symlink(src, dst) - - Create a symbolic link pointing to src named dst. - """ - _posix.symlink(src, sys.getPath(dst)) - - def readlink(path): - """readlink(path) -> path - - Return a string representing the path to which the symbolic link - points. - """ - return _posix.readlink(sys.getPath(path)) - - def getegid(): - """getegid() -> egid - - Return the current process's effective group id.""" - return _posix.getegid() - - def geteuid(): - """geteuid() -> euid - - Return the current process's effective user id.""" - return _posix.geteuid() - - def getgid(): - """getgid() -> gid - - Return the current process's group id.""" - return _posix.getgid() - - def getlogin(): - """getlogin() -> string - - Return the actual login name.""" - return _posix.getlogin() - - def getpgrp(): - """getpgrp() -> pgrp - - Return the current process group id.""" - return _posix.getpgrp() - - def getppid(): - """getppid() -> ppid - - Return the parent's process id.""" - return _posix.getppid() - - def getuid(): - """getuid() -> uid - - Return the current process's user id.""" - return _posix.getuid() - - def setpgrp(): - """setpgrp() - - Make this process a session leader.""" - return _posix.setpgrp() - - def setsid(): - """setsid() - - Call the system call setsid().""" - return _posix.setsid() - - # This implementation of fork partially works on - # Jython. Diagnosing what works, what doesn't, and fixing it is - # left for another day. In any event, this would only be - # marginally useful. - - # def fork(): - # """fork() -> pid - # - # Fork a child process. - # Return 0 to child process and PID of child to parent process.""" - # return _posix.fork() - - def kill(pid, sig): - """kill(pid, sig) - - Kill a process with a signal.""" - return _posix.kill(pid, sig) - - def wait(): - """wait() -> (pid, status) - - Wait for completion of a child process.""" - - status = jarray.zeros(1, 'i') - res_pid = _posix.wait(status) - if res_pid == -1: - raise OSError(status[0], strerror(status[0])) - return res_pid, status[0] - - def waitpid(pid, options): - """waitpid(pid, options) -> (pid, status) - - Wait for completion of a given child process.""" - status = jarray.zeros(1, 'i') - res_pid = _posix.waitpid(pid, status, options) - if res_pid == -1: - raise OSError(status[0], strerror(status[0])) - return res_pid, status[0] - - def fdatasync(fd): - """fdatasync(fildes) - - force write of file with filedescriptor to disk. - does not force update of metadata. - """ - _fsync(fd, False) - - __all__.extend(['link', 'symlink', 'readlink', 'getegid', 'geteuid', - 'getgid', 'getlogin', 'getpgrp', 'getppid', 'getuid', - 'setpgrp', 'setsid', 'kill', 'wait', 'waitpid', - 'fdatasync']) - -def fsync(fd): - """fsync(fildes) - - force write of file with filedescriptor to disk. - """ - _fsync(fd, True) - -def _fsync(fd, metadata): - """Internal fsync impl""" - rawio = FileDescriptors.get(fd) - rawio.checkClosed() - - from java.nio.channels import FileChannel - channel = rawio.getChannel() - if not isinstance(channel, FileChannel): - raise OSError(errno.EINVAL, strerror(errno.EINVAL)) - - try: - channel.force(metadata) - except java.io.IOException, ioe: - raise OSError(ioe) - -def getpid(): - """getpid() -> pid - - Return the current process id.""" - return _posix.getpid() - -def isatty(fileno): - """isatty(fd) -> bool - - Return True if the file descriptor 'fd' is an open file descriptor - connected to the slave end of a terminal.""" - from java.io import FileDescriptor - - if isinstance(fileno, int): - if fileno == 0: - fd = getattr(FileDescriptor, 'in') - elif fileno == 1: - fd = FileDescriptor.out - elif fileno == 2: - fd = FileDescriptor.err - else: - raise NotImplemented('Integer file descriptor compatibility only ' - 'available for stdin, stdout and stderr (0-2)') - - return _posix.isatty(fd) - - if isinstance(fileno, FileDescriptor): - return _posix.isatty(fileno) - - if not isinstance(fileno, IOBase): - raise TypeError('a file descriptor is required') - - return fileno.isatty() - -def umask(new_mask): - """umask(new_mask) -> old_mask - - Set the current numeric umask and return the previous umask.""" - return _posix.umask(int(new_mask)) - - -from java.security import SecureRandom -urandom_source = None - -def urandom(n): - global urandom_source - if urandom_source is None: - urandom_source = SecureRandom() - buffer = jarray.zeros(n, 'b') - urandom_source.nextBytes(buffer) - return buffer.tostring() Added: trunk/jython/Lib/os.py =================================================================== --- trunk/jython/Lib/os.py (rev 0) +++ trunk/jython/Lib/os.py 2009-10-19 21:56:07 UTC (rev 6873) @@ -0,0 +1,763 @@ +r"""OS routines for Mac, NT, or Posix depending on what system we're on. + +This exports: + - all functions from posix, nt, os2, or ce, e.g. unlink, stat, etc. + - os.path is one of the modules posixpath, or ntpath + - os.name is 'posix', 'nt', 'os2', 'ce' or 'riscos' + - os.curdir is a string representing the current directory ('.' or ':') + - os.pardir is a string representing the parent directory ('..' or '::') + - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\') + - os.extsep is the extension separator ('.' or '/') + - os.altsep is the alternate pathname separator (None or '/') + - os.pathsep is the component separator used in $PATH etc + - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n') + - os.defpath is the default search path for executables + - os.devnull is the file path of the null device ('/dev/null', etc.) + +Programs that import and use 'os' stand a better chance of being +portable between different platforms. Of course, they must then +only use functions that are defined by all platforms (e.g., unlink +and opendir), and leave all pathname manipulation to os.path +(e.g., split and join). +""" + +#' + +import sys, errno + +_names = sys.builtin_module_names + +# Note: more names are added to __all__ later. +__all__ = ["altsep", "curdir", "pardir", "sep", "extsep", "pathsep", "linesep", + "defpath", "name", "path", "devnull", + "SEEK_SET", "SEEK_CUR", "SEEK_END"] + +def _get_exports_list(module): + try: + return list(module.__all__) + except AttributeError: + return [n for n in dir(module) if n[0] != '_'] + +if 'posix' in _names: + name = 'posix' + linesep = '\n' + from posix import * + try: + from posix import _exit + except ImportError: + pass + import posixpath as path + + import posix + __all__.extend(_get_exports_list(posix)) + del posix + +elif 'nt' in _names: + name = 'nt' + linesep = '\r\n' + from nt import * + try: + from nt import _exit + except ImportError: + pass + import ntpath as path + + import nt + __all__.extend(_get_exports_list(nt)) + del nt + +elif 'os2' in _names: + name = 'os2' + linesep = '\r\n' + from os2 import * + try: + from os2 import _exit + except ImportError: + pass + if sys.version.find('EMX GCC') == -1: + import ntpath as path + else: + import os2emxpath as path + from _emx_link import link + + import os2 + __all__.extend(_get_exports_list(os2)) + del os2 + +elif 'ce' in _names: + name = 'ce' + linesep = '\r\n' + from ce import * + try: + from ce import _exit + except ImportError: + pass + # We can use the standard Windows path. + import ntpath as path + + import ce + __all__.extend(_get_exports_list(ce)) + del ce + +elif 'riscos' in _names: + name = 'riscos' + linesep = '\n' + from riscos import * + try: + from riscos import _exit + except ImportError: + pass + import riscospath as path + + import riscos + __all__.extend(_get_exports_list(riscos)) + del riscos + +else: + raise ImportError, 'no os specific module found' + +sys.modules['os.path'] = path +from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep, + devnull) + +del _names + +# Python uses fixed values for the SEEK_ constants; they are mapped +# to native constants if necessary in posixmodule.c +SEEK_SET = 0 +SEEK_CUR = 1 +SEEK_END = 2 + +#' + +# Super directory utilities. +# (Inspired by Eric Raymond; the doc strings are mostly his) + +def makedirs(name, mode=0777): + """makedirs(path [, mode=0777]) + + Super-mkdir; create a leaf directory and all intermediate ones. + Works like mkdir, except that any intermediate path segment (not + just the rightmost) will be created if it does not exist. This is + recursive. + + """ + head, tail = path.split(name) + if not tail: + head, tail = path.split(head) + if head and tail and not path.exists(head): + try: + makedirs(head, mode) + except OSError, e: + # be happy if someone already created the path + if e.errno != errno.EEXIST: + raise + if tail == curdir: # xxx/newdir/. exists if xxx/newdir exists + return + mkdir(name, mode) + +def removedirs(name): + """removedirs(path) + + Super-rmdir; remove a leaf directory and all empty intermediate + ones. Works like rmdir except that, if the leaf directory is + successfully removed, directories corresponding to rightmost path + segments will be pruned away until either the whole path is + consumed or an error occurs. Errors during this latter phase are + ignored -- they generally mean that a directory was not empty. + + """ + rmdir(name) + head, tail = path.split(name) + if not tail: + head, tail = path.split(head) + while head and tail: + try: + rmdir(head) + except error: + break + head, tail = path.split(head) + +def renames(old, new): + """renames(old, new) + + Super-rename; create directories as necessary and delete any left + empty. Works like rename, except creation of any intermediate + directories needed to make the new pathname good is attempted + first. After the rename, directories corresponding to rightmost + path segments of the old name will be pruned way until either the + whole path is consumed or a nonempty directory is found. + + Note: this function can fail with the new directory structure made + if you lack permissions needed to unlink the leaf directory or + file. + + """ + head, tail = path.split(new) + if head and tail and not path.exists(head): + makedirs(head) + rename(old, new) + head, tail = path.split(old) + if head and tail: + try: + removedirs(head) + except error: + pass + +__all__.extend(["makedirs", "removedirs", "renames"]) + +def walk(top, topdown=True, onerror=None, followlinks=False): + """Directory tree generator. + + For each directory in the directory tree rooted at top (including top + itself, but excluding '.' and '..'), yields a 3-tuple + + dirpath, dirnames, filenames + + dirpath is a string, the path to the directory. dirnames is a list of + the names of the subdirectories in dirpath (excluding '.' and '..'). + filenames is a list of the names of the non-directory files in dirpath. + Note that the names in the lists are just names, with no path components. + To get a full path (which begins with top) to a file or directory in + dirpath, do os.path.join(dirpath, name). + + If optional arg 'topdown' is true or not specified, the triple for a + directory is generated before the triples for any of its subdirectories + (directories are generated top down). If topdown is false, the triple + for a directory is generated after the triples for all of its + subdirectories (directories are generated bottom up). + + When topdown is true, the caller can modify the dirnames list in-place + (e.g., via del or slice assignment), and walk will only recurse into the + subdirectories whose names remain in dirnames; this can be used to prune + the search, or to impose a specific order of visiting. Modifying + dirnames when topdown is false is ineffective, since the directories in + dirnames have already been generated by the time dirnames itself is + generated. + + By default errors from the os.listdir() call are ignored. If + optional arg 'onerror' is specified, it should be a function; it + will be called with one argument, an os.error instance. It can + report the error to continue with the walk, or raise the exception + to abort the walk. Note that the filename is available as the + filename attribute of the exception object. + + By default, os.walk does not follow symbolic links to subdirectories on + systems that support them. In order to get this functionality, set the + optional argument 'followlinks' to true. + + Caution: if you pass a relative pathname for top, don't change the + current working directory between resumptions of walk. walk never + changes the current directory, and assumes that the client doesn't + either. + + Example: + + import os + from os.path import join, getsize + for root, dirs, files in os.walk('python/Lib/email'): + print root, "consumes", + print sum([getsize(join(root, name)) for name in files]), + print "bytes in", len(files), "non-directory files" + if 'CVS' in dirs: + dirs.remove('CVS') # don't visit CVS directories + """ + + from os.path import join, isdir, islink + + # We may not have read permission for top, in which case we can't + # get a list of the files the directory contains. os.path.walk + # always suppressed the exception then, rather than blow up for a + # minor reason when (say) a thousand readable directories are still + # left to visit. That logic is copied here. + try: + # Note that listdir and error are globals in this module due + # to earlier import-*. + names = listdir(top) + except error, err: + if onerror is not None: + onerror(err) + return + + dirs, nondirs = [], [] + for name in names: + if isdir(join(top, name)): + dirs.append(name) + else: + nondirs.append(name) + + if topdown: + yield top, dirs, nondirs + for name in dirs: + path = join(top, name) + if followlinks or not islink(path): + for x in walk(path, topdown, onerror, followlinks): + yield x + if not topdown: + yield top, dirs, nondirs + +__all__.append("walk") + +# Make sure os.environ exists, at least +try: + environ +except NameError: + environ = {} + +def execl(file, *args): + """execl(file, *args) + + Execute the executable file with argument list args, replacing the + current process. """ + execv(file, args) + +def execle(file, *args): + """execle(file, *args, env) + + Execute the executable file with argument list args and + environment env, replacing the current process. """ + env = args[-1] + execve(file, args[:-1], env) + +def execlp(file, *args): + """execlp(file, *args) + + Execute the executable file (which is searched for along $PATH) + with argument list args, replacing the current process. """ + execvp(file, args) + +def execlpe(file, *args): + """execlpe(file, *args, env) + + Execute the executable file (which is searched for along $PATH) + with argument list args and environment env, replacing the current + process. """ + env = args[-1] + execvpe(file, args[:-1], env) + +def execvp(file, args): + """execp(file, args) + + Execute the executable file (which is searched for along $PATH) + with argument list args, replacing the current process. + args may be a list or tuple of strings. """ + _execvpe(file, args) + +def execvpe(file, args, env): + """execvpe(file, args, env) + + Execute the executable file (which is searched for along $PATH) + with argument list args and environment env , replacing the + current process. + args may be a list or tuple of strings. """ + _execvpe(file, args, env) + +__all__.extend(["execl","execle","execlp","execlpe","execvp","execvpe"]) + +def _execvpe(file, args, env=None): + if env is not None: + func = execve + argrest = (args, env) + else: + func = execv + argrest = (args,) + env = environ + + head, tail = path.split(file) + if head: + func(file, *argrest) + return + if 'PATH' in env: + envpath = env['PATH'] + else: + envpath = defpath + PATH = envpath.split(pathsep) + saved_exc = None + saved_tb = None + for dir in PATH: + fullname = path.join(dir, file) + try: + func(fullname, *argrest) + except error, e: + tb = sys.exc_info()[2] + if (e.errno != errno.ENOENT and e.errno != errno.ENOTDIR + and saved_exc is None): + saved_exc = e + saved_tb = tb + if saved_exc: + raise error, saved_exc, saved_tb + raise error, e, tb + +# Change environ to automatically call putenv() if it exists +try: + # This will fail if there's no putenv + putenv +except NameError: + pass +else: + import UserDict + + # Fake unsetenv() for Windows + # not sure about os2 here but + # I'm guessing they are the same. + + if name in ('os2', 'nt'): + def unsetenv(key): + putenv(key, "") + + if name == "riscos": + # On RISC OS, all env access goes through getenv and putenv + from riscosenviron import _Environ + elif name in ('os2', 'nt'): # Where Env Var Names Must Be UPPERCASE + # But we store them as upper case + class _Environ(UserDict.IterableUserDict): + def __init__(self, environ): + UserDict.UserDict.__init__(self) + data = self.data + for k, v in environ.items(): + data[k.upper()] = v + def __setitem__(self, key, item): + putenv(key, item) + self.data[key.upper()] = item + def __getitem__(self, key): + return self.data[key.upper()] + try: + unsetenv + except NameError: + def __delitem__(self, key): + del self.data[key.upper()] + else: + def __delitem__(self, key): + unsetenv(key) + del self.data[key.upper()] + def clear(self): + for key in self.data.keys(): + unsetenv(key) + del self.data[key] + def pop(self, key, *args): + unsetenv(key) + return self.data.pop(key.upper(), *args) + def has_key(self, key): + return key.upper() in self.data + def __contains__(self, key): + return key.upper() in self.data + def get(self, key, failobj=None): + return self.data.get(key.upper(), failobj) + def update(self, dict=None, **kwargs): + if dict: + try: + keys = dict.keys() + except AttributeError: + # List of (key, value) + for k, v in dict: + self[k] = v + else: + # got keys + # cannot use items(), since mappings + # may not have them. + for k in keys: + self[k] = dict[k] + if kwargs: + self.update(kwargs) + def copy(self): + return dict(self) + + else: # Where Env Var Names Can Be Mixed Case + class _Environ(UserDict.IterableUserDict): + def __init__(self, environ): + UserDict.UserDict.__init__(self) + self.data = environ + def __setitem__(self, key, item): + putenv(key, item) + self.data[key] = item + def update(self, dict=None, **kwargs): + if dict: + try: + keys = dict.keys() + except AttributeError: + # List of (key, value) + for k, v in dict: + self[k] = v + else: + # got keys + # cannot use items(), since mappings + # may not have them. + for k in keys: + self[k] = dict[k] + if kwargs: + self.update(kwargs) + try: + unsetenv + except NameError: + pass + else: + def __delitem__(self, key): + unsetenv(key) + del self.data[key] + def clear(self): + for key in self.data.keys(): + unsetenv(key) + del self.data[key] + def pop(self, key, *args): + unsetenv(key) + return self.data.pop(key, *args) + def copy(self): + return dict(self) + + + environ = _Environ(environ) + +def getenv(key, default=None): + """Get an environment variable, return None if it doesn't exist. + The optional second argument can specify an alternate default.""" + return environ.get(key, default) +__all__.append("getenv") + +def _exists(name): + try: + eval(name) + return True + except NameError: + return False + +# Supply spawn*() (probably only for Unix) +if _exists("fork") and not _exists("spawnv") and _exists("execv"): + + P_WAIT = 0 + P_NOWAIT = P_NOWAITO = 1 + + # XXX Should we support P_DETACH? I suppose it could fork()**2 + # and close the std I/O streams. Also, P_OVERLAY is the same + # as execv*()? + + def _spawnvef(mode, file, args, env, func): + # Internal helper; func is the exec*() function to use + pid = fork() + if not pid: + # Child + try: + if env is None: + func(file, args) + else: + func(file, args, env) + except: + _exit(127) + else: + # Parent + if mode == P_NOWAIT: + return pid # Caller is responsible for waiting! + while 1: + wpid, sts = waitpid(pid, 0) + if WIFSTOPPED(sts): + continue + elif WIFSIGNALED(sts): + return -WTERMSIG(sts) + elif WIFEXITED(sts): + return WEXITSTATUS(sts) + else: + raise error, "Not stopped, signaled or exited???" + + def spawnv(mode, file, args): + """spawnv(mode, file, args) -> integer + +Execute file with arguments from args in a subprocess. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, None, execv) + + def spawnve(mode, file, args, env): + """spawnve(mode, file, args, env) -> integer + +Execute file with arguments from args in a subprocess with the +specified environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, env, execve) + + # Note: spawnvp[e] is't currently supported on Windows + + def spawnvp(mode, file, args): + """spawnvp(mode, file, args) -> integer + +Execute file (which is looked for along $PATH) with arguments from +args in a subprocess. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, None, execvp) + + def spawnvpe(mode, file, args, env): + """spawnvpe(mode, file, args, env) -> integer + +Execute file (which is looked for along $PATH) with arguments from +args in a subprocess with the supplied environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return _spawnvef(mode, file, args, env, execvpe) + +if _exists("spawnv"): + # These aren't supplied by the basic Windows code + # but can be easily implemented in Python + + def spawnl(mode, file, *args): + """spawnl(mode, file, *args) -> integer + +Execute file with arguments from args in a subprocess. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + return spawnv(mode, file, args) + + def spawnle(mode, file, *args): + """spawnle(mode, file, *args, env) -> integer + +Execute file with arguments from args in a subprocess with the +supplied environment. +If mode == P_NOWAIT return the pid of the process. +If mode == P_WAIT return the process's exit code if it exits normally; +otherwise return -SIG, where SIG is the signal that killed it. """ + env = args[-1] + return spawnve(mode, file, args[:-1], env) + + + __all__.exte... [truncated message content] |
From: <pj...@us...> - 2009-10-20 03:28:41
|
Revision: 6882 http://jython.svn.sourceforge.net/jython/?rev=6882&view=rev Author: pjenvey Date: 2009-10-20 03:28:30 +0000 (Tue, 20 Oct 2009) Log Message: ----------- only import these when necessary Modified Paths: -------------- trunk/jython/Lib/ntpath.py trunk/jython/Lib/posixpath.py Modified: trunk/jython/Lib/ntpath.py =================================================================== --- trunk/jython/Lib/ntpath.py 2009-10-20 01:20:43 UTC (rev 6881) +++ trunk/jython/Lib/ntpath.py 2009-10-20 03:28:30 UTC (rev 6882) @@ -5,11 +5,9 @@ module as os.path. """ -import java.io.File import os import stat import sys -from org.python.core.Py import newString __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -487,6 +485,9 @@ from nt import _getfullpathname except ImportError: # not running on Windows - mock up something sensible + import java.io.File + from org.python.core.Py import newString + def abspath(path): """Return the absolute version of a path.""" if not isabs(path): Modified: trunk/jython/Lib/posixpath.py =================================================================== --- trunk/jython/Lib/posixpath.py 2009-10-20 01:20:43 UTC (rev 6881) +++ trunk/jython/Lib/posixpath.py 2009-10-20 03:28:30 UTC (rev 6882) @@ -10,11 +10,8 @@ for manipulation of the pathname component of URLs. """ -import java.io.File -import java.io.IOException import os import stat -from org.python.core.Py import newString __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -217,6 +214,10 @@ # Are two filenames really pointing to the same file? if not os._native_posix: + import java.io.File + import java.io.IOException + from org.python.core.Py import newString + def samefile(f1, f2): """Test whether two pathnames reference the same actual file""" canon1 = newString(java.io.File(_ensure_str(f1)).getCanonicalPath()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-10-20 03:43:55
|
Revision: 6883 http://jython.svn.sourceforge.net/jython/?rev=6883&view=rev Author: pjenvey Date: 2009-10-20 03:43:46 +0000 (Tue, 20 Oct 2009) Log Message: ----------- identify reflected funcs as routines so they can show up in help() Modified Paths: -------------- trunk/jython/Lib/inspect.py Added Paths: ----------- trunk/jython/Lib/test/test_inspect_jy.py Modified: trunk/jython/Lib/inspect.py =================================================================== --- trunk/jython/Lib/inspect.py 2009-10-20 03:28:30 UTC (rev 6882) +++ trunk/jython/Lib/inspect.py 2009-10-20 03:43:46 UTC (rev 6883) @@ -30,6 +30,7 @@ import sys, os, types, string, re, dis, imp, tokenize, linecache from operator import attrgetter +ReflectedFunctionType = type(os.listdir) # ----------------------------------------------------------- type-checking def ismodule(object): @@ -196,7 +197,8 @@ return (isbuiltin(object) or isfunction(object) or ismethod(object) - or ismethoddescriptor(object)) + or ismethoddescriptor(object) + or isinstance(object, ReflectedFunctionType)) def getmembers(object, predicate=None): """Return all members of an object as (name, value) pairs sorted by name. Added: trunk/jython/Lib/test/test_inspect_jy.py =================================================================== --- trunk/jython/Lib/test/test_inspect_jy.py (rev 0) +++ trunk/jython/Lib/test/test_inspect_jy.py 2009-10-20 03:43:46 UTC (rev 6883) @@ -0,0 +1,21 @@ +"""Misc inspect tests + +Made for Jython. +""" +import inspect +import unittest +from java.lang import System +from test import test_support + +class InspectTestCase(unittest.TestCase): + + def test_java_routine(self): + self.assertTrue(inspect.isroutine(System.arraycopy)) + + +def test_main(): + test_support.run_unittest(InspectTestCase) + + +if __name__ == '__main__': + test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-10-20 04:25:03
|
Revision: 6885 http://jython.svn.sourceforge.net/jython/?rev=6885&view=rev Author: pjenvey Date: 2009-10-20 04:24:54 +0000 (Tue, 20 Oct 2009) Log Message: ----------- fix missing imports and reference to now fixed os.__name__ Modified Paths: -------------- trunk/jython/Lib/posix.py trunk/jython/Lib/test/test_import_jy.py Modified: trunk/jython/Lib/posix.py =================================================================== --- trunk/jython/Lib/posix.py 2009-10-20 04:04:53 UTC (rev 6884) +++ trunk/jython/Lib/posix.py 2009-10-20 04:24:54 UTC (rev 6885) @@ -278,11 +278,12 @@ FileIO(filename, 'w').close() if exclusive and creating: + from java.io import IOException try: if not File(sys.getPath(filename)).createNewFile(): raise OSError(errno.EEXIST, strerror(errno.EEXIST), filename) - except java.io.IOException, ioe: + except IOException, ioe: raise OSError(ioe) mode = '%s%s%s%s' % (reading and 'r' or '', @@ -541,6 +542,7 @@ rawio = FileDescriptors.get(fd) rawio.checkClosed() + from java.io import IOException from java.nio.channels import FileChannel channel = rawio.getChannel() if not isinstance(channel, FileChannel): @@ -548,7 +550,7 @@ try: channel.force(metadata) - except java.io.IOException, ioe: + except IOException, ioe: raise OSError(ioe) def getpid(): Modified: trunk/jython/Lib/test/test_import_jy.py =================================================================== --- trunk/jython/Lib/test/test_import_jy.py 2009-10-20 04:04:53 UTC (rev 6884) +++ trunk/jython/Lib/test/test_import_jy.py 2009-10-20 04:24:54 UTC (rev 6885) @@ -100,10 +100,8 @@ def test_override(self): modname = os.path.__name__ tests = [ - ("import os.path" , "('os.path', None, -1, '_%s')" - % os._name), - ("import os.path as path2", "('os.path', None, -1, '_%s')" - % os._name), + ("import os.path" , "('os.path', None, -1, 'os')"), + ("import os.path as path2", "('os.path', None, -1, 'os')"), ("from os.path import *" , "('os.path', ('*',), -1, '%s')" % modname), ("from os.path import join", This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-10-27 22:22:21
|
Revision: 6918 http://jython.svn.sourceforge.net/jython/?rev=6918&view=rev Author: pjenvey Date: 2009-10-27 22:21:57 +0000 (Tue, 27 Oct 2009) Log Message: ----------- has_key -> in, etc Modified Paths: -------------- trunk/jython/Lib/select.py trunk/jython/Lib/socket.py Modified: trunk/jython/Lib/select.py =================================================================== --- trunk/jython/Lib/select.py 2009-10-27 22:16:17 UTC (rev 6917) +++ trunk/jython/Lib/select.py 2009-10-27 22:21:57 UTC (rev 6918) @@ -117,7 +117,7 @@ else: try: timeout = int(timeout) - if timeout == 0: + if not timeout: self.selector.selectNow() else: # No multiplication required: both cpython and java use millisecond timeouts @@ -224,7 +224,7 @@ registered_for_read[fd] = 1 # And now the write list for fd in write_fd_list: - if registered_for_read.has_key(fd): + if fd in registered_for_read: # registering a second time overwrites the first pobj.register(fd, POLLIN|POLLOUT) else: Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2009-10-27 22:16:17 UTC (rev 6917) +++ trunk/jython/Lib/socket.py 2009-10-27 22:21:57 UTC (rev 6918) @@ -259,7 +259,7 @@ self.jsocket.setSoTimeout(self._timeout_millis) def getsockopt(self, level, option): - if self.options.has_key( (level, option) ): + if (level, option) in self.options: result = getattr(self.jsocket, "get%s" % self.options[ (level, option) ])() if option == SO_LINGER: if result == -1: @@ -272,7 +272,7 @@ raise error(errno.ENOPROTOOPT, "Socket option '%s' (level '%s') not supported on socket(%s)" % (_constant_to_name(option), _constant_to_name(level), str(self.jsocket))) def setsockopt(self, level, option, value): - if self.options.has_key( (level, option) ): + if (level, option) in self.options: if option == SO_LINGER: values = struct.unpack('ii', value) self.jsocket.setSoLinger(*values) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pj...@us...> - 2009-10-31 01:48:38
|
Revision: 6936 http://jython.svn.sourceforge.net/jython/?rev=6936&view=rev Author: pjenvey Date: 2009-10-31 01:48:29 +0000 (Sat, 31 Oct 2009) Log Message: ----------- only use the Group/Passwd interface API, and convert the unicode values to regular strs Modified Paths: -------------- trunk/jython/Lib/grp.py trunk/jython/Lib/pwd.py Modified: trunk/jython/Lib/grp.py =================================================================== --- trunk/jython/Lib/grp.py 2009-10-31 01:46:11 UTC (rev 6935) +++ trunk/jython/Lib/grp.py 2009-10-31 01:48:29 UTC (rev 6936) @@ -18,6 +18,7 @@ __all__ = ['getgrgid', 'getgrnam', 'getgrall'] from os import _name, _posix_impl +from org.python.core.Py import newString if _name == 'nt': raise ImportError, 'grp module not supported on Windows' @@ -34,8 +35,9 @@ attrs = ['gr_name', 'gr_passwd', 'gr_gid', 'gr_mem'] def __new__(cls, grp): - return tuple.__new__(cls, (grp.gr_name, grp.gr_passwd, grp.gr_gid, - list(grp.getMembers()))) + grp = (newString(grp.name), newString(grp.password), grp.GID, + [newString(member) for member in grp.members]) + return tuple.__new__(cls, grp) def __getattr__(self, attr): try: Modified: trunk/jython/Lib/pwd.py =================================================================== --- trunk/jython/Lib/pwd.py 2009-10-31 01:46:11 UTC (rev 6935) +++ trunk/jython/Lib/pwd.py 2009-10-31 01:48:29 UTC (rev 6936) @@ -11,6 +11,7 @@ __all__ = ['getpwuid', 'getpwnam', 'getpwall'] from os import _name, _posix_impl +from org.python.core.Py import newString if _name == 'nt': raise ImportError, 'pwd module not supported on Windows' @@ -28,7 +29,10 @@ 'pw_dir', 'pw_shell'] def __new__(cls, pwd): - return tuple.__new__(cls, (getattr(pwd, attr) for attr in cls.attrs)) + pwd = (newString(pwd.loginName), newString(pwd.password), pwd.UID, + pwd.GID, newString(pwd.GECOS), newString(pwd.home), + newString(pwd.shell)) + return tuple.__new__(cls, pwd) def __getattr__(self, attr): try: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |