From: <pj...@us...> - 2009-11-22 22:10:11
|
Revision: 6948 http://jython.svn.sourceforge.net/jython/?rev=6948&view=rev Author: pjenvey Date: 2009-11-22 22:09:52 +0000 (Sun, 22 Nov 2009) Log Message: ----------- finally move the remaining functions to PosixModule, hide posix's __docs__ fixes #1396 Modified Paths: -------------- trunk/jython/Lib/os.py trunk/jython/NEWS trunk/jython/src/org/python/modules/Setup.java trunk/jython/src/org/python/modules/posix/PosixModule.java Removed Paths: ------------- trunk/jython/Lib/nt.py trunk/jython/Lib/posix.py Deleted: trunk/jython/Lib/nt.py =================================================================== --- trunk/jython/Lib/nt.py 2009-11-22 20:24:31 UTC (rev 6947) +++ trunk/jython/Lib/nt.py 2009-11-22 22:09:52 UTC (rev 6948) @@ -1,2 +0,0 @@ -from posix import __all__ -from posix import * Modified: trunk/jython/Lib/os.py =================================================================== --- trunk/jython/Lib/os.py 2009-11-22 20:24:31 UTC (rev 6947) +++ trunk/jython/Lib/os.py 2009-11-22 22:09:52 UTC (rev 6948) @@ -39,7 +39,7 @@ return [n for n in dir(module) if n[0] != '_'] name = 'java' -if '_posix' in _names: +if 'posix' in _names: _name = 'posix' linesep = '\n' from posix import * @@ -53,7 +53,7 @@ __all__.extend(_get_exports_list(posix)) del posix -elif '_nt' in _names: +elif 'nt' in _names: _name = 'nt' linesep = '\r\n' from nt import * @@ -67,7 +67,7 @@ __all__.extend(_get_exports_list(nt)) del nt -elif '_os2' in _names: +elif 'os2' in _names: _name = 'os2' linesep = '\r\n' from os2 import * @@ -85,7 +85,7 @@ __all__.extend(_get_exports_list(os2)) del os2 -elif '_ce' in _names: +elif 'ce' in _names: _name = 'ce' linesep = '\r\n' from ce import * @@ -100,7 +100,7 @@ __all__.extend(_get_exports_list(ce)) del ce -elif '_riscos' in _names: +elif 'riscos' in _names: _name = 'riscos' linesep = '\n' from riscos import * @@ -663,3 +663,42 @@ bytes += read(_urandomfd, n - len(bytes)) close(_urandomfd) return bytes + +# Supply os.popen() +def popen(cmd, mode='r', bufsize=-1): + """popen(command [, mode='r' [, bufsize]]) -> pipe + + Open a pipe to/from a command returning a file object. + """ + if not isinstance(cmd, (str, unicode)): + raise TypeError('invalid cmd type (%s, expected string)' % type(cmd)) + if mode not in ('r', 'w'): + raise ValueError("invalid mode %r" % mode) + import subprocess + if mode == 'r': + proc = subprocess.Popen(cmd, bufsize=bufsize, shell=True, + stdout=subprocess.PIPE) + return _wrap_close(proc.stdout, proc) + elif mode == 'w': + proc = subprocess.Popen(cmd, bufsize=bufsize, shell=True, + stdin=subprocess.PIPE) + return _wrap_close(proc.stdin, proc) + +# 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) Deleted: trunk/jython/Lib/posix.py =================================================================== --- trunk/jython/Lib/posix.py 2009-11-22 20:24:31 UTC (rev 6947) +++ trunk/jython/Lib/posix.py 2009-11-22 22:09:52 UTC (rev 6948) @@ -1,209 +0,0 @@ -""" -This module provides access to operating system functionality that is -standardized by the C Standard and the POSIX standard (a thinly -disguised Unix interface). Refer to the library manual and -corresponding Unix manual entries for more information on calls. -""" -try: - import _posix - from _posix import * -except: - import _nt as _posix - from _nt import * -import errno -import sys - -__all__ = [name for name in _posix.__all__ if not name.startswith('__doc__')] -__all__.extend(['getenv', 'isatty', 'popen', 'putenv', - 'rename', 'rmdir', 'system', - 'unsetenv', 'urandom', 'utime']) - -_name = _posix.__name__[1:] - -# Java class representing the size of a time_t. internal use, lazily set -_time_t = None - -# For urandom -urandom_source = None - -def rename(path, newpath): - """rename(old, new) - - Rename a file or directory. - """ - from java.io import File - if not File(sys.getPath(path)).renameTo(File(sys.getPath(newpath))): - raise OSError(0, "couldn't rename file", path) - -def rmdir(path): - """rmdir(path) - - Remove a directory.""" - from java.io import File - 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) - -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_impl.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 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) - -def putenv(key, value): - """putenv(key, value) - - Change or add an environment variable. - """ - # XXX: put/unset/getenv should probably be deprecated - import os - os.environ[key] = value - -def unsetenv(key): - """unsetenv(key) - - Delete an environment variable. - """ - import os - try: - del os.environ[key] - except KeyError: - pass - -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.""" - import os - return os.environ.get(key, default) - -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_impl.isatty(fd) - - if isinstance(fileno, FileDescriptor): - return _posix_impl.isatty(fileno) - - from org.python.core.io import IOBase - if not isinstance(fileno, IOBase): - raise TypeError('a file descriptor is required') - - return fileno.isatty() - -def urandom(n): - global urandom_source - if urandom_source is None: - from java.security import SecureRandom - urandom_source = SecureRandom() - import jarray - buffer = jarray.zeros(n, 'b') - urandom_source.nextBytes(buffer) - return buffer.tostring() Modified: trunk/jython/NEWS =================================================================== --- trunk/jython/NEWS 2009-11-22 20:24:31 UTC (rev 6947) +++ trunk/jython/NEWS 2009-11-22 22:09:52 UTC (rev 6948) @@ -10,6 +10,7 @@ - [ 1496 ] fix os.listdir errno for non-existing dirs - [ 1499 ] PostgreSQL datahandler should return Decimals instead of floats for NUMERIC/DECIMAL columns - [ 1477 ] os.setpgrp and posix.setpgrp fail with TypeError + - [ 1396 ] Assigning os module funcs as class attributes incompatible with CPython - Fix runtime issues during exitfuncs triggered via SystemRestart (such as during Django or Pylons development mode reloading) Modified: trunk/jython/src/org/python/modules/Setup.java =================================================================== --- trunk/jython/src/org/python/modules/Setup.java 2009-11-22 20:24:31 UTC (rev 6947) +++ trunk/jython/src/org/python/modules/Setup.java 2009-11-22 22:09:52 UTC (rev 6948) @@ -59,6 +59,6 @@ "_ast:org.python.antlr.ast.AstModule", "_marshal", "_threading:org.python.modules._threading._threading", - "_" + PosixModule.getOSName() + ":org.python.modules.posix.PosixModule" + PosixModule.getOSName() + ":org.python.modules.posix.PosixModule" }; } Modified: trunk/jython/src/org/python/modules/posix/PosixModule.java =================================================================== --- trunk/jython/src/org/python/modules/posix/PosixModule.java 2009-11-22 20:24:31 UTC (rev 6947) +++ trunk/jython/src/org/python/modules/posix/PosixModule.java 2009-11-22 22:09:52 UTC (rev 6948) @@ -5,6 +5,7 @@ import com.kenai.constantine.platform.Errno; import java.io.File; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; @@ -12,12 +13,15 @@ import java.nio.channels.Channel; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; +import java.security.SecureRandom; +import java.util.Iterator; import java.util.Map; import org.jruby.ext.posix.FileStat; import org.jruby.ext.posix.JavaPOSIX; import org.jruby.ext.posix.POSIX; import org.jruby.ext.posix.POSIXFactory; +import org.jruby.ext.posix.util.Platform; import org.python.core.ClassDictInit; import org.python.core.Py; @@ -25,12 +29,15 @@ import org.python.core.PyDictionary; import org.python.core.PyException; import org.python.core.PyFile; +import org.python.core.PyFloat; +import org.python.core.PyInteger; import org.python.core.PyList; import org.python.core.PyObject; import org.python.core.PyString; import org.python.core.PyTuple; import org.python.core.ThreadState; import org.python.core.imp; +import org.python.core.io.IOBase; import org.python.core.io.FileDescriptors; import org.python.core.io.FileIO; import org.python.core.io.RawIOBase; @@ -38,13 +45,7 @@ import org.python.core.util.StringUtil; /** - * The underlying _posix or _nt module, named depending on the platform. - * - * This currently contains only some of the basics of the posix/nt modules (which are - * implemented in Python), most importantly things like PythonPOSIXHandler that are slower - * to instantiate and thus would affect startup time. - * - * Eventually more if not all of the pure Python module should end up here. + * The posix/nt module, depending on the platform. */ public class PosixModule implements ClassDictInit { @@ -79,6 +80,11 @@ /** os.path.realpath function for use by chdir. Lazily loaded. */ private static PyObject realpath; + /** Lazily initialzed singleton source for urandom. */ + private static class UrandomSource { + static final SecureRandom INSTANCE = new SecureRandom(); + } + public static void classDictInit(PyObject dict) { // only expose the open flags we support dict.__setitem__("O_RDONLY", Py.newInteger(O_RDONLY)); @@ -116,9 +122,18 @@ dict.__setitem__("getOSName", null); dict.__setitem__("badFD", null); - dict.__setitem__("__all__", dict.invoke("keys")); + // Hide __doc__s + PyList keys = (PyList)dict.invoke("keys"); + for (Iterator<?> it = keys.listIterator(); it.hasNext();) { + String key = (String)it.next(); + if (key.startsWith("__doc__")) { + it.remove(); + dict.__setitem__(key, null); + } + } + dict.__setitem__("__all__", keys); - dict.__setitem__("__name__", new PyString("_" + os.getModuleName())); + dict.__setitem__("__name__", new PyString(os.getModuleName())); dict.__setitem__("__doc__", __doc__); } @@ -377,6 +392,42 @@ return posix.getpgrp(); } + public static PyString __doc__isatty = new PyString( + "isatty(fd) -> bool\n\n" + + "Return True if the file descriptor 'fd' is an open file descriptor\n" + + "connected to the slave end of a terminal."); + public static boolean isatty(PyObject fdObj) { + if (fdObj instanceof PyInteger) { + FileDescriptor fd; + switch (fdObj.asInt()) { + case 0: + fd = FileDescriptor.in; + break; + case 1: + fd = FileDescriptor.out; + break; + case 2: + fd = FileDescriptor.err; + break; + default: + throw Py.NotImplementedError("Integer file descriptor compatibility only " + + "available for stdin, stdout and stderr (0-2)"); + } + return posix.isatty(fd); + } + + Object tojava = fdObj.__tojava__(FileDescriptor.class); + if (tojava != Py.NoConversion) { + return posix.isatty((FileDescriptor)tojava); + } + + tojava = fdObj.__tojava__(IOBase.class); + if (tojava == Py.NoConversion) { + throw Py.TypeError("a file descriptor is required"); + } + return ((IOBase)tojava).isatty(); + } + public static PyString __doc__kill = new PyString( "kill(pid, sig)\n\n" + "Kill a process with a signal."); @@ -534,6 +585,26 @@ return new FileIO(path, fileIOMode); } + public static PyString __doc__popen = new PyString( + "popen(command [, mode='r' [, bufsize]]) -> pipe\n\n" + + "Open a pipe to/from a command returning a file object."); + public static PyObject popen(PyObject[] args, String[] kwds) { + // XXX: popen lives in posix in 2.x, but moves to os in 3.x. It's easier for us to + // keep it in os + // import os; return os.popen(*args, **kwargs) + return imp.load("os").__getattr__("popen").__call__(args, kwds); + } + + public static PyString __doc__putenv = new PyString( + "putenv(key, value)\n\n" + + "Change or add an environment variable."); + public static void putenv(String key, String value) { + // XXX: Consider deprecating putenv/unsetenv + // import os; os.environ[key] = value + PyObject environ = imp.load("os").__getattr__("environ"); + environ.__setitem__(key, new PyString(value)); + } + public static PyString __doc__read = new PyString( "read(fd, buffersize) -> string\n\n" + "Read a file descriptor."); @@ -564,6 +635,32 @@ unlink(path); } + public static PyString __doc__rename = new PyString( + "rename(old, new)\n\n" + + "Rename a file or directory."); + public static void rename(String oldpath, String newpath) { + if (!new RelativeFile(oldpath).renameTo(new RelativeFile(newpath))) { + PyObject args = new PyTuple(Py.Zero, new PyString("Couldn't rename file")); + throw new PyException(Py.OSError, args); + } + } + + public static PyString __doc__rmdir = new PyString( + "rmdir(path)\n\n" + + "Remove a directory."); + public static void rmdir(String path) { + File file = new RelativeFile(path); + if (!file.exists()) { + throw Py.OSError(Errno.ENOENT, path); + } else if (!file.isDirectory()) { + throw Py.OSError(Errno.ENOTDIR, path); + } else if (!file.delete()) { + PyObject args = new PyTuple(Py.Zero, new PyString("Couldn't delete directory"), + new PyString(path)); + throw new PyException(Py.OSError, args); + } + } + public static PyString __doc__setpgrp = new PyString( "setpgrp()\n\n" + "Make this process a session leader."); @@ -610,6 +707,15 @@ posix.symlink(src, absolutePath(dst)); } + public static PyString __doc__system = new PyString( + "system(command) -> exit_status\n\n" + + "Execute the command (a string) in a subshell."); + public static void system(PyObject command) { + // import subprocess; subprocess.call(command, shell=True) + imp.load("subprocess").invoke("call", command, new PyObject[] {Py.True}, + new String[] {"shell"}); + } + public static PyString __doc__umask = new PyString( "umask(new_mask) -> old_mask\n\n" + "Set the current numeric umask and return the previous umask."); @@ -638,6 +744,47 @@ } } + public static PyString __doc__utime = new PyString( + "utime(path, (atime, mtime))\n" + + "utime(path, None)\n\n" + + "Set the access and modified time of the file to the given values. If the\n" + + "second form is used, set the access and modified times to the current time."); + public static void utime(String path, PyObject times) { + long[] atimeval; + long[] mtimeval; + + if (times == Py.None) { + atimeval = mtimeval = null; + } else if (times instanceof PyTuple && times.__len__() == 2) { + atimeval = extractTimeval(times.__getitem__(0)); + mtimeval = extractTimeval(times.__getitem__(1)); + } else { + throw Py.TypeError("utime() arg 2 must be a tuple (atime, mtime)"); + } + posix.utimes(absolutePath(path), atimeval, mtimeval); + } + + /** + * Convert seconds (with a possible fraction) from epoch to a 2 item array of seconds, + * microseconds from epoch as longs. + * + * @param seconds a PyObject number + * @return a 2 item long[] + */ + private static long[] extractTimeval(PyObject seconds) { + long[] timeval = new long[] {Platform.IS_32_BIT ? seconds.asInt() : seconds.asLong(), 0L}; + if (seconds instanceof PyFloat) { + // can't exceed 1000000 + long usec = (long)((seconds.asDouble() % 1.0) * 1e6); + if (usec < 0) { + // If rounding gave us a negative number, truncate + usec = 0; + } + timeval[1] = usec; + } + return timeval; + } + public static PyString __doc__wait = new PyString( "wait() -> (pid, status)\n\n" + "Wait for completion of a child process."); @@ -674,6 +821,30 @@ } } + public static PyString __doc__unsetenv = new PyString( + "unsetenv(key)\n\n" + + "Delete an environment variable."); + public static void unsetenv(String key) { + // import os; try: del os.environ[key]; except KeyError: pass + PyObject environ = imp.load("os").__getattr__("environ"); + try { + environ.__delitem__(key); + } catch (PyException pye) { + if (!pye.match(Py.KeyError)) { + throw pye; + } + } + } + + public static PyString __doc__urandom = new PyString( + "urandom(n) -> str\n\n" + + "Return a string of n random bytes suitable for cryptographic use."); + public static PyObject urandom(int n) { + byte[] buf = new byte[n]; + UrandomSource.INSTANCE.nextBytes(buf); + return new PyString(StringUtil.fromBytes(buf)); + } + /** * Helper function for the subprocess module, returns the potential shell commands for * this OS. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |