netref.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. """
  2. NetRef - transparent network references implementation.
  3. """
  4. import sys
  5. import inspect
  6. import types
  7. import cPickle as pickle
  8. from rpyc.core import consts
  9. _local_netref_attrs = frozenset([
  10. '____conn__', '____oid__', '__class__', '__cmp__', '__del__', '__delattr__',
  11. '__dir__', '__doc__', '__getattr__', '__getattribute__', '__hash__',
  12. '__init__', '__metaclass__', '__module__', '__new__', '__reduce__',
  13. '__reduce_ex__', '__repr__', '__setattr__', '__slots__', '__str__',
  14. '__weakref__', '__dict__', '__members__', '__methods__',
  15. ])
  16. _builtin_types = [
  17. type, object, types.InstanceType, types.ClassType, bool, complex, dict,
  18. file, float, int, list, long, slice, str, basestring, tuple, unicode,
  19. str, set, frozenset, Exception, types.NoneType, types.DictProxyType,
  20. types.BuiltinFunctionType, types.GeneratorType, types.MethodType,
  21. types.CodeType, types.FrameType, types.TracebackType, xrange,
  22. types.ModuleType, types.FunctionType,
  23. type(int.__add__), # wrapper_descriptor
  24. type((1).__add__), # method-wrapper
  25. type(iter([])), # listiterator
  26. type(iter(())), # tupleiterator
  27. type(iter(xrange(10))), # rangeiterator
  28. type(iter(set())), # setiterator
  29. ]
  30. _normalized_builtin_types = dict(((t.__name__, t.__module__), t)
  31. for t in _builtin_types)
  32. def syncreq(proxy, handler, *args):
  33. """performs a synchronous request on the given proxy object"""
  34. conn = object.__getattribute__(proxy, "____conn__")
  35. oid = object.__getattribute__(proxy, "____oid__")
  36. return conn().sync_request(handler, oid, *args)
  37. def asyncreq(proxy, handler, *args):
  38. """performs an asynchronous request on the given proxy object,
  39. retuning an AsyncResult"""
  40. conn = object.__getattribute__(proxy, "____conn__")
  41. oid = object.__getattribute__(proxy, "____oid__")
  42. return conn().async_request(handler, oid, *args)
  43. class NetrefMetaclass(type):
  44. """a metaclass just to customize the __repr__ of netref classes"""
  45. __slots__ = ()
  46. def __repr__(self):
  47. if self.__module__:
  48. return "<netref class '%s.%s'>" % (self.__module__, self.__name__)
  49. else:
  50. return "<netref class '%s'>" % (self.__name__,)
  51. class BaseNetref(object):
  52. """the base netref object, from which all netref classes derive"""
  53. __metaclass__ = NetrefMetaclass
  54. __slots__ = ["____conn__", "____oid__", "__weakref__"]
  55. def __init__(self, conn, oid):
  56. self.____conn__ = conn
  57. self.____oid__ = oid
  58. def __del__(self):
  59. try:
  60. asyncreq(self, consts.HANDLE_DEL)
  61. except Exception:
  62. # raised in a destructor, most likely on program termination,
  63. # it's safe to ignore all exceptions here
  64. pass
  65. def __getattribute__(self, name):
  66. if name in _local_netref_attrs:
  67. if name == "__class__":
  68. cls = object.__getattribute__(self, "__class__")
  69. if cls is None:
  70. cls = self.__getattr__("__class__")
  71. return cls
  72. elif name == "__doc__":
  73. return self.__getattr__("__doc__")
  74. elif name == "__members__": # for Python < 2.6
  75. return self.__dir__()
  76. else:
  77. return object.__getattribute__(self, name)
  78. elif name == "__call__": # IronPython issue #10
  79. return object.__getattribute__(self, "__call__")
  80. else:
  81. return syncreq(self, consts.HANDLE_GETATTR, name)
  82. def __getattr__(self, name):
  83. return syncreq(self, consts.HANDLE_GETATTR, name)
  84. def __delattr__(self, name):
  85. if name in _local_netref_attrs:
  86. object.__delattr__(self, name)
  87. else:
  88. syncreq(self, consts.HANDLE_DELATTR, name)
  89. def __setattr__(self, name, value):
  90. if name in _local_netref_attrs:
  91. object.__setattr__(self, name, value)
  92. else:
  93. syncreq(self, consts.HANDLE_SETATTR, name, value)
  94. def __dir__(self):
  95. return list(syncreq(self, consts.HANDLE_DIR))
  96. # support for metaclasses
  97. def __hash__(self):
  98. return syncreq(self, consts.HANDLE_HASH)
  99. def __cmp__(self, other):
  100. return syncreq(self, consts.HANDLE_CMP, other)
  101. def __repr__(self):
  102. return syncreq(self, consts.HANDLE_REPR)
  103. def __str__(self):
  104. return syncreq(self, consts.HANDLE_STR)
  105. # support for pickle
  106. def __reduce_ex__(self, proto):
  107. return pickle.loads, (syncreq(self, consts.HANDLE_PICKLE, proto),)
  108. def _make_method(name, doc):
  109. name = str(name) # IronPython issue #10
  110. if name == "__call__":
  111. def __call__(_self, *args, **kwargs):
  112. kwargs = tuple(kwargs.items())
  113. return syncreq(_self, consts.HANDLE_CALL, args, kwargs)
  114. __call__.__doc__ = doc
  115. return __call__
  116. else:
  117. def method(_self, *args, **kwargs):
  118. kwargs = tuple(kwargs.items())
  119. return syncreq(_self, consts.HANDLE_CALLATTR, name, args, kwargs)
  120. method.__name__ = name
  121. method.__doc__ = doc
  122. return method
  123. def inspect_methods(obj):
  124. """returns a list of (method name, docstring) tuples of all the methods of
  125. the given object"""
  126. methods = {}
  127. attrs = {}
  128. if isinstance(obj, type):
  129. # don't forget the darn metaclass
  130. mros = list(reversed(type(obj).__mro__)) + list(reversed(obj.__mro__))
  131. else:
  132. mros = reversed(type(obj).__mro__)
  133. for basecls in mros:
  134. attrs.update(basecls.__dict__)
  135. for name, attr in attrs.iteritems():
  136. if name not in _local_netref_attrs and hasattr(attr, "__call__"):
  137. methods[name] = inspect.getdoc(attr)
  138. return methods.items()
  139. def class_factory(clsname, modname, methods):
  140. clsname = str(clsname) # IronPython issue #10
  141. modname = str(modname) # IronPython issue #10
  142. ns = {"__slots__" : ()}
  143. for name, doc in methods:
  144. name = str(name) # IronPython issue #10
  145. if name not in _local_netref_attrs:
  146. ns[name] = _make_method(name, doc)
  147. ns["__module__"] = modname
  148. if modname in sys.modules and hasattr(sys.modules[modname], clsname):
  149. ns["__class__"] = getattr(sys.modules[modname], clsname)
  150. elif (clsname, modname) in _normalized_builtin_types:
  151. ns["__class__"] = _normalized_builtin_types[clsname, modname]
  152. else:
  153. # to be resolved by the instance
  154. ns["__class__"] = None
  155. return type(clsname, (BaseNetref,), ns)
  156. builtin_classes_cache = {}
  157. for cls in _builtin_types:
  158. builtin_classes_cache[cls.__name__, cls.__module__] = class_factory(
  159. cls.__name__, cls.__module__, inspect_methods(cls))