ucloud-sdk-python3 0.11.81__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. ucloud/__init__.py +0 -0
  2. ucloud/client.py +469 -0
  3. ucloud/core/__init__.py +0 -0
  4. ucloud/core/auth/__init__.py +3 -0
  5. ucloud/core/auth/_cfg.py +72 -0
  6. ucloud/core/client/__init__.py +8 -0
  7. ucloud/core/client/_cfg.py +96 -0
  8. ucloud/core/client/_client.py +176 -0
  9. ucloud/core/exc/__init__.py +9 -0
  10. ucloud/core/exc/_exc.py +94 -0
  11. ucloud/core/transport/__init__.py +4 -0
  12. ucloud/core/transport/_requests.py +135 -0
  13. ucloud/core/transport/http.py +120 -0
  14. ucloud/core/transport/utils.py +40 -0
  15. ucloud/core/typesystem/__init__.py +0 -0
  16. ucloud/core/typesystem/abstract.py +60 -0
  17. ucloud/core/typesystem/encoder.py +33 -0
  18. ucloud/core/typesystem/fields.py +149 -0
  19. ucloud/core/typesystem/schema.py +97 -0
  20. ucloud/core/utils/__init__.py +0 -0
  21. ucloud/core/utils/compat.py +15 -0
  22. ucloud/core/utils/deco.py +31 -0
  23. ucloud/core/utils/log.py +29 -0
  24. ucloud/core/utils/middleware.py +63 -0
  25. ucloud/helpers/__init__.py +0 -0
  26. ucloud/helpers/utils.py +90 -0
  27. ucloud/helpers/wait.py +108 -0
  28. ucloud/services/__init__.py +0 -0
  29. ucloud/services/cube/__init__.py +0 -0
  30. ucloud/services/cube/client.py +618 -0
  31. ucloud/services/cube/schemas/__init__.py +0 -0
  32. ucloud/services/cube/schemas/apis.py +548 -0
  33. ucloud/services/cube/schemas/models.py +58 -0
  34. ucloud/services/iam/__init__.py +0 -0
  35. ucloud/services/iam/client.py +1078 -0
  36. ucloud/services/iam/schemas/__init__.py +0 -0
  37. ucloud/services/iam/schemas/apis.py +973 -0
  38. ucloud/services/iam/schemas/models.py +127 -0
  39. ucloud/services/ipsecvpn/__init__.py +0 -0
  40. ucloud/services/ipsecvpn/client.py +522 -0
  41. ucloud/services/ipsecvpn/schemas/__init__.py +0 -0
  42. ucloud/services/ipsecvpn/schemas/apis.py +455 -0
  43. ucloud/services/ipsecvpn/schemas/models.py +134 -0
  44. ucloud/services/ipv6gw/__init__.py +0 -0
  45. ucloud/services/ipv6gw/client.py +44 -0
  46. ucloud/services/ipv6gw/schemas/__init__.py +0 -0
  47. ucloud/services/ipv6gw/schemas/apis.py +34 -0
  48. ucloud/services/ipv6gw/schemas/models.py +3 -0
  49. ucloud/services/isms/__init__.py +0 -0
  50. ucloud/services/isms/client.py +330 -0
  51. ucloud/services/isms/schemas/__init__.py +0 -0
  52. ucloud/services/isms/schemas/apis.py +272 -0
  53. ucloud/services/isms/schemas/models.py +50 -0
  54. ucloud/services/pathx/__init__.py +0 -0
  55. ucloud/services/pathx/client.py +1656 -0
  56. ucloud/services/pathx/schemas/__init__.py +0 -0
  57. ucloud/services/pathx/schemas/apis.py +1289 -0
  58. ucloud/services/pathx/schemas/models.py +420 -0
  59. ucloud/services/stepflow/__init__.py +0 -0
  60. ucloud/services/stepflow/client.py +98 -0
  61. ucloud/services/stepflow/schemas/__init__.py +0 -0
  62. ucloud/services/stepflow/schemas/apis.py +67 -0
  63. ucloud/services/stepflow/schemas/models.py +37 -0
  64. ucloud/services/sts/__init__.py +0 -0
  65. ucloud/services/sts/client.py +46 -0
  66. ucloud/services/sts/schemas/__init__.py +0 -0
  67. ucloud/services/sts/schemas/apis.py +35 -0
  68. ucloud/services/sts/schemas/models.py +16 -0
  69. ucloud/services/tidb/__init__.py +0 -0
  70. ucloud/services/tidb/client.py +120 -0
  71. ucloud/services/tidb/schemas/__init__.py +0 -0
  72. ucloud/services/tidb/schemas/apis.py +103 -0
  73. ucloud/services/tidb/schemas/models.py +11 -0
  74. ucloud/services/uaaa/__init__.py +0 -0
  75. ucloud/services/uaaa/client.py +311 -0
  76. ucloud/services/uaaa/schemas/__init__.py +0 -0
  77. ucloud/services/uaaa/schemas/apis.py +252 -0
  78. ucloud/services/uaaa/schemas/models.py +47 -0
  79. ucloud/services/uaccount/__init__.py +0 -0
  80. ucloud/services/uaccount/client.py +547 -0
  81. ucloud/services/uaccount/schemas/__init__.py +0 -0
  82. ucloud/services/uaccount/schemas/apis.py +442 -0
  83. ucloud/services/uaccount/schemas/models.py +128 -0
  84. ucloud/services/uads/__init__.py +0 -0
  85. ucloud/services/uads/client.py +1148 -0
  86. ucloud/services/uads/schemas/__init__.py +0 -0
  87. ucloud/services/uads/schemas/apis.py +983 -0
  88. ucloud/services/uads/schemas/models.py +199 -0
  89. ucloud/services/ubill/__init__.py +0 -0
  90. ucloud/services/ubill/client.py +248 -0
  91. ucloud/services/ubill/schemas/__init__.py +0 -0
  92. ucloud/services/ubill/schemas/apis.py +183 -0
  93. ucloud/services/ubill/schemas/models.py +107 -0
  94. ucloud/services/ucdn/__init__.py +0 -0
  95. ucloud/services/ucdn/client.py +1964 -0
  96. ucloud/services/ucdn/schemas/__init__.py +0 -0
  97. ucloud/services/ucdn/schemas/apis.py +1395 -0
  98. ucloud/services/ucdn/schemas/models.py +576 -0
  99. ucloud/services/ucloudstack/__init__.py +0 -0
  100. ucloud/services/ucloudstack/client.py +3352 -0
  101. ucloud/services/ucloudstack/schemas/__init__.py +0 -0
  102. ucloud/services/ucloudstack/schemas/apis.py +2887 -0
  103. ucloud/services/ucloudstack/schemas/models.py +560 -0
  104. ucloud/services/ucompshare/__init__.py +0 -0
  105. ucloud/services/ucompshare/client.py +820 -0
  106. ucloud/services/ucompshare/schemas/__init__.py +0 -0
  107. ucloud/services/ucompshare/schemas/apis.py +623 -0
  108. ucloud/services/ucompshare/schemas/models.py +241 -0
  109. ucloud/services/udb/__init__.py +0 -0
  110. ucloud/services/udb/client.py +2463 -0
  111. ucloud/services/udb/schemas/__init__.py +0 -0
  112. ucloud/services/udb/schemas/apis.py +2053 -0
  113. ucloud/services/udb/schemas/models.py +319 -0
  114. ucloud/services/udbproxy/__init__.py +0 -0
  115. ucloud/services/udbproxy/client.py +67 -0
  116. ucloud/services/udbproxy/schemas/__init__.py +0 -0
  117. ucloud/services/udbproxy/schemas/apis.py +38 -0
  118. ucloud/services/udbproxy/schemas/models.py +31 -0
  119. ucloud/services/uddb/__init__.py +0 -0
  120. ucloud/services/uddb/client.py +456 -0
  121. ucloud/services/uddb/schemas/__init__.py +0 -0
  122. ucloud/services/uddb/schemas/apis.py +520 -0
  123. ucloud/services/uddb/schemas/models.py +96 -0
  124. ucloud/services/udi/__init__.py +0 -0
  125. ucloud/services/udi/client.py +250 -0
  126. ucloud/services/udi/schemas/__init__.py +0 -0
  127. ucloud/services/udi/schemas/apis.py +205 -0
  128. ucloud/services/udi/schemas/models.py +58 -0
  129. ucloud/services/udisk/__init__.py +0 -0
  130. ucloud/services/udisk/client.py +832 -0
  131. ucloud/services/udisk/schemas/__init__.py +0 -0
  132. ucloud/services/udisk/schemas/apis.py +741 -0
  133. ucloud/services/udisk/schemas/models.py +100 -0
  134. ucloud/services/udns/__init__.py +0 -0
  135. ucloud/services/udns/client.py +380 -0
  136. ucloud/services/udns/schemas/__init__.py +0 -0
  137. ucloud/services/udns/schemas/apis.py +293 -0
  138. ucloud/services/udns/schemas/models.py +58 -0
  139. ucloud/services/udpn/__init__.py +0 -0
  140. ucloud/services/udpn/client.py +240 -0
  141. ucloud/services/udpn/schemas/__init__.py +0 -0
  142. ucloud/services/udpn/schemas/apis.py +203 -0
  143. ucloud/services/udpn/schemas/models.py +29 -0
  144. ucloud/services/udts/__init__.py +0 -0
  145. ucloud/services/udts/client.py +410 -0
  146. ucloud/services/udts/schemas/__init__.py +0 -0
  147. ucloud/services/udts/schemas/apis.py +403 -0
  148. ucloud/services/udts/schemas/models.py +93 -0
  149. ucloud/services/uec/__init__.py +0 -0
  150. ucloud/services/uec/client.py +1510 -0
  151. ucloud/services/uec/schemas/__init__.py +0 -0
  152. ucloud/services/uec/schemas/apis.py +1195 -0
  153. ucloud/services/uec/schemas/models.py +316 -0
  154. ucloud/services/ufile/__init__.py +0 -0
  155. ucloud/services/ufile/client.py +698 -0
  156. ucloud/services/ufile/schemas/__init__.py +0 -0
  157. ucloud/services/ufile/schemas/apis.py +542 -0
  158. ucloud/services/ufile/schemas/models.py +139 -0
  159. ucloud/services/ufs/__init__.py +0 -0
  160. ucloud/services/ufs/client.py +328 -0
  161. ucloud/services/ufs/schemas/__init__.py +0 -0
  162. ucloud/services/ufs/schemas/apis.py +265 -0
  163. ucloud/services/ufs/schemas/models.py +52 -0
  164. ucloud/services/ugn/__init__.py +0 -0
  165. ucloud/services/ugn/client.py +857 -0
  166. ucloud/services/ugn/schemas/__init__.py +0 -0
  167. ucloud/services/ugn/schemas/apis.py +678 -0
  168. ucloud/services/ugn/schemas/models.py +191 -0
  169. ucloud/services/uhost/__init__.py +0 -0
  170. ucloud/services/uhost/client.py +1647 -0
  171. ucloud/services/uhost/schemas/__init__.py +0 -0
  172. ucloud/services/uhost/schemas/apis.py +1483 -0
  173. ucloud/services/uhost/schemas/models.py +427 -0
  174. ucloud/services/uhub/__init__.py +0 -0
  175. ucloud/services/uhub/client.py +229 -0
  176. ucloud/services/uhub/schemas/__init__.py +0 -0
  177. ucloud/services/uhub/schemas/apis.py +194 -0
  178. ucloud/services/uhub/schemas/models.py +39 -0
  179. ucloud/services/uk8s/__init__.py +0 -0
  180. ucloud/services/uk8s/client.py +729 -0
  181. ucloud/services/uk8s/schemas/__init__.py +0 -0
  182. ucloud/services/uk8s/schemas/apis.py +639 -0
  183. ucloud/services/uk8s/schemas/models.py +179 -0
  184. ucloud/services/ulb/__init__.py +0 -0
  185. ucloud/services/ulb/client.py +2285 -0
  186. ucloud/services/ulb/schemas/__init__.py +0 -0
  187. ucloud/services/ulb/schemas/apis.py +1678 -0
  188. ucloud/services/ulb/schemas/models.py +591 -0
  189. ucloud/services/ulighthost/__init__.py +0 -0
  190. ucloud/services/ulighthost/client.py +576 -0
  191. ucloud/services/ulighthost/schemas/__init__.py +0 -0
  192. ucloud/services/ulighthost/schemas/apis.py +445 -0
  193. ucloud/services/ulighthost/schemas/models.py +133 -0
  194. ucloud/services/umem/__init__.py +0 -0
  195. ucloud/services/umem/client.py +1829 -0
  196. ucloud/services/umem/schemas/__init__.py +0 -0
  197. ucloud/services/umem/schemas/apis.py +1477 -0
  198. ucloud/services/umem/schemas/models.py +327 -0
  199. ucloud/services/umongodb/__init__.py +0 -0
  200. ucloud/services/umongodb/client.py +752 -0
  201. ucloud/services/umongodb/schemas/__init__.py +0 -0
  202. ucloud/services/umongodb/schemas/apis.py +567 -0
  203. ucloud/services/umongodb/schemas/models.py +220 -0
  204. ucloud/services/unet/__init__.py +0 -0
  205. ucloud/services/unet/client.py +1278 -0
  206. ucloud/services/unet/schemas/__init__.py +0 -0
  207. ucloud/services/unet/schemas/apis.py +1006 -0
  208. ucloud/services/unet/schemas/models.py +275 -0
  209. ucloud/services/unvs/__init__.py +0 -0
  210. ucloud/services/unvs/client.py +87 -0
  211. ucloud/services/unvs/schemas/__init__.py +0 -0
  212. ucloud/services/unvs/schemas/apis.py +66 -0
  213. ucloud/services/unvs/schemas/models.py +19 -0
  214. ucloud/services/upfs/__init__.py +0 -0
  215. ucloud/services/upfs/client.py +252 -0
  216. ucloud/services/upfs/schemas/__init__.py +0 -0
  217. ucloud/services/upfs/schemas/apis.py +204 -0
  218. ucloud/services/upfs/schemas/models.py +36 -0
  219. ucloud/services/upgsql/__init__.py +0 -0
  220. ucloud/services/upgsql/client.py +1007 -0
  221. ucloud/services/upgsql/schemas/__init__.py +0 -0
  222. ucloud/services/upgsql/schemas/apis.py +827 -0
  223. ucloud/services/upgsql/schemas/models.py +158 -0
  224. ucloud/services/uphone/__init__.py +0 -0
  225. ucloud/services/uphone/client.py +2122 -0
  226. ucloud/services/uphone/schemas/__init__.py +0 -0
  227. ucloud/services/uphone/schemas/apis.py +1799 -0
  228. ucloud/services/uphone/schemas/models.py +357 -0
  229. ucloud/services/uphost/__init__.py +0 -0
  230. ucloud/services/uphost/client.py +847 -0
  231. ucloud/services/uphost/schemas/__init__.py +0 -0
  232. ucloud/services/uphost/schemas/apis.py +689 -0
  233. ucloud/services/uphost/schemas/models.py +175 -0
  234. ucloud/services/urocketmq/__init__.py +0 -0
  235. ucloud/services/urocketmq/client.py +117 -0
  236. ucloud/services/urocketmq/schemas/__init__.py +0 -0
  237. ucloud/services/urocketmq/schemas/apis.py +92 -0
  238. ucloud/services/urocketmq/schemas/models.py +14 -0
  239. ucloud/services/uslk/__init__.py +0 -0
  240. ucloud/services/uslk/client.py +249 -0
  241. ucloud/services/uslk/schemas/__init__.py +0 -0
  242. ucloud/services/uslk/schemas/apis.py +191 -0
  243. ucloud/services/uslk/schemas/models.py +74 -0
  244. ucloud/services/usms/__init__.py +0 -0
  245. ucloud/services/usms/client.py +759 -0
  246. ucloud/services/usms/schemas/__init__.py +0 -0
  247. ucloud/services/usms/schemas/apis.py +653 -0
  248. ucloud/services/usms/schemas/models.py +215 -0
  249. ucloud/services/utsdb/__init__.py +0 -0
  250. ucloud/services/utsdb/client.py +604 -0
  251. ucloud/services/utsdb/schemas/__init__.py +0 -0
  252. ucloud/services/utsdb/schemas/apis.py +515 -0
  253. ucloud/services/utsdb/schemas/models.py +61 -0
  254. ucloud/services/uvms/__init__.py +0 -0
  255. ucloud/services/uvms/client.py +119 -0
  256. ucloud/services/uvms/schemas/__init__.py +0 -0
  257. ucloud/services/uvms/schemas/apis.py +88 -0
  258. ucloud/services/uvms/schemas/models.py +40 -0
  259. ucloud/services/vpc/__init__.py +0 -0
  260. ucloud/services/vpc/client.py +3233 -0
  261. ucloud/services/vpc/schemas/__init__.py +0 -0
  262. ucloud/services/vpc/schemas/apis.py +2529 -0
  263. ucloud/services/vpc/schemas/models.py +651 -0
  264. ucloud/testing/__init__.py +0 -0
  265. ucloud/testing/driver/__init__.py +5 -0
  266. ucloud/testing/driver/_scenario.py +93 -0
  267. ucloud/testing/driver/_specification.py +57 -0
  268. ucloud/testing/driver/_step.py +166 -0
  269. ucloud/testing/env.py +28 -0
  270. ucloud/testing/exc.py +18 -0
  271. ucloud/testing/funcs.py +68 -0
  272. ucloud/testing/mock.py +28 -0
  273. ucloud/testing/op.py +177 -0
  274. ucloud/testing/utest.py +195 -0
  275. ucloud/version.py +1 -0
  276. ucloud_sdk_python3-0.11.81.dist-info/LICENSE +202 -0
  277. ucloud_sdk_python3-0.11.81.dist-info/METADATA +71 -0
  278. ucloud_sdk_python3-0.11.81.dist-info/RECORD +280 -0
  279. ucloud_sdk_python3-0.11.81.dist-info/WHEEL +5 -0
  280. ucloud_sdk_python3-0.11.81.dist-info/top_level.txt +1 -0
@@ -0,0 +1,176 @@
1
+ import typing
2
+ import logging
3
+ import sys
4
+ import time
5
+
6
+ from ucloud import version
7
+ from ucloud.core.client._cfg import Config
8
+ from ucloud.core.transport import (
9
+ Transport,
10
+ RequestsTransport,
11
+ Request,
12
+ SSLOption,
13
+ http,
14
+ )
15
+ from ucloud.core.typesystem import encoder
16
+ from ucloud.core.utils import log
17
+ from ucloud.core.utils.middleware import Middleware
18
+ from ucloud.core import auth, exc
19
+
20
+
21
+ class Client:
22
+ def __init__(
23
+ self,
24
+ config: dict,
25
+ transport: typing.Optional[Transport] = None,
26
+ middleware: typing.Optional[Middleware] = None,
27
+ logger: typing.Optional[logging.Logger] = None,
28
+ ):
29
+ cfg, cred = self._parse_dict_config(config)
30
+ self.config = cfg
31
+ self.credential = cred
32
+ self.transport = transport or RequestsTransport()
33
+ self.logger = logger or log.default_logger
34
+ if middleware is None:
35
+ middleware = Middleware()
36
+ middleware.response(self.logged_response_handler)
37
+ middleware.request(self.logged_request_handler)
38
+ middleware.exception(self.logged_exception_handler)
39
+ self._middleware = middleware
40
+
41
+ def invoke(self, action: str, args: dict = None, **options) -> dict:
42
+ """invoke will invoke the action with arguments data and options
43
+
44
+ :param str action: the api action, like `CreateUHostInstance`
45
+ :param dict args: arguments of api(action), see doc: `UCloud API Documentation <https://docs.ucloud.cn/api>`__
46
+ :return:
47
+ """
48
+ retries = 0
49
+ max_retries = options.get("max_retries") or self.config.max_retries
50
+ timeout = options.get("timeout") or self.config.timeout
51
+
52
+ while retries <= max_retries:
53
+ try:
54
+ return self._send(
55
+ action, args or {}, max_retries=max_retries, timeout=timeout
56
+ )
57
+ except exc.UCloudException as e:
58
+ if e.retryable and retries != max_retries:
59
+ logging.info(
60
+ "Retrying {action}: {args}".format(
61
+ action=action, args=args
62
+ )
63
+ )
64
+ retries += 1
65
+ continue
66
+ raise e
67
+
68
+ @property
69
+ def middleware(self) -> Middleware:
70
+ return self._middleware
71
+
72
+ def logged_request_handler(self, req):
73
+ action = req.get("Action", "")
74
+ self.logger.info("[request] {} {}".format(action, req))
75
+ return req
76
+
77
+ def logged_response_handler(self, resp, http_resp: http.Response = None):
78
+ action = resp.get("Action", "")
79
+ request_uuid = http_resp and http_resp.request_uuid
80
+ self.logger.info(
81
+ "[response] [{}] {} {}".format(request_uuid or "*", action, resp)
82
+ )
83
+ return resp
84
+
85
+ def logged_exception_handler(self, e: Exception):
86
+ if isinstance(e, exc.RetCodeException):
87
+ self.logger.warning(e)
88
+ else:
89
+ self.logger.exception(e)
90
+ return e
91
+
92
+ @staticmethod
93
+ def _parse_dict_config(
94
+ config: dict,
95
+ ) -> typing.Tuple[Config, auth.Credential]:
96
+ return Config.from_dict(config), auth.Credential.from_dict(config)
97
+
98
+ def _send(self, action: str, args: dict, max_retries, timeout) -> dict:
99
+ args["Action"] = action
100
+
101
+ # inject request middleware
102
+ for handler in self.middleware.request_handlers:
103
+ args = handler(args)
104
+
105
+ # send http request
106
+ try:
107
+ req = self._build_http_request(args)
108
+
109
+ resp = self.transport.send(
110
+ req,
111
+ ssl_option=SSLOption(
112
+ self.config.ssl_verify,
113
+ self.config.ssl_cacert,
114
+ self.config.ssl_cert,
115
+ self.config.ssl_key,
116
+ ),
117
+ timeout=timeout,
118
+ max_retries=max_retries,
119
+ )
120
+ data = resp.json()
121
+ except Exception as e:
122
+ for handler in self.middleware.exception_handlers:
123
+ handler(e)
124
+ raise e
125
+
126
+ # inject response middleware
127
+ for handler in self.middleware.response_handlers:
128
+ data = handler(data, resp)
129
+
130
+ # return when successful
131
+ if int(data.get("RetCode", -1)) == 0:
132
+ return data
133
+
134
+ # inject exception middleware
135
+ ret_code_exc = exc.RetCodeException(
136
+ action=req.data.get("Action", ""),
137
+ code=int(data.get("RetCode", 0)),
138
+ message=data.get("Message", ""),
139
+ request_uuid=resp.request_uuid,
140
+ )
141
+ for handler in self.middleware.exception_handlers:
142
+ handler(ret_code_exc)
143
+ raise ret_code_exc
144
+
145
+ def _build_http_request(self, args: dict) -> Request:
146
+ config = {
147
+ "Region": self.config.region,
148
+ "ProjectId": self.config.project_id,
149
+ }
150
+ payload = {k: v for k, v in config.items() if v is not None}
151
+ payload.update({k: v for k, v in args.items() if v is not None})
152
+ payload = encoder.encode(payload)
153
+ payload["Signature"] = self.credential.verify_ac(payload)
154
+
155
+ return Request(
156
+ url=self.config.base_url,
157
+ method="post",
158
+ data=payload,
159
+ headers={
160
+ "User-Agent": self._build_user_agent(),
161
+ "Content-Type": "application/x-www-form-urlencoded",
162
+ "U-Timestamp-Ms": str(int(round(time.time() * 1000))),
163
+ },
164
+ )
165
+
166
+ def _build_user_agent(self) -> str:
167
+ python_version = "{v[0]}.{v[1]}.{v[2]}".format(v=sys.version_info)
168
+ user_agent = "Python/{python_version} Python-SDK/{sdk_version}".format(
169
+ python_version=python_version, sdk_version=version.version
170
+ ) + (self.config.user_agent or "")
171
+ return user_agent
172
+
173
+ def __repr__(self):
174
+ return '<{}(region="{}")>'.format(
175
+ self.__class__.__name__, self.config.region
176
+ )
@@ -0,0 +1,9 @@
1
+ from ucloud.core.exc._exc import (
2
+ UCloudException,
3
+ ValidationException,
4
+ RetCodeException,
5
+ RetryTimeoutException,
6
+ TransportException,
7
+ HTTPStatusException,
8
+ InvalidResponseException,
9
+ )
@@ -0,0 +1,94 @@
1
+ from collections.abc import Iterable
2
+
3
+ from ucloud.core.utils import compat
4
+
5
+
6
+ class UCloudException(Exception):
7
+ @property
8
+ def retryable(self):
9
+ return False
10
+
11
+
12
+ MAX_COMMON_RET_CODE = 2000
13
+
14
+
15
+ class TransportException(UCloudException):
16
+ pass
17
+
18
+
19
+ class HTTPStatusException(TransportException):
20
+ def __init__(self, status_code: int, request_uuid: str = None):
21
+ self.status_code = status_code
22
+ self.request_uuid = request_uuid
23
+
24
+ @property
25
+ def retryable(self):
26
+ return self.status_code in [429, 502, 503, 504]
27
+
28
+ def __str__(self):
29
+ return "[{uuid}] {self.status_code} http status error".format(
30
+ self=self, uuid=self.request_uuid or "*"
31
+ )
32
+
33
+
34
+ class InvalidResponseException(TransportException):
35
+ def __init__(self, content: bytes, message: str, request_uuid: str = None):
36
+ self.content = content
37
+ self.message = message
38
+ self.request_uuid = request_uuid
39
+
40
+ @property
41
+ def retryable(self):
42
+ return False
43
+
44
+ def __str__(self):
45
+ return "[{uuid}] {self.message}: {self.content}".format(
46
+ self=self, uuid=self.request_uuid or "*"
47
+ )
48
+
49
+
50
+ class RetCodeException(UCloudException):
51
+ def __init__(
52
+ self, action: str, code: int, message: str, request_uuid: str = None
53
+ ):
54
+ self.action = action
55
+ self.code = code
56
+ self.message = message
57
+ self.request_uuid = request_uuid
58
+
59
+ @property
60
+ def retryable(self):
61
+ return self.code > MAX_COMMON_RET_CODE
62
+
63
+ def __str__(self):
64
+ return "[{uuid}] {self.action} - {self.code}: {self.message}".format(
65
+ self=self, uuid=self.request_uuid or "*"
66
+ )
67
+
68
+ def json(self):
69
+ return {
70
+ "RetCode": self.code,
71
+ "Message": self.message or "",
72
+ "Action": self.action or "",
73
+ }
74
+
75
+
76
+ class RetryTimeoutException(UCloudException):
77
+ pass
78
+
79
+
80
+ class ValidationException(UCloudException):
81
+ def __init__(self, e=None):
82
+ if isinstance(e, compat.string_types):
83
+ self.errors = [e]
84
+ elif isinstance(e, Iterable):
85
+ self.errors = e or []
86
+ else:
87
+ self.errors = [e]
88
+
89
+ @property
90
+ def retryable(self):
91
+ return False
92
+
93
+ def __str__(self):
94
+ return str([str(e) for e in self.errors])
@@ -0,0 +1,4 @@
1
+ from ucloud.core.transport.http import Request, Response, Transport, SSLOption
2
+ from ucloud.core.transport._requests import RequestsTransport
3
+
4
+ __all__ = ["Request", "Response", "Transport", "RequestsTransport", "SSLOption"]
@@ -0,0 +1,135 @@
1
+ import time
2
+ import typing
3
+ import requests
4
+ from urllib3.util.retry import Retry
5
+ from requests.adapters import HTTPAdapter
6
+ from ucloud.core.transport import http
7
+ from ucloud.core.transport.http import Request, Response, SSLOption
8
+ from ucloud.core.utils.middleware import Middleware
9
+ from ucloud.core import exc
10
+
11
+
12
+ class RequestsTransport(http.Transport):
13
+ """transport is the implementation of http client, use for send a request and return a http response
14
+
15
+ :type max_retries: int
16
+ :param max_retries: max retries is the max number of transport request when occur http error
17
+ :type backoff_factor: float
18
+ :param backoff_factor: backoff factor will calculate the backoff delay during retrying,
19
+ the backoff delay = {backoff factor} * (2 ^ ({number of total retries} - 1))
20
+ :type status_forcelist: tuple
21
+ :param status_forcelist: the status code list that could be retried
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ max_retries: int = 3,
27
+ backoff_factor: float = 0.3,
28
+ status_forcelist: typing.Tuple[int] = (500, 502, 504),
29
+ ):
30
+ self.max_retries = max_retries
31
+ self.backoff_factor = backoff_factor
32
+ self.status_forcelist = status_forcelist
33
+
34
+ self._adapter = self._load_adapter(max_retries)
35
+ self._middleware = Middleware()
36
+
37
+ def send(self, req: Request, **options: typing.Any) -> http.Response:
38
+ """send request and return the response
39
+
40
+ :param req: the full http request descriptor
41
+ :return: the response of http request
42
+ """
43
+ for handler in self.middleware.request_handlers:
44
+ req = handler(req)
45
+
46
+ try:
47
+ resp = self._send(req, **options)
48
+ except Exception as e:
49
+ for handler in self.middleware.exception_handlers:
50
+ handler(e)
51
+ raise e
52
+
53
+ for handler in self.middleware.response_handlers:
54
+ resp = handler(resp)
55
+
56
+ return resp
57
+
58
+ @property
59
+ def middleware(self) -> Middleware:
60
+ """the middleware object, see :mod:
61
+
62
+ :return: the transport middleware
63
+ """
64
+ return self._middleware
65
+
66
+ def _send(self, req: Request, **options: typing.Any) -> requests.Response:
67
+ with requests.Session() as session:
68
+ adapter = self._load_adapter(options.get("max_retries"))
69
+ session.mount("http://", adapter=adapter)
70
+ session.mount("https://", adapter=adapter)
71
+
72
+ ssl_option = options.get("ssl_option")
73
+ kwargs = self._build_ssl_option(ssl_option) if ssl_option else {}
74
+
75
+ req.request_time = time.time()
76
+ session_resp = session.request(
77
+ method=req.method.upper(),
78
+ url=req.url,
79
+ json=req.json,
80
+ data=req.data,
81
+ params=req.params,
82
+ headers=req.headers,
83
+ timeout=options.get("timeout"),
84
+ **kwargs
85
+ )
86
+ resp = self.convert_response(session_resp)
87
+ resp.request = req
88
+ resp.response_time = time.time()
89
+
90
+ if resp.status_code >= 400:
91
+ raise exc.HTTPStatusException(
92
+ resp.status_code, resp.request_uuid
93
+ )
94
+ return resp
95
+
96
+ @staticmethod
97
+ def _build_ssl_option(ssl_option):
98
+ kwargs = {"verify": ssl_option.ssl_verify and ssl_option.ssl_cacert}
99
+ if not ssl_option.ssl_cert:
100
+ return kwargs
101
+
102
+ if ssl_option.ssl_key:
103
+ kwargs["cert"] = (ssl_option.ssl_cert, ssl_option.ssl_key)
104
+ else:
105
+ kwargs["cert"] = ssl_option.ssl_cert
106
+ return kwargs
107
+
108
+ def _load_adapter(
109
+ self, max_retries: typing.Optional[int] = None
110
+ ) -> HTTPAdapter:
111
+ if max_retries is None and self._adapter is not None:
112
+ return self._adapter
113
+
114
+ max_retries = max_retries or 0
115
+ adapter = HTTPAdapter()
116
+ adapter.max_retries = Retry(
117
+ total=max_retries,
118
+ read=max_retries,
119
+ connect=max_retries,
120
+ backoff_factor=self.backoff_factor,
121
+ status_forcelist=self.status_forcelist,
122
+ )
123
+ return adapter
124
+
125
+ @staticmethod
126
+ def convert_response(r: requests.Response) -> Response:
127
+ return Response(
128
+ url=r.url,
129
+ method=r.request.method,
130
+ status_code=r.status_code,
131
+ reason=r.reason,
132
+ headers=r.headers,
133
+ content=r.content,
134
+ encoding=r.encoding or r.apparent_encoding,
135
+ )
@@ -0,0 +1,120 @@
1
+ import typing
2
+ import logging
3
+ import json as json_mod
4
+
5
+ from ucloud.core import exc
6
+ from ucloud.core.transport import utils
7
+ from ucloud.core.utils.compat import str
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class Request:
13
+ def __init__(
14
+ self,
15
+ url: str,
16
+ method: str = "GET",
17
+ params: dict = None,
18
+ data: dict = None,
19
+ json: dict = None,
20
+ headers: dict = None,
21
+ **kwargs
22
+ ):
23
+ self.url = url
24
+ self.method = method
25
+ self.params = params
26
+ self.data = data
27
+ self.json = json
28
+ self.headers = headers
29
+ self.request_time = 0
30
+
31
+ def payload(self):
32
+ payload = (self.params or {}).copy()
33
+ payload.update(self.data or {})
34
+ payload.update(self.json or {})
35
+ return payload
36
+
37
+
38
+ REQUEST_UUID_HEADER_KEY = "X-UCLOUD-REQUEST-UUID"
39
+
40
+
41
+ class Response:
42
+ def __init__(
43
+ self,
44
+ url: str,
45
+ method: str,
46
+ request: Request = None,
47
+ status_code: int = None,
48
+ reason: str = None,
49
+ headers: dict = None,
50
+ content: bytes = None,
51
+ encoding: str = None,
52
+ **kwargs
53
+ ):
54
+ self.url = url
55
+ self.method = method
56
+ self.request = request
57
+ self.status_code = status_code
58
+ self.reason = reason
59
+ self.content = content
60
+ self.encoding = encoding
61
+ self.response_time = 0
62
+ self.headers = headers or {}
63
+ self.request_uuid = self.headers.get(REQUEST_UUID_HEADER_KEY)
64
+
65
+ def json(self, **kwargs) -> typing.Optional[dict]:
66
+ """json will return the bytes of content"""
67
+ if not self.content:
68
+ return None
69
+
70
+ try:
71
+ return self._decode_json(**kwargs)
72
+ except Exception as e:
73
+ raise exc.InvalidResponseException(
74
+ self.content, str(e), request_uuid=self.request_uuid
75
+ )
76
+
77
+ @property
78
+ def text(self):
79
+ """text will return the unicode string of content,
80
+ see `requests.Response.text`
81
+ """
82
+ if not self.content:
83
+ return str("")
84
+
85
+ # Decode unicode from given encoding.
86
+ try:
87
+ content = str(self.content, self.encoding, errors="replace")
88
+ except (LookupError, TypeError):
89
+ content = str(self.content, errors="replace")
90
+ return content
91
+
92
+ def _decode_json(self, **kwargs):
93
+ encoding = utils.guess_json_utf(self.content)
94
+ if encoding is not None:
95
+ try:
96
+ return json_mod.loads(self.content.decode(encoding), **kwargs)
97
+ except UnicodeDecodeError:
98
+ pass
99
+ return json_mod.loads(self.text, **kwargs)
100
+
101
+
102
+ class SSLOption:
103
+ def __init__(
104
+ self,
105
+ ssl_verify: bool = True,
106
+ ssl_cacert: str = None,
107
+ ssl_cert: str = None,
108
+ ssl_key: str = None,
109
+ ):
110
+ self.ssl_verify = ssl_verify
111
+ self.ssl_cacert = ssl_cacert
112
+ self.ssl_cert = ssl_cert
113
+ self.ssl_key = ssl_key
114
+
115
+
116
+ class Transport:
117
+ """the abstract class of transport implementation"""
118
+
119
+ def send(self, req: Request, **options: typing.Any) -> Response:
120
+ raise NotImplementedError
@@ -0,0 +1,40 @@
1
+ import codecs
2
+
3
+ # Null bytes; no need to recreate these on each call to guess_json_utf
4
+ _null = "\x00".encode("ascii") # encoding to ASCII for Python 3
5
+ _null2 = _null * 2
6
+ _null3 = _null * 3
7
+
8
+
9
+ def guess_json_utf(data):
10
+ """guess_json_utf will detect the encoding of bytes,
11
+ see `requests.utils.guess_json_utf`
12
+
13
+ :rtype: str
14
+ """
15
+ # JSON always starts with two ASCII characters, so detection is as
16
+ # easy as counting the nulls and from their location and count
17
+ # determine the encoding. Also detect a BOM, if present.
18
+ sample = data[:4]
19
+ if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE):
20
+ return "utf-32" # BOM included
21
+ if sample[:3] == codecs.BOM_UTF8:
22
+ return "utf-8-sig" # BOM included, MS style (discouraged)
23
+ if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):
24
+ return "utf-16" # BOM included
25
+ nullcount = sample.count(_null)
26
+ if nullcount == 0:
27
+ return "utf-8"
28
+ if nullcount == 2:
29
+ if sample[::2] == _null2: # 1st and 3rd are null
30
+ return "utf-16-be"
31
+ if sample[1::2] == _null2: # 2nd and 4th are null
32
+ return "utf-16-le"
33
+ # Did not detect 2 valid UTF-16 ascii-range characters
34
+ if nullcount == 3:
35
+ if sample[:3] == _null3:
36
+ return "utf-32-be"
37
+ if sample[1:] == _null3:
38
+ return "utf-32-le"
39
+ # Did not detect a valid UTF-32 ascii-range character
40
+ return None
File without changes
@@ -0,0 +1,60 @@
1
+ import typing
2
+
3
+ from ucloud.core import exc
4
+
5
+
6
+ class Field(object):
7
+ def __init__(
8
+ self,
9
+ required: bool = False,
10
+ default: typing.Any = None,
11
+ dump_to: str = None,
12
+ load_from: str = None,
13
+ strict: bool = None,
14
+ **kwargs
15
+ ):
16
+ self.required = required
17
+ self.default = default
18
+ self.dump_to = dump_to
19
+ self.load_from = load_from
20
+ self.options = kwargs
21
+ self.strict = bool(strict) # None as False
22
+
23
+ def dumps(self, value, **kwargs):
24
+ raise NotImplementedError
25
+
26
+ def loads(self, value, **kwargs):
27
+ raise NotImplementedError
28
+
29
+ @staticmethod
30
+ def fail(name, expected, got):
31
+ msg = "invalid field {}, expect {}, got {}".format(name, expected, got)
32
+ raise exc.ValidationException(msg)
33
+
34
+
35
+ class Schema(object):
36
+ fields = {}
37
+
38
+ def __init__(
39
+ self,
40
+ required: bool = False,
41
+ default: typing.Union[typing.Callable, typing.Any] = dict,
42
+ dump_to: str = None,
43
+ load_from: str = None,
44
+ strict: bool = False,
45
+ case_sensitive: bool = False,
46
+ **kwargs
47
+ ):
48
+ self.required = required
49
+ self.default = default
50
+ self.dump_to = dump_to
51
+ self.load_from = load_from
52
+ self.options = kwargs
53
+ self.strict = strict
54
+ self.case_sensitive = case_sensitive
55
+
56
+ def dumps(self, d: dict) -> dict:
57
+ raise NotImplementedError
58
+
59
+ def loads(self, d: dict) -> dict:
60
+ raise NotImplementedError
@@ -0,0 +1,33 @@
1
+ from ucloud.core.utils.compat import str
2
+
3
+
4
+ def encode(d: dict) -> dict:
5
+ result = {}
6
+
7
+ for k, v in d.items():
8
+ if isinstance(v, dict):
9
+ for ek, ev in encode(v).items():
10
+ result["{}.{}".format(k, ek)] = encode_value(ev)
11
+ elif isinstance(v, list):
12
+ for i, item in enumerate(v):
13
+ if isinstance(item, dict):
14
+ for ek, ev in encode(item).items():
15
+ result["{}.{}.{}".format(k, i, ek)] = encode_value(ev)
16
+ else:
17
+ result["{}.{}".format(k, i)] = encode_value(item)
18
+ else:
19
+ result[k] = encode_value(v)
20
+
21
+ return result
22
+
23
+
24
+ def encode_value(v):
25
+ # bool only accept lower case
26
+ if isinstance(v, bool):
27
+ return "true" if v else "false"
28
+
29
+ # api gateway will try to decode float as int in lua syntax
30
+ if isinstance(v, float):
31
+ return str(int(v)) if v % 1 == 0 else str(v)
32
+
33
+ return str(v)