gooderp18绿色标准版
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

186 行
6.4KB

  1. import zeep
  2. from decimal import Decimal
  3. from datetime import date, datetime, timedelta
  4. from requests import Response
  5. from types import SimpleNamespace, FunctionType
  6. TIMEOUT = 30
  7. SERIALIZABLE_TYPES = (
  8. type(None), bool, int, float, str, bytes, tuple, list, dict, Decimal, date, datetime, timedelta, Response
  9. )
  10. class Client:
  11. """A wrapper for Zeep.Client
  12. * providing a simpler API to pass timeouts and session,
  13. * restricting its attributes to a few, most-commonly used accross Odoo's modules,
  14. * serializing the returned values of its methods.
  15. """
  16. def __init__(self, *args, **kwargs):
  17. transport = kwargs.setdefault('transport', zeep.Transport())
  18. # The timeout for loading wsdl and xsd documents.
  19. transport.load_timeout = kwargs.pop('timeout', None) or transport.load_timeout or TIMEOUT
  20. # The timeout for operations (POST/GET)
  21. transport.operation_timeout = kwargs.pop('operation_timeout', None) or transport.operation_timeout or TIMEOUT
  22. # The `requests.session` used for HTTP requests
  23. transport.session = kwargs.pop('session', None) or transport.session
  24. client = zeep.Client(*args, **kwargs)
  25. self.__obj = client
  26. self.__service = None
  27. @classmethod
  28. def __serialize_object(cls, obj):
  29. if isinstance(obj, list):
  30. return [cls.__serialize_object(sub) for sub in obj]
  31. if isinstance(obj, (dict, zeep.xsd.valueobjects.CompoundValue)):
  32. result = SerialProxy(**{key: cls.__serialize_object(obj[key]) for key in obj})
  33. return result
  34. if type(obj) in SERIALIZABLE_TYPES:
  35. return obj
  36. raise ValueError(f'{obj} is not serializable')
  37. @classmethod
  38. def __serialize_object_wrapper(cls, method):
  39. def wrapper(*args, **kwargs):
  40. return cls.__serialize_object(method(*args, **kwargs))
  41. return wrapper
  42. @property
  43. def service(self):
  44. if not self.__service:
  45. self.__service = ReadOnlyMethodNamespace(**{
  46. key: self.__serialize_object_wrapper(operation)
  47. for key, operation in self.__obj.service._operations.items()
  48. })
  49. return self.__service
  50. def type_factory(self, namespace):
  51. types = self.__obj.wsdl.types
  52. namespace = namespace if namespace in types.namespaces else types.get_ns_prefix(namespace)
  53. documents = types.documents.get_by_namespace(namespace, fail_silently=True)
  54. types = {
  55. key[len(f'{{{namespace}}}'):]: type_
  56. for document in documents
  57. for key, type_ in document._types.items()
  58. }
  59. return ReadOnlyMethodNamespace(**{key: self.__serialize_object_wrapper(type_) for key, type_ in types.items()})
  60. def get_type(self, name):
  61. return self.__serialize_object_wrapper(self.__obj.wsdl.types.get_type(name))
  62. def create_service(self, binding_name, address):
  63. service = self.__obj.create_service(binding_name, address)
  64. return ReadOnlyMethodNamespace(**{
  65. key: self.__serialize_object_wrapper(operation)
  66. for key, operation in service._operations.items()
  67. })
  68. def bind(self, service_name, port_name):
  69. service = self.__obj.bind(service_name, port_name)
  70. operations = {
  71. key: self.__serialize_object_wrapper(operation)
  72. for key, operation in service._operations.items()
  73. }
  74. operations['_binding_options'] = service._binding_options
  75. return ReadOnlyMethodNamespace(**operations)
  76. class ReadOnlyMethodNamespace(SimpleNamespace):
  77. """A read-only attribute-based namespace not prefixed by `_` and restricted to functions.
  78. By default, `types.SympleNamespace` doesn't implement `__setitem__` and `__delitem__`,
  79. no need to implement them to ensure the read-only property of this class.
  80. """
  81. def __init__(self, **kwargs):
  82. assert all(
  83. (not key.startswith('_') and isinstance(value, FunctionType))
  84. or
  85. (key == '_binding_options' and isinstance(value, dict))
  86. for key, value in kwargs.items()
  87. )
  88. super().__init__(**kwargs)
  89. def __getitem__(self, key):
  90. return self.__dict__[key]
  91. def __setattr__(self, key, value):
  92. raise NotImplementedError
  93. def __delattr__(self, key):
  94. raise NotImplementedError
  95. class SerialProxy(SimpleNamespace):
  96. """An attribute-based namespace not prefixed by `_` and restricted to few types.
  97. It pretends to be a zeep `CompoundValue` so zeep.helpers.serialize_object threats it as such.
  98. `__getitem__` and `__delitem__` are supported, but `__setitem__` is prevented,
  99. e.g.
  100. ```py
  101. proxy = SerialProxy(foo='foo')
  102. proxy.foo # Allowed
  103. proxy['foo'] # Allowed
  104. proxy.foo = 'bar' # Allowed
  105. proxy['foo'] = 'bar' # Prevented
  106. del proxy.foo # Allowed
  107. del proxy['foo'] # Allowed
  108. ```
  109. """
  110. # Pretend to be a CompoundValue so zeep can serialize this when sending a request with this object in the payload
  111. # https://stackoverflow.com/a/42958013
  112. # https://github.com/mvantellingen/python-zeep/blob/a65b4363c48b5c3f687b8df570bcbada8ba66b9b/src/zeep/helpers.py#L15
  113. @property
  114. def __class__(self):
  115. return zeep.xsd.valueobjects.CompoundValue
  116. def __init__(self, **kwargs):
  117. for key, value in kwargs.items():
  118. self.__check(key, value)
  119. super().__init__(**kwargs)
  120. def __setattr__(self, key, value):
  121. self.__check(key, value)
  122. return super().__setattr__(key, value)
  123. def __getitem__(self, key):
  124. self.__check(key, None)
  125. return self.__getattribute__(key)
  126. # Not required as SimpleNamespace doesn't implement it by default, but this makes it explicit.
  127. def __setitem__(self, key, value):
  128. raise NotImplementedError
  129. def __delitem__(self, key):
  130. self.__check(key, None)
  131. self.__delattr__(key)
  132. def __iter__(self):
  133. return iter(self.__dict__)
  134. def __repr__(self):
  135. return repr(self.__dict__)
  136. def __str__(self):
  137. return str(self.__dict__)
  138. def keys(self):
  139. return self.__dict__.keys()
  140. def values(self):
  141. return self.__dict__.values()
  142. def items(self):
  143. return self.__dict__.items()
  144. @classmethod
  145. def __check(cls, key, value):
  146. assert not key.startswith('_') or key.startswith('_value_')
  147. assert type(value) in SERIALIZABLE_TYPES + (SerialProxy,)
上海开阖软件有限公司 沪ICP备12045867号-1