diff options
-rw-r--r-- | Doc/library/mmap.rst | 17 | ||||
-rw-r--r-- | Doc/library/socketserver.rst | 16 | ||||
-rw-r--r-- | Doc/library/threading.rst | 13 | ||||
-rw-r--r-- | Doc/library/trace.rst | 8 | ||||
-rw-r--r-- | Doc/whatsnew/2.6.rst | 14 | ||||
-rw-r--r-- | Lib/SocketServer.py | 45 | ||||
-rw-r--r-- | Lib/test/test_builtin.py | 5 | ||||
-rw-r--r-- | Lib/test/test_grammar.py | 2 | ||||
-rw-r--r-- | Lib/test/test_mmap.py | 36 | ||||
-rw-r--r-- | Lib/test/test_socket.py | 1 | ||||
-rw-r--r-- | Lib/test/test_ssl.py | 22 | ||||
-rw-r--r-- | Lib/test/test_textwrap.py | 13 | ||||
-rw-r--r-- | Lib/test/test_xmlrpc.py | 3 | ||||
-rw-r--r-- | Lib/textwrap.py | 7 | ||||
-rw-r--r-- | Lib/tokenize.py | 6 | ||||
-rw-r--r-- | Lib/trace.py | 8 | ||||
-rw-r--r-- | Misc/ACKS | 6 | ||||
-rw-r--r-- | Modules/mmapmodule.c | 39 | ||||
-rw-r--r-- | Parser/tokenizer.c | 19 | ||||
-rw-r--r-- | Python/modsupport.c | 16 | ||||
-rw-r--r-- | Python/mystrtoul.c | 45 |
21 files changed, 301 insertions, 40 deletions
diff --git a/Doc/library/mmap.rst b/Doc/library/mmap.rst index 9e552fd..37391d0 100644 --- a/Doc/library/mmap.rst +++ b/Doc/library/mmap.rst @@ -137,11 +137,12 @@ Memory-mapped file objects support the following methods: an exception being raised. -.. method:: mmap.find(string[, start]) +.. method:: mmap.find(string[, start[, end]]) - Returns the lowest index in the object where the substring *string* is found. - Returns ``-1`` on failure. *start* is the index at which the search begins, and - defaults to zero. + Returns the lowest index in the object where the substring *string* is found, + such that *string* is contained in the range [*start*, *end*]. Optional + arguments *start* and *end* are interpreted as in slice notation. + Returns ``-1`` on failure. .. method:: mmap.flush([offset, size]) @@ -186,6 +187,14 @@ Memory-mapped file objects support the following methods: :exc:`TypeError` exception. +.. method:: mmap.rfind(string[, start[, end]]) + + Returns the highest index in the object where the substring *string* is + found, such that *string* is contained in the range [*start*, + *end*]. Optional arguments *start* and *end* are interpreted as in slice + notation. Returns ``-1`` on failure. + + .. method:: mmap.seek(pos[, whence]) Set the file's current position. *whence* argument is optional and defaults to diff --git a/Doc/library/socketserver.rst b/Doc/library/socketserver.rst index c900ea7..2c85c86 100644 --- a/Doc/library/socketserver.rst +++ b/Doc/library/socketserver.rst @@ -44,7 +44,7 @@ to behave autonomously; the default is :const:`False`, meaning that Python will not exit until all threads created by :class:`ThreadingMixIn` have exited. Server classes have the same external methods and attributes, no matter what -network protocol they use: +network protocol they use. Server Creation Notes @@ -193,6 +193,13 @@ The server classes support the following class variables: The type of socket used by the server; :const:`socket.SOCK_STREAM` and :const:`socket.SOCK_DGRAM` are two possible values. +.. data:: timeout + + Timeout duration, measured in seconds, or :const:`None` if no timeout is desired. + If no incoming requests are received within the timeout period, + the :meth:`handle_timeout` method is called and then the server resumes waiting for + requests. + There are various server methods that can be overridden by subclasses of base server classes like :class:`TCPServer`; these methods aren't useful to external users of the server object. @@ -220,6 +227,13 @@ users of the server object. method raises an exception. The default action is to print the traceback to standard output and continue handling further requests. +.. function:: handle_timeout() + + This function is called when the :attr:`timeout` attribute has been set to a + value other than :const:`None` and the timeout period has passed with no + requests being received. The default action for forking servers is + to collect the status of any child processes that have exited, while + in threading servers this method does nothing. .. function:: process_request(request, client_address) diff --git a/Doc/library/threading.rst b/Doc/library/threading.rst index c015372..6f3e95b 100644 --- a/Doc/library/threading.rst +++ b/Doc/library/threading.rst @@ -615,18 +615,19 @@ impossible to detect the termination of alien threads. When the *timeout* argument is present and not ``None``, it should be a floating point number specifying a timeout for the operation in seconds (or fractions - thereof). As :meth:`join` always returns ``None``, you must call - :meth:`isAlive` to decide whether a timeout happened. + thereof). As :meth:`join` always returns ``None``, you must call :meth:`isAlive` + after :meth:`join` to decide whether a timeout happened -- if the thread is + still alive, the :meth:`join` call timed out. When the *timeout* argument is not present or ``None``, the operation will block until the thread terminates. A thread can be :meth:`join`\ ed many times. - :meth:`join` may throw a :exc:`RuntimeError`, if an attempt is made to join the - current thread as that would cause a deadlock. It is also an error to - :meth:`join` a thread before it has been started and attempts to do so raises - same exception. + :meth:`join` raises a :exc:`RuntimeError` if an attempt is made to join + the current thread as that would cause a deadlock. It is also an error to + :meth:`join` a thread before it has been started and attempts to do so + raises the same exception. .. method:: Thread.getName() diff --git a/Doc/library/trace.rst b/Doc/library/trace.rst index 91cf1a4..9d0c0c4 100644 --- a/Doc/library/trace.rst +++ b/Doc/library/trace.rst @@ -64,12 +64,14 @@ The following command-line arguments are supported: stdout for each file processed. :option:`--ignore-module` - Ignore the named module and its submodules (if it is a package). May be given + Accepts comma separated list of module names. Ignore each of the named + module and its submodules (if it is a package). May be given multiple times. :option:`--ignore-dir` - Ignore all modules and packages in the named directory and subdirectories. May - be given multiple times. + Ignore all modules and packages in the named directory and subdirectories + (multiple directories can be joined by os.pathsep). May be given multiple + times. .. _trace-api: diff --git a/Doc/whatsnew/2.6.rst b/Doc/whatsnew/2.6.rst index 46922a4..e386b36 100644 --- a/Doc/whatsnew/2.6.rst +++ b/Doc/whatsnew/2.6.rst @@ -960,6 +960,13 @@ complete list of changes, or look through the CVS logs for all the details. .. Patch #1490190 +* :class:`mmap` objects now have a :meth:`rfind` method that finds + a substring, beginning at the end of the string and searching + backwards. The :meth:`find` method + also gained a *end* parameter containing the index at which to stop + the forward search. + (Contributed by John Lenton.) + * The :mod:`new` module has been removed from Python 3.0. Importing it therefore triggers a warning message when Python is running in 3.0-warning @@ -1102,6 +1109,13 @@ complete list of changes, or look through the CVS logs for all the details. (Contributed by Alberto Bertogli.) .. Patch #1646 + +* The base classes in the :mod:`SocketServer` module now support + calling a :meth:`handle_timeout` method after a span of inactivity + specified by the server's :attr:`timeout` attribute. (Contributed + by Michael Pomraning.) + + .. Patch #742598 * A new variable in the :mod:`sys` module, :attr:`float_info`, is an object diff --git a/Lib/SocketServer.py b/Lib/SocketServer.py index 994a3c6..f62b7df 100644 --- a/Lib/SocketServer.py +++ b/Lib/SocketServer.py @@ -158,6 +158,7 @@ class BaseServer: - server_bind() - server_activate() - get_request() -> request, client_address + - handle_timeout() - verify_request(request, client_address) - server_close() - process_request(request, client_address) @@ -171,6 +172,7 @@ class BaseServer: Class variables that may be overridden by derived classes or instances: + - timeout - address_family - socket_type - allow_reuse_address @@ -182,6 +184,8 @@ class BaseServer: """ + timeout = None + def __init__(self, server_address, RequestHandlerClass): """Constructor. May be extended, do not override.""" self.server_address = server_address @@ -204,8 +208,9 @@ class BaseServer: # finishing a request is fairly arbitrary. Remember: # # - handle_request() is the top-level call. It calls - # get_request(), verify_request() and process_request() - # - get_request() is different for stream or datagram sockets + # await_request(), verify_request() and process_request() + # - get_request(), called by await_request(), is different for + # stream or datagram sockets # - process_request() is the place that may fork a new process # or create a new thread to finish the request # - finish_request() instantiates the request handler class; @@ -214,7 +219,7 @@ class BaseServer: def handle_request(self): """Handle one request, possibly blocking.""" try: - request, client_address = self.get_request() + request, client_address = self.await_request() except socket.error: return if self.verify_request(request, client_address): @@ -224,6 +229,28 @@ class BaseServer: self.handle_error(request, client_address) self.close_request(request) + def await_request(self): + """Call get_request or handle_timeout, observing self.timeout. + + Returns value from get_request() or raises socket.timeout exception if + timeout was exceeded. + """ + if self.timeout is not None: + # If timeout == 0, you're responsible for your own fd magic. + import select + fd_sets = select.select([self], [], [], self.timeout) + if not fd_sets[0]: + self.handle_timeout() + raise socket.timeout("Listening timed out") + return self.get_request() + + def handle_timeout(self): + """Called if no new request arrives within self.timeout. + + Overridden by ForkingMixIn. + """ + pass + def verify_request(self, request, client_address): """Verify the request. May be overridden. @@ -289,6 +316,7 @@ class TCPServer(BaseServer): - server_bind() - server_activate() - get_request() -> request, client_address + - handle_timeout() - verify_request(request, client_address) - process_request(request, client_address) - close_request(request) @@ -301,6 +329,7 @@ class TCPServer(BaseServer): Class variables that may be overridden by derived classes or instances: + - timeout - address_family - socket_type - request_queue_size (only for stream sockets) @@ -405,11 +434,12 @@ class ForkingMixIn: """Mix-in class to handle each request in a new process.""" + timeout = 300 active_children = None max_children = 40 def collect_children(self): - """Internal routine to wait for died children.""" + """Internal routine to wait for children that have exited.""" while self.active_children: if len(self.active_children) < self.max_children: options = os.WNOHANG @@ -424,6 +454,13 @@ class ForkingMixIn: if not pid: break self.active_children.remove(pid) + def handle_timeout(self): + """Wait for zombies after self.timeout seconds of inactivity. + + May be extended, do not override. + """ + self.collect_children() + def process_request(self, request, client_address): """Fork a new subprocess to process the request.""" self.collect_children() diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 4cf5916..2718bbf 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -743,6 +743,11 @@ class BuiltinTest(unittest.TestCase): self.assertEqual(int('0O123', 8), 83) self.assertEqual(int('0B100', 2), 4) + # Bug 1679: "0x" is not a valid hex literal + self.assertRaises(ValueError, int, "0x", 16) + self.assertRaises(ValueError, int, "0x", 0) + + # SF bug 1334662: int(string, base) wrong answers # Various representations of 2**32 evaluated to 0 # rather than 2**32 in previous versions diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 7ab7557..0777307 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -32,6 +32,8 @@ class TokenTests(unittest.TestCase): self.assertEquals(0o377, 255) self.assertEquals(2147483647, 0o17777777777) self.assertEquals(0b1001, 9) + # "0x" is not a valid literal + self.assertRaises(SyntaxError, eval, "0x") from sys import maxsize if maxsize == 2147483647: self.assertEquals(-2147483647-1, -0o20000000000) diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index 3d30109..5bf7eb0 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -252,6 +252,42 @@ class MmapTests(unittest.TestCase): self.assertEqual(m.find(slice + b'x'), -1) m.close() + def test_find_end(self): + # test the new 'end' parameter works as expected + f = open(TESTFN, 'w+') + data = 'one two ones' + n = len(data) + f.write(data) + f.flush() + m = mmap.mmap(f.fileno(), n) + f.close() + + self.assertEqual(m.find('one'), 0) + self.assertEqual(m.find('ones'), 8) + self.assertEqual(m.find('one', 0, -1), 0) + self.assertEqual(m.find('one', 1), 8) + self.assertEqual(m.find('one', 1, -1), 8) + self.assertEqual(m.find('one', 1, -2), -1) + + + def test_rfind(self): + # test the new 'end' parameter works as expected + f = open(TESTFN, 'w+') + data = 'one two ones' + n = len(data) + f.write(data) + f.flush() + m = mmap.mmap(f.fileno(), n) + f.close() + + self.assertEqual(m.rfind('one'), 8) + self.assertEqual(m.rfind('one '), 0) + self.assertEqual(m.rfind('one', 0, -1), 8) + self.assertEqual(m.rfind('one', 0, -2), 0) + self.assertEqual(m.rfind('one', 1, -1), 8) + self.assertEqual(m.rfind('one', 1, -2), -1) + + def test_double_close(self): # make sure a double close doesn't crash on Solaris (Bug# 665913) f = open(TESTFN, 'wb+') diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 23b7759..d3b870f 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -288,7 +288,6 @@ class GeneralModuleTests(unittest.TestCase): def testRefCountGetNameInfo(self): # Testing reference count for getnameinfo - import sys if hasattr(sys, "getrefcount"): try: # On some versions, this loses a reference diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 81943a5..34bb31a 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -38,6 +38,27 @@ def handle_error(prefix): class BasicTests(unittest.TestCase): + def testSSLconnect(self): + if not test_support.is_resource_enabled('network'): + return + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE) + s.connect(("svn.python.org", 443)) + c = s.getpeercert() + if c: + raise test_support.TestFailed("Peer cert %s shouldn't be here!") + s.close() + + # this should fail because we have no verification certs + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + try: + s.connect(("svn.python.org", 443)) + except ssl.SSLError: + pass + finally: + s.close() + def testCrucialConstants(self): ssl.PROTOCOL_SSLv2 ssl.PROTOCOL_SSLv23 @@ -81,7 +102,6 @@ class BasicTests(unittest.TestCase): class NetworkedTests(unittest.TestCase): def testConnect(self): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_NONE) s.connect(("svn.python.org", 443)) diff --git a/Lib/test/test_textwrap.py b/Lib/test/test_textwrap.py index b226c71..3f2239d 100644 --- a/Lib/test/test_textwrap.py +++ b/Lib/test/test_textwrap.py @@ -385,6 +385,19 @@ How *do* you spell that odd word, anyways? ' o'], subsequent_indent = ' '*15) + # bug 1146. Prevent a long word to be wrongly wrapped when the + # preceding word is exactly one character shorter than the width + self.check_wrap(self.text, 12, + ['Did you say ', + '"supercalifr', + 'agilisticexp', + 'ialidocious?', + '" How *do*', + 'you spell', + 'that odd', + 'word,', + 'anyways?']) + def test_nobreak_long(self): # Test with break_long_words disabled self.wrapper.break_long_words = 0 diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index ade6f84..16ef798 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -347,7 +347,8 @@ class SimpleServerTestCase(unittest.TestCase): # protocol error; provide additional information in test output self.fail("%s\n%s" % (e, e.headers)) - def test_404(self): + # [ch] The test 404 is causing lots of false alarms. + def XXXtest_404(self): # send POST with httplib, it should return 404 header and # 'Not Found' message. conn = httplib.HTTPConnection('localhost', PORT) diff --git a/Lib/textwrap.py b/Lib/textwrap.py index e6e1b97..7e05c1a 100644 --- a/Lib/textwrap.py +++ b/Lib/textwrap.py @@ -159,7 +159,12 @@ class TextWrapper: Handle a chunk of text (most likely a word, not whitespace) that is too long to fit in any line. """ - space_left = max(width - cur_len, 1) + # Figure out when indent is larger than the specified width, and make + # sure at least one character is stripped off on every pass + if width < 1: + space_left = 1 + else: + space_left = width - cur_len # If we're allowed to break long words, then do so: put as much # of the next chunk onto the current line as will fit. diff --git a/Lib/tokenize.py b/Lib/tokenize.py index 0d9a3fb..797b6e0 100644 --- a/Lib/tokenize.py +++ b/Lib/tokenize.py @@ -49,9 +49,9 @@ Comment = r'#[^\r\n]*' Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) Name = r'[a-zA-Z_]\w*' -Hexnumber = r'0[xX][\da-fA-F]*' -Binnumber = r'0[bB][01]*' -Octnumber = r'0[oO][0-7]*' +Hexnumber = r'0[xX][\da-fA-F]+' +Binnumber = r'0[bB][01]+' +Octnumber = r'0[oO][0-7]+' Decnumber = r'(?:0+|[1-9]\d*)' Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) Exponent = r'[eE][-+]?\d+' diff --git a/Lib/trace.py b/Lib/trace.py index f6da026..c52c8a8 100644 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -96,8 +96,9 @@ Modifiers: (Can only be used with --count or --report.) Filters, may be repeated multiple times: ---ignore-module=<mod> Ignore the given module and its submodules - (if it is a package). +--ignore-module=<mod> Ignore the given module(s) and its submodules + (if it is a package). Accepts comma separated + list of module names --ignore-dir=<dir> Ignore files in the given directory (multiple directories can be joined by os.pathsep). """ % sys.argv[0]) @@ -725,7 +726,8 @@ def main(argv=None): continue if opt == "--ignore-module": - ignore_modules.append(val) + for mod in val.split(","): + ignore_modules.append(mod.strip()) continue if opt == "--ignore-dir": @@ -109,8 +109,6 @@ Tony Campbell Brett Cannon Mike Carlton Terry Carroll -Brian Leair -Luke Kenneth Casson Leighton Donn Cave Per Cederqvist Octavian Cerna @@ -390,6 +388,7 @@ Piers Lauder Ben Laurie Simon Law Chris Lawrence +Brian Leair Christopher Lee Inyeol Lee John J. Lee @@ -397,7 +396,9 @@ Thomas Lee Luc Lefebvre Kip Lehman Joerg Lehmann +Luke Kenneth Casson Leighton Marc-Andre Lemburg +John Lenton Mark Levinson William Lewis Robert van Liere @@ -524,6 +525,7 @@ Martijn Pieters François Pinard Zach Pincus Michael Piotrowski +Michael Pomraning Iustin Pop John Popplewell Amrit Prem diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 8302767..ab9eedc 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -254,19 +254,22 @@ mmap_read_method(mmap_object *self, } static PyObject * -mmap_find_method(mmap_object *self, - PyObject *args) +mmap_gfind(mmap_object *self, + PyObject *args, + int reverse) { Py_ssize_t start = self->pos; + Py_ssize_t end = self->size; char *needle; Py_ssize_t len; CHECK_VALID(NULL); - if (!PyArg_ParseTuple(args, "s#|n:find", &needle, &len, &start)) { + if (!PyArg_ParseTuple(args, reverse ? "s#|nn:rfind" : "s#|nn:find", + &needle, &len, &start, &end)) { return NULL; } else { char *p; - char *e = self->data + self->size; + char sign = reverse ? -1 : 1; if (start < 0) start += self->size; @@ -275,7 +278,18 @@ mmap_find_method(mmap_object *self, else if ((size_t)start > self->size) start = self->size; - for (p = self->data + start; p + len <= e; ++p) { + if (end < 0) + end += self->size; + if (end < 0) + end = 0; + else if ((size_t)end > self->size) + end = self->size; + + start += (Py_ssize_t)self->data; + end += (Py_ssize_t)self->data; + + for (p = (char *)(reverse ? end - len : start); + p >= (char *)start && p + len <= (char *)end; p+=sign) { Py_ssize_t i; for (i = 0; i < len && needle[i] == p[i]; ++i) /* nothing */; @@ -287,6 +301,20 @@ mmap_find_method(mmap_object *self, } } +static PyObject * +mmap_find_method(mmap_object *self, + PyObject *args) +{ + return mmap_gfind(self, args, 0); +} + +static PyObject * +mmap_rfind_method(mmap_object *self, + PyObject *args) +{ + return mmap_gfind(self, args, 1); +} + static int is_writable(mmap_object *self) { @@ -604,6 +632,7 @@ mmap_move_method(mmap_object *self, PyObject *args) static struct PyMethodDef mmap_object_methods[] = { {"close", (PyCFunction) mmap_close_method, METH_NOARGS}, {"find", (PyCFunction) mmap_find_method, METH_VARARGS}, + {"rfind", (PyCFunction) mmap_rfind_method, METH_VARARGS}, {"flush", (PyCFunction) mmap_flush_method, METH_VARARGS}, {"move", (PyCFunction) mmap_move_method, METH_VARARGS}, {"read", (PyCFunction) mmap_read_method, METH_VARARGS}, diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 79ed87d..3733f49 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1352,19 +1352,38 @@ tok_get(register struct tok_state *tok, char **p_start, char **p_end) goto imaginary; #endif if (c == 'x' || c == 'X') { + /* Hex */ + c = tok_nextc(tok); + if (!isxdigit(c)) { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } do { c = tok_nextc(tok); } while (isxdigit(c)); } else if (c == 'o' || c == 'O') { /* Octal */ + c = tok_nextc(tok); + if (c < '0' || c > '8') { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } do { c = tok_nextc(tok); } while ('0' <= c && c < '8'); } else if (c == 'b' || c == 'B') { /* Binary */ + c = tok_nextc(tok); + if (c != '0' && c != '1') { + tok->done = E_TOKEN; + tok_backup(tok, c); + return ERRORTOKEN; + } do { c = tok_nextc(tok); } while (c == '0' || c == '1'); diff --git a/Python/modsupport.c b/Python/modsupport.c index 68e1fa4..3d90ede 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -696,11 +696,23 @@ PyModule_AddObject(PyObject *m, const char *name, PyObject *o) int PyModule_AddIntConstant(PyObject *m, const char *name, long value) { - return PyModule_AddObject(m, name, PyLong_FromLong(value)); + PyObject *o = PyLong_FromLong(value); + if (!o) + return -1; + if (PyModule_AddObject(m, name, o) == 0) + return 0; + Py_DECREF(o); + return -1; } int PyModule_AddStringConstant(PyObject *m, const char *name, const char *value) { - return PyModule_AddObject(m, name, PyUnicode_FromString(value)); + PyObject *o = PyUnicode_FromString(value); + if (!o) + return -1; + if (PyModule_AddObject(m, name, o) == 0) + return 0; + Py_DECREF(o); + return -1; } diff --git a/Python/mystrtoul.c b/Python/mystrtoul.c index cf23051..c26111a 100644 --- a/Python/mystrtoul.c +++ b/Python/mystrtoul.c @@ -116,12 +116,30 @@ PyOS_strtoul(register char *str, char **ptr, int base) if (*str == '0') { ++str; if (*str == 'x' || *str == 'X') { + /* there must be at least one digit after 0x */ + if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 16) { + if (ptr) + *ptr = str; + return 0; + } ++str; base = 16; } else if (*str == 'o' || *str == 'O') { + /* there must be at least one digit after 0o */ + if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 8) { + if (ptr) + *ptr = str; + return 0; + } ++str; base = 8; } else if (*str == 'b' || *str == 'B') { + /* there must be at least one digit after 0b */ + if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 2) { + if (ptr) + *ptr = str; + return 0; + } ++str; base = 2; } else { @@ -143,22 +161,43 @@ PyOS_strtoul(register char *str, char **ptr, int base) case 16: if (*str == '0') { ++str; - if (*str == 'x' || *str == 'X') + if (*str == 'x' || *str == 'X') { + /* there must be at least one digit after 0x */ + if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 16) { + if (ptr) + *ptr = str; + return 0; + } ++str; + } } break; case 8: if (*str == '0') { ++str; - if (*str == 'o' || *str == 'O') + if (*str == 'o' || *str == 'O') { + /* there must be at least one digit after 0o */ + if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 8) { + if (ptr) + *ptr = str; + return 0; + } ++str; + } } break; case 2: if(*str == '0') { ++str; - if (*str == 'b' || *str == 'B') + if (*str == 'b' || *str == 'B') { + /* there must be at least one digit after 0b */ + if (_PyLong_DigitValue[Py_CHARMASK(str[1])] >= 2) { + if (ptr) + *ptr = str; + return 0; + } ++str; + } } break; } |