summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/itertools.rst10
-rw-r--r--Include/object.h3
-rw-r--r--Lib/abc.py47
-rw-r--r--Lib/decimal.py9
-rw-r--r--Lib/test/test_descrtut.py1
-rw-r--r--Lib/test/test_socketserver.py217
-rw-r--r--Lib/threading.py39
-rw-r--r--Objects/typeobject.c98
8 files changed, 248 insertions, 176 deletions
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 826fa45..73abb89 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -327,7 +327,7 @@ loops that truncate the stream.
example :func:`islice` or :func:`takewhile`).
-.. function:: product(*iterables)
+.. function:: product(*iterables[, repeat])
Cartesian product of input iterables.
@@ -340,11 +340,15 @@ loops that truncate the stream.
so that if the inputs iterables are sorted, the product tuples are emitted
in sorted order.
+ To compute the product of an iterable with itself, specify the number of
+ repetitions with the optional *repeat* keyword argument. For example,
+ ``product(A, repeat=4)`` means the same as ``product(A, A, A, A)``.
+
Equivalent to the following except that the actual implementation does not
build-up intermediate results in memory::
- def product(*args):
- pools = map(tuple, args)
+ def product(*args, **kwds):
+ pools = map(tuple, args) * kwds.get('repeat', 1)
if pools:
result = [[]]
for pool in pools:
diff --git a/Include/object.h b/Include/object.h
index a2aadb6..b4b9ce2 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -532,6 +532,9 @@ given type object has a specified feature.
#define Py_TPFLAGS_HAVE_VERSION_TAG (1L<<18)
#define Py_TPFLAGS_VALID_VERSION_TAG (1L<<19)
+/* Type is abstract and cannot be instantiated */
+#define Py_TPFLAGS_IS_ABSTRACT (1L<<20)
+
/* These flags are used to determine if a type is a subclass. */
#define Py_TPFLAGS_INT_SUBCLASS (1L<<23)
#define Py_TPFLAGS_LONG_SUBCLASS (1L<<24)
diff --git a/Lib/abc.py b/Lib/abc.py
index d86b6ea..05ac7e4 100644
--- a/Lib/abc.py
+++ b/Lib/abc.py
@@ -52,50 +52,6 @@ class abstractproperty(property):
__isabstractmethod__ = True
-class _Abstract(object):
-
- """Helper class inserted into the bases by ABCMeta (using _fix_bases()).
-
- You should never need to explicitly subclass this class.
- """
-
- def __new__(cls, *args, **kwds):
- am = cls.__dict__.get("__abstractmethods__")
- if am:
- raise TypeError("Can't instantiate abstract class %s "
- "with abstract methods %s" %
- (cls.__name__, ", ".join(sorted(am))))
- if (args or kwds) and cls.__init__ is object.__init__:
- raise TypeError("Can't pass arguments to __new__ "
- "without overriding __init__")
- return super().__new__(cls)
-
- @classmethod
- def __subclasshook__(cls, subclass):
- """Abstract classes can override this to customize issubclass().
-
- This is invoked early on by __subclasscheck__() below. It
- should return True, False or NotImplemented. If it returns
- NotImplemented, the normal algorithm is used. Otherwise, it
- overrides the normal algorithm (and the outcome is cached).
- """
- return NotImplemented
-
-
-def _fix_bases(bases):
- """Helper method that inserts _Abstract in the bases if needed."""
- for base in bases:
- if issubclass(base, _Abstract):
- # _Abstract is already a base (maybe indirectly)
- return bases
- if object in bases:
- # Replace object with _Abstract
- return tuple([_Abstract if base is object else base
- for base in bases])
- # Append _Abstract to the end
- return bases + (_Abstract,)
-
-
class ABCMeta(type):
"""Metaclass for defining Abstract Base Classes (ABCs).
@@ -118,7 +74,6 @@ class ABCMeta(type):
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace):
- bases = _fix_bases(bases)
cls = super().__new__(mcls, name, bases, namespace)
# Compute set of abstract method names
abstracts = {name
@@ -129,7 +84,7 @@ class ABCMeta(type):
value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
- cls.__abstractmethods__ = abstracts
+ cls.__abstractmethods__ = frozenset(abstracts)
# Set up inheritance registry
cls._abc_registry = WeakSet()
cls._abc_cache = WeakSet()
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 2e89d29..6706402 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -1649,6 +1649,9 @@ class Decimal(_numbers.Real, _numbers.Inexact):
else:
return -1
+ def __round__(self):
+ return self._round_down(0)
+
def _round_up(self, prec):
"""Rounds away from 0."""
return -self._round_down(prec)
@@ -1684,6 +1687,9 @@ class Decimal(_numbers.Real, _numbers.Inexact):
else:
return -self._round_down(prec)
+ def __ceil__(self):
+ return self._round_ceiling(0)
+
def _round_floor(self, prec):
"""Rounds down (not towards 0 if negative)"""
if not self._sign:
@@ -1691,6 +1697,9 @@ class Decimal(_numbers.Real, _numbers.Inexact):
else:
return -self._round_down(prec)
+ def __floor__(self):
+ return self._round_floor(0)
+
def _round_05up(self, prec):
"""Round down unless digit prec-1 is 0 or 5."""
if prec and self._int[prec-1] not in '05':
diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py
index 5ce2119..4933c9f 100644
--- a/Lib/test/test_descrtut.py
+++ b/Lib/test/test_descrtut.py
@@ -196,6 +196,7 @@ You can get the information from the list type:
'__setattr__',
'__setitem__',
'__str__',
+ '__subclasshook__',
'append',
'count',
'extend',
diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py
index 69f0b53..25734fd 100644
--- a/Lib/test/test_socketserver.py
+++ b/Lib/test/test_socketserver.py
@@ -2,13 +2,15 @@
Test suite for SocketServer.py.
"""
-import os
-import socket
import errno
import imp
+import os
import select
-import time
+import signal
+import socket
+import tempfile
import threading
+import time
import unittest
import SocketServer
@@ -19,7 +21,6 @@ from test.test_support import TESTFN as TEST_FILE
test.test_support.requires("network")
NREQ = 3
-DELAY = 0.5
TEST_STR = b"hello world\n"
HOST = "localhost"
@@ -27,14 +28,6 @@ HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX")
HAVE_FORKING = hasattr(os, "fork") and os.name != "os2"
-class MyMixinHandler:
- def handle(self):
- time.sleep(DELAY)
- line = self.rfile.readline()
- time.sleep(DELAY)
- self.wfile.write(line)
-
-
def receive(sock, n, timeout=20):
r, w, x = select.select([sock], [], [], timeout)
if sock in r:
@@ -42,14 +35,6 @@ def receive(sock, n, timeout=20):
else:
raise RuntimeError("timed out on %r" % (sock,))
-
-class MyStreamHandler(MyMixinHandler, SocketServer.StreamRequestHandler):
- pass
-
-class MyDatagramHandler(MyMixinHandler,
- SocketServer.DatagramRequestHandler):
- pass
-
if HAVE_UNIX_SOCKETS:
class ForkingUnixStreamServer(SocketServer.ForkingMixIn,
SocketServer.UnixStreamServer):
@@ -111,47 +96,28 @@ class ServerThread(threading.Thread):
pass
if verbose: print("thread: creating server")
svr = svrcls(self.__addr, self.__hdlrcls)
- # pull the address out of the server in case it changed
- # this can happen if another process is using the port
- addr = svr.server_address
- if addr:
- self.__addr = addr
- if self.__addr != svr.socket.getsockname():
- raise RuntimeError('server_address was %s, expected %s' %
- (self.__addr, svr.socket.getsockname()))
+ # We had the OS pick a port, so pull the real address out of
+ # the server.
+ self.addr = svr.server_address
+ self.port = self.addr[1]
+ if self.addr != svr.socket.getsockname():
+ raise RuntimeError('server_address was %s, expected %s' %
+ (self.addr, svr.socket.getsockname()))
self.ready.set()
if verbose: print("thread: serving three times")
svr.serve_a_few()
if verbose: print("thread: done")
-class ForgivingTCPServer(SocketServer.TCPServer):
- # prevent errors if another process is using the port we want
- def server_bind(self):
- host, default_port = self.server_address
- # this code shamelessly stolen from test.test_support
- # the ports were changed to protect the innocent
- import sys
- for port in [default_port, 3434, 8798, 23833]:
- try:
- self.server_address = host, port
- SocketServer.TCPServer.server_bind(self)
- break
- except socket.error as e:
- (err, msg) = e
- if err != errno.EADDRINUSE:
- raise
- print(' WARNING: failed to listen on port %d, trying another' % port, file=sys.__stderr__)
-
class SocketServerTest(unittest.TestCase):
"""Test all socket servers."""
def setUp(self):
+ signal.alarm(20) # Kill deadlocks after 20 seconds.
self.port_seed = 0
self.test_files = []
def tearDown(self):
- time.sleep(DELAY)
reap_children()
for fn in self.test_files:
@@ -160,16 +126,18 @@ class SocketServerTest(unittest.TestCase):
except os.error:
pass
self.test_files[:] = []
-
- def pickport(self):
- self.port_seed += 1
- return 10000 + (os.getpid() % 1000)*10 + self.port_seed
+ signal.alarm(0) # Didn't deadlock.
def pickaddr(self, proto):
if proto == socket.AF_INET:
- return (HOST, self.pickport())
+ return (HOST, 0)
else:
- fn = TEST_FILE + str(self.pickport())
+ # XXX: We need a way to tell AF_UNIX to pick its own name
+ # like AF_INET provides port==0.
+ dir = None
+ if os.name == 'os2':
+ dir = '\socket'
+ fn = tempfile.mktemp(prefix='unix_socket.', dir=dir)
if os.name == 'os2':
# AF_UNIX socket names on OS/2 require a specific prefix
# which can't include a drive letter and must also use
@@ -178,7 +146,6 @@ class SocketServerTest(unittest.TestCase):
fn = fn[2:]
if fn[0] in (os.sep, os.altsep):
fn = fn[1:]
- fn = os.path.join('\socket', fn)
if os.sep == '/':
fn = fn.replace(os.sep, os.altsep)
else:
@@ -186,25 +153,31 @@ class SocketServerTest(unittest.TestCase):
self.test_files.append(fn)
return fn
- def run_servers(self, proto, servers, hdlrcls, testfunc):
- for svrcls in servers:
- addr = self.pickaddr(proto)
- if verbose:
- print("ADDR =", addr)
- print("CLASS =", svrcls)
- t = ServerThread(addr, svrcls, hdlrcls)
- if verbose: print("server created")
- t.start()
- if verbose: print("server running")
- for i in range(NREQ):
- t.ready.wait(10*DELAY)
- self.assert_(t.ready.isSet(),
- "Server not ready within a reasonable time")
- if verbose: print("test client", i)
- testfunc(proto, addr)
- if verbose: print("waiting for server")
- t.join()
- if verbose: print("done")
+
+ def run_server(self, svrcls, hdlrbase, testfunc):
+ class MyHandler(hdlrbase):
+ def handle(self):
+ line = self.rfile.readline()
+ self.wfile.write(line)
+
+ addr = self.pickaddr(svrcls.address_family)
+ if verbose:
+ print("ADDR =", addr)
+ print("CLASS =", svrcls)
+ t = ServerThread(addr, svrcls, MyHandler)
+ if verbose: print("server created")
+ t.start()
+ if verbose: print("server running")
+ t.ready.wait(10)
+ self.assert_(t.ready.isSet(),
+ "%s not ready within a reasonable time" % svrcls)
+ addr = t.addr
+ for i in range(NREQ):
+ if verbose: print("test client", i)
+ testfunc(svrcls.address_family, addr)
+ if verbose: print("waiting for server")
+ t.join()
+ if verbose: print("done")
def stream_examine(self, proto, addr):
s = socket.socket(proto, socket.SOCK_STREAM)
@@ -227,47 +200,74 @@ class SocketServerTest(unittest.TestCase):
self.assertEquals(buf, TEST_STR)
s.close()
- def test_TCPServers(self):
- # Test SocketServer.TCPServer
- servers = [ForgivingTCPServer, SocketServer.ThreadingTCPServer]
- if HAVE_FORKING:
- servers.append(SocketServer.ForkingTCPServer)
- self.run_servers(socket.AF_INET, servers,
- MyStreamHandler, self.stream_examine)
-
- def test_UDPServers(self):
- # Test SocketServer.UDPServer
- servers = [SocketServer.UDPServer,
- SocketServer.ThreadingUDPServer]
- if HAVE_FORKING:
- servers.append(SocketServer.ForkingUDPServer)
- self.run_servers(socket.AF_INET, servers, MyDatagramHandler,
- self.dgram_examine)
-
- def test_stream_servers(self):
- # Test SocketServer's stream servers
- if not HAVE_UNIX_SOCKETS:
- return
- servers = [SocketServer.UnixStreamServer,
- SocketServer.ThreadingUnixStreamServer]
+ def test_TCPServer(self):
+ self.run_server(SocketServer.TCPServer,
+ SocketServer.StreamRequestHandler,
+ self.stream_examine)
+
+ def test_ThreadingTCPServer(self):
+ self.run_server(SocketServer.ThreadingTCPServer,
+ SocketServer.StreamRequestHandler,
+ self.stream_examine)
+
+ if HAVE_FORKING:
+ def test_ThreadingTCPServer(self):
+ self.run_server(SocketServer.ForkingTCPServer,
+ SocketServer.StreamRequestHandler,
+ self.stream_examine)
+
+ if HAVE_UNIX_SOCKETS:
+ def test_UnixStreamServer(self):
+ self.run_server(SocketServer.UnixStreamServer,
+ SocketServer.StreamRequestHandler,
+ self.stream_examine)
+
+ def test_ThreadingUnixStreamServer(self):
+ self.run_server(SocketServer.ThreadingUnixStreamServer,
+ SocketServer.StreamRequestHandler,
+ self.stream_examine)
+
if HAVE_FORKING:
- servers.append(ForkingUnixStreamServer)
- self.run_servers(socket.AF_UNIX, servers, MyStreamHandler,
- self.stream_examine)
+ def test_ForkingUnixStreamServer(self):
+ self.run_server(ForkingUnixStreamServer,
+ SocketServer.StreamRequestHandler,
+ self.stream_examine)
+
+ def test_UDPServer(self):
+ self.run_server(SocketServer.UDPServer,
+ SocketServer.DatagramRequestHandler,
+ self.dgram_examine)
+
+ def test_ThreadingUDPServer(self):
+ self.run_server(SocketServer.ThreadingUDPServer,
+ SocketServer.DatagramRequestHandler,
+ self.dgram_examine)
+
+ if HAVE_FORKING:
+ def test_ForkingUDPServer(self):
+ self.run_server(SocketServer.ForkingUDPServer,
+ SocketServer.DatagramRequestHandler,
+ self.dgram_examine)
# Alas, on Linux (at least) recvfrom() doesn't return a meaningful
# client address so this cannot work:
- # def test_dgram_servers(self):
- # # Test SocketServer.UnixDatagramServer
- # if not HAVE_UNIX_SOCKETS:
- # return
- # servers = [SocketServer.UnixDatagramServer,
- # SocketServer.ThreadingUnixDatagramServer]
+ # if HAVE_UNIX_SOCKETS:
+ # def test_UnixDatagramServer(self):
+ # self.run_server(SocketServer.UnixDatagramServer,
+ # SocketServer.DatagramRequestHandler,
+ # self.dgram_examine)
+ #
+ # def test_ThreadingUnixDatagramServer(self):
+ # self.run_server(SocketServer.ThreadingUnixDatagramServer,
+ # SocketServer.DatagramRequestHandler,
+ # self.dgram_examine)
+ #
# if HAVE_FORKING:
- # servers.append(ForkingUnixDatagramServer)
- # self.run_servers(socket.AF_UNIX, servers, MyDatagramHandler,
- # self.dgram_examine)
+ # def test_ForkingUnixDatagramServer(self):
+ # self.run_server(SocketServer.ForkingUnixDatagramServer,
+ # SocketServer.DatagramRequestHandler,
+ # self.dgram_examine)
def test_main():
@@ -279,3 +279,4 @@ def test_main():
if __name__ == "__main__":
test_main()
+ signal.alarm(3) # Shutdown shouldn't take more than 3 seconds.
diff --git a/Lib/threading.py b/Lib/threading.py
index c09ec6a..d010b80 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -403,7 +403,7 @@ class Thread(_Verbose):
self._args = args
self._kwargs = kwargs
self._daemonic = self._set_daemon()
- self._started = False
+ self._started = Event()
self._stopped = False
self._block = Condition(Lock())
self._initialized = True
@@ -418,7 +418,7 @@ class Thread(_Verbose):
def __repr__(self):
assert self._initialized, "Thread.__init__() was not called"
status = "initial"
- if self._started:
+ if self._started.isSet():
status = "started"
if self._stopped:
status = "stopped"
@@ -429,7 +429,8 @@ class Thread(_Verbose):
def start(self):
if not self._initialized:
raise RuntimeError("thread.__init__() not called")
- if self._started:
+
+ if self._started.isSet():
raise RuntimeError("thread already started")
if __debug__:
self._note("%s.start(): starting thread", self)
@@ -437,8 +438,7 @@ class Thread(_Verbose):
_limbo[self] = self
_active_limbo_lock.release()
_start_new_thread(self._bootstrap, ())
- self._started = True
- _sleep(0.000001) # 1 usec, to let the thread run (Solaris hack)
+ self._started.wait()
def run(self):
try:
@@ -455,11 +455,11 @@ class Thread(_Verbose):
# happen when a daemon thread wakes up at an unfortunate
# moment, finds the world around it destroyed, and raises some
# random exception *** while trying to report the exception in
- # __bootstrap_inner() below ***. Those random exceptions
+ # _bootstrap_inner() below ***. Those random exceptions
# don't help anybody, and they confuse users, so we suppress
# them. We suppress them only when it appears that the world
# indeed has already been destroyed, so that exceptions in
- # __bootstrap_inner() during normal business hours are properly
+ # _bootstrap_inner() during normal business hours are properly
# reported. Also, we only suppress them for daemonic threads;
# if a non-daemonic encounters this, something else is wrong.
try:
@@ -471,29 +471,29 @@ class Thread(_Verbose):
def _bootstrap_inner(self):
try:
- self._started = True
+ self._started.set()
_active_limbo_lock.acquire()
_active[_get_ident()] = self
del _limbo[self]
_active_limbo_lock.release()
if __debug__:
- self._note("%s.__bootstrap(): thread started", self)
+ self._note("%s._bootstrap(): thread started", self)
if _trace_hook:
- self._note("%s.__bootstrap(): registering trace hook", self)
+ self._note("%s._bootstrap(): registering trace hook", self)
_sys.settrace(_trace_hook)
if _profile_hook:
- self._note("%s.__bootstrap(): registering profile hook", self)
+ self._note("%s._bootstrap(): registering profile hook", self)
_sys.setprofile(_profile_hook)
try:
self.run()
except SystemExit:
if __debug__:
- self._note("%s.__bootstrap(): raised SystemExit", self)
+ self._note("%s._bootstrap(): raised SystemExit", self)
except:
if __debug__:
- self._note("%s.__bootstrap(): unhandled exception", self)
+ self._note("%s._bootstrap(): unhandled exception", self)
# If sys.stderr is no more (most likely from interpreter
# shutdown) use self._stderr. Otherwise still use sys (as in
# _sys) in case sys.stderr was redefined since the creation of
@@ -526,7 +526,7 @@ class Thread(_Verbose):
del exc_type, exc_value, exc_tb
else:
if __debug__:
- self._note("%s.__bootstrap(): normal return", self)
+ self._note("%s._bootstrap(): normal return", self)
finally:
with _active_limbo_lock:
self._stop()
@@ -580,7 +580,7 @@ class Thread(_Verbose):
def join(self, timeout=None):
if not self._initialized:
raise RuntimeError("Thread.__init__() not called")
- if not self._started:
+ if not self._started.isSet():
raise RuntimeError("cannot join thread before it is started")
if self is currentThread():
raise RuntimeError("cannot join current thread")
@@ -621,7 +621,7 @@ class Thread(_Verbose):
def isAlive(self):
assert self._initialized, "Thread.__init__() not called"
- return self._started and not self._stopped
+ return self._started.isSet() and not self._stopped
def isDaemon(self):
assert self._initialized, "Thread.__init__() not called"
@@ -630,7 +630,7 @@ class Thread(_Verbose):
def setDaemon(self, daemonic):
if not self._initialized:
raise RuntimeError("Thread.__init__() not called")
- if self._started:
+ if self._started.isSet():
raise RuntimeError("cannot set daemon status of active thread");
self._daemonic = daemonic
@@ -672,7 +672,7 @@ class _MainThread(Thread):
def __init__(self):
Thread.__init__(self, name="MainThread")
- self._started = True
+ self._started.set()
_active_limbo_lock.acquire()
_active[_get_ident()] = self
_active_limbo_lock.release()
@@ -718,7 +718,8 @@ class _DummyThread(Thread):
# instance is immortal, that's bad, so release this resource.
del self._block
- self._started = True
+
+ self._started.set()
_active_limbo_lock.acquire()
_active[_get_ident()] = self
_active_limbo_lock.release()
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 1d6336c..7a6d258 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -319,6 +319,40 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
}
static PyObject *
+type_abstractmethods(PyTypeObject *type, void *context)
+{
+ PyObject *mod = PyDict_GetItemString(type->tp_dict,
+ "__abstractmethods__");
+ if (!mod) {
+ PyErr_Format(PyExc_AttributeError, "__abstractmethods__");
+ return NULL;
+ }
+ Py_XINCREF(mod);
+ return mod;
+}
+
+static int
+type_set_abstractmethods(PyTypeObject *type, PyObject *value, void *context)
+{
+ /* __abstractmethods__ should only be set once on a type, in
+ abc.ABCMeta.__new__, so this function doesn't do anything
+ special to update subclasses.
+ */
+ int res = PyDict_SetItemString(type->tp_dict,
+ "__abstractmethods__", value);
+ if (res == 0) {
+ type_modified(type);
+ if (value && PyObject_IsTrue(value)) {
+ type->tp_flags |= Py_TPFLAGS_IS_ABSTRACT;
+ }
+ else {
+ type->tp_flags &= ~Py_TPFLAGS_IS_ABSTRACT;
+ }
+ }
+ return res;
+}
+
+static PyObject *
type_get_bases(PyTypeObject *type, void *context)
{
Py_INCREF(type->tp_bases);
@@ -555,6 +589,8 @@ static PyGetSetDef type_getsets[] = {
{"__name__", (getter)type_name, (setter)type_set_name, NULL},
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
{"__module__", (getter)type_module, (setter)type_set_module, NULL},
+ {"__abstractmethods__", (getter)type_abstractmethods,
+ (setter)type_set_abstractmethods, NULL},
{"__dict__", (getter)type_dict, NULL, NULL},
{"__doc__", (getter)type_get_doc, NULL, NULL},
{0}
@@ -2638,6 +2674,52 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
if (err < 0)
return NULL;
+
+ if (type->tp_flags & Py_TPFLAGS_IS_ABSTRACT) {
+ static PyObject *comma = NULL;
+ PyObject *abstract_methods = NULL;
+ PyObject *builtins;
+ PyObject *sorted;
+ PyObject *sorted_methods = NULL;
+ PyObject *joined = NULL;
+
+ /* Compute ", ".join(sorted(type.__abstractmethods__))
+ into joined. */
+ abstract_methods = type_abstractmethods(type, NULL);
+ if (abstract_methods == NULL)
+ goto error;
+ builtins = PyEval_GetBuiltins();
+ if (builtins == NULL)
+ goto error;
+ sorted = PyDict_GetItemString(builtins, "sorted");
+ if (sorted == NULL)
+ goto error;
+ sorted_methods = PyObject_CallFunctionObjArgs(sorted,
+ abstract_methods,
+ NULL);
+ if (sorted_methods == NULL)
+ goto error;
+ if (comma == NULL) {
+ comma = PyUnicode_InternFromString(", ");
+ if (comma == NULL)
+ goto error;
+ }
+ joined = PyObject_CallMethod(comma, "join",
+ "O", sorted_methods);
+ if (joined == NULL)
+ goto error;
+
+ PyErr_Format(PyExc_TypeError,
+ "Can't instantiate abstract class %s "
+ "with abstract methods %U",
+ type->tp_name,
+ joined);
+ error:
+ Py_XDECREF(joined);
+ Py_XDECREF(sorted_methods);
+ Py_XDECREF(abstract_methods);
+ return NULL;
+ }
return type->tp_alloc(type, 0);
}
@@ -3143,6 +3225,20 @@ object_reduce_ex(PyObject *self, PyObject *args)
return _common_reduce(self, proto);
}
+static PyObject *
+object_subclasshook(PyObject *cls, PyObject *args)
+{
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+PyDoc_STRVAR(object_subclasshook_doc,
+"Abstract classes can override this to customize issubclass().\n"
+"\n"
+"This is invoked early on by abc.ABCMeta.__subclasscheck__().\n"
+"It should return True, False or NotImplemented. If it returns\n"
+"NotImplemented, the normal algorithm is used. Otherwise, it\n"
+"overrides the normal algorithm (and the outcome is cached).\n");
/*
from PEP 3101, this code implements:
@@ -3183,6 +3279,8 @@ static PyMethodDef object_methods[] = {
PyDoc_STR("helper for pickle")},
{"__reduce__", object_reduce, METH_VARARGS,
PyDoc_STR("helper for pickle")},
+ {"__subclasshook__", object_subclasshook, METH_CLASS | METH_VARARGS,
+ object_subclasshook_doc},
{"__format__", object_format, METH_VARARGS,
PyDoc_STR("default object formatter")},
{0}