Author: Ronan Lamy <[email protected]>
Branch: py3k
Changeset: r86630:991f4c162891
Date: 2016-08-28 02:53 +0100
http://bitbucket.org/pypy/pypy/changeset/991f4c162891/
Log: Resynchronise rpython/ with branch 'py3.5'
diff --git a/rpython/rlib/_rsocket_rffi.py b/rpython/rlib/_rsocket_rffi.py
--- a/rpython/rlib/_rsocket_rffi.py
+++ b/rpython/rlib/_rsocket_rffi.py
@@ -176,6 +176,7 @@
SOCK_DGRAM SOCK_RAW SOCK_RDM SOCK_SEQPACKET SOCK_STREAM
+SOCK_CLOEXEC
SOL_SOCKET SOL_IPX SOL_AX25 SOL_ATALK SOL_NETROM SOL_ROSE
@@ -319,6 +320,8 @@
[('p_proto', rffi.INT),
])
+CConfig.HAVE_ACCEPT4 = platform.Has('accept4')
+
if _POSIX:
CConfig.nfds_t = platform.SimpleType('nfds_t')
CConfig.pollfd = platform.Struct('struct pollfd',
@@ -541,6 +544,12 @@
socketaccept = external('accept', [socketfd_type, sockaddr_ptr,
socklen_t_ptr], socketfd_type,
save_err=SAVE_ERR)
+HAVE_ACCEPT4 = cConfig.HAVE_ACCEPT4
+if HAVE_ACCEPT4:
+ socketaccept4 = external('accept4', [socketfd_type, sockaddr_ptr,
+ socklen_t_ptr, rffi.INT],
+ socketfd_type,
+ save_err=SAVE_ERR)
socketbind = external('bind', [socketfd_type, sockaddr_ptr, socklen_t],
rffi.INT, save_err=SAVE_ERR)
socketlisten = external('listen', [socketfd_type, rffi.INT], rffi.INT,
diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py
--- a/rpython/rlib/rposix.py
+++ b/rpython/rlib/rposix.py
@@ -372,15 +372,27 @@
raise OSError(get_saved_errno(), '%s failed' % name)
return result
+def _dup(fd, inheritable=True):
+ validate_fd(fd)
+ if inheritable:
+ res = c_dup(fd)
+ else:
+ res = c_dup_noninheritable(fd)
+ return res
+
@replace_os_function('dup')
-def dup(fd):
- validate_fd(fd)
- return handle_posix_error('dup', c_dup(fd))
+def dup(fd, inheritable=True):
+ res = _dup(fd, inheritable)
+ return handle_posix_error('dup', res)
@replace_os_function('dup2')
-def dup2(fd, newfd):
+def dup2(fd, newfd, inheritable=True):
validate_fd(fd)
- handle_posix_error('dup2', c_dup2(fd, newfd))
+ if inheritable:
+ res = c_dup2(fd, newfd)
+ else:
+ res = c_dup2_noninheritable(fd, newfd)
+ handle_posix_error('dup2', res)
#___________________________________________________________________
@@ -1122,37 +1134,77 @@
c_open_osfhandle = external('_open_osfhandle', [rffi.INTPTR_T,
rffi.INT],
rffi.INT)
+ HAVE_PIPE2 = False
+ HAVE_DUP3 = False
+ O_CLOEXEC = None
else:
INT_ARRAY_P = rffi.CArrayPtr(rffi.INT)
c_pipe = external('pipe', [INT_ARRAY_P], rffi.INT,
save_err=rffi.RFFI_SAVE_ERRNO)
+ class CConfig:
+ _compilation_info_ = eci
+ HAVE_PIPE2 = rffi_platform.Has('pipe2')
+ HAVE_DUP3 = rffi_platform.Has('dup3')
+ O_CLOEXEC = rffi_platform.DefinedConstantInteger('O_CLOEXEC')
+ config = rffi_platform.configure(CConfig)
+ HAVE_PIPE2 = config['HAVE_PIPE2']
+ HAVE_DUP3 = config['HAVE_DUP3']
+ O_CLOEXEC = config['O_CLOEXEC']
+ if HAVE_PIPE2:
+ c_pipe2 = external('pipe2', [INT_ARRAY_P, rffi.INT], rffi.INT,
+ save_err=rffi.RFFI_SAVE_ERRNO)
@replace_os_function('pipe')
-def pipe():
+def pipe(flags=0):
+ # 'flags' might be ignored. Check the result.
if _WIN32:
+ # 'flags' ignored
pread = lltype.malloc(rwin32.LPHANDLE.TO, 1, flavor='raw')
pwrite = lltype.malloc(rwin32.LPHANDLE.TO, 1, flavor='raw')
try:
- if not CreatePipe(
- pread, pwrite, lltype.nullptr(rffi.VOIDP.TO), 0):
- raise WindowsError(rwin32.GetLastError_saved(),
- "CreatePipe failed")
+ ok = CreatePipe(
+ pread, pwrite, lltype.nullptr(rffi.VOIDP.TO), 0)
hread = rffi.cast(rffi.INTPTR_T, pread[0])
hwrite = rffi.cast(rffi.INTPTR_T, pwrite[0])
finally:
lltype.free(pwrite, flavor='raw')
lltype.free(pread, flavor='raw')
- fdread = c_open_osfhandle(hread, 0)
- fdwrite = c_open_osfhandle(hwrite, 1)
+ if ok:
+ fdread = c_open_osfhandle(hread, 0)
+ fdwrite = c_open_osfhandle(hwrite, 1)
+ if fdread == -1 or fdwrite == -1:
+ rwin32.CloseHandle(hread)
+ rwin32.CloseHandle(hwrite)
+ ok = 0
+ if not ok:
+ raise WindowsError(rwin32.GetLastError_saved(),
+ "CreatePipe failed")
return (fdread, fdwrite)
else:
filedes = lltype.malloc(INT_ARRAY_P.TO, 2, flavor='raw')
try:
- handle_posix_error('pipe', c_pipe(filedes))
+ if HAVE_PIPE2 and _pipe2_syscall.attempt_syscall():
+ res = c_pipe2(filedes, flags)
+ if _pipe2_syscall.fallback(res):
+ res = c_pipe(filedes)
+ else:
+ res = c_pipe(filedes) # 'flags' ignored
+ handle_posix_error('pipe', res)
return (widen(filedes[0]), widen(filedes[1]))
finally:
lltype.free(filedes, flavor='raw')
+def pipe2(flags):
+ # Only available if there is really a c_pipe2 function.
+ # No fallback to pipe() if we get ENOSYS.
+ filedes = lltype.malloc(INT_ARRAY_P.TO, 2, flavor='raw')
+ try:
+ res = c_pipe2(filedes, flags)
+ handle_posix_error('pipe2', res)
+ return (widen(filedes[0]), widen(filedes[1]))
+ finally:
+ lltype.free(filedes, flavor='raw')
+
c_link = external('link', [rffi.CCHARP, rffi.CCHARP], rffi.INT,
save_err=rffi.RFFI_SAVE_ERRNO,)
c_symlink = external('symlink', [rffi.CCHARP, rffi.CCHARP], rffi.INT,
@@ -2088,14 +2140,46 @@
eci_inheritable = eci.merge(ExternalCompilationInfo(
- separate_module_sources=["""
+ separate_module_sources=[r"""
+#include <errno.h>
+
RPY_EXTERN
int rpy_set_inheritable(int fd, int inheritable)
{
- /* XXX minimal impl. XXX */
- int request = inheritable ? FIONCLEX : FIOCLEX;
- return ioctl(fd, request, NULL);
+ static int ioctl_works = -1;
+ int flags;
+
+ if (ioctl_works != 0) {
+ int request = inheritable ? FIONCLEX : FIOCLEX;
+ int err = ioctl(fd, request, NULL);
+ if (!err) {
+ ioctl_works = 1;
+ return 0;
+ }
+
+ if (errno != ENOTTY && errno != EACCES) {
+ return -1;
+ }
+ else {
+ /* ENOTTY: The ioctl is declared but not supported by the
+ kernel. EACCES: SELinux policy, this can be the case on
+ Android. */
+ ioctl_works = 0;
+ }
+ /* fallback to fcntl() if ioctl() does not work */
+ }
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0)
+ return -1;
+
+ if (inheritable)
+ flags &= ~FD_CLOEXEC;
+ else
+ flags |= FD_CLOEXEC;
+ return fcntl(fd, F_SETFD, flags);
}
+
RPY_EXTERN
int rpy_get_inheritable(int fd)
{
@@ -2104,8 +2188,64 @@
return -1;
return !(flags & FD_CLOEXEC);
}
- """],
- post_include_bits=['RPY_EXTERN int rpy_set_inheritable(int, int);']))
+
+RPY_EXTERN
+int rpy_dup_noninheritable(int fd)
+{
+#ifdef _WIN32
+#error NotImplementedError
+#endif
+
+#ifdef F_DUPFD_CLOEXEC
+ return fcntl(fd, F_DUPFD_CLOEXEC, 0);
+#else
+ fd = dup(fd);
+ if (fd >= 0) {
+ if (rpy_set_inheritable(fd, 0) != 0) {
+ close(fd);
+ return -1;
+ }
+ }
+ return fd;
+#endif
+}
+
+RPY_EXTERN
+int rpy_dup2_noninheritable(int fd, int fd2)
+{
+#ifdef _WIN32
+#error NotImplementedError
+#endif
+
+#ifdef F_DUP2FD_CLOEXEC
+ return fcntl(fd, F_DUP2FD_CLOEXEC, fd2);
+
+#else
+# if %(HAVE_DUP3)d /* HAVE_DUP3 */
+ static int dup3_works = -1;
+ if (dup3_works != 0) {
+ if (dup3(fd, fd2, O_CLOEXEC) >= 0)
+ return 0;
+ if (dup3_works == -1)
+ dup3_works = (errno != ENOSYS);
+ if (dup3_works)
+ return -1;
+ }
+# endif
+ if (dup2(fd, fd2) < 0)
+ return -1;
+ if (rpy_set_inheritable(fd2, 0) != 0) {
+ close(fd2);
+ return -1;
+ }
+ return 0;
+#endif
+}
+ """ % {'HAVE_DUP3': HAVE_DUP3}],
+ post_include_bits=['RPY_EXTERN int rpy_set_inheritable(int, int);\n'
+ 'RPY_EXTERN int rpy_get_inheritable(int);\n'
+ 'RPY_EXTERN int rpy_dup_noninheritable(int);\n'
+ 'RPY_EXTERN int rpy_dup2_noninheritable(int, int);\n']))
c_set_inheritable = external('rpy_set_inheritable', [rffi.INT, rffi.INT],
rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO,
@@ -2113,12 +2253,56 @@
c_get_inheritable = external('rpy_get_inheritable', [rffi.INT],
rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO,
compilation_info=eci_inheritable)
+c_dup_noninheritable = external('rpy_dup_noninheritable', [rffi.INT],
+ rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO,
+ compilation_info=eci_inheritable)
+c_dup2_noninheritable = external('rpy_dup2_noninheritable',
[rffi.INT,rffi.INT],
+ rffi.INT, save_err=rffi.RFFI_SAVE_ERRNO,
+ compilation_info=eci_inheritable)
def set_inheritable(fd, inheritable):
- error = c_set_inheritable(fd, inheritable)
- handle_posix_error('set_inheritable', error)
+ result = c_set_inheritable(fd, inheritable)
+ handle_posix_error('set_inheritable', result)
def get_inheritable(fd):
res = c_get_inheritable(fd)
res = handle_posix_error('get_inheritable', res)
return res != 0
+
+class SetNonInheritableCache(object):
+ """Make one prebuilt instance of this for each path that creates
+ file descriptors, where you don't necessarily know if that function
+ returns inheritable or non-inheritable file descriptors.
+ """
+ _immutable_fields_ = ['cached_inheritable?']
+ cached_inheritable = -1 # -1 = don't know yet; 0 = off; 1 = on
+
+ def set_non_inheritable(self, fd):
+ if self.cached_inheritable == -1:
+ self.cached_inheritable = get_inheritable(fd)
+ if self.cached_inheritable == 1:
+ # 'fd' is inheritable; we must manually turn it off
+ set_inheritable(fd, False)
+
+ def _cleanup_(self):
+ self.cached_inheritable = -1
+
+class ENoSysCache(object):
+ """Cache whether a system call returns ENOSYS or not."""
+ _immutable_fields_ = ['cached_nosys?']
+ cached_nosys = -1 # -1 = don't know; 0 = no; 1 = yes, getting ENOSYS
+
+ def attempt_syscall(self):
+ return self.cached_nosys != 1
+
+ def fallback(self, res):
+ nosys = self.cached_nosys
+ if nosys == -1:
+ nosys = (res < 0 and get_saved_errno() == errno.ENOSYS)
+ self.cached_nosys = nosys
+ return nosys
+
+ def _cleanup_(self):
+ self.cached_nosys = -1
+
+_pipe2_syscall = ENoSysCache()
diff --git a/rpython/rlib/rsocket.py b/rpython/rlib/rsocket.py
--- a/rpython/rlib/rsocket.py
+++ b/rpython/rlib/rsocket.py
@@ -8,10 +8,11 @@
# XXX this does not support yet the least common AF_xxx address families
# supported by CPython. See http://bugs.pypy.org/issue1942
+from errno import EINVAL
from rpython.rlib import _rsocket_rffi as _c, jit, rgc
from rpython.rlib.objectmodel import instantiate, keepalive_until_here
from rpython.rlib.rarithmetic import intmask, r_uint
-from rpython.rlib import rthread
+from rpython.rlib import rthread, rposix
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rtyper.lltypesystem.rffi import sizeof, offsetof
from rpython.rtyper.extregistry import ExtRegistryEntry
@@ -522,12 +523,28 @@
timeout = -1.0
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0,
- fd=_c.INVALID_SOCKET):
+ fd=_c.INVALID_SOCKET, inheritable=True):
"""Create a new socket."""
if _c.invalid_socket(fd):
- fd = _c.socket(family, type, proto)
- if _c.invalid_socket(fd):
- raise self.error_handler()
+ if not inheritable and SOCK_CLOEXEC is not None:
+ # Non-inheritable: we try to call socket() with
+ # SOCK_CLOEXEC, which may fail. If we get EINVAL,
+ # then we fall back to the SOCK_CLOEXEC-less case.
+ fd = _c.socket(family, type | SOCK_CLOEXEC, proto)
+ if fd < 0:
+ if _c.geterrno() == EINVAL:
+ # Linux older than 2.6.27 does not support
+ # SOCK_CLOEXEC. An EINVAL might be caused by
+ # random other things, though. Don't cache.
+ pass
+ else:
+ raise self.error_handler()
+ if _c.invalid_socket(fd):
+ fd = _c.socket(family, type, proto)
+ if _c.invalid_socket(fd):
+ raise self.error_handler()
+ if not inheritable:
+ sock_set_inheritable(fd, False)
# PLAT RISCOS
self.fd = fd
self.family = family
@@ -630,20 +647,33 @@
return addr, addr.addr_p, addrlen_p
@jit.dont_look_inside
- def accept(self):
+ def accept(self, inheritable=True):
"""Wait for an incoming connection.
Return (new socket fd, client address)."""
if self._select(False) == 1:
raise SocketTimeout
address, addr_p, addrlen_p = self._addrbuf()
try:
- newfd = _c.socketaccept(self.fd, addr_p, addrlen_p)
+ remove_inheritable = not inheritable
+ if (not inheritable and SOCK_CLOEXEC is not None
+ and _c.HAVE_ACCEPT4
+ and _accept4_syscall.attempt_syscall()):
+ newfd = _c.socketaccept4(self.fd, addr_p, addrlen_p,
+ SOCK_CLOEXEC)
+ if _accept4_syscall.fallback(newfd):
+ newfd = _c.socketaccept(self.fd, addr_p, addrlen_p)
+ else:
+ remove_inheritable = False
+ else:
+ newfd = _c.socketaccept(self.fd, addr_p, addrlen_p)
addrlen = addrlen_p[0]
finally:
lltype.free(addrlen_p, flavor='raw')
address.unlock()
if _c.invalid_socket(newfd):
raise self.error_handler()
+ if remove_inheritable:
+ sock_set_inheritable(newfd, False)
address.addrlen = rffi.cast(lltype.Signed, addrlen)
return (newfd, address)
@@ -1032,6 +1062,12 @@
return result
make_socket._annspecialcase_ = 'specialize:arg(4)'
+def sock_set_inheritable(fd, inheritable):
+ try:
+ rposix.set_inheritable(fd, inheritable)
+ except OSError as e:
+ raise CSocketError(e.errno)
+
class SocketError(Exception):
applevelerrcls = 'error'
def __init__(self):
@@ -1090,7 +1126,7 @@
if hasattr(_c, 'socketpair'):
def socketpair(family=socketpair_default_family, type=SOCK_STREAM, proto=0,
- SocketClass=RSocket):
+ SocketClass=RSocket, inheritable=True):
"""socketpair([family[, type[, proto]]]) -> (socket object, socket
object)
Create a pair of socket objects from the sockets returned by the
platform
@@ -1100,18 +1136,41 @@
"""
result = lltype.malloc(_c.socketpair_t, 2, flavor='raw')
try:
- res = _c.socketpair(family, type, proto, result)
+ res = -1
+ remove_inheritable = not inheritable
+ if not inheritable and SOCK_CLOEXEC is not None:
+ # Non-inheritable: we try to call socketpair() with
+ # SOCK_CLOEXEC, which may fail. If we get EINVAL,
+ # then we fall back to the SOCK_CLOEXEC-less case.
+ res = _c.socketpair(family, type | SOCK_CLOEXEC,
+ proto, result)
+ if res < 0:
+ if _c.geterrno() == EINVAL:
+ # Linux older than 2.6.27 does not support
+ # SOCK_CLOEXEC. An EINVAL might be caused by
+ # random other things, though. Don't cache.
+ pass
+ else:
+ raise last_error()
+ else:
+ remove_inheritable = False
+ #
if res < 0:
- raise last_error()
+ res = _c.socketpair(family, type, proto, result)
+ if res < 0:
+ raise last_error()
fd0 = rffi.cast(lltype.Signed, result[0])
fd1 = rffi.cast(lltype.Signed, result[1])
finally:
lltype.free(result, flavor='raw')
+ if remove_inheritable:
+ sock_set_inheritable(fd0, False)
+ sock_set_inheritable(fd1, False)
return (make_socket(fd0, family, type, proto, SocketClass),
make_socket(fd1, family, type, proto, SocketClass))
if _c.WIN32:
- def dup(fd):
+ def dup(fd, inheritable=True):
with lltype.scoped_alloc(_c.WSAPROTOCOL_INFO, zero=True) as info:
if _c.WSADuplicateSocket(fd, rwin32.GetCurrentProcessId(), info):
raise last_error()
@@ -1122,15 +1181,16 @@
raise last_error()
return result
else:
- def dup(fd):
- fd = _c.dup(fd)
+ def dup(fd, inheritable=True):
+ fd = rposix._dup(fd, inheritable)
if fd < 0:
raise last_error()
return fd
-def fromfd(fd, family, type, proto=0, SocketClass=RSocket):
+def fromfd(fd, family, type, proto=0, SocketClass=RSocket, inheritable=True):
# Dup the fd so it and the socket can be closed independently
- return make_socket(dup(fd), family, type, proto, SocketClass)
+ fd = dup(fd, inheritable=inheritable)
+ return make_socket(fd, family, type, proto, SocketClass)
def getdefaulttimeout():
return defaults.timeout
@@ -1407,3 +1467,5 @@
if timeout < 0.0:
timeout = -1.0
defaults.timeout = timeout
+
+_accept4_syscall = rposix.ENoSysCache()
diff --git a/rpython/rlib/rsre/rsre_char.py b/rpython/rlib/rsre/rsre_char.py
--- a/rpython/rlib/rsre/rsre_char.py
+++ b/rpython/rlib/rsre/rsre_char.py
@@ -24,12 +24,15 @@
#### Constants
# Identifying as _sre from Python 2.3 and onwards (at least up to 2.7)
-MAGIC = 20031017
+# UPDATE: change was necessary for Python 3.3 changes
+MAGIC = 20140917
if sys.maxint > 2**32:
MAXREPEAT = int(2**32 - 1)
+ MAXGROUPS = int(2**31 - 1)
else:
MAXREPEAT = int(2**31 - 1)
+ MAXGROUPS = int((2**31 / sys.maxint / 2) - 1)
# In _sre.c this is bytesize of the code word type of the C implementation.
# There it's 2 for normal Python builds and more for wide unicode builds
(large
diff --git a/rpython/rlib/test/test_rposix.py b/rpython/rlib/test/test_rposix.py
--- a/rpython/rlib/test/test_rposix.py
+++ b/rpython/rlib/test/test_rposix.py
@@ -589,3 +589,18 @@
assert rposix.get_inheritable(fd1) == False
os.close(fd1)
os.close(fd2)
+
+def test_SetNonInheritableCache():
+ cache = rposix.SetNonInheritableCache()
+ fd1, fd2 = os.pipe()
+ assert rposix.get_inheritable(fd1) == True
+ assert rposix.get_inheritable(fd1) == True
+ assert cache.cached_inheritable == -1
+ cache.set_non_inheritable(fd1)
+ assert cache.cached_inheritable == 1
+ cache.set_non_inheritable(fd2)
+ assert cache.cached_inheritable == 1
+ assert rposix.get_inheritable(fd1) == False
+ assert rposix.get_inheritable(fd1) == False
+ os.close(fd1)
+ os.close(fd2)
diff --git a/rpython/rlib/test/test_rsocket.py
b/rpython/rlib/test/test_rsocket.py
--- a/rpython/rlib/test/test_rsocket.py
+++ b/rpython/rlib/test/test_rsocket.py
@@ -119,6 +119,16 @@
s1.close()
s2.close()
+def test_socketpair_inheritable():
+ if sys.platform == "win32":
+ py.test.skip('No socketpair on Windows')
+ for inh in [False, True]:
+ s1, s2 = socketpair(inheritable=inh)
+ assert rposix.get_inheritable(s1.fd) == inh
+ assert rposix.get_inheritable(s2.fd) == inh
+ s1.close()
+ s2.close()
+
def test_socketpair_recvinto_1():
class Buffer:
def setslice(self, start, string):
@@ -378,6 +388,12 @@
s1.close()
s2.close()
+def test_inheritable():
+ for inh in [False, True]:
+ s1 = RSocket(inheritable=inh)
+ assert rposix.get_inheritable(s1.fd) == inh
+ s1.close()
+
def test_getaddrinfo_http():
lst = getaddrinfo('localhost', 'http')
assert isinstance(lst, list)
diff --git a/rpython/translator/sandbox/test/test_sandbox.py
b/rpython/translator/sandbox/test/test_sandbox.py
--- a/rpython/translator/sandbox/test/test_sandbox.py
+++ b/rpython/translator/sandbox/test/test_sandbox.py
@@ -58,7 +58,7 @@
exe = compile(entry_point)
g, f = run_in_subprocess(exe)
expect(f, g, "ll_os.ll_os_open", ("/tmp/foobar", os.O_RDONLY, 0777), 77)
- expect(f, g, "ll_os.ll_os_dup", (77,), 78)
+ expect(f, g, "ll_os.ll_os_dup", (77, True), 78)
g.close()
tail = f.read()
f.close()
@@ -94,7 +94,7 @@
exe = compile(entry_point)
g, f = run_in_subprocess(exe)
- expect(f, g, "ll_os.ll_os_dup2", (34, 56), None)
+ expect(f, g, "ll_os.ll_os_dup2", (34, 56, True), None)
expect(f, g, "ll_os.ll_os_access", ("spam", 77), True)
g.close()
tail = f.read()
@@ -134,7 +134,7 @@
exe = compile(entry_point)
g, f = run_in_subprocess(exe)
expect(f, g, "ll_time.ll_time_time", (), 3.141592)
- expect(f, g, "ll_os.ll_os_dup", (3141,), 3)
+ expect(f, g, "ll_os.ll_os_dup", (3141, True), 3)
g.close()
tail = f.read()
f.close()
@@ -149,7 +149,7 @@
exe = compile(entry_point)
g, f = run_in_subprocess(exe)
expect(f, g, "ll_os.ll_os_getcwd", (), "/tmp/foo/bar")
- expect(f, g, "ll_os.ll_os_dup", (len("/tmp/foo/bar"),), 3)
+ expect(f, g, "ll_os.ll_os_dup", (len("/tmp/foo/bar"), True), 3)
g.close()
tail = f.read()
f.close()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit