| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | # | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | # Module which deals with pickling of objects. | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | # | 
					
						
							|  |  |  | # multiprocessing/reduction.py | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2010-12-14 01:38:16 +00:00
										 |  |  | # Copyright (c) 2006-2008, R Oudkerk | 
					
						
							| 
									
										
										
										
											2012-04-30 12:13:55 +01:00
										 |  |  | # Licensed to PSF under a Contributor Agreement. | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 16:05:26 +02:00
										 |  |  | from abc import ABCMeta | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | import copyreg | 
					
						
							|  |  |  | import functools | 
					
						
							|  |  |  | import io | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | import pickle | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | import socket | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-16 16:41:56 +01:00
										 |  |  | from . import context | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | __all__ = ['send_handle', 'recv_handle', 'ForkingPickler', 'register', 'dump'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | HAVE_SEND_HANDLE = (sys.platform == 'win32' or | 
					
						
							|  |  |  |                     (hasattr(socket, 'CMSG_LEN') and | 
					
						
							|  |  |  |                      hasattr(socket, 'SCM_RIGHTS') and | 
					
						
							|  |  |  |                      hasattr(socket.socket, 'sendmsg'))) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | # Pickler subclass | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | class ForkingPickler(pickle.Pickler): | 
					
						
							|  |  |  |     '''Pickler subclass used by multiprocessing.''' | 
					
						
							|  |  |  |     _extra_reducers = {} | 
					
						
							|  |  |  |     _copyreg_dispatch_table = copyreg.dispatch_table | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, *args): | 
					
						
							|  |  |  |         super().__init__(*args) | 
					
						
							|  |  |  |         self.dispatch_table = self._copyreg_dispatch_table.copy() | 
					
						
							|  |  |  |         self.dispatch_table.update(self._extra_reducers) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def register(cls, type, reduce): | 
					
						
							|  |  |  |         '''Register a reduce function for a type.''' | 
					
						
							|  |  |  |         cls._extra_reducers[type] = reduce | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def dumps(cls, obj, protocol=None): | 
					
						
							|  |  |  |         buf = io.BytesIO() | 
					
						
							|  |  |  |         cls(buf, protocol).dump(obj) | 
					
						
							|  |  |  |         return buf.getbuffer() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     loads = pickle.loads | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | register = ForkingPickler.register | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def dump(obj, file, protocol=None): | 
					
						
							|  |  |  |     '''Replacement for pickle.dump() using ForkingPickler.''' | 
					
						
							|  |  |  |     ForkingPickler(file, protocol).dump(obj) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Platform specific definitions | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if sys.platform == 'win32': | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |     # Windows | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |     __all__ += ['DupHandle', 'duplicate', 'steal_handle'] | 
					
						
							| 
									
										
										
										
											2012-04-18 20:51:15 +02:00
										 |  |  |     import _winapi | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-27 11:40:24 +02:00
										 |  |  |     def duplicate(handle, target_process=None, inheritable=False, | 
					
						
							|  |  |  |                   *, source_process=None): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         '''Duplicate a handle.  (target_process is a handle not a pid!)''' | 
					
						
							| 
									
										
										
										
											2018-06-27 11:40:24 +02:00
										 |  |  |         current_process = _winapi.GetCurrentProcess() | 
					
						
							|  |  |  |         if source_process is None: | 
					
						
							|  |  |  |             source_process = current_process | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         if target_process is None: | 
					
						
							| 
									
										
										
										
											2018-06-27 11:40:24 +02:00
										 |  |  |             target_process = current_process | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         return _winapi.DuplicateHandle( | 
					
						
							| 
									
										
										
										
											2018-06-27 11:40:24 +02:00
										 |  |  |             source_process, handle, target_process, | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |             0, inheritable, _winapi.DUPLICATE_SAME_ACCESS) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def steal_handle(source_pid, handle): | 
					
						
							|  |  |  |         '''Steal a handle from process identified by source_pid.''' | 
					
						
							|  |  |  |         source_process_handle = _winapi.OpenProcess( | 
					
						
							|  |  |  |             _winapi.PROCESS_DUP_HANDLE, False, source_pid) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return _winapi.DuplicateHandle( | 
					
						
							|  |  |  |                 source_process_handle, handle, | 
					
						
							|  |  |  |                 _winapi.GetCurrentProcess(), 0, False, | 
					
						
							|  |  |  |                 _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE) | 
					
						
							|  |  |  |         finally: | 
					
						
							|  |  |  |             _winapi.CloseHandle(source_process_handle) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  |     def send_handle(conn, handle, destination_pid): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         '''Send a handle over a local connection.''' | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |         dh = DupHandle(handle, _winapi.DUPLICATE_SAME_ACCESS, destination_pid) | 
					
						
							|  |  |  |         conn.send(dh) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def recv_handle(conn): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         '''Receive a handle over a local connection.''' | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |         return conn.recv().detach() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class DupHandle(object): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         '''Picklable wrapper for a handle.''' | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |         def __init__(self, handle, access, pid=None): | 
					
						
							|  |  |  |             if pid is None: | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |                 # We just duplicate the handle in the current process and | 
					
						
							|  |  |  |                 # let the receiving process steal the handle. | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |                 pid = os.getpid() | 
					
						
							|  |  |  |             proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False, pid) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 self._handle = _winapi.DuplicateHandle( | 
					
						
							|  |  |  |                     _winapi.GetCurrentProcess(), | 
					
						
							|  |  |  |                     handle, proc, access, False, 0) | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 _winapi.CloseHandle(proc) | 
					
						
							|  |  |  |             self._access = access | 
					
						
							|  |  |  |             self._pid = pid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         def detach(self): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |             '''Get the handle.  This should only be called once.''' | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |             # retrieve handle from process which currently owns it | 
					
						
							|  |  |  |             if self._pid == os.getpid(): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |                 # The handle has already been duplicated for this process. | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |                 return self._handle | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |             # We must steal the handle from the process whose pid is self._pid. | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |             proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False, | 
					
						
							|  |  |  |                                        self._pid) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 return _winapi.DuplicateHandle( | 
					
						
							|  |  |  |                     proc, self._handle, _winapi.GetCurrentProcess(), | 
					
						
							|  |  |  |                     self._access, False, _winapi.DUPLICATE_CLOSE_SOURCE) | 
					
						
							|  |  |  |             finally: | 
					
						
							|  |  |  |                 _winapi.CloseHandle(proc) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | else: | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  |     # Unix | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |     __all__ += ['DupFd', 'sendfds', 'recvfds'] | 
					
						
							|  |  |  |     import array | 
					
						
							| 
									
										
										
										
											2012-08-16 16:48:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # On MacOSX we should acknowledge receipt of fds -- see Issue14669 | 
					
						
							|  |  |  |     ACKNOWLEDGE = sys.platform == 'darwin' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |     def sendfds(sock, fds): | 
					
						
							|  |  |  |         '''Send an array of fds over an AF_UNIX socket.''' | 
					
						
							|  |  |  |         fds = array.array('i', fds) | 
					
						
							|  |  |  |         msg = bytes([len(fds) % 256]) | 
					
						
							|  |  |  |         sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, fds)]) | 
					
						
							|  |  |  |         if ACKNOWLEDGE and sock.recv(1) != b'A': | 
					
						
							|  |  |  |             raise RuntimeError('did not receive acknowledgement of fd') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def recvfds(sock, size): | 
					
						
							|  |  |  |         '''Receive an array of fds over an AF_UNIX socket.''' | 
					
						
							|  |  |  |         a = array.array('i') | 
					
						
							|  |  |  |         bytes_size = a.itemsize * size | 
					
						
							| 
									
										
										
										
											2018-09-28 10:51:05 +01:00
										 |  |  |         msg, ancdata, flags, addr = sock.recvmsg(1, socket.CMSG_SPACE(bytes_size)) | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         if not msg and not ancdata: | 
					
						
							|  |  |  |             raise EOFError | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if ACKNOWLEDGE: | 
					
						
							|  |  |  |                 sock.send(b'A') | 
					
						
							|  |  |  |             if len(ancdata) != 1: | 
					
						
							|  |  |  |                 raise RuntimeError('received %d items of ancdata' % | 
					
						
							|  |  |  |                                    len(ancdata)) | 
					
						
							|  |  |  |             cmsg_level, cmsg_type, cmsg_data = ancdata[0] | 
					
						
							|  |  |  |             if (cmsg_level == socket.SOL_SOCKET and | 
					
						
							|  |  |  |                 cmsg_type == socket.SCM_RIGHTS): | 
					
						
							|  |  |  |                 if len(cmsg_data) % a.itemsize != 0: | 
					
						
							|  |  |  |                     raise ValueError | 
					
						
							|  |  |  |                 a.frombytes(cmsg_data) | 
					
						
							| 
									
										
										
										
											2017-08-29 17:52:18 -05:00
										 |  |  |                 if len(a) % 256 != msg[0]: | 
					
						
							|  |  |  |                     raise AssertionError( | 
					
						
							|  |  |  |                         "Len is {0:n} but msg[0] is {1!r}".format( | 
					
						
							|  |  |  |                             len(a), msg[0])) | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |                 return list(a) | 
					
						
							|  |  |  |         except (ValueError, IndexError): | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         raise RuntimeError('Invalid data received') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  |     def send_handle(conn, handle, destination_pid): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         '''Send a handle over a local connection.''' | 
					
						
							| 
									
										
										
										
											2011-09-24 20:04:29 +02:00
										 |  |  |         with socket.fromfd(conn.fileno(), socket.AF_UNIX, socket.SOCK_STREAM) as s: | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |             sendfds(s, [handle]) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def recv_handle(conn): | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         '''Receive a handle over a local connection.''' | 
					
						
							| 
									
										
										
										
											2011-09-24 20:04:29 +02:00
										 |  |  |         with socket.fromfd(conn.fileno(), socket.AF_UNIX, socket.SOCK_STREAM) as s: | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |             return recvfds(s, 1)[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def DupFd(fd): | 
					
						
							|  |  |  |         '''Return a wrapper for an fd.''' | 
					
						
							| 
									
										
										
										
											2013-10-16 16:41:56 +01:00
										 |  |  |         popen_obj = context.get_spawning_popen() | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  |         if popen_obj is not None: | 
					
						
							|  |  |  |             return popen_obj.DupFd(popen_obj.duplicate_for_child(fd)) | 
					
						
							|  |  |  |         elif HAVE_SEND_HANDLE: | 
					
						
							|  |  |  |             from . import resource_sharer | 
					
						
							|  |  |  |             return resource_sharer.DupFd(fd) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ValueError('SCM_RIGHTS appears not to be available') | 
					
						
							| 
									
										
										
										
											2012-04-24 22:56:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | # | 
					
						
							|  |  |  | # Try making some callable types picklable | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | def _reduce_method(m): | 
					
						
							|  |  |  |     if m.__self__ is None: | 
					
						
							|  |  |  |         return getattr, (m.__class__, m.__func__.__name__) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return getattr, (m.__self__, m.__func__.__name__) | 
					
						
							|  |  |  | class _C: | 
					
						
							|  |  |  |     def f(self): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | register(type(_C().f), _reduce_method) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | def _reduce_method_descriptor(m): | 
					
						
							|  |  |  |     return getattr, (m.__objclass__, m.__name__) | 
					
						
							|  |  |  | register(type(list.append), _reduce_method_descriptor) | 
					
						
							|  |  |  | register(type(int.__add__), _reduce_method_descriptor) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | def _reduce_partial(p): | 
					
						
							|  |  |  |     return _rebuild_partial, (p.func, p.args, p.keywords or {}) | 
					
						
							|  |  |  | def _rebuild_partial(func, args, keywords): | 
					
						
							|  |  |  |     return functools.partial(func, *args, **keywords) | 
					
						
							|  |  |  | register(functools.partial, _reduce_partial) | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | # Make sockets picklable | 
					
						
							| 
									
										
										
										
											2008-06-11 16:44:04 +00:00
										 |  |  | # | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-14 15:35:41 +01:00
										 |  |  | if sys.platform == 'win32': | 
					
						
							|  |  |  |     def _reduce_socket(s): | 
					
						
							|  |  |  |         from .resource_sharer import DupSocket | 
					
						
							|  |  |  |         return _rebuild_socket, (DupSocket(s),) | 
					
						
							|  |  |  |     def _rebuild_socket(ds): | 
					
						
							|  |  |  |         return ds.detach() | 
					
						
							|  |  |  |     register(socket.socket, _reduce_socket) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | else: | 
					
						
							|  |  |  |     def _reduce_socket(s): | 
					
						
							|  |  |  |         df = DupFd(s.fileno()) | 
					
						
							|  |  |  |         return _rebuild_socket, (df, s.family, s.type, s.proto) | 
					
						
							|  |  |  |     def _rebuild_socket(df, family, type, proto): | 
					
						
							|  |  |  |         fd = df.detach() | 
					
						
							|  |  |  |         return socket.socket(family, type, proto, fileno=fd) | 
					
						
							|  |  |  |     register(socket.socket, _reduce_socket) | 
					
						
							| 
									
										
										
										
											2016-09-09 18:03:10 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AbstractReducer(metaclass=ABCMeta): | 
					
						
							|  |  |  |     '''Abstract base class for use in implementing a Reduction class
 | 
					
						
							|  |  |  |     suitable for use in replacing the standard reduction mechanism | 
					
						
							|  |  |  |     used in multiprocessing.'''
 | 
					
						
							|  |  |  |     ForkingPickler = ForkingPickler | 
					
						
							|  |  |  |     register = register | 
					
						
							|  |  |  |     dump = dump | 
					
						
							|  |  |  |     send_handle = send_handle | 
					
						
							|  |  |  |     recv_handle = recv_handle | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if sys.platform == 'win32': | 
					
						
							|  |  |  |         steal_handle = steal_handle | 
					
						
							|  |  |  |         duplicate = duplicate | 
					
						
							|  |  |  |         DupHandle = DupHandle | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         sendfds = sendfds | 
					
						
							|  |  |  |         recvfds = recvfds | 
					
						
							|  |  |  |         DupFd = DupFd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     _reduce_method = _reduce_method | 
					
						
							|  |  |  |     _reduce_method_descriptor = _reduce_method_descriptor | 
					
						
							|  |  |  |     _rebuild_partial = _rebuild_partial | 
					
						
							|  |  |  |     _reduce_socket = _reduce_socket | 
					
						
							|  |  |  |     _rebuild_socket = _rebuild_socket | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, *args): | 
					
						
							|  |  |  |         register(type(_C().f), _reduce_method) | 
					
						
							|  |  |  |         register(type(list.append), _reduce_method_descriptor) | 
					
						
							|  |  |  |         register(type(int.__add__), _reduce_method_descriptor) | 
					
						
							|  |  |  |         register(functools.partial, _reduce_partial) | 
					
						
							|  |  |  |         register(socket.socket, _reduce_socket) |