mirror of https://github.com/python/cpython
Merge
This commit is contained in:
commit
8e0d25504c
|
@ -247,6 +247,30 @@ Directory and files operations
|
|||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. function:: which(cmd, mode=os.F_OK | os.X_OK, path=None)
|
||||
|
||||
Return the path to an executable which would be run if the given *cmd*
|
||||
was called. If no *cmd* would be called, return ``None``.
|
||||
|
||||
*mode* is a permission mask passed a to :func:`os.access`, by default
|
||||
determining if the file exists and executable.
|
||||
|
||||
When no *path* is specified, the results of :func:`os.environ` are
|
||||
used, returning either the "PATH" value or a fallback of :attr:`os.defpath`.
|
||||
|
||||
On Windows, the current directory is always prepended to the *path*
|
||||
whether or not you use the default or provide your own, which
|
||||
is the behavior the command shell uses when finding executables.
|
||||
Additionaly, when finding the *cmd* in the *path*, the
|
||||
``PATHEXT`` environment variable is checked. For example, if you
|
||||
call ``shutil.which("python")``, :func:`which` will search
|
||||
``PATHEXT`` to know that it should look for ``python.exe`` within
|
||||
the *path* directories.
|
||||
|
||||
>>> print(shutil.which("python"))
|
||||
'c:\\python33\\python.exe'
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: Error
|
||||
|
||||
|
|
|
@ -59,6 +59,22 @@ Functions, Constants, and Exceptions
|
|||
.. versionchanged:: 3.3
|
||||
:exc:`SSLError` used to be a subtype of :exc:`socket.error`.
|
||||
|
||||
.. attribute:: library
|
||||
|
||||
A string mnemonic designating the OpenSSL submodule in which the error
|
||||
occurred, such as ``SSL``, ``PEM`` or ``X509``. The range of possible
|
||||
values depends on the OpenSSL version.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. attribute:: reason
|
||||
|
||||
A string mnemonic designating the reason this error occurred, for
|
||||
example ``CERTIFICATE_VERIFY_FAILED``. The range of possible
|
||||
values depends on the OpenSSL version.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. exception:: SSLZeroReturnError
|
||||
|
||||
A subclass of :exc:`SSLError` raised when trying to read or write and
|
||||
|
|
|
@ -106,6 +106,22 @@ always available.
|
|||
This function should be used for internal and specialized purposes only.
|
||||
|
||||
|
||||
.. function:: _debugmallocstats()
|
||||
|
||||
Print low-level information to stderr about the state of CPython's memory
|
||||
allocator.
|
||||
|
||||
If Python is configured --with-pydebug, it also performs some expensive
|
||||
internal consistency checks.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. impl-detail::
|
||||
|
||||
This function is specific to CPython. The exact output format is not
|
||||
defined here, and may change.
|
||||
|
||||
|
||||
.. data:: dllhandle
|
||||
|
||||
Integer specifying the handle of the Python DLL. Availability: Windows.
|
||||
|
|
|
@ -545,7 +545,7 @@ The module defines the following functions and data items:
|
|||
+-------+-------------------+---------------------------------+
|
||||
| N/A | :attr:`tm_zone` | abbreviation of timezone name |
|
||||
+-------+-------------------+---------------------------------+
|
||||
| N/A | :attr:`tm_gmtoff` | offset from UTC in seconds |
|
||||
| N/A | :attr:`tm_gmtoff` | offset east of UTC in seconds |
|
||||
+-------+-------------------+---------------------------------+
|
||||
|
||||
Note that unlike the C structure, the month value is a range of [1, 12], not
|
||||
|
|
|
@ -111,6 +111,7 @@ PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key);
|
|||
#ifndef Py_LIMITED_API
|
||||
int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value);
|
||||
PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
|
||||
PyAPI_FUNC(void) _PyDict_DebugMallocStats(FILE *out);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -110,6 +110,8 @@ PyAPI_FUNC(double) _PyFloat_Unpack8(const unsigned char *p, int le);
|
|||
/* free list api */
|
||||
PyAPI_FUNC(int) PyFloat_ClearFreeList(void);
|
||||
|
||||
PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out);
|
||||
|
||||
/* Format the object based on the format_spec, as defined in PEP 3101
|
||||
(Advanced String Formatting). */
|
||||
PyAPI_FUNC(int) _PyFloat_FormatAdvancedWriter(
|
||||
|
|
|
@ -79,6 +79,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
|
|||
|
||||
PyAPI_FUNC(int) PyFrame_ClearFreeList(void);
|
||||
|
||||
PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out);
|
||||
|
||||
/* Return the line of code the frame is currently executing. */
|
||||
PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *);
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ PyAPI_FUNC(PyObject *) PyList_AsTuple(PyObject *);
|
|||
PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *);
|
||||
|
||||
PyAPI_FUNC(int) PyList_ClearFreeList(void);
|
||||
PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);
|
||||
#endif
|
||||
|
||||
/* Macro, trading safety for speed */
|
||||
|
|
|
@ -82,6 +82,11 @@ typedef struct {
|
|||
|
||||
PyAPI_FUNC(int) PyCFunction_ClearFreeList(void);
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(void) _PyCFunction_DebugMallocStats(FILE *out);
|
||||
PyAPI_FUNC(void) _PyMethod_DebugMallocStats(FILE *out);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -977,6 +977,14 @@ PyAPI_DATA(PyObject *) _PyTrash_delete_later;
|
|||
else \
|
||||
_PyTrash_deposit_object((PyObject*)op);
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(void)
|
||||
_PyDebugAllocatorStats(FILE *out, const char *block_name, int num_blocks,
|
||||
size_t sizeof_block);
|
||||
PyAPI_FUNC(void)
|
||||
_PyObject_DebugTypeStats(FILE *out);
|
||||
#endif /* ifndef Py_LIMITED_API */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -101,13 +101,15 @@ PyAPI_FUNC(void) PyObject_Free(void *);
|
|||
|
||||
/* Macros */
|
||||
#ifdef WITH_PYMALLOC
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(void) _PyObject_DebugMallocStats(FILE *out);
|
||||
#endif /* #ifndef Py_LIMITED_API */
|
||||
#ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */
|
||||
PyAPI_FUNC(void *) _PyObject_DebugMalloc(size_t nbytes);
|
||||
PyAPI_FUNC(void *) _PyObject_DebugRealloc(void *p, size_t nbytes);
|
||||
PyAPI_FUNC(void) _PyObject_DebugFree(void *p);
|
||||
PyAPI_FUNC(void) _PyObject_DebugDumpAddress(const void *p);
|
||||
PyAPI_FUNC(void) _PyObject_DebugCheckAddress(const void *p);
|
||||
PyAPI_FUNC(void) _PyObject_DebugMallocStats(void);
|
||||
PyAPI_FUNC(void *) _PyObject_DebugMallocApi(char api, size_t nbytes);
|
||||
PyAPI_FUNC(void *) _PyObject_DebugReallocApi(char api, void *p, size_t nbytes);
|
||||
PyAPI_FUNC(void) _PyObject_DebugFreeApi(char api, void *p);
|
||||
|
|
|
@ -101,6 +101,7 @@ PyAPI_FUNC(PyObject *) PySet_Pop(PyObject *set);
|
|||
PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable);
|
||||
|
||||
PyAPI_FUNC(int) PySet_ClearFreeList(void);
|
||||
PyAPI_FUNC(void) _PySet_DebugMallocStats(FILE *out);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -63,6 +63,9 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *);
|
|||
#endif
|
||||
|
||||
PyAPI_FUNC(int) PyTuple_ClearFreeList(void);
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(void) _PyTuple_DebugMallocStats(FILE *out);
|
||||
#endif /* Py_LIMITED_API */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1510,13 +1510,13 @@ class datetime(date):
|
|||
# implied by tm_isdst.
|
||||
delta = local - datetime(*_time.gmtime(ts)[:6])
|
||||
dst = _time.daylight and localtm.tm_isdst > 0
|
||||
gmtoff = _time.altzone if dst else _time.timezone
|
||||
if delta == timedelta(seconds=-gmtoff):
|
||||
gmtoff = -(_time.altzone if dst else _time.timezone)
|
||||
if delta == timedelta(seconds=gmtoff):
|
||||
tz = timezone(delta, _time.tzname[dst])
|
||||
else:
|
||||
tz = timezone(delta)
|
||||
else:
|
||||
tz = timezone(timedelta(seconds=-gmtoff), zone)
|
||||
tz = timezone(timedelta(seconds=gmtoff), zone)
|
||||
|
||||
elif not isinstance(tz, tzinfo):
|
||||
raise TypeError("tz argument must be an instance of tzinfo")
|
||||
|
|
|
@ -36,7 +36,7 @@ __all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",
|
|||
"register_archive_format", "unregister_archive_format",
|
||||
"get_unpack_formats", "register_unpack_format",
|
||||
"unregister_unpack_format", "unpack_archive",
|
||||
"ignore_patterns", "chown"]
|
||||
"ignore_patterns", "chown", "which"]
|
||||
# disk_usage is added later, if available on the platform
|
||||
|
||||
class Error(EnvironmentError):
|
||||
|
@ -961,3 +961,50 @@ def get_terminal_size(fallback=(80, 24)):
|
|||
lines = size.lines
|
||||
|
||||
return os.terminal_size((columns, lines))
|
||||
|
||||
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
||||
"""Given a file, mode, and a path string, return the path whichs conform
|
||||
to the given mode on the path."""
|
||||
# Check that a given file can be accessed with the correct mode.
|
||||
# Additionally check that `file` is not a directory, as on Windows
|
||||
# directories pass the os.access check.
|
||||
def _access_check(fn, mode):
|
||||
if (os.path.exists(fn) and os.access(fn, mode)
|
||||
and not os.path.isdir(fn)):
|
||||
return True
|
||||
return False
|
||||
|
||||
# Short circuit. If we're given a full path which matches the mode
|
||||
# and it exists, we're done here.
|
||||
if _access_check(cmd, mode):
|
||||
return cmd
|
||||
|
||||
path = (path or os.environ.get("PATH", os.defpath)).split(os.pathsep)
|
||||
|
||||
if sys.platform == "win32":
|
||||
# The current directory takes precedence on Windows.
|
||||
if not os.curdir in path:
|
||||
path.insert(0, os.curdir)
|
||||
|
||||
# PATHEXT is necessary to check on Windows.
|
||||
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
|
||||
# See if the given file matches any of the expected path extensions.
|
||||
# This will allow us to short circuit when given "python.exe".
|
||||
matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())]
|
||||
# If it does match, only test that one, otherwise we have to try others.
|
||||
files = [cmd + ext.lower() for ext in pathext] if not matches else [cmd]
|
||||
else:
|
||||
# On other platforms you don't have things like PATHEXT to tell you
|
||||
# what file suffixes are executable, so just pass on cmd as-is.
|
||||
files = [cmd]
|
||||
|
||||
seen = set()
|
||||
for dir in path:
|
||||
dir = os.path.normcase(dir)
|
||||
if not dir in seen:
|
||||
seen.add(dir)
|
||||
for thefile in files:
|
||||
name = os.path.join(dir, thefile)
|
||||
if _access_check(name, mode):
|
||||
return name
|
||||
return None
|
||||
|
|
|
@ -3278,16 +3278,18 @@ class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
|
|||
self.assertEqual(dt.astimezone(None), dt)
|
||||
self.assertEqual(dt.astimezone(), dt)
|
||||
|
||||
# Note that offset in TZ variable has the opposite sign to that
|
||||
# produced by %z directive.
|
||||
@support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
|
||||
def test_astimezone_default_eastern(self):
|
||||
dt = self.theclass(2012, 11, 4, 6, 30, tzinfo=timezone.utc)
|
||||
local = dt.astimezone()
|
||||
self.assertEqual(dt, local)
|
||||
self.assertEqual(local.strftime("%z %Z"), "+0500 EST")
|
||||
self.assertEqual(local.strftime("%z %Z"), "-0500 EST")
|
||||
dt = self.theclass(2012, 11, 4, 5, 30, tzinfo=timezone.utc)
|
||||
local = dt.astimezone()
|
||||
self.assertEqual(dt, local)
|
||||
self.assertEqual(local.strftime("%z %Z"), "+0400 EDT")
|
||||
self.assertEqual(local.strftime("%z %Z"), "-0400 EDT")
|
||||
|
||||
def test_aware_subtract(self):
|
||||
cls = self.theclass
|
||||
|
|
|
@ -222,36 +222,38 @@ class SkipitemTest(unittest.TestCase):
|
|||
in Python/getargs.c, but neglected to update our poor friend
|
||||
skipitem() in the same file. (If so, shame on you!)
|
||||
|
||||
This function brute-force tests all** ASCII characters (1 to 127
|
||||
inclusive) as format units, checking to see that
|
||||
PyArg_ParseTupleAndKeywords() return consistent errors both when
|
||||
the unit is attempted to be used and when it is skipped. If the
|
||||
format unit doesn't exist, we'll get one of two specific error
|
||||
messages (one for used, one for skipped); if it does exist we
|
||||
*won't* get that error--we'll get either no error or some other
|
||||
error. If we get the "does not exist" error for one test and
|
||||
not for the other, there's a mismatch, and the test fails.
|
||||
With a few exceptions**, this function brute-force tests all
|
||||
printable ASCII*** characters (32 to 126 inclusive) as format units,
|
||||
checking to see that PyArg_ParseTupleAndKeywords() return consistent
|
||||
errors both when the unit is attempted to be used and when it is
|
||||
skipped. If the format unit doesn't exist, we'll get one of two
|
||||
specific error messages (one for used, one for skipped); if it does
|
||||
exist we *won't* get that error--we'll get either no error or some
|
||||
other error. If we get the specific "does not exist" error for one
|
||||
test and not for the other, there's a mismatch, and the test fails.
|
||||
|
||||
** Some format units have special funny semantics and it would
|
||||
be difficult to accomodate them here. Since these are all
|
||||
well-established and properly skipped in skipitem() we can
|
||||
get away with not testing them--this test is really intended
|
||||
to catch *new* format units.
|
||||
|
||||
*** Python C source files must be ASCII. Therefore it's impossible
|
||||
to have non-ASCII format units.
|
||||
|
||||
** Okay, it actually skips some ASCII characters. Some characters
|
||||
have special funny semantics, and it would be difficult to
|
||||
accomodate them here.
|
||||
"""
|
||||
empty_tuple = ()
|
||||
tuple_1 = (0,)
|
||||
dict_b = {'b':1}
|
||||
keywords = ["a", "b"]
|
||||
|
||||
# Python C source files must be ASCII,
|
||||
# therefore we'll never have a format unit > 127
|
||||
for i in range(1, 128):
|
||||
for i in range(32, 127):
|
||||
c = chr(i)
|
||||
|
||||
# skip non-printable characters, no one is insane enough to define
|
||||
# one as a format unit
|
||||
# skip parentheses, the error reporting is inconsistent about them
|
||||
# skip 'e', it's always a two-character code
|
||||
# skip '|' and '$', they don't represent arguments anyway
|
||||
if (not c.isprintable()) or (c in '()e|$'):
|
||||
if c in '()e|$':
|
||||
continue
|
||||
|
||||
# test the format unit when not skipped
|
||||
|
|
|
@ -10,7 +10,7 @@ import shutil
|
|||
import importlib
|
||||
import unittest
|
||||
|
||||
from test.support import run_unittest, create_empty_file
|
||||
from test.support import run_unittest, create_empty_file, verbose
|
||||
from reprlib import repr as r # Don't shadow builtin repr
|
||||
from reprlib import Repr
|
||||
from reprlib import recursive_repr
|
||||
|
@ -248,6 +248,8 @@ class LongReprTest(unittest.TestCase):
|
|||
# (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx#maxpath)
|
||||
self.skipTest("test paths too long (%d characters) for Windows' 260 character limit"
|
||||
% cached_path_len)
|
||||
elif os.name == 'nt' and verbose:
|
||||
print("len(cached_path_len) =", len(cached_path_len))
|
||||
|
||||
def test_module(self):
|
||||
self._check_path_limitations(self.pkgname)
|
||||
|
|
|
@ -1128,6 +1128,59 @@ class TestShutil(unittest.TestCase):
|
|||
self.assertEqual(['foo'], os.listdir(rv))
|
||||
|
||||
|
||||
class TestWhich(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
# Give the temp_file an ".exe" suffix for all.
|
||||
# It's needed on Windows and not harmful on other platforms.
|
||||
self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
|
||||
suffix=".exe")
|
||||
os.chmod(self.temp_file.name, stat.S_IXUSR)
|
||||
self.addCleanup(self.temp_file.close)
|
||||
self.dir, self.file = os.path.split(self.temp_file.name)
|
||||
|
||||
def test_basic(self):
|
||||
# Given an EXE in a directory, it should be returned.
|
||||
rv = shutil.which(self.file, path=self.dir)
|
||||
self.assertEqual(rv, self.temp_file.name)
|
||||
|
||||
def test_full_path_short_circuit(self):
|
||||
# When given the fully qualified path to an executable that exists,
|
||||
# it should be returned.
|
||||
rv = shutil.which(self.temp_file.name, path=self.temp_dir)
|
||||
self.assertEqual(self.temp_file.name, rv)
|
||||
|
||||
def test_non_matching_mode(self):
|
||||
# Set the file read-only and ask for writeable files.
|
||||
os.chmod(self.temp_file.name, stat.S_IREAD)
|
||||
rv = shutil.which(self.file, path=self.dir, mode=os.W_OK)
|
||||
self.assertIsNone(rv)
|
||||
|
||||
def test_relative(self):
|
||||
old_cwd = os.getcwd()
|
||||
base_dir, tail_dir = os.path.split(self.dir)
|
||||
os.chdir(base_dir)
|
||||
try:
|
||||
rv = shutil.which(self.file, path=tail_dir)
|
||||
self.assertEqual(rv, os.path.join(tail_dir, self.file))
|
||||
finally:
|
||||
os.chdir(old_cwd)
|
||||
|
||||
def test_nonexistent_file(self):
|
||||
# Return None when no matching executable file is found on the path.
|
||||
rv = shutil.which("foo.exe", path=self.dir)
|
||||
self.assertIsNone(rv)
|
||||
|
||||
@unittest.skipUnless(sys.platform == "win32",
|
||||
"pathext check is Windows-only")
|
||||
def test_pathext_checking(self):
|
||||
# Ask for the file without the ".exe" extension, then ensure that
|
||||
# it gets found properly with the extension.
|
||||
rv = shutil.which(self.temp_file.name[:-4], path=self.dir)
|
||||
self.assertEqual(self.temp_file.name, rv)
|
||||
|
||||
|
||||
class TestMove(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -1460,7 +1513,7 @@ class TermsizeTests(unittest.TestCase):
|
|||
|
||||
def test_main():
|
||||
support.run_unittest(TestShutil, TestMove, TestCopyFile,
|
||||
TermsizeTests)
|
||||
TermsizeTests, TestWhich)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_main()
|
||||
|
|
|
@ -552,7 +552,7 @@ class ContextTests(unittest.TestCase):
|
|||
with self.assertRaises(FileNotFoundError) as cm:
|
||||
ctx.load_dh_params(WRONGCERT)
|
||||
self.assertEqual(cm.exception.errno, errno.ENOENT)
|
||||
with self.assertRaisesRegex(ssl.SSLError, "PEM routines"):
|
||||
with self.assertRaises(ssl.SSLError) as cm:
|
||||
ctx.load_dh_params(CERTFILE)
|
||||
|
||||
@skip_if_broken_ubuntu_ssl
|
||||
|
@ -590,6 +590,47 @@ class ContextTests(unittest.TestCase):
|
|||
self.assertRaises(ValueError, ctx.set_ecdh_curve, b"foo")
|
||||
|
||||
|
||||
class SSLErrorTests(unittest.TestCase):
|
||||
|
||||
def test_str(self):
|
||||
# The str() of a SSLError doesn't include the errno
|
||||
e = ssl.SSLError(1, "foo")
|
||||
self.assertEqual(str(e), "foo")
|
||||
self.assertEqual(e.errno, 1)
|
||||
# Same for a subclass
|
||||
e = ssl.SSLZeroReturnError(1, "foo")
|
||||
self.assertEqual(str(e), "foo")
|
||||
self.assertEqual(e.errno, 1)
|
||||
|
||||
def test_lib_reason(self):
|
||||
# Test the library and reason attributes
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
with self.assertRaises(ssl.SSLError) as cm:
|
||||
ctx.load_dh_params(CERTFILE)
|
||||
self.assertEqual(cm.exception.library, 'PEM')
|
||||
self.assertEqual(cm.exception.reason, 'NO_START_LINE')
|
||||
s = str(cm.exception)
|
||||
self.assertTrue(s.startswith("[PEM: NO_START_LINE] no start line"), s)
|
||||
|
||||
def test_subclass(self):
|
||||
# Check that the appropriate SSLError subclass is raised
|
||||
# (this only tests one of them)
|
||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
with socket.socket() as s:
|
||||
s.bind(("127.0.0.1", 0))
|
||||
s.listen(5)
|
||||
with socket.socket() as c:
|
||||
c.connect(s.getsockname())
|
||||
c.setblocking(False)
|
||||
c = ctx.wrap_socket(c, False, do_handshake_on_connect=False)
|
||||
with self.assertRaises(ssl.SSLWantReadError) as cm:
|
||||
c.do_handshake()
|
||||
s = str(cm.exception)
|
||||
self.assertTrue(s.startswith("The operation did not complete (read)"), s)
|
||||
# For compatibility
|
||||
self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ)
|
||||
|
||||
|
||||
class NetworkedTests(unittest.TestCase):
|
||||
|
||||
def test_connect(self):
|
||||
|
@ -1931,7 +1972,7 @@ def test_main(verbose=False):
|
|||
if not os.path.exists(filename):
|
||||
raise support.TestFailed("Can't read certificate file %r" % filename)
|
||||
|
||||
tests = [ContextTests, BasicSocketTests]
|
||||
tests = [ContextTests, BasicSocketTests, SSLErrorTests]
|
||||
|
||||
if support.is_resource_enabled('network'):
|
||||
tests.append(NetworkedTests)
|
||||
|
|
|
@ -603,6 +603,12 @@ class SysModuleTest(unittest.TestCase):
|
|||
self.assertEqual(sys.implementation.name,
|
||||
sys.implementation.name.lower())
|
||||
|
||||
def test_debugmallocstats(self):
|
||||
# Test sys._debugmallocstats()
|
||||
from test.script_helper import assert_python_ok
|
||||
args = ['-c', 'import sys; sys._debugmallocstats()']
|
||||
ret, out, err = assert_python_ok(*args)
|
||||
self.assertIn(b"free PyDictObjects", err)
|
||||
|
||||
class SizeofTest(unittest.TestCase):
|
||||
|
||||
|
|
11
Misc/NEWS
11
Misc/NEWS
|
@ -42,6 +42,14 @@ Library
|
|||
|
||||
- Issue: #15138: base64.urlsafe_{en,de}code() are now 3-4x faster.
|
||||
|
||||
- Issue #444582: Add shutil.which, for finding programs on the system path.
|
||||
Original patch by Erik Demaine, with later iterations by Jan Killian
|
||||
and Brian Curtin.
|
||||
|
||||
- Issue #14837: SSL errors now have ``library`` and ``reason`` attributes
|
||||
describing precisely what happened and in which OpenSSL submodule. The
|
||||
str() of a SSLError is also enhanced accordingly.
|
||||
|
||||
- Issue #9527: datetime.astimezone() method will now supply a class
|
||||
timezone instance corresponding to the system local timezone when
|
||||
called with no arguments.
|
||||
|
@ -149,6 +157,9 @@ Library
|
|||
- Issue #14963: Convert contextlib.ExitStack.__exit__ to use an iterative
|
||||
algorithm (Patch by Alon Horev)
|
||||
|
||||
- Issue #14785: Add sys._debugmallocstats() to help debug low-level memory
|
||||
allocation issues
|
||||
|
||||
C-API
|
||||
-----
|
||||
|
||||
|
|
|
@ -809,14 +809,16 @@ new_timezone(PyObject *offset, PyObject *name)
|
|||
}
|
||||
if (GET_TD_MICROSECONDS(offset) != 0 || GET_TD_SECONDS(offset) % 60 != 0) {
|
||||
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
|
||||
" representing a whole number of minutes");
|
||||
" representing a whole number of minutes,"
|
||||
" not %R.", offset);
|
||||
return NULL;
|
||||
}
|
||||
if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0) ||
|
||||
GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) {
|
||||
PyErr_Format(PyExc_ValueError, "offset must be a timedelta"
|
||||
" strictly between -timedelta(hours=24) and"
|
||||
" timedelta(hours=24).");
|
||||
" timedelta(hours=24),"
|
||||
" not %R.", offset);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -4686,12 +4688,11 @@ datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
|||
}
|
||||
|
||||
static PyObject *
|
||||
local_timezone(PyObject *utc_time)
|
||||
local_timezone(PyDateTime_DateTime *utc_time)
|
||||
{
|
||||
PyObject *result = NULL;
|
||||
struct tm *timep;
|
||||
time_t timestamp;
|
||||
long offset;
|
||||
PyObject *delta;
|
||||
PyObject *one_second;
|
||||
PyObject *seconds;
|
||||
|
@ -4716,21 +4717,18 @@ local_timezone(PyObject *utc_time)
|
|||
return NULL;
|
||||
timep = localtime(×tamp);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
offset = timep->tm_gmtoff;
|
||||
zone = timep->tm_zone;
|
||||
delta = new_delta(0, -offset, 0, 0);
|
||||
delta = new_delta(0, timep->tm_gmtoff, 0, 1);
|
||||
#else /* HAVE_STRUCT_TM_TM_ZONE */
|
||||
{
|
||||
PyObject *local_time;
|
||||
Py_INCREF(utc_time->tzinfo);
|
||||
local_time = new_datetime(timep->tm_year + 1900, timep->tm_mon + 1,
|
||||
timep->tm_mday, timep->tm_hour, timep->tm_min,
|
||||
timep->tm_sec, utc_time->tzinfo);
|
||||
if (local_time == NULL) {
|
||||
Py_DECREF(utc_time->tzinfo);
|
||||
timep->tm_sec, DATE_GET_MICROSECOND(utc_time),
|
||||
utc_time->tzinfo);
|
||||
if (local_time == NULL)
|
||||
goto error;
|
||||
}
|
||||
delta = datetime_subtract(local_time, utc_time);
|
||||
delta = datetime_subtract(local_time, (PyObject*)utc_time);
|
||||
/* XXX: before relying on tzname, we should compare delta
|
||||
to the offset implied by timezone/altzone */
|
||||
if (daylight && timep->tm_isdst >= 0)
|
||||
|
@ -4752,10 +4750,10 @@ local_timezone(PyObject *utc_time)
|
|||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
static PyDateTime_DateTime *
|
||||
datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *result;
|
||||
PyDateTime_DateTime *result;
|
||||
PyObject *offset;
|
||||
PyObject *temp;
|
||||
PyObject *tzinfo = Py_None;
|
||||
|
@ -4775,7 +4773,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
|||
/* Conversion to self's own time zone is a NOP. */
|
||||
if (self->tzinfo == tzinfo) {
|
||||
Py_INCREF(self);
|
||||
return (PyObject *)self;
|
||||
return self;
|
||||
}
|
||||
|
||||
/* Convert self to UTC. */
|
||||
|
@ -4791,14 +4789,14 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
|||
}
|
||||
|
||||
/* result = self - offset */
|
||||
result = add_datetime_timedelta(self,
|
||||
(PyDateTime_Delta *)offset, -1);
|
||||
result = (PyDateTime_DateTime *)add_datetime_timedelta(self,
|
||||
(PyDateTime_Delta *)offset, -1);
|
||||
Py_DECREF(offset);
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Attach new tzinfo and let fromutc() do the rest. */
|
||||
temp = ((PyDateTime_DateTime *)result)->tzinfo;
|
||||
temp = result->tzinfo;
|
||||
if (tzinfo == Py_None) {
|
||||
tzinfo = local_timezone(result);
|
||||
if (tzinfo == NULL) {
|
||||
|
@ -4808,11 +4806,12 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
|||
}
|
||||
else
|
||||
Py_INCREF(tzinfo);
|
||||
((PyDateTime_DateTime *)result)->tzinfo = tzinfo;
|
||||
result->tzinfo = tzinfo;
|
||||
Py_DECREF(temp);
|
||||
|
||||
temp = result;
|
||||
result = _PyObject_CallMethodId(tzinfo, &PyId_fromutc, "O", temp);
|
||||
temp = (PyObject *)result;
|
||||
result = (PyDateTime_DateTime *)
|
||||
_PyObject_CallMethodId(tzinfo, &PyId_fromutc, "O", temp);
|
||||
Py_DECREF(temp);
|
||||
|
||||
return result;
|
||||
|
|
262
Modules/_ssl.c
262
Modules/_ssl.c
|
@ -76,6 +76,16 @@ enum py_ssl_version {
|
|||
PY_SSL_VERSION_TLS1
|
||||
};
|
||||
|
||||
struct py_ssl_error_code {
|
||||
const char *mnemonic;
|
||||
int library, reason;
|
||||
};
|
||||
|
||||
struct py_ssl_library_code {
|
||||
const char *library;
|
||||
int code;
|
||||
};
|
||||
|
||||
/* Include symbols from _socket module */
|
||||
#include "socketmodule.h"
|
||||
|
||||
|
@ -97,6 +107,9 @@ static PySocketModule_APIObject PySocketModule;
|
|||
#include "openssl/err.h"
|
||||
#include "openssl/rand.h"
|
||||
|
||||
/* Include generated data (error codes) */
|
||||
#include "_ssl_data.h"
|
||||
|
||||
/* SSL error object */
|
||||
static PyObject *PySSLErrorObject;
|
||||
static PyObject *PySSLZeroReturnErrorObject;
|
||||
|
@ -105,6 +118,11 @@ static PyObject *PySSLWantWriteErrorObject;
|
|||
static PyObject *PySSLSyscallErrorObject;
|
||||
static PyObject *PySSLEOFErrorObject;
|
||||
|
||||
/* Error mappings */
|
||||
static PyObject *err_codes_to_names;
|
||||
static PyObject *err_names_to_codes;
|
||||
static PyObject *lib_codes_to_names;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
||||
/* serves as a flag to see whether we've initialized the SSL thread support. */
|
||||
|
@ -202,22 +220,134 @@ typedef enum {
|
|||
#define ERRSTR1(x,y,z) (x ":" y ": " z)
|
||||
#define ERRSTR(x) ERRSTR1("_ssl.c", STRINGIFY2(__LINE__), x)
|
||||
|
||||
/* XXX It might be helpful to augment the error message generated
|
||||
below with the name of the SSL function that generated the error.
|
||||
I expect it's obvious most of the time.
|
||||
*/
|
||||
|
||||
/*
|
||||
* SSL errors.
|
||||
*/
|
||||
|
||||
PyDoc_STRVAR(SSLError_doc,
|
||||
"An error occurred in the SSL implementation.");
|
||||
|
||||
PyDoc_STRVAR(SSLZeroReturnError_doc,
|
||||
"SSL/TLS session closed cleanly.");
|
||||
|
||||
PyDoc_STRVAR(SSLWantReadError_doc,
|
||||
"Non-blocking SSL socket needs to read more data\n"
|
||||
"before the requested operation can be completed.");
|
||||
|
||||
PyDoc_STRVAR(SSLWantWriteError_doc,
|
||||
"Non-blocking SSL socket needs to write more data\n"
|
||||
"before the requested operation can be completed.");
|
||||
|
||||
PyDoc_STRVAR(SSLSyscallError_doc,
|
||||
"System error when attempting SSL operation.");
|
||||
|
||||
PyDoc_STRVAR(SSLEOFError_doc,
|
||||
"SSL/TLS connection terminated abruptly.");
|
||||
|
||||
static PyObject *
|
||||
SSLError_str(PyOSErrorObject *self)
|
||||
{
|
||||
if (self->strerror != NULL && PyUnicode_Check(self->strerror)) {
|
||||
Py_INCREF(self->strerror);
|
||||
return self->strerror;
|
||||
}
|
||||
else
|
||||
return PyObject_Str(self->args);
|
||||
}
|
||||
|
||||
static PyType_Slot sslerror_type_slots[] = {
|
||||
{Py_tp_base, NULL}, /* Filled out in module init as it's not a constant */
|
||||
{Py_tp_doc, SSLError_doc},
|
||||
{Py_tp_str, SSLError_str},
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
static PyType_Spec sslerror_type_spec = {
|
||||
"ssl.SSLError",
|
||||
sizeof(PyOSErrorObject),
|
||||
0,
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
sslerror_type_slots
|
||||
};
|
||||
|
||||
static void
|
||||
fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr,
|
||||
int lineno, unsigned long errcode)
|
||||
{
|
||||
PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL;
|
||||
PyObject *init_value, *msg, *key;
|
||||
_Py_IDENTIFIER(reason);
|
||||
_Py_IDENTIFIER(library);
|
||||
|
||||
if (errcode != 0) {
|
||||
int lib, reason;
|
||||
|
||||
lib = ERR_GET_LIB(errcode);
|
||||
reason = ERR_GET_REASON(errcode);
|
||||
key = Py_BuildValue("ii", lib, reason);
|
||||
if (key == NULL)
|
||||
goto fail;
|
||||
reason_obj = PyDict_GetItem(err_codes_to_names, key);
|
||||
Py_DECREF(key);
|
||||
if (reason_obj == NULL) {
|
||||
/* XXX if reason < 100, it might reflect a library number (!!) */
|
||||
PyErr_Clear();
|
||||
}
|
||||
key = PyLong_FromLong(lib);
|
||||
if (key == NULL)
|
||||
goto fail;
|
||||
lib_obj = PyDict_GetItem(lib_codes_to_names, key);
|
||||
Py_DECREF(key);
|
||||
if (lib_obj == NULL) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
if (errstr == NULL)
|
||||
errstr = ERR_reason_error_string(errcode);
|
||||
}
|
||||
if (errstr == NULL)
|
||||
errstr = "unknown error";
|
||||
|
||||
if (reason_obj && lib_obj)
|
||||
msg = PyUnicode_FromFormat("[%S: %S] %s (_ssl.c:%d)",
|
||||
lib_obj, reason_obj, errstr, lineno);
|
||||
else if (lib_obj)
|
||||
msg = PyUnicode_FromFormat("[%S] %s (_ssl.c:%d)",
|
||||
lib_obj, errstr, lineno);
|
||||
else
|
||||
msg = PyUnicode_FromFormat("%s (_ssl.c:%d)", errstr, lineno);
|
||||
|
||||
if (msg == NULL)
|
||||
goto fail;
|
||||
init_value = Py_BuildValue("iN", ssl_errno, msg);
|
||||
err_value = PyObject_CallObject(type, init_value);
|
||||
Py_DECREF(init_value);
|
||||
if (err_value == NULL)
|
||||
goto fail;
|
||||
if (reason_obj == NULL)
|
||||
reason_obj = Py_None;
|
||||
if (_PyObject_SetAttrId(err_value, &PyId_reason, reason_obj))
|
||||
goto fail;
|
||||
if (lib_obj == NULL)
|
||||
lib_obj = Py_None;
|
||||
if (_PyObject_SetAttrId(err_value, &PyId_library, lib_obj))
|
||||
goto fail;
|
||||
PyErr_SetObject(type, err_value);
|
||||
fail:
|
||||
Py_XDECREF(err_value);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
||||
{
|
||||
PyObject *v;
|
||||
PyObject *type = PySSLErrorObject;
|
||||
char buf[2048];
|
||||
char *errstr;
|
||||
char *errstr = NULL;
|
||||
int err;
|
||||
enum py_ssl_error p = PY_SSL_ERROR_NONE;
|
||||
unsigned long e = 0;
|
||||
|
||||
assert(ret <= 0);
|
||||
e = ERR_peek_last_error();
|
||||
|
||||
if (obj->ssl != NULL) {
|
||||
err = SSL_get_error(obj->ssl, ret);
|
||||
|
@ -248,7 +378,6 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
{
|
||||
unsigned long e = ERR_get_error();
|
||||
if (e == 0) {
|
||||
PySocketSockObject *s
|
||||
= (PySocketSockObject *) PyWeakref_GetObject(obj->Socket);
|
||||
|
@ -260,9 +389,9 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
/* underlying BIO reported an I/O error */
|
||||
Py_INCREF(s);
|
||||
ERR_clear_error();
|
||||
v = s->errorhandler();
|
||||
s->errorhandler();
|
||||
Py_DECREF(s);
|
||||
return v;
|
||||
return NULL;
|
||||
} else { /* possible? */
|
||||
p = PY_SSL_ERROR_SYSCALL;
|
||||
type = PySSLSyscallErrorObject;
|
||||
|
@ -270,60 +399,43 @@ PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno)
|
|||
}
|
||||
} else {
|
||||
p = PY_SSL_ERROR_SYSCALL;
|
||||
/* XXX Protected by global interpreter lock */
|
||||
errstr = ERR_error_string(e, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SSL_ERROR_SSL:
|
||||
{
|
||||
unsigned long e = ERR_get_error();
|
||||
p = PY_SSL_ERROR_SSL;
|
||||
if (e != 0)
|
||||
/* XXX Protected by global interpreter lock */
|
||||
errstr = ERR_error_string(e, NULL);
|
||||
else { /* possible? */
|
||||
if (e == 0)
|
||||
/* possible? */
|
||||
errstr = "A failure in the SSL library occurred";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
p = PY_SSL_ERROR_INVALID_ERROR_CODE;
|
||||
errstr = "Invalid error code";
|
||||
}
|
||||
} else {
|
||||
errstr = ERR_error_string(ERR_peek_last_error(), NULL);
|
||||
}
|
||||
PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
|
||||
fill_and_set_sslerror(type, p, errstr, lineno, e);
|
||||
ERR_clear_error();
|
||||
v = Py_BuildValue("(is)", p, buf);
|
||||
if (v != NULL) {
|
||||
PyErr_SetObject(type, v);
|
||||
Py_DECREF(v);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_setSSLError (char *errstr, int errcode, char *filename, int lineno) {
|
||||
|
||||
char buf[2048];
|
||||
PyObject *v;
|
||||
|
||||
if (errstr == NULL) {
|
||||
if (errstr == NULL)
|
||||
errcode = ERR_peek_last_error();
|
||||
errstr = ERR_error_string(errcode, NULL);
|
||||
}
|
||||
PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr);
|
||||
else
|
||||
errcode = 0;
|
||||
fill_and_set_sslerror(PySSLErrorObject, errcode, errstr, lineno, errcode);
|
||||
ERR_clear_error();
|
||||
v = Py_BuildValue("(is)", errcode, buf);
|
||||
if (v != NULL) {
|
||||
PyErr_SetObject(PySSLErrorObject, v);
|
||||
Py_DECREF(v);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSL objects
|
||||
*/
|
||||
|
||||
static PySSLSocket *
|
||||
newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock,
|
||||
enum py_ssl_server_or_client socket_type,
|
||||
|
@ -2520,27 +2632,6 @@ parse_openssl_version(unsigned long libver,
|
|||
*major = libver & 0xFF;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(SSLError_doc,
|
||||
"An error occurred in the SSL implementation.");
|
||||
|
||||
PyDoc_STRVAR(SSLZeroReturnError_doc,
|
||||
"SSL/TLS session closed cleanly.");
|
||||
|
||||
PyDoc_STRVAR(SSLWantReadError_doc,
|
||||
"Non-blocking SSL socket needs to read more data\n"
|
||||
"before the requested operation can be completed.");
|
||||
|
||||
PyDoc_STRVAR(SSLWantWriteError_doc,
|
||||
"Non-blocking SSL socket needs to write more data\n"
|
||||
"before the requested operation can be completed.");
|
||||
|
||||
PyDoc_STRVAR(SSLSyscallError_doc,
|
||||
"System error when attempting SSL operation.");
|
||||
|
||||
PyDoc_STRVAR(SSLEOFError_doc,
|
||||
"SSL/TLS connection terminated abruptly.");
|
||||
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__ssl(void)
|
||||
{
|
||||
|
@ -2548,6 +2639,8 @@ PyInit__ssl(void)
|
|||
unsigned long libver;
|
||||
unsigned int major, minor, fix, patch, status;
|
||||
PySocketModule_APIObject *socket_api;
|
||||
struct py_ssl_error_code *errcode;
|
||||
struct py_ssl_library_code *libcode;
|
||||
|
||||
if (PyType_Ready(&PySSLContext_Type) < 0)
|
||||
return NULL;
|
||||
|
@ -2577,12 +2670,11 @@ PyInit__ssl(void)
|
|||
OpenSSL_add_all_algorithms();
|
||||
|
||||
/* Add symbols to module dict */
|
||||
PySSLErrorObject = PyErr_NewExceptionWithDoc("ssl.SSLError",
|
||||
SSLError_doc,
|
||||
PyExc_OSError,
|
||||
NULL);
|
||||
sslerror_type_slots[0].pfunc = PyExc_OSError;
|
||||
PySSLErrorObject = PyType_FromSpec(&sslerror_type_spec);
|
||||
if (PySSLErrorObject == NULL)
|
||||
return NULL;
|
||||
|
||||
PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc(
|
||||
"ssl.SSLZeroReturnError", SSLZeroReturnError_doc,
|
||||
PySSLErrorObject, NULL);
|
||||
|
@ -2705,6 +2797,50 @@ PyInit__ssl(void)
|
|||
Py_INCREF(r);
|
||||
PyModule_AddObject(m, "HAS_NPN", r);
|
||||
|
||||
/* Mappings for error codes */
|
||||
err_codes_to_names = PyDict_New();
|
||||
err_names_to_codes = PyDict_New();
|
||||
if (err_codes_to_names == NULL || err_names_to_codes == NULL)
|
||||
return NULL;
|
||||
errcode = error_codes;
|
||||
while (errcode->mnemonic != NULL) {
|
||||
PyObject *mnemo, *key;
|
||||
mnemo = PyUnicode_FromString(errcode->mnemonic);
|
||||
key = Py_BuildValue("ii", errcode->library, errcode->reason);
|
||||
if (mnemo == NULL || key == NULL)
|
||||
return NULL;
|
||||
if (PyDict_SetItem(err_codes_to_names, key, mnemo))
|
||||
return NULL;
|
||||
if (PyDict_SetItem(err_names_to_codes, mnemo, key))
|
||||
return NULL;
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(mnemo);
|
||||
errcode++;
|
||||
}
|
||||
if (PyModule_AddObject(m, "err_codes_to_names", err_codes_to_names))
|
||||
return NULL;
|
||||
if (PyModule_AddObject(m, "err_names_to_codes", err_names_to_codes))
|
||||
return NULL;
|
||||
|
||||
lib_codes_to_names = PyDict_New();
|
||||
if (lib_codes_to_names == NULL)
|
||||
return NULL;
|
||||
libcode = library_codes;
|
||||
while (libcode->library != NULL) {
|
||||
PyObject *mnemo, *key;
|
||||
key = PyLong_FromLong(libcode->code);
|
||||
mnemo = PyUnicode_FromString(libcode->library);
|
||||
if (key == NULL || mnemo == NULL)
|
||||
return NULL;
|
||||
if (PyDict_SetItem(lib_codes_to_names, key, mnemo))
|
||||
return NULL;
|
||||
Py_DECREF(key);
|
||||
Py_DECREF(mnemo);
|
||||
libcode++;
|
||||
}
|
||||
if (PyModule_AddObject(m, "lib_codes_to_names", lib_codes_to_names))
|
||||
return NULL;
|
||||
|
||||
/* OpenSSL version */
|
||||
/* SSLeay() gives us the version of the library linked against,
|
||||
which could be different from the headers version.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,6 +23,7 @@ typedef struct {
|
|||
PyObject_HEAD
|
||||
PyThread_type_lock lock_lock;
|
||||
PyObject *in_weakreflist;
|
||||
char locked; /* for sanity checking */
|
||||
} lockobject;
|
||||
|
||||
static void
|
||||
|
@ -32,9 +33,8 @@ lock_dealloc(lockobject *self)
|
|||
PyObject_ClearWeakRefs((PyObject *) self);
|
||||
if (self->lock_lock != NULL) {
|
||||
/* Unlock the lock so it's safe to free it */
|
||||
PyThread_acquire_lock(self->lock_lock, 0);
|
||||
PyThread_release_lock(self->lock_lock);
|
||||
|
||||
if (self->locked)
|
||||
PyThread_release_lock(self->lock_lock);
|
||||
PyThread_free_lock(self->lock_lock);
|
||||
}
|
||||
PyObject_Del(self);
|
||||
|
@ -62,9 +62,13 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
|
|||
|
||||
|
||||
do {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
r = PyThread_acquire_lock_timed(lock, microseconds, 1);
|
||||
Py_END_ALLOW_THREADS
|
||||
/* first a simple non-blocking try without releasing the GIL */
|
||||
r = PyThread_acquire_lock_timed(lock, 0, 0);
|
||||
if (r == PY_LOCK_FAILURE && microseconds != 0) {
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
r = PyThread_acquire_lock_timed(lock, microseconds, 1);
|
||||
Py_END_ALLOW_THREADS
|
||||
}
|
||||
|
||||
if (r == PY_LOCK_INTR) {
|
||||
/* Run signal handlers if we were interrupted. Propagate
|
||||
|
@ -135,6 +139,8 @@ lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (r == PY_LOCK_ACQUIRED)
|
||||
self->locked = 1;
|
||||
return PyBool_FromLong(r == PY_LOCK_ACQUIRED);
|
||||
}
|
||||
|
||||
|
@ -153,13 +159,13 @@ static PyObject *
|
|||
lock_PyThread_release_lock(lockobject *self)
|
||||
{
|
||||
/* Sanity check: the lock must be locked */
|
||||
if (PyThread_acquire_lock(self->lock_lock, 0)) {
|
||||
PyThread_release_lock(self->lock_lock);
|
||||
if (!self->locked) {
|
||||
PyErr_SetString(ThreadError, "release unlocked lock");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyThread_release_lock(self->lock_lock);
|
||||
self->locked = 0;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
@ -175,11 +181,7 @@ but it needn't be locked by the same thread that unlocks it.");
|
|||
static PyObject *
|
||||
lock_locked_lock(lockobject *self)
|
||||
{
|
||||
if (PyThread_acquire_lock(self->lock_lock, 0)) {
|
||||
PyThread_release_lock(self->lock_lock);
|
||||
return PyBool_FromLong(0L);
|
||||
}
|
||||
return PyBool_FromLong(1L);
|
||||
return PyBool_FromLong((long)self->locked);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(locked_doc,
|
||||
|
@ -313,14 +315,7 @@ rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
|
|||
self->rlock_count = count;
|
||||
Py_RETURN_TRUE;
|
||||
}
|
||||
|
||||
if (self->rlock_count > 0 ||
|
||||
!PyThread_acquire_lock(self->rlock_lock, 0)) {
|
||||
if (microseconds == 0) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
r = acquire_timed(self->rlock_lock, microseconds);
|
||||
}
|
||||
r = acquire_timed(self->rlock_lock, microseconds);
|
||||
if (r == PY_LOCK_ACQUIRED) {
|
||||
assert(self->rlock_count == 0);
|
||||
self->rlock_owner = tid;
|
||||
|
@ -548,6 +543,7 @@ newlockobject(void)
|
|||
if (self == NULL)
|
||||
return NULL;
|
||||
self->lock_lock = PyThread_allocate_lock();
|
||||
self->locked = 0;
|
||||
self->in_weakreflist = NULL;
|
||||
if (self->lock_lock == NULL) {
|
||||
Py_DECREF(self);
|
||||
|
|
|
@ -400,6 +400,15 @@ PyMethod_Fini(void)
|
|||
(void)PyMethod_ClearFreeList();
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyMethod_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PyMethodObject",
|
||||
numfree, sizeof(PyMethodObject));
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
* instance method
|
||||
*/
|
||||
|
|
|
@ -255,6 +255,15 @@ PyDict_ClearFreeList(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyDict_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PyDictObject", numfree, sizeof(PyDictObject));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PyDict_Fini(void)
|
||||
{
|
||||
|
|
|
@ -1933,6 +1933,16 @@ PyFloat_Fini(void)
|
|||
(void)PyFloat_ClearFreeList();
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyFloat_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PyFloatObject",
|
||||
numfree, sizeof(PyFloatObject));
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* _PyFloat_{Pack,Unpack}{4,8}. See floatobject.h.
|
||||
*/
|
||||
|
|
|
@ -955,3 +955,13 @@ PyFrame_Fini(void)
|
|||
Py_XDECREF(builtin_object);
|
||||
builtin_object = NULL;
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyFrame_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PyFrameObject",
|
||||
numfree, sizeof(PyFrameObject));
|
||||
}
|
||||
|
||||
|
|
|
@ -117,6 +117,15 @@ PyList_Fini(void)
|
|||
PyList_ClearFreeList();
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyList_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PyListObject",
|
||||
numfree, sizeof(PyListObject));
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyList_New(Py_ssize_t size)
|
||||
{
|
||||
|
|
|
@ -338,6 +338,15 @@ PyCFunction_Fini(void)
|
|||
(void)PyCFunction_ClearFreeList();
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyCFunction_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PyCFunction",
|
||||
numfree, sizeof(PyCFunction));
|
||||
}
|
||||
|
||||
/* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(),
|
||||
but it's part of the API so we need to keep a function around that
|
||||
existing C extensions can call.
|
||||
|
|
|
@ -1852,6 +1852,18 @@ PyMem_Free(void *p)
|
|||
PyMem_FREE(p);
|
||||
}
|
||||
|
||||
void
|
||||
_PyObject_DebugTypeStats(FILE *out)
|
||||
{
|
||||
_PyCFunction_DebugMallocStats(out);
|
||||
_PyDict_DebugMallocStats(out);
|
||||
_PyFloat_DebugMallocStats(out);
|
||||
_PyFrame_DebugMallocStats(out);
|
||||
_PyList_DebugMallocStats(out);
|
||||
_PyMethod_DebugMallocStats(out);
|
||||
_PySet_DebugMallocStats(out);
|
||||
_PyTuple_DebugMallocStats(out);
|
||||
}
|
||||
|
||||
/* These methods are used to control infinite recursion in repr, str, print,
|
||||
etc. Container objects that may recursively contain themselves,
|
||||
|
|
|
@ -523,12 +523,10 @@ static struct arena_object* usable_arenas = NULL;
|
|||
/* Number of arenas allocated that haven't been free()'d. */
|
||||
static size_t narenas_currently_allocated = 0;
|
||||
|
||||
#ifdef PYMALLOC_DEBUG
|
||||
/* Total number of times malloc() called to allocate an arena. */
|
||||
static size_t ntimes_arena_allocated = 0;
|
||||
/* High water mark (max value ever seen) for narenas_currently_allocated. */
|
||||
static size_t narenas_highwater = 0;
|
||||
#endif
|
||||
|
||||
/* Allocate a new arena. If we run out of memory, return NULL. Else
|
||||
* allocate a new arena, and return the address of an arena_object
|
||||
|
@ -545,7 +543,7 @@ new_arena(void)
|
|||
|
||||
#ifdef PYMALLOC_DEBUG
|
||||
if (Py_GETENV("PYTHONMALLOCSTATS"))
|
||||
_PyObject_DebugMallocStats();
|
||||
_PyObject_DebugMallocStats(stderr);
|
||||
#endif
|
||||
if (unused_arena_objects == NULL) {
|
||||
uint i;
|
||||
|
@ -613,11 +611,9 @@ new_arena(void)
|
|||
arenaobj->address = (uptr)address;
|
||||
|
||||
++narenas_currently_allocated;
|
||||
#ifdef PYMALLOC_DEBUG
|
||||
++ntimes_arena_allocated;
|
||||
if (narenas_currently_allocated > narenas_highwater)
|
||||
narenas_highwater = narenas_currently_allocated;
|
||||
#endif
|
||||
arenaobj->freepools = NULL;
|
||||
/* pool_address <- first pool-aligned address in the arena
|
||||
nfreepools <- number of whole pools that fit after alignment */
|
||||
|
@ -1723,17 +1719,19 @@ _PyObject_DebugDumpAddress(const void *p)
|
|||
}
|
||||
}
|
||||
|
||||
#endif /* PYMALLOC_DEBUG */
|
||||
|
||||
static size_t
|
||||
printone(const char* msg, size_t value)
|
||||
printone(FILE *out, const char* msg, size_t value)
|
||||
{
|
||||
int i, k;
|
||||
char buf[100];
|
||||
size_t origvalue = value;
|
||||
|
||||
fputs(msg, stderr);
|
||||
fputs(msg, out);
|
||||
for (i = (int)strlen(msg); i < 35; ++i)
|
||||
fputc(' ', stderr);
|
||||
fputc('=', stderr);
|
||||
fputc(' ', out);
|
||||
fputc('=', out);
|
||||
|
||||
/* Write the value with commas. */
|
||||
i = 22;
|
||||
|
@ -1754,17 +1752,33 @@ printone(const char* msg, size_t value)
|
|||
|
||||
while (i >= 0)
|
||||
buf[i--] = ' ';
|
||||
fputs(buf, stderr);
|
||||
fputs(buf, out);
|
||||
|
||||
return origvalue;
|
||||
}
|
||||
|
||||
/* Print summary info to stderr about the state of pymalloc's structures.
|
||||
void
|
||||
_PyDebugAllocatorStats(FILE *out,
|
||||
const char *block_name, int num_blocks, size_t sizeof_block)
|
||||
{
|
||||
char buf1[128];
|
||||
char buf2[128];
|
||||
PyOS_snprintf(buf1, sizeof(buf1),
|
||||
"%d %ss * %zd bytes each",
|
||||
num_blocks, block_name, sizeof_block);
|
||||
PyOS_snprintf(buf2, sizeof(buf2),
|
||||
"%48s ", buf1);
|
||||
(void)printone(out, buf2, num_blocks * sizeof_block);
|
||||
}
|
||||
|
||||
#ifdef WITH_PYMALLOC
|
||||
|
||||
/* Print summary info to "out" about the state of pymalloc's structures.
|
||||
* In Py_DEBUG mode, also perform some expensive internal consistency
|
||||
* checks.
|
||||
*/
|
||||
void
|
||||
_PyObject_DebugMallocStats(void)
|
||||
_PyObject_DebugMallocStats(FILE *out)
|
||||
{
|
||||
uint i;
|
||||
const uint numclasses = SMALL_REQUEST_THRESHOLD >> ALIGNMENT_SHIFT;
|
||||
|
@ -1793,7 +1807,7 @@ _PyObject_DebugMallocStats(void)
|
|||
size_t total;
|
||||
char buf[128];
|
||||
|
||||
fprintf(stderr, "Small block threshold = %d, in %u size classes.\n",
|
||||
fprintf(out, "Small block threshold = %d, in %u size classes.\n",
|
||||
SMALL_REQUEST_THRESHOLD, numclasses);
|
||||
|
||||
for (i = 0; i < numclasses; ++i)
|
||||
|
@ -1847,10 +1861,10 @@ _PyObject_DebugMallocStats(void)
|
|||
}
|
||||
assert(narenas == narenas_currently_allocated);
|
||||
|
||||
fputc('\n', stderr);
|
||||
fputc('\n', out);
|
||||
fputs("class size num pools blocks in use avail blocks\n"
|
||||
"----- ---- --------- ------------- ------------\n",
|
||||
stderr);
|
||||
out);
|
||||
|
||||
for (i = 0; i < numclasses; ++i) {
|
||||
size_t p = numpools[i];
|
||||
|
@ -1861,7 +1875,7 @@ _PyObject_DebugMallocStats(void)
|
|||
assert(b == 0 && f == 0);
|
||||
continue;
|
||||
}
|
||||
fprintf(stderr, "%5u %6u "
|
||||
fprintf(out, "%5u %6u "
|
||||
"%11" PY_FORMAT_SIZE_T "u "
|
||||
"%15" PY_FORMAT_SIZE_T "u "
|
||||
"%13" PY_FORMAT_SIZE_T "u\n",
|
||||
|
@ -1871,35 +1885,36 @@ _PyObject_DebugMallocStats(void)
|
|||
pool_header_bytes += p * POOL_OVERHEAD;
|
||||
quantization += p * ((POOL_SIZE - POOL_OVERHEAD) % size);
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
(void)printone("# times object malloc called", serialno);
|
||||
|
||||
(void)printone("# arenas allocated total", ntimes_arena_allocated);
|
||||
(void)printone("# arenas reclaimed", ntimes_arena_allocated - narenas);
|
||||
(void)printone("# arenas highwater mark", narenas_highwater);
|
||||
(void)printone("# arenas allocated current", narenas);
|
||||
fputc('\n', out);
|
||||
#ifdef PYMALLOC_DEBUG
|
||||
(void)printone(out, "# times object malloc called", serialno);
|
||||
#endif
|
||||
(void)printone(out, "# arenas allocated total", ntimes_arena_allocated);
|
||||
(void)printone(out, "# arenas reclaimed", ntimes_arena_allocated - narenas);
|
||||
(void)printone(out, "# arenas highwater mark", narenas_highwater);
|
||||
(void)printone(out, "# arenas allocated current", narenas);
|
||||
|
||||
PyOS_snprintf(buf, sizeof(buf),
|
||||
"%" PY_FORMAT_SIZE_T "u arenas * %d bytes/arena",
|
||||
narenas, ARENA_SIZE);
|
||||
(void)printone(buf, narenas * ARENA_SIZE);
|
||||
(void)printone(out, buf, narenas * ARENA_SIZE);
|
||||
|
||||
fputc('\n', stderr);
|
||||
fputc('\n', out);
|
||||
|
||||
total = printone("# bytes in allocated blocks", allocated_bytes);
|
||||
total += printone("# bytes in available blocks", available_bytes);
|
||||
total = printone(out, "# bytes in allocated blocks", allocated_bytes);
|
||||
total += printone(out, "# bytes in available blocks", available_bytes);
|
||||
|
||||
PyOS_snprintf(buf, sizeof(buf),
|
||||
"%u unused pools * %d bytes", numfreepools, POOL_SIZE);
|
||||
total += printone(buf, (size_t)numfreepools * POOL_SIZE);
|
||||
total += printone(out, buf, (size_t)numfreepools * POOL_SIZE);
|
||||
|
||||
total += printone("# bytes lost to pool headers", pool_header_bytes);
|
||||
total += printone("# bytes lost to quantization", quantization);
|
||||
total += printone("# bytes lost to arena alignment", arena_alignment);
|
||||
(void)printone("Total", total);
|
||||
total += printone(out, "# bytes lost to pool headers", pool_header_bytes);
|
||||
total += printone(out, "# bytes lost to quantization", quantization);
|
||||
total += printone(out, "# bytes lost to arena alignment", arena_alignment);
|
||||
(void)printone(out, "Total", total);
|
||||
}
|
||||
|
||||
#endif /* PYMALLOC_DEBUG */
|
||||
#endif /* #ifdef WITH_PYMALLOC */
|
||||
|
||||
#ifdef Py_USING_MEMORY_DEBUGGER
|
||||
/* Make this function last so gcc won't inline it since the definition is
|
||||
|
|
|
@ -1133,6 +1133,16 @@ PySet_Fini(void)
|
|||
Py_CLEAR(emptyfrozenset);
|
||||
}
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PySet_DebugMallocStats(FILE *out)
|
||||
{
|
||||
_PyDebugAllocatorStats(out,
|
||||
"free PySetObject",
|
||||
numfree, sizeof(PySetObject));
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
set_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
|
|
|
@ -45,6 +45,22 @@ show_track(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Print summary info about the state of the optimized allocator */
|
||||
void
|
||||
_PyTuple_DebugMallocStats(FILE *out)
|
||||
{
|
||||
#if PyTuple_MAXSAVESIZE > 0
|
||||
int i;
|
||||
char buf[128];
|
||||
for (i = 1; i < PyTuple_MAXSAVESIZE; i++) {
|
||||
PyOS_snprintf(buf, sizeof(buf),
|
||||
"free %d-sized PyTupleObject", i);
|
||||
_PyDebugAllocatorStats(out,
|
||||
buf,
|
||||
numfree[i], _PyObject_VAR_SIZE(&PyTuple_Type, i));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyTuple_New(register Py_ssize_t size)
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ResourceCompile>
|
||||
<PreprocessorDefinitions>_DEBUG</PreprocessorDefinitions>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<BuildMacro Include="PyDebugExt">
|
||||
|
|
|
@ -642,7 +642,7 @@ Py_Finalize(void)
|
|||
#endif /* Py_TRACE_REFS */
|
||||
#ifdef PYMALLOC_DEBUG
|
||||
if (Py_GETENV("PYTHONMALLOCSTATS"))
|
||||
_PyObject_DebugMallocStats();
|
||||
_PyObject_DebugMallocStats(stderr);
|
||||
#endif
|
||||
|
||||
call_ll_exitfuncs();
|
||||
|
|
|
@ -997,6 +997,27 @@ a 11-tuple where the entries in the tuple are counts of:\n\
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
static PyObject *
|
||||
sys_debugmallocstats(PyObject *self, PyObject *args)
|
||||
{
|
||||
#ifdef WITH_PYMALLOC
|
||||
_PyObject_DebugMallocStats(stderr);
|
||||
fputc('\n', stderr);
|
||||
#endif
|
||||
_PyObject_DebugTypeStats(stderr);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
PyDoc_STRVAR(debugmallocstats_doc,
|
||||
"_debugmallocstats()\n\
|
||||
\n\
|
||||
Print summary info to stderr about the state of\n\
|
||||
pymalloc's structures.\n\
|
||||
\n\
|
||||
In Py_DEBUG mode, also perform some expensive internal consistency\n\
|
||||
checks.\n\
|
||||
");
|
||||
|
||||
#ifdef Py_TRACE_REFS
|
||||
/* Defined in objects.c because it uses static globals if that file */
|
||||
extern PyObject *_Py_GetObjects(PyObject *, PyObject *);
|
||||
|
@ -1093,6 +1114,8 @@ static PyMethodDef sys_methods[] = {
|
|||
{"settrace", sys_settrace, METH_O, settrace_doc},
|
||||
{"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc},
|
||||
{"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc},
|
||||
{"_debugmallocstats", sys_debugmallocstats, METH_VARARGS,
|
||||
debugmallocstats_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
"""
|
||||
|
||||
import sys, os, time, difflib, optparse
|
||||
from datetime import datetime, timezone
|
||||
|
||||
def file_mtime(path):
|
||||
t = datetime.fromtimestamp(os.stat(path).st_mtime,
|
||||
timezone.utc)
|
||||
return t.astimezone().isoformat()
|
||||
|
||||
def main():
|
||||
|
||||
|
@ -30,10 +36,12 @@ def main():
|
|||
n = options.lines
|
||||
fromfile, tofile = args
|
||||
|
||||
fromdate = time.ctime(os.stat(fromfile).st_mtime)
|
||||
todate = time.ctime(os.stat(tofile).st_mtime)
|
||||
fromlines = open(fromfile, 'U').readlines()
|
||||
tolines = open(tofile, 'U').readlines()
|
||||
fromdate = file_mtime(fromfile)
|
||||
todate = file_mtime(tofile)
|
||||
with open(fromfile, 'U') as ff:
|
||||
fromlines = ff.readlines()
|
||||
with open(tofile, 'U') as tf:
|
||||
tolines = tf.readlines()
|
||||
|
||||
if options.u:
|
||||
diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
#! /usr/bin/env python3
|
||||
|
||||
"""
|
||||
This script should be called *manually* when we want to upgrade SSLError
|
||||
`library` and `reason` mnemnonics to a more recent OpenSSL version.
|
||||
|
||||
It takes two arguments:
|
||||
- the path to the OpenSSL include files' directory
|
||||
(e.g. openssl-1.0.1-beta3/include/openssl/)
|
||||
- the path to the C file to be generated
|
||||
(probably Modules/_ssl_data.h)
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def parse_error_codes(h_file, prefix):
|
||||
pat = re.compile(r"#define\W+(%s([\w]+))\W+(\d+)\b" % re.escape(prefix))
|
||||
codes = []
|
||||
with open(h_file, "r", encoding="latin1") as f:
|
||||
for line in f:
|
||||
match = pat.search(line)
|
||||
if match:
|
||||
code, name, num = match.groups()
|
||||
num = int(num)
|
||||
codes.append((code, name, num))
|
||||
return codes
|
||||
|
||||
if __name__ == "__main__":
|
||||
openssl_inc = sys.argv[1]
|
||||
outfile = sys.argv[2]
|
||||
use_stdout = outfile == '-'
|
||||
f = sys.stdout if use_stdout else open(outfile, "w")
|
||||
error_libraries = (
|
||||
# (library code, mnemonic, error prefix, header file)
|
||||
('ERR_LIB_PEM', 'PEM', 'PEM_R_', 'pem.h'),
|
||||
('ERR_LIB_SSL', 'SSL', 'SSL_R_', 'ssl.h'),
|
||||
('ERR_LIB_X509', 'X509', 'X509_R_', 'x509.h'),
|
||||
)
|
||||
def w(l):
|
||||
f.write(l + "\n")
|
||||
w("/* File generated by Tools/ssl/make_ssl_data.py */")
|
||||
w("/* Generated on %s */" % datetime.datetime.now().isoformat())
|
||||
w("")
|
||||
|
||||
w("static struct py_ssl_library_code library_codes[] = {")
|
||||
for libcode, mnemo, _, _ in error_libraries:
|
||||
w(' {"%s", %s},' % (mnemo, libcode))
|
||||
w(' { NULL }')
|
||||
w('};')
|
||||
w("")
|
||||
|
||||
w("static struct py_ssl_error_code error_codes[] = {")
|
||||
for libcode, _, prefix, h_file in error_libraries:
|
||||
codes = parse_error_codes(os.path.join(openssl_inc, h_file), prefix)
|
||||
for code, name, num in sorted(codes):
|
||||
w(' #ifdef %s' % (code))
|
||||
w(' {"%s", %s, %s},' % (name, libcode, code))
|
||||
w(' #else')
|
||||
w(' {"%s", %s, %d},' % (name, libcode, num))
|
||||
w(' #endif')
|
||||
w(' { NULL }')
|
||||
w('};')
|
||||
if not use_stdout:
|
||||
f.close()
|
Loading…
Reference in New Issue