diff options
-rw-r--r-- | Doc/library/codecs.rst | 9 | ||||
-rw-r--r-- | Doc/library/os.rst | 153 | ||||
-rw-r--r-- | Include/descrobject.h | 1 | ||||
-rw-r--r-- | Lib/idlelib/PyShell.py | 35 | ||||
-rw-r--r-- | Lib/idlelib/ScriptBinding.py | 33 | ||||
-rw-r--r-- | Lib/subprocess.py | 56 | ||||
-rw-r--r-- | Lib/test/test_exceptions.py | 8 | ||||
-rw-r--r-- | Lib/test/test_faulthandler.py | 3 | ||||
-rw-r--r-- | Lib/test/test_os.py | 94 | ||||
-rw-r--r-- | Misc/NEWS | 11 | ||||
-rw-r--r-- | Modules/posixmodule.c | 402 | ||||
-rw-r--r-- | Objects/descrobject.c | 9 | ||||
-rw-r--r-- | Objects/object.c | 3 | ||||
-rw-r--r-- | PC/errmap.h | 1 | ||||
-rw-r--r-- | PC/generrmap.c | 21 | ||||
-rw-r--r-- | Python/import.c | 2 | ||||
-rwxr-xr-x | configure | 2 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | pyconfig.h.in | 3 |
19 files changed, 765 insertions, 83 deletions
diff --git a/Doc/library/codecs.rst b/Doc/library/codecs.rst index 90bd0dd..84593f2 100644 --- a/Doc/library/codecs.rst +++ b/Doc/library/codecs.rst @@ -840,7 +840,7 @@ There's another encoding that is able to encoding the full range of Unicode characters: UTF-8. UTF-8 is an 8-bit encoding, which means there are no issues with byte order in UTF-8. Each byte in a UTF-8 byte sequence consists of two parts: Marker bits (the most significant bits) and payload bits. The marker bits -are a sequence of zero to six 1 bits followed by a 0 bit. Unicode characters are +are a sequence of zero to four ``1`` bits followed by a ``0`` bit. Unicode characters are encoded like this (with x being payload bits, which when concatenated give the Unicode character): @@ -853,12 +853,7 @@ Unicode character): +-----------------------------------+----------------------------------------------+ | ``U-00000800`` ... ``U-0000FFFF`` | 1110xxxx 10xxxxxx 10xxxxxx | +-----------------------------------+----------------------------------------------+ -| ``U-00010000`` ... ``U-001FFFFF`` | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | -+-----------------------------------+----------------------------------------------+ -| ``U-00200000`` ... ``U-03FFFFFF`` | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx | -+-----------------------------------+----------------------------------------------+ -| ``U-04000000`` ... ``U-7FFFFFFF`` | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx | -| | 10xxxxxx | +| ``U-00010000`` ... ``U-0010FFFF`` | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | +-----------------------------------+----------------------------------------------+ The least significant bit of the Unicode character is the rightmost x bit. diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 0c5c40c..636ac24 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -29,11 +29,6 @@ Notes on the availability of these functions: objects, and result in an object of the same type, if a path or file name is returned. -.. note:: - - If not separately noted, all functions that claim "Availability: Unix" are - supported on Mac OS X, which builds on a Unix core. - * An "Availability: Unix" note means that this function is commonly found on Unix systems. It does not make any claims about its existence on a specific operating system. @@ -751,6 +746,26 @@ as internal buffering of data. This function is not available on MacOS. +.. function:: fgetxattr(fd, attr) + + This works exactly like :func:`getxattr` but operates on a file descriptor, + *fd*, instead of a path. + + Availability: Linux + + .. versionadded:: 3.3 + + +.. function:: flistxattr(fd) + + This is exactly like :func:`listxattr` but operates on a file descriptor, + *fd*, instead of a path. + + Availability: Linux + + .. versionadded:: 3.3 + + .. function:: fdlistdir(fd) Like :func:`listdir`, but uses a file descriptor instead and always returns @@ -836,6 +851,27 @@ as internal buffering of data. Availability: Unix. +.. function:: fremovexattr(fd, attr) + + This works exactly like :func:`removexattr` but operates on a file + descriptor, *fd*, instead of a path. + + Availability: Linux + + .. versionadded:: 3.3 + + +.. function:: fsetxattr(fd, attr, value, flags=0) + + This works exactly like :func:`setxattr` but on a file descriptor, *fd*, + instead of a path. + + + Availability: Linux + + .. versionadded:: 3.3 + + .. function:: futimesat(dirfd, path, (atime, mtime)) futimesat(dirfd, path, None) @@ -1536,6 +1572,17 @@ Files and Directories Availability: Unix. +.. function:: getxattr(path, attr) + + Return the value of the extended filesystem attribute *attr* for + *path*. *attr* can be bytes or str. If it is str, it is encoded with the + filesystem encoding. + + Availability: Linux + + .. versionadded:: 3.3 + + .. function:: lchflags(path, flags) Set the flags of *path* to the numeric *flags*, like :func:`chflags`, but do not @@ -1561,6 +1608,15 @@ Files and Directories Availability: Unix. +.. function:: lgetxattr(path, attr) + + This works exactly like :func:`getxattr` but doesn't follow symlinks. + + Availability: Linux + + .. versionadded:: 3.3 + + .. function:: link(source, link_name) Create a hard link pointing to *source* named *link_name*. @@ -1585,6 +1641,44 @@ Files and Directories .. versionchanged:: 3.2 The *path* parameter became optional. + +.. function:: listxattr(path) + + Return a list of the extended filesystem attributes on *path*. Attributes are + returned as string decoded with the filesystem encoding. + + Availability: Linux + + .. versionadded:: 3.3 + + +.. function:: llistxattr(path) + + This works exactly like :func:`listxattr` but doesn't follow symlinks. + + Availability: Linux + + .. versionadded:: 3.3 + + +.. function:: lremoveattr(path, attr) + + This works exactly like :func:`removeattr` but doesn't follow symlinks. + + Availability: Linux + + .. versionadded:: 3.3 + + +.. function:: lsetxattr(path, attr, value, flags=0) + + This works exactly like :func:`setxattr` but doesn't follow symlinks. + + Availability: Linux + + .. versionadded:: 3.3 + + .. function:: lstat(path) Perform the equivalent of an :c:func:`lstat` system call on the given path. @@ -1758,6 +1852,17 @@ Files and Directories successfully removed. +.. function:: removexattr(path, attr) + + Removes the extended filesystem attribute *attr* from *path*. *attr* should + be bytes or str. If it is a string, it is encoded with the filesystem + encoding. + + Availability: Linux + + .. versionadded:: 3.3 + + .. function:: rename(src, dst) Rename the file or directory *src* to *dst*. If *dst* is a directory, @@ -1794,6 +1899,44 @@ Files and Directories Availability: Unix, Windows. +.. data:: XATTR_SIZE_MAX + + The maximum size the value of an extended attribute can be. Currently, this + is 64 kilobytes on Linux. + + +.. data:: XATTR_CREATE + + This is a possible value for the flags argument in :func:`setxattr`. It + indicates the operation must create an attribute. + + +.. data:: XATTR_REPLACE + + This is a possible value for the flags argument in :func:`setxattr`. It + indicates the operation must replace an existing attribute. + + +.. function:: setxattr(path, attr, value, flags=0) + + Set the extended filesystem attribute *attr* on *path* to *value*. *attr* + must be a bytes or str with no embedded NULs. If it is str, it is encoded + with the filesystem encoding. *flags* may be :data:`XATTR_REPLACE` or + :data:`XATTR_CREATE`. If :data:`XATTR_REPLACE` is given and the attribute + does not exist, ``EEXISTS`` will be raised. If :data:`XATTR_CREATE` is given + and the attribute already exists, the attribute will not be created and + ``ENODATA`` will be raised. + + Availability: Linux + + .. note:: + + A bug in Linux kernel versions less than 2.6.39 caused the flags argument + to be ignored on some filesystems. + + .. versionadded:: 3.3 + + .. function:: stat(path) Perform the equivalent of a :c:func:`stat` system call on the given path. diff --git a/Include/descrobject.h b/Include/descrobject.h index f715fe1..646b3cc 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -77,6 +77,7 @@ PyAPI_DATA(PyTypeObject) PyMemberDescr_Type; PyAPI_DATA(PyTypeObject) PyMethodDescr_Type; PyAPI_DATA(PyTypeObject) PyWrapperDescr_Type; PyAPI_DATA(PyTypeObject) PyDictProxy_Type; +PyAPI_DATA(PyTypeObject) _PyMethodWrapper_Type; PyAPI_FUNC(PyObject *) PyDescr_NewMethod(PyTypeObject *, PyMethodDef *); PyAPI_FUNC(PyObject *) PyDescr_NewClassMethod(PyTypeObject *, PyMethodDef *); diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py index dcce2eb..da74729 100644 --- a/Lib/idlelib/PyShell.py +++ b/Lib/idlelib/PyShell.py @@ -1,16 +1,17 @@ #! /usr/bin/env python3 +import getopt import os import os.path -import sys -import getopt import re import socket -import time +import subprocess +import sys import threading +import time +import tokenize import traceback import types -import subprocess import linecache from code import InteractiveInterpreter @@ -201,18 +202,18 @@ class PyShellEditorWindow(EditorWindow): breaks = self.breakpoints filename = self.io.filename try: - lines = open(self.breakpointPath,"r").readlines() + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() except IOError: lines = [] - new_file = open(self.breakpointPath,"w") - for line in lines: - if not line.startswith(filename + '='): - new_file.write(line) - self.update_breakpoints() - breaks = self.breakpoints - if breaks: - new_file.write(filename + '=' + str(breaks) + '\n') - new_file.close() + with open(self.breakpointPath, "w") as new_file: + for line in lines: + if not line.startswith(filename + '='): + new_file.write(line) + self.update_breakpoints() + breaks = self.breakpoints + if breaks: + new_file.write(filename + '=' + str(breaks) + '\n') def restore_file_breaks(self): self.text.update() # this enables setting "BREAK" tags to be visible @@ -220,7 +221,8 @@ class PyShellEditorWindow(EditorWindow): if filename is None: return if os.path.isfile(self.breakpointPath): - lines = open(self.breakpointPath,"r").readlines() + with open(self.breakpointPath, "r") as fp: + lines = fp.readlines() for line in lines: if line.startswith(filename + '='): breakpoint_linenumbers = eval(line[len(filename)+1:]) @@ -571,7 +573,8 @@ class ModifiedInterpreter(InteractiveInterpreter): def execfile(self, filename, source=None): "Execute an existing file" if source is None: - source = open(filename, "r").read() + with tokenize.open(filename) as fp: + source = fp.read() try: code = compile(source, filename, "exec") except (OverflowError, SyntaxError): diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py index 90972b5..915e56e 100644 --- a/Lib/idlelib/ScriptBinding.py +++ b/Lib/idlelib/ScriptBinding.py @@ -67,25 +67,20 @@ class ScriptBinding: def tabnanny(self, filename): # XXX: tabnanny should work on binary files as well - with open(filename, 'r', encoding='iso-8859-1') as f: - two_lines = f.readline() + f.readline() - encoding = IOBinding.coding_spec(two_lines) - if not encoding: - encoding = 'utf-8' - f = open(filename, 'r', encoding=encoding) - try: - tabnanny.process_tokens(tokenize.generate_tokens(f.readline)) - except tokenize.TokenError as msg: - msgtxt, (lineno, start) = msg - self.editwin.gotoline(lineno) - self.errorbox("Tabnanny Tokenizing Error", - "Token Error: %s" % msgtxt) - return False - except tabnanny.NannyNag as nag: - # The error messages from tabnanny are too confusing... - self.editwin.gotoline(nag.get_lineno()) - self.errorbox("Tab/space error", indent_message) - return False + with tokenize.open(filename) as f: + try: + tabnanny.process_tokens(tokenize.generate_tokens(f.readline)) + except tokenize.TokenError as msg: + msgtxt, (lineno, start) = msg + self.editwin.gotoline(lineno) + self.errorbox("Tabnanny Tokenizing Error", + "Token Error: %s" % msgtxt) + return False + except tabnanny.NannyNag as nag: + # The error messages from tabnanny are too confusing... + self.editwin.gotoline(nag.get_lineno()) + self.errorbox("Tab/space error", indent_message) + return False return True def checksyntax(self, filename): diff --git a/Lib/subprocess.py b/Lib/subprocess.py index db64588..2c5c888 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -464,13 +464,13 @@ def call(*popenargs, timeout=None, **kwargs): retcode = call(["ls", "-l"]) """ - p = Popen(*popenargs, **kwargs) - try: - return p.wait(timeout=timeout) - except TimeoutExpired: - p.kill() - p.wait() - raise + with Popen(*popenargs, **kwargs) as p: + try: + return p.wait(timeout=timeout) + except: + p.kill() + p.wait() + raise def check_call(*popenargs, **kwargs): @@ -514,16 +514,20 @@ def check_output(*popenargs, timeout=None, **kwargs): """ if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') - process = Popen(*popenargs, stdout=PIPE, **kwargs) - try: - output, unused_err = process.communicate(timeout=timeout) - except TimeoutExpired: - process.kill() - output, unused_err = process.communicate() - raise TimeoutExpired(process.args, timeout, output=output) - retcode = process.poll() - if retcode: - raise CalledProcessError(retcode, process.args, output=output) + with Popen(*popenargs, stdout=PIPE, **kwargs) as process: + try: + output, unused_err = process.communicate(timeout=timeout) + except TimeoutExpired: + process.kill() + output, unused_err = process.communicate() + raise TimeoutExpired(process.args, timeout, output=output) + except: + process.kill() + process.wait() + raise + retcode = process.poll() + if retcode: + raise CalledProcessError(retcode, process.args, output=output) return output @@ -618,11 +622,19 @@ def getstatusoutput(cmd): >>> subprocess.getstatusoutput('/bin/junk') (256, 'sh: /bin/junk: not found') """ - pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r') - text = pipe.read() - sts = pipe.close() - if sts is None: sts = 0 - if text[-1:] == '\n': text = text[:-1] + with os.popen('{ ' + cmd + '; } 2>&1', 'r') as pipe: + try: + text = pipe.read() + sts = pipe.close() + except: + process = pipe._proc + process.kill() + process.wait() + raise + if sts is None: + sts = 0 + if text[-1:] == '\n': + text = text[:-1] return sts, text diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 718d05c..9be6958 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -5,6 +5,7 @@ import sys import unittest import pickle import weakref +import errno from test.support import (TESTFN, unlink, run_unittest, captured_output, gc_collect, cpython_only, no_tracing) @@ -852,6 +853,13 @@ class ExceptionTests(unittest.TestCase): self.fail("RuntimeError not raised") self.assertEqual(wr(), None) + def test_errno_ENOTDIR(self): + # Issue #12802: "not a directory" errors are ENOTDIR even on Windows + with self.assertRaises(OSError) as cm: + os.listdir(__file__) + self.assertEqual(cm.exception.errno, errno.ENOTDIR, cm.exception) + + def test_main(): run_unittest(ExceptionTests) diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 331dd2e..977cb39 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -174,6 +174,9 @@ faulthandler._fatal_error(b'xyz') 2, 'xyz') + @unittest.skipIf(sys.platform.startswith('openbsd') and HAVE_THREADS, + "Issue #12868: sigaltstack() doesn't work on " + "OpenBSD if Python is compiled with pthread") @unittest.skipIf(not hasattr(faulthandler, '_stack_overflow'), 'need faulthandler._stack_overflow()') def test_stack_overflow(self): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index 1d5f11c..569b218 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -14,6 +14,8 @@ import shutil from test import support import contextlib import mmap +import platform +import re import uuid import asyncore import asynchat @@ -1506,6 +1508,97 @@ class TestSendfile(unittest.TestCase): raise +def supports_extended_attributes(): + if not hasattr(os, "setxattr"): + return False + try: + with open(support.TESTFN, "wb") as fp: + try: + os.fsetxattr(fp.fileno(), b"user.test", b"") + except OSError as e: + if e.errno != errno.ENOTSUP: + raise + return False + finally: + support.unlink(support.TESTFN) + # Kernels < 2.6.39 don't respect setxattr flags. + kernel_version = platform.release() + m = re.match("2.6.(\d{1,2})", kernel_version) + return m is None or int(m.group(1)) >= 39 + + +@unittest.skipUnless(supports_extended_attributes(), + "no non-broken extended attribute support") +class ExtendedAttributeTests(unittest.TestCase): + + def tearDown(self): + support.unlink(support.TESTFN) + + def _check_xattrs_str(self, s, getxattr, setxattr, removexattr, listxattr): + fn = support.TESTFN + open(fn, "wb").close() + with self.assertRaises(OSError) as cm: + getxattr(fn, s("user.test")) + self.assertEqual(cm.exception.errno, errno.ENODATA) + self.assertEqual(listxattr(fn), []) + setxattr(fn, s("user.test"), b"") + self.assertEqual(listxattr(fn), ["user.test"]) + self.assertEqual(getxattr(fn, b"user.test"), b"") + setxattr(fn, s("user.test"), b"hello", os.XATTR_REPLACE) + self.assertEqual(getxattr(fn, b"user.test"), b"hello") + with self.assertRaises(OSError) as cm: + setxattr(fn, s("user.test"), b"bye", os.XATTR_CREATE) + self.assertEqual(cm.exception.errno, errno.EEXIST) + with self.assertRaises(OSError) as cm: + setxattr(fn, s("user.test2"), b"bye", os.XATTR_REPLACE) + self.assertEqual(cm.exception.errno, errno.ENODATA) + setxattr(fn, s("user.test2"), b"foo", os.XATTR_CREATE) + self.assertEqual(sorted(listxattr(fn)), ["user.test", "user.test2"]) + removexattr(fn, s("user.test")) + with self.assertRaises(OSError) as cm: + getxattr(fn, s("user.test")) + self.assertEqual(cm.exception.errno, errno.ENODATA) + self.assertEqual(listxattr(fn), ["user.test2"]) + self.assertEqual(getxattr(fn, s("user.test2")), b"foo") + setxattr(fn, s("user.test"), b"a"*1024) + self.assertEqual(getxattr(fn, s("user.test")), b"a"*1024) + removexattr(fn, s("user.test")) + many = sorted("user.test{}".format(i) for i in range(100)) + for thing in many: + setxattr(fn, thing, b"x") + self.assertEqual(sorted(listxattr(fn)), many) + + def _check_xattrs(self, *args): + def make_bytes(s): + return bytes(s, "ascii") + self._check_xattrs_str(str, *args) + support.unlink(support.TESTFN) + self._check_xattrs_str(make_bytes, *args) + + def test_simple(self): + self._check_xattrs(os.getxattr, os.setxattr, os.removexattr, + os.listxattr) + + def test_lpath(self): + self._check_xattrs(os.lgetxattr, os.lsetxattr, os.lremovexattr, + os.llistxattr) + + def test_fds(self): + def getxattr(path, *args): + with open(path, "rb") as fp: + return os.fgetxattr(fp.fileno(), *args) + def setxattr(path, *args): + with open(path, "wb") as fp: + os.fsetxattr(fp.fileno(), *args) + def removexattr(path, *args): + with open(path, "wb") as fp: + os.fremovexattr(fp.fileno(), *args) + def listxattr(path, *args): + with open(path, "rb") as fp: + return os.flistxattr(fp.fileno(), *args) + self._check_xattrs(getxattr, setxattr, removexattr, listxattr) + + @support.reap_threads def test_main(): support.run_unittest( @@ -1529,6 +1622,7 @@ def test_main(): LinkTests, TestSendfile, ProgramPriorityTests, + ExtendedAttributeTests, ) if __name__ == "__main__": @@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1? Core and Builtins ----------------- +- Issue #12802: the Windows error ERROR_DIRECTORY (numbered 267) is now + mapped to POSIX errno ENOTDIR (previously EINVAL). + - Issue #9200: The str.is* methods now work with strings that contain non-BMP characters even in narrow Unicode builds. @@ -268,6 +271,14 @@ Core and Builtins Library ------- +- Issue #12636: IDLE reads the coding cookie when executing a Python script. + +- Issue #12494: On error, call(), check_call(), check_output() and + getstatusoutput() functions of the subprocess module now kill the process, + read its status (to avoid zombis) and close pipes. + +- Issue #12720: Expose low-level Linux extended file attribute functions in os. + - Issue #10946: The distutils commands bdist_dumb, bdist_wininst and bdist_msi now respect a --skip-build option given to bdist. The packaging commands were fixed too. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index d701690..1416a7c 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -107,6 +107,10 @@ corresponding Unix manual entries for more information on calls."); #include <sched.h> #endif +#ifdef HAVE_ATTR_XATTR_H +#include <attr/xattr.h> +#endif + #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> @@ -9950,6 +9954,384 @@ posix_mkfifoat(PyObject *self, PyObject *args) } #endif +#ifdef HAVE_ATTR_XATTR_H + +static int +try_getxattr(const char *path, const char *name, + ssize_t (*get)(const char *, const char *, void *, size_t), + Py_ssize_t buf_size, PyObject **res) +{ + PyObject *value; + Py_ssize_t len; + + assert(buf_size <= XATTR_SIZE_MAX); + value = PyBytes_FromStringAndSize(NULL, buf_size); + if (!value) + return 0; + Py_BEGIN_ALLOW_THREADS; + len = get(path, name, PyBytes_AS_STRING(value), buf_size); + Py_END_ALLOW_THREADS; + if (len < 0) { + Py_DECREF(value); + if (errno == ERANGE) { + value = NULL; + } + else { + posix_error(); + return 0; + } + } + else if (len != buf_size) { + /* Can only shrink. */ + _PyBytes_Resize(&value, len); + } + *res = value; + return 1; +} + +static PyObject * +getxattr_common(const char *path, PyObject *name_obj, + ssize_t (*get)(const char *, const char *, void *, size_t)) +{ + PyObject *value; + const char *name = PyBytes_AS_STRING(name_obj); + + /* Try a small value first. */ + if (!try_getxattr(path, name, get, 128, &value)) + return NULL; + if (value) + return value; + /* Now the maximum possible one. */ + if (!try_getxattr(path, name, get, XATTR_SIZE_MAX, &value)) + return NULL; + assert(value); + return value; +} + +PyDoc_STRVAR(posix_getxattr__doc__, +"getxattr(path, attr) -> value\n\n\ +Return the value of extended attribute *name* on *path*."); + +static PyObject * +posix_getxattr(PyObject *self, PyObject *args) +{ + PyObject *path, *res, *name; + + if (!PyArg_ParseTuple(args, "O&O&:getxattr", PyUnicode_FSConverter, &path, + PyUnicode_FSConverter, &name)) + return NULL; + res = getxattr_common(PyBytes_AS_STRING(path), name, getxattr); + Py_DECREF(path); + Py_DECREF(name); + return res; +} + +PyDoc_STRVAR(posix_lgetxattr__doc__, +"lgetxattr(path, attr) -> value\n\n\ +Like getxattr but don't follow symlinks."); + +static PyObject * +posix_lgetxattr(PyObject *self, PyObject *args) +{ + PyObject *path, *res, *name; + + if (!PyArg_ParseTuple(args, "O&O&:lgetxattr", PyUnicode_FSConverter, &path, + PyUnicode_FSConverter, &name)) + return NULL; + res = getxattr_common(PyBytes_AS_STRING(path), name, lgetxattr); + Py_DECREF(path); + Py_DECREF(name); + return res; +} + +static ssize_t +wrap_fgetxattr(const char *path, const char *name, void *value, size_t size) +{ + /* Hack to share code. */ + return fgetxattr((int)(Py_uintptr_t)path, name, value, size); +} + +PyDoc_STRVAR(posix_fgetxattr__doc__, +"fgetxattr(fd, attr) -> value\n\n\ +Like getxattr but operate on a fd instead of a path."); + +static PyObject * +posix_fgetxattr(PyObject *self, PyObject *args) +{ + PyObject *res, *name; + int fd; + + if (!PyArg_ParseTuple(args, "iO&:fgetxattr", &fd, PyUnicode_FSConverter, &name)) + return NULL; + res = getxattr_common((const char *)(Py_uintptr_t)fd, name, wrap_fgetxattr); + Py_DECREF(name); + return res; +} + +PyDoc_STRVAR(posix_setxattr__doc__, +"setxattr(path, attr, value, flags=0)\n\n\ +Set extended attribute *attr* on *path* to *value*."); + +static PyObject * +posix_setxattr(PyObject *self, PyObject *args) +{ + PyObject *path, *name; + Py_buffer data; + int flags = 0, err; + + if (!PyArg_ParseTuple(args, "O&O&y*|i:setxattr", PyUnicode_FSConverter, + &path, PyUnicode_FSConverter, &name, &data, &flags)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + err = setxattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name), + data.buf, data.len, flags); + Py_END_ALLOW_THREADS; + Py_DECREF(path); + Py_DECREF(name); + PyBuffer_Release(&data); + if (err) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_lsetxattr__doc__, +"lsetxattr(path, attr, value, flags=0)\n\n\ +Like setxattr but don't follow symlinks."); + +static PyObject * +posix_lsetxattr(PyObject *self, PyObject *args) +{ + PyObject *path, *name; + Py_buffer data; + int flags = 0, err; + + if (!PyArg_ParseTuple(args, "O&O&y*|i:lsetxattr", PyUnicode_FSConverter, + &path, PyUnicode_FSConverter, &name, &data, &flags)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + err = lsetxattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name), + data.buf, data.len, flags); + Py_END_ALLOW_THREADS; + Py_DECREF(path); + Py_DECREF(name); + PyBuffer_Release(&data); + if (err) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_fsetxattr__doc__, +"fsetxattr(fd, attr, value, flags=0)\n\n\ +Like setxattr but operates on *fd* instead of a path."); + +static PyObject * +posix_fsetxattr(PyObject *self, PyObject *args) +{ + Py_buffer data; + const char *name; + int fd, flags = 0, err; + + if (!PyArg_ParseTuple(args, "iO&y*|i:fsetxattr", &fd, PyUnicode_FSConverter, + &name, &data, &flags)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + err = fsetxattr(fd, PyBytes_AS_STRING(name), data.buf, data.len, flags); + Py_END_ALLOW_THREADS; + Py_DECREF(name); + PyBuffer_Release(&data); + if (err) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_removexattr__doc__, +"removexattr(path, attr)\n\n\ +Remove extended attribute *attr* on *path*."); + +static PyObject * +posix_removexattr(PyObject *self, PyObject *args) +{ + PyObject *path, *name; + int err; + + if (!PyArg_ParseTuple(args, "O&O&:removexattr", PyUnicode_FSConverter, &path, + PyUnicode_FSConverter, &name)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + err = removexattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name)); + Py_END_ALLOW_THREADS; + Py_DECREF(path); + Py_DECREF(name); + if (err) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_lremovexattr__doc__, +"lremovexattr(path, attr)\n\n\ +Like removexattr but don't follow symlinks."); + +static PyObject * +posix_lremovexattr(PyObject *self, PyObject *args) +{ + PyObject *path, *name; + int err; + + if (!PyArg_ParseTuple(args, "O&O&:lremovexattr", PyUnicode_FSConverter, &path, + PyUnicode_FSConverter, &name)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + err = lremovexattr(PyBytes_AS_STRING(path), PyBytes_AS_STRING(name)); + Py_END_ALLOW_THREADS; + Py_DECREF(path); + Py_DECREF(name); + if (err) + return posix_error(); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(posix_fremovexattr__doc__, +"fremovexattr(fd, attr)\n\n\ +Like removexattr but operates on a file descriptor."); + +static PyObject * +posix_fremovexattr(PyObject *self, PyObject *args) +{ + PyObject *name; + int fd, err; + + if (!PyArg_ParseTuple(args, "iO&:fremovexattr", &fd, + PyUnicode_FSConverter, &name)) + return NULL; + Py_BEGIN_ALLOW_THREADS; + err = fremovexattr(fd, PyBytes_AS_STRING(name)); + Py_END_ALLOW_THREADS; + Py_DECREF(name); + if (err) + return posix_error(); + Py_RETURN_NONE; +} + +static Py_ssize_t +try_listxattr(const char *path, ssize_t (*list)(const char *, char *, size_t), + Py_ssize_t buf_size, char **buf) +{ + Py_ssize_t len; + + *buf = PyMem_MALLOC(buf_size); + if (!*buf) { + PyErr_NoMemory(); + return -1; + } + Py_BEGIN_ALLOW_THREADS; + len = list(path, *buf, buf_size); + Py_END_ALLOW_THREADS; + if (len < 0) { + PyMem_FREE(*buf); + if (errno != ERANGE) + posix_error(); + return -1; + } + return len; +} + +static PyObject * +listxattr_common(const char *path, ssize_t (*list)(const char *, char *, size_t)) +{ + PyObject *res, *attr; + Py_ssize_t len, err, start, i; + char *buf; + + len = try_listxattr(path, list, 256, &buf); + if (len < 0) { + if (PyErr_Occurred()) + return NULL; + len = try_listxattr(path, list, XATTR_LIST_MAX, &buf); + if (len < 0) + return NULL; + } + res = PyList_New(0); + if (!res) { + PyMem_FREE(buf); + return NULL; + } + for (start = i = 0; i < len; i++) { + if (!buf[i]) { + attr = PyUnicode_DecodeFSDefaultAndSize(&buf[start], i - start); + if (!attr) { + Py_DECREF(res); + PyMem_FREE(buf); + return NULL; + } + err = PyList_Append(res, attr); + Py_DECREF(attr); + if (err) { + Py_DECREF(res); + PyMem_FREE(buf); + return NULL; + } + start = i + 1; + } + } + PyMem_FREE(buf); + return res; +} + +PyDoc_STRVAR(posix_listxattr__doc__, +"listxattr(path)\n\n\ +Return a list of extended attributes on *path*."); + +static PyObject * +posix_listxattr(PyObject *self, PyObject *args) +{ + PyObject *path, *res; + + if (!PyArg_ParseTuple(args, "O&:listxattr", PyUnicode_FSConverter, &path)) + return NULL; + res = listxattr_common(PyBytes_AS_STRING(path), listxattr); + Py_DECREF(path); + return res; +} + +PyDoc_STRVAR(posix_llistxattr__doc__, +"llistxattr(path)\n\n\ +Like listxattr but don't follow symlinks.."); + +static PyObject * +posix_llistxattr(PyObject *self, PyObject *args) +{ + PyObject *path, *res; + + if (!PyArg_ParseTuple(args, "O&:llistxattr", PyUnicode_FSConverter, &path)) + return NULL; + res = listxattr_common(PyBytes_AS_STRING(path), llistxattr); + Py_DECREF(path); + return res; +} + +static ssize_t +wrap_flistxattr(const char *path, char *buf, size_t len) +{ + /* Hack to share code. */ + return flistxattr((int)(Py_uintptr_t)path, buf, len); +} + +PyDoc_STRVAR(posix_flistxattr__doc__, +"flistxattr(path)\n\n\ +Like flistxattr but operates on a file descriptor."); + +static PyObject * +posix_flistxattr(PyObject *self, PyObject *args) +{ + long fd; + + if (!PyArg_ParseTuple(args, "i:flistxattr", &fd)) + return NULL; + return listxattr_common((const char *)(Py_uintptr_t)fd, wrap_flistxattr); +} + +#endif /* HAVE_ATTR_XATTR_H */ + static PyMethodDef posix_methods[] = { {"access", posix_access, METH_VARARGS, posix_access__doc__}, #ifdef HAVE_TTYNAME @@ -10399,6 +10781,20 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_MKFIFOAT {"mkfifoat", posix_mkfifoat, METH_VARARGS, posix_mkfifoat__doc__}, #endif +#ifdef HAVE_ATTR_XATTR_H + {"setxattr", posix_setxattr, METH_VARARGS, posix_setxattr__doc__}, + {"lsetxattr", posix_lsetxattr, METH_VARARGS, posix_lsetxattr__doc__}, + {"fsetxattr", posix_fsetxattr, METH_VARARGS, posix_fsetxattr__doc__}, + {"getxattr", posix_getxattr, METH_VARARGS, posix_getxattr__doc__}, + {"lgetxattr", posix_lgetxattr, METH_VARARGS, posix_lgetxattr__doc__}, + {"fgetxattr", posix_fgetxattr, METH_VARARGS, posix_fgetxattr__doc__}, + {"removexattr", posix_removexattr, METH_VARARGS, posix_removexattr__doc__}, + {"lremovexattr", posix_lremovexattr, METH_VARARGS, posix_lremovexattr__doc__}, + {"fremovexattr", posix_fremovexattr, METH_VARARGS, posix_fremovexattr__doc__}, + {"listxattr", posix_listxattr, METH_VARARGS, posix_listxattr__doc__}, + {"llistxattr", posix_llistxattr, METH_VARARGS, posix_llistxattr__doc__}, + {"flistxattr", posix_flistxattr, METH_VARARGS, posix_flistxattr__doc__}, +#endif {NULL, NULL} /* Sentinel */ }; @@ -10848,6 +11244,12 @@ all_ins(PyObject *d) #endif #endif +#ifdef HAVE_ATTR_XATTR_H + if (ins(d, "XATTR_CREATE", (long)XATTR_CREATE)) return -1; + if (ins(d, "XATTR_REPLACE", (long)XATTR_REPLACE)) return -1; + if (ins(d, "XATTR_SIZE_MAX", (long)XATTR_SIZE_MAX)) return -1; +#endif + #if defined(PYOS_OS2) if (insertvalues(d)) return -1; #endif diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 93daefd..a786bae 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -846,16 +846,13 @@ PyDictProxy_New(PyObject *dict) /* This has no reason to be in this file except that adding new files is a bit of a pain */ -/* forward */ -static PyTypeObject wrappertype; - typedef struct { PyObject_HEAD PyWrapperDescrObject *descr; PyObject *self; } wrapperobject; -#define Wrapper_Check(v) (Py_TYPE(v) == &wrappertype) +#define Wrapper_Check(v) (Py_TYPE(v) == &_PyMethodWrapper_Type) static void wrapper_dealloc(wrapperobject *wp) @@ -1021,7 +1018,7 @@ wrapper_traverse(PyObject *self, visitproc visit, void *arg) return 0; } -static PyTypeObject wrappertype = { +PyTypeObject _PyMethodWrapper_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "method-wrapper", /* tp_name */ sizeof(wrapperobject), /* tp_basicsize */ @@ -1070,7 +1067,7 @@ PyWrapper_New(PyObject *d, PyObject *self) assert(_PyObject_RealIsSubclass((PyObject *)Py_TYPE(self), (PyObject *)PyDescr_TYPE(descr))); - wp = PyObject_GC_New(wrapperobject, &wrappertype); + wp = PyObject_GC_New(wrapperobject, &_PyMethodWrapper_Type); if (wp != NULL) { Py_INCREF(descr); wp->descr = descr; diff --git a/Objects/object.c b/Objects/object.c index cf49e1b..52acf12 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1558,6 +1558,9 @@ _Py_ReadyTypes(void) if (PyType_Ready(&PyWrapperDescr_Type) < 0) Py_FatalError("Can't initialize wrapper type"); + if (PyType_Ready(&_PyMethodWrapper_Type) < 0) + Py_FatalError("Can't initialize method wrapper type"); + if (PyType_Ready(&PyEllipsis_Type) < 0) Py_FatalError("Can't initialize ellipsis type"); diff --git a/PC/errmap.h b/PC/errmap.h index d225aa4..8dde31c 100644 --- a/PC/errmap.h +++ b/PC/errmap.h @@ -72,6 +72,7 @@ int winerror_to_errno(int winerror) case 202: return 8; case 206: return 2; case 215: return 11; + case 267: return 20; case 1816: return 12; default: return EINVAL; } diff --git a/PC/generrmap.c b/PC/generrmap.c index bf1081b..0323cd4 100644 --- a/PC/generrmap.c +++ b/PC/generrmap.c @@ -1,3 +1,6 @@ +#include <windows.h> +#include <fcntl.h> +#include <io.h> #include <stdio.h> #include <errno.h> @@ -6,15 +9,21 @@ int main() { int i; + _setmode(fileno(stdout), O_BINARY); printf("/* Generated file. Do not edit. */\n"); printf("int winerror_to_errno(int winerror)\n"); - printf("{\n\tswitch(winerror) {\n"); + printf("{\n switch(winerror) {\n"); for(i=1; i < 65000; i++) { _dosmaperr(i); - if (errno == EINVAL) - continue; - printf("\t\tcase %d: return %d;\n", i, errno); + if (errno == EINVAL) { + /* Issue #12802 */ + if (i == ERROR_DIRECTORY) + errno = ENOTDIR; + else + continue; + } + printf(" case %d: return %d;\n", i, errno); } - printf("\t\tdefault: return EINVAL;\n"); - printf("\t}\n}\n"); + printf(" default: return EINVAL;\n"); + printf(" }\n}\n"); } diff --git a/Python/import.c b/Python/import.c index 19e975a..1411248 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2653,7 +2653,9 @@ PyObject * PyImport_ImportModuleNoBlock(const char *name) { PyObject *nameobj, *modules, *result; +#ifdef WITH_THREAD long me; +#endif /* Try to get the module from sys.modules[name] */ modules = PyImport_GetModuleDict(); @@ -6090,7 +6090,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi -for ac_header in asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ +for ac_header in asm/types.h attr/xattr.h conio.h curses.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ sched.h shadow.h signal.h stdint.h stropts.h termios.h \ diff --git a/configure.in b/configure.in index c10f67a..43745fb 100644 --- a/configure.in +++ b/configure.in @@ -1299,7 +1299,7 @@ dnl AC_MSG_RESULT($cpp_type) # checks for header files AC_HEADER_STDC -AC_CHECK_HEADERS(asm/types.h conio.h curses.h direct.h dlfcn.h errno.h \ +AC_CHECK_HEADERS(asm/types.h attr/xattr.h conio.h curses.h direct.h dlfcn.h errno.h \ fcntl.h grp.h \ ieeefp.h io.h langinfo.h libintl.h ncurses.h poll.h process.h pthread.h \ sched.h shadow.h signal.h stdint.h stropts.h termios.h \ diff --git a/pyconfig.h.in b/pyconfig.h.in index eed1ff7..bf9e7fe 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -64,6 +64,9 @@ /* Define if GCC supports __attribute__((format(PyArg_ParseTuple, 2, 3))) */ #undef HAVE_ATTRIBUTE_FORMAT_PARSETUPLE +/* Define to 1 if you have the <attr/xattr.h> header file. */ +#undef HAVE_ATTR_XATTR_H + /* Define to 1 if you have the `bind_textdomain_codeset' function. */ #undef HAVE_BIND_TEXTDOMAIN_CODESET |