webull-openapi-python-sdk 1.0.0__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 (295) hide show
  1. samples/__init__.py +1 -0
  2. samples/data/__init__.py +1 -0
  3. samples/data/data_client.py +57 -0
  4. samples/data/data_streaming_client.py +86 -0
  5. samples/data/data_streaming_client_async.py +101 -0
  6. samples/trade/__init__.py +0 -0
  7. samples/trade/trade_client.py +163 -0
  8. samples/trade/trade_client_v2.py +181 -0
  9. samples/trade/trade_event_client.py +47 -0
  10. webull/__init__.py +1 -0
  11. webull/core/__init__.py +12 -0
  12. webull/core/auth/__init__.py +0 -0
  13. webull/core/auth/algorithm/__init__.py +0 -0
  14. webull/core/auth/algorithm/sha_hmac1.py +65 -0
  15. webull/core/auth/algorithm/sha_hmac256.py +75 -0
  16. webull/core/auth/composer/__init__.py +0 -0
  17. webull/core/auth/composer/default_signature_composer.py +125 -0
  18. webull/core/auth/credentials.py +46 -0
  19. webull/core/auth/signers/__init__.py +0 -0
  20. webull/core/auth/signers/app_key_signer.py +72 -0
  21. webull/core/auth/signers/signer.py +48 -0
  22. webull/core/auth/signers/signer_factory.py +58 -0
  23. webull/core/cache/__init__.py +225 -0
  24. webull/core/client.py +410 -0
  25. webull/core/common/__init__.py +0 -0
  26. webull/core/common/api_type.py +19 -0
  27. webull/core/common/easy_enum.py +35 -0
  28. webull/core/common/region.py +7 -0
  29. webull/core/compat.py +85 -0
  30. webull/core/context/__init__.py +0 -0
  31. webull/core/context/request_context_holder.py +33 -0
  32. webull/core/data/endpoints.json +22 -0
  33. webull/core/data/retry_config.json +15 -0
  34. webull/core/endpoint/__init__.py +8 -0
  35. webull/core/endpoint/chained_endpoint_resolver.py +57 -0
  36. webull/core/endpoint/default_endpoint_resolver.py +60 -0
  37. webull/core/endpoint/local_config_regional_endpoint_resolver.py +77 -0
  38. webull/core/endpoint/resolver_endpoint_request.py +46 -0
  39. webull/core/endpoint/user_customized_endpoint_resolver.py +55 -0
  40. webull/core/exception/__init__.py +0 -0
  41. webull/core/exception/error_code.py +23 -0
  42. webull/core/exception/error_msg.py +21 -0
  43. webull/core/exception/exceptions.py +53 -0
  44. webull/core/headers.py +57 -0
  45. webull/core/http/__init__.py +0 -0
  46. webull/core/http/initializer/__init__.py +0 -0
  47. webull/core/http/initializer/client_initializer.py +79 -0
  48. webull/core/http/initializer/token/__init__.py +0 -0
  49. webull/core/http/initializer/token/bean/__init__.py +0 -0
  50. webull/core/http/initializer/token/bean/access_token.py +40 -0
  51. webull/core/http/initializer/token/bean/check_token_request.py +44 -0
  52. webull/core/http/initializer/token/bean/create_token_request.py +45 -0
  53. webull/core/http/initializer/token/bean/refresh_token_request.py +44 -0
  54. webull/core/http/initializer/token/token_manager.py +208 -0
  55. webull/core/http/initializer/token/token_operation.py +72 -0
  56. webull/core/http/method_type.py +43 -0
  57. webull/core/http/protocol_type.py +43 -0
  58. webull/core/http/request.py +121 -0
  59. webull/core/http/response.py +166 -0
  60. webull/core/request.py +278 -0
  61. webull/core/retry/__init__.py +0 -0
  62. webull/core/retry/backoff_strategy.py +102 -0
  63. webull/core/retry/retry_condition.py +214 -0
  64. webull/core/retry/retry_policy.py +63 -0
  65. webull/core/retry/retry_policy_context.py +51 -0
  66. webull/core/utils/__init__.py +0 -0
  67. webull/core/utils/common.py +62 -0
  68. webull/core/utils/data.py +25 -0
  69. webull/core/utils/desensitize.py +33 -0
  70. webull/core/utils/validation.py +49 -0
  71. webull/core/vendored/__init__.py +0 -0
  72. webull/core/vendored/requests/__init__.py +94 -0
  73. webull/core/vendored/requests/__version__.py +28 -0
  74. webull/core/vendored/requests/_internal_utils.py +56 -0
  75. webull/core/vendored/requests/adapters.py +539 -0
  76. webull/core/vendored/requests/api.py +166 -0
  77. webull/core/vendored/requests/auth.py +307 -0
  78. webull/core/vendored/requests/certs.py +34 -0
  79. webull/core/vendored/requests/compat.py +85 -0
  80. webull/core/vendored/requests/cookies.py +555 -0
  81. webull/core/vendored/requests/exceptions.py +136 -0
  82. webull/core/vendored/requests/help.py +134 -0
  83. webull/core/vendored/requests/hooks.py +48 -0
  84. webull/core/vendored/requests/models.py +960 -0
  85. webull/core/vendored/requests/packages/__init__.py +17 -0
  86. webull/core/vendored/requests/packages/certifi/__init__.py +17 -0
  87. webull/core/vendored/requests/packages/certifi/__main__.py +16 -0
  88. webull/core/vendored/requests/packages/certifi/cacert.pem +4433 -0
  89. webull/core/vendored/requests/packages/certifi/core.py +51 -0
  90. webull/core/vendored/requests/packages/chardet/__init__.py +53 -0
  91. webull/core/vendored/requests/packages/chardet/big5freq.py +400 -0
  92. webull/core/vendored/requests/packages/chardet/big5prober.py +61 -0
  93. webull/core/vendored/requests/packages/chardet/chardistribution.py +247 -0
  94. webull/core/vendored/requests/packages/chardet/charsetgroupprober.py +120 -0
  95. webull/core/vendored/requests/packages/chardet/charsetprober.py +159 -0
  96. webull/core/vendored/requests/packages/chardet/cli/__init__.py +1 -0
  97. webull/core/vendored/requests/packages/chardet/cli/chardetect.py +99 -0
  98. webull/core/vendored/requests/packages/chardet/codingstatemachine.py +102 -0
  99. webull/core/vendored/requests/packages/chardet/compat.py +48 -0
  100. webull/core/vendored/requests/packages/chardet/cp949prober.py +63 -0
  101. webull/core/vendored/requests/packages/chardet/enums.py +90 -0
  102. webull/core/vendored/requests/packages/chardet/escprober.py +115 -0
  103. webull/core/vendored/requests/packages/chardet/escsm.py +260 -0
  104. webull/core/vendored/requests/packages/chardet/eucjpprober.py +106 -0
  105. webull/core/vendored/requests/packages/chardet/euckrfreq.py +209 -0
  106. webull/core/vendored/requests/packages/chardet/euckrprober.py +61 -0
  107. webull/core/vendored/requests/packages/chardet/euctwfreq.py +401 -0
  108. webull/core/vendored/requests/packages/chardet/euctwprober.py +60 -0
  109. webull/core/vendored/requests/packages/chardet/gb2312freq.py +297 -0
  110. webull/core/vendored/requests/packages/chardet/gb2312prober.py +60 -0
  111. webull/core/vendored/requests/packages/chardet/hebrewprober.py +306 -0
  112. webull/core/vendored/requests/packages/chardet/jisfreq.py +339 -0
  113. webull/core/vendored/requests/packages/chardet/jpcntx.py +247 -0
  114. webull/core/vendored/requests/packages/chardet/langbulgarianmodel.py +242 -0
  115. webull/core/vendored/requests/packages/chardet/langcyrillicmodel.py +347 -0
  116. webull/core/vendored/requests/packages/chardet/langgreekmodel.py +239 -0
  117. webull/core/vendored/requests/packages/chardet/langhebrewmodel.py +214 -0
  118. webull/core/vendored/requests/packages/chardet/langhungarianmodel.py +239 -0
  119. webull/core/vendored/requests/packages/chardet/langthaimodel.py +213 -0
  120. webull/core/vendored/requests/packages/chardet/langturkishmodel.py +207 -0
  121. webull/core/vendored/requests/packages/chardet/latin1prober.py +159 -0
  122. webull/core/vendored/requests/packages/chardet/mbcharsetprober.py +105 -0
  123. webull/core/vendored/requests/packages/chardet/mbcsgroupprober.py +68 -0
  124. webull/core/vendored/requests/packages/chardet/mbcssm.py +586 -0
  125. webull/core/vendored/requests/packages/chardet/sbcharsetprober.py +146 -0
  126. webull/core/vendored/requests/packages/chardet/sbcsgroupprober.py +87 -0
  127. webull/core/vendored/requests/packages/chardet/sjisprober.py +106 -0
  128. webull/core/vendored/requests/packages/chardet/universaldetector.py +300 -0
  129. webull/core/vendored/requests/packages/chardet/utf8prober.py +96 -0
  130. webull/core/vendored/requests/packages/chardet/version.py +23 -0
  131. webull/core/vendored/requests/packages/urllib3/__init__.py +114 -0
  132. webull/core/vendored/requests/packages/urllib3/_collections.py +346 -0
  133. webull/core/vendored/requests/packages/urllib3/connection.py +405 -0
  134. webull/core/vendored/requests/packages/urllib3/connectionpool.py +910 -0
  135. webull/core/vendored/requests/packages/urllib3/contrib/__init__.py +0 -0
  136. webull/core/vendored/requests/packages/urllib3/contrib/_appengine_environ.py +44 -0
  137. webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/__init__.py +0 -0
  138. webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/bindings.py +607 -0
  139. webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/low_level.py +360 -0
  140. webull/core/vendored/requests/packages/urllib3/contrib/appengine.py +303 -0
  141. webull/core/vendored/requests/packages/urllib3/contrib/ntlmpool.py +125 -0
  142. webull/core/vendored/requests/packages/urllib3/contrib/pyopenssl.py +484 -0
  143. webull/core/vendored/requests/packages/urllib3/contrib/securetransport.py +818 -0
  144. webull/core/vendored/requests/packages/urllib3/contrib/socks.py +206 -0
  145. webull/core/vendored/requests/packages/urllib3/exceptions.py +260 -0
  146. webull/core/vendored/requests/packages/urllib3/fields.py +192 -0
  147. webull/core/vendored/requests/packages/urllib3/filepost.py +112 -0
  148. webull/core/vendored/requests/packages/urllib3/packages/__init__.py +19 -0
  149. webull/core/vendored/requests/packages/urllib3/packages/backports/__init__.py +0 -0
  150. webull/core/vendored/requests/packages/urllib3/packages/backports/makefile.py +67 -0
  151. webull/core/vendored/requests/packages/urllib3/packages/ordered_dict.py +273 -0
  152. webull/core/vendored/requests/packages/urllib3/packages/six.py +882 -0
  153. webull/core/vendored/requests/packages/urllib3/packages/socks.py +887 -0
  154. webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +19 -0
  155. webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py +170 -0
  156. webull/core/vendored/requests/packages/urllib3/poolmanager.py +467 -0
  157. webull/core/vendored/requests/packages/urllib3/request.py +164 -0
  158. webull/core/vendored/requests/packages/urllib3/response.py +721 -0
  159. webull/core/vendored/requests/packages/urllib3/util/__init__.py +68 -0
  160. webull/core/vendored/requests/packages/urllib3/util/connection.py +148 -0
  161. webull/core/vendored/requests/packages/urllib3/util/queue.py +35 -0
  162. webull/core/vendored/requests/packages/urllib3/util/request.py +132 -0
  163. webull/core/vendored/requests/packages/urllib3/util/response.py +101 -0
  164. webull/core/vendored/requests/packages/urllib3/util/retry.py +426 -0
  165. webull/core/vendored/requests/packages/urllib3/util/selectors.py +601 -0
  166. webull/core/vendored/requests/packages/urllib3/util/ssl_.py +396 -0
  167. webull/core/vendored/requests/packages/urllib3/util/timeout.py +256 -0
  168. webull/core/vendored/requests/packages/urllib3/util/url.py +252 -0
  169. webull/core/vendored/requests/packages/urllib3/util/wait.py +164 -0
  170. webull/core/vendored/requests/packages.py +28 -0
  171. webull/core/vendored/requests/sessions.py +750 -0
  172. webull/core/vendored/requests/status_codes.py +105 -0
  173. webull/core/vendored/requests/structures.py +119 -0
  174. webull/core/vendored/requests/utils.py +916 -0
  175. webull/core/vendored/six.py +905 -0
  176. webull/data/__init__.py +3 -0
  177. webull/data/common/__init__.py +0 -0
  178. webull/data/common/category.py +26 -0
  179. webull/data/common/connect_ack.py +29 -0
  180. webull/data/common/direction.py +25 -0
  181. webull/data/common/exchange_code.py +33 -0
  182. webull/data/common/exercise_style.py +22 -0
  183. webull/data/common/expiration_cycle.py +26 -0
  184. webull/data/common/instrument_status.py +23 -0
  185. webull/data/common/option_type.py +20 -0
  186. webull/data/common/subscribe_type.py +22 -0
  187. webull/data/common/timespan.py +29 -0
  188. webull/data/data_client.py +35 -0
  189. webull/data/data_streaming_client.py +89 -0
  190. webull/data/internal/__init__.py +0 -0
  191. webull/data/internal/default_retry_policy.py +84 -0
  192. webull/data/internal/exceptions.py +60 -0
  193. webull/data/internal/quotes_client.py +314 -0
  194. webull/data/internal/quotes_decoder.py +40 -0
  195. webull/data/internal/quotes_payload_decoder.py +35 -0
  196. webull/data/internal/quotes_topic.py +36 -0
  197. webull/data/quotes/__init__.py +0 -0
  198. webull/data/quotes/instrument.py +33 -0
  199. webull/data/quotes/market_data.py +187 -0
  200. webull/data/quotes/market_streaming_data.py +66 -0
  201. webull/data/quotes/subscribe/__init__.py +0 -0
  202. webull/data/quotes/subscribe/ask_bid_result.py +49 -0
  203. webull/data/quotes/subscribe/basic_result.py +45 -0
  204. webull/data/quotes/subscribe/broker_result.py +33 -0
  205. webull/data/quotes/subscribe/message_pb2.py +37 -0
  206. webull/data/quotes/subscribe/order_result.py +30 -0
  207. webull/data/quotes/subscribe/payload_type.py +19 -0
  208. webull/data/quotes/subscribe/quote_decoder.py +28 -0
  209. webull/data/quotes/subscribe/quote_result.py +47 -0
  210. webull/data/quotes/subscribe/snapshot_decoder.py +30 -0
  211. webull/data/quotes/subscribe/snapshot_result.py +69 -0
  212. webull/data/quotes/subscribe/tick_decoder.py +29 -0
  213. webull/data/quotes/subscribe/tick_result.py +47 -0
  214. webull/data/request/__init__.py +0 -0
  215. webull/data/request/get_batch_historical_bars_request.py +43 -0
  216. webull/data/request/get_corp_action_request.py +47 -0
  217. webull/data/request/get_eod_bars_request.py +32 -0
  218. webull/data/request/get_historical_bars_request.py +43 -0
  219. webull/data/request/get_instruments_request.py +30 -0
  220. webull/data/request/get_quotes_request.py +35 -0
  221. webull/data/request/get_snapshot_request.py +38 -0
  222. webull/data/request/get_tick_request.py +37 -0
  223. webull/data/request/subscribe_request.py +43 -0
  224. webull/data/request/unsubscribe_request.py +42 -0
  225. webull/trade/__init__.py +2 -0
  226. webull/trade/common/__init__.py +0 -0
  227. webull/trade/common/account_type.py +22 -0
  228. webull/trade/common/category.py +29 -0
  229. webull/trade/common/combo_ticker_type.py +23 -0
  230. webull/trade/common/combo_type.py +31 -0
  231. webull/trade/common/currency.py +24 -0
  232. webull/trade/common/forbid_reason.py +27 -0
  233. webull/trade/common/instrument_type.py +27 -0
  234. webull/trade/common/markets.py +27 -0
  235. webull/trade/common/order_entrust_type.py +21 -0
  236. webull/trade/common/order_side.py +23 -0
  237. webull/trade/common/order_status.py +25 -0
  238. webull/trade/common/order_tif.py +24 -0
  239. webull/trade/common/order_type.py +30 -0
  240. webull/trade/common/trade_policy.py +22 -0
  241. webull/trade/common/trading_date_type.py +24 -0
  242. webull/trade/common/trailing_type.py +23 -0
  243. webull/trade/events/__init__.py +0 -0
  244. webull/trade/events/default_retry_policy.py +64 -0
  245. webull/trade/events/events_pb2.py +43 -0
  246. webull/trade/events/events_pb2_grpc.py +66 -0
  247. webull/trade/events/signature_composer.py +61 -0
  248. webull/trade/events/types.py +21 -0
  249. webull/trade/request/__init__.py +0 -0
  250. webull/trade/request/cancel_order_request.py +28 -0
  251. webull/trade/request/get_account_balance_request.py +28 -0
  252. webull/trade/request/get_account_positions_request.py +30 -0
  253. webull/trade/request/get_account_profile_request.py +26 -0
  254. webull/trade/request/get_app_subscriptions.py +28 -0
  255. webull/trade/request/get_open_orders_request.py +30 -0
  256. webull/trade/request/get_order_detail_request.py +27 -0
  257. webull/trade/request/get_today_orders_request.py +31 -0
  258. webull/trade/request/get_trade_calendar_request.py +30 -0
  259. webull/trade/request/get_trade_instrument_detail_request.py +24 -0
  260. webull/trade/request/get_trade_security_detail_request.py +42 -0
  261. webull/trade/request/get_tradeable_instruments_request.py +27 -0
  262. webull/trade/request/palce_order_request.py +91 -0
  263. webull/trade/request/place_order_request_v2.py +58 -0
  264. webull/trade/request/replace_order_request.py +73 -0
  265. webull/trade/request/replace_order_request_v2.py +38 -0
  266. webull/trade/request/v2/__init__.py +0 -0
  267. webull/trade/request/v2/cancel_option_request.py +28 -0
  268. webull/trade/request/v2/cancel_order_request.py +28 -0
  269. webull/trade/request/v2/get_account_balance_request.py +28 -0
  270. webull/trade/request/v2/get_account_list.py +23 -0
  271. webull/trade/request/v2/get_account_positions_request.py +24 -0
  272. webull/trade/request/v2/get_order_detail_request.py +26 -0
  273. webull/trade/request/v2/get_order_history_request.py +35 -0
  274. webull/trade/request/v2/palce_order_request.py +87 -0
  275. webull/trade/request/v2/place_option_request.py +64 -0
  276. webull/trade/request/v2/preview_option_request.py +28 -0
  277. webull/trade/request/v2/preview_order_request.py +59 -0
  278. webull/trade/request/v2/replace_option_request.py +28 -0
  279. webull/trade/request/v2/replace_order_request.py +57 -0
  280. webull/trade/trade/__init__.py +0 -0
  281. webull/trade/trade/account_info.py +83 -0
  282. webull/trade/trade/order_operation.py +246 -0
  283. webull/trade/trade/trade_calendar.py +37 -0
  284. webull/trade/trade/trade_instrument.py +72 -0
  285. webull/trade/trade/v2/__init__.py +0 -0
  286. webull/trade/trade/v2/account_info_v2.py +55 -0
  287. webull/trade/trade/v2/order_operation_v2.py +206 -0
  288. webull/trade/trade_client.py +43 -0
  289. webull/trade/trade_events_client.py +233 -0
  290. webull_openapi_python_sdk-1.0.0.dist-info/METADATA +28 -0
  291. webull_openapi_python_sdk-1.0.0.dist-info/RECORD +295 -0
  292. webull_openapi_python_sdk-1.0.0.dist-info/WHEEL +5 -0
  293. webull_openapi_python_sdk-1.0.0.dist-info/licenses/LICENSE +202 -0
  294. webull_openapi_python_sdk-1.0.0.dist-info/licenses/NOTICE +56 -0
  295. webull_openapi_python_sdk-1.0.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,601 @@
1
+ # Copyright 2022 Webull
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Backport of selectors.py from Python 3.5+ to support Python < 3.4
16
+ # Also has the behavior specified in PEP 475 which is to retry syscalls
17
+ # in the case of an EINTR error. This module is required because selectors34
18
+ # does not follow this behavior and instead returns that no dile descriptor
19
+ # events have occurred rather than retry the syscall. The decision to drop
20
+ # support for select.devpoll is made to maintain 100% test coverage.
21
+
22
+ import errno
23
+ import math
24
+ import select
25
+ import socket
26
+ import sys
27
+ import time
28
+ from collections import namedtuple
29
+
30
+ try:
31
+ from collections.abc import Mapping
32
+ except ImportError:
33
+ from collections import Mapping
34
+
35
+
36
+ try:
37
+ monotonic = time.monotonic
38
+ except (AttributeError, ImportError): # Python 3.3<
39
+ monotonic = time.time
40
+
41
+ EVENT_READ = (1 << 0)
42
+ EVENT_WRITE = (1 << 1)
43
+
44
+ HAS_SELECT = True # Variable that shows whether the platform has a selector.
45
+ _SYSCALL_SENTINEL = object() # Sentinel in case a system call returns None.
46
+ _DEFAULT_SELECTOR = None
47
+
48
+
49
+ class SelectorError(Exception):
50
+ def __init__(self, errcode):
51
+ super(SelectorError, self).__init__()
52
+ self.errno = errcode
53
+
54
+ def __repr__(self):
55
+ return "<SelectorError errno={0}>".format(self.errno)
56
+
57
+ def __str__(self):
58
+ return self.__repr__()
59
+
60
+
61
+ def _fileobj_to_fd(fileobj):
62
+ """ Return a file descriptor from a file object. If
63
+ given an integer will simply return that integer back. """
64
+ if isinstance(fileobj, int):
65
+ fd = fileobj
66
+ else:
67
+ try:
68
+ fd = int(fileobj.fileno())
69
+ except (AttributeError, TypeError, ValueError):
70
+ raise ValueError("Invalid file object: {0!r}".format(fileobj))
71
+ if fd < 0:
72
+ raise ValueError("Invalid file descriptor: {0}".format(fd))
73
+ return fd
74
+
75
+
76
+ # Determine which function to use to wrap system calls because Python 3.5+
77
+ # already handles the case when system calls are interrupted.
78
+ if sys.version_info >= (3, 5):
79
+ def _syscall_wrapper(func, _, *args, **kwargs):
80
+ """ This is the short-circuit version of the below logic
81
+ because in Python 3.5+ all system calls automatically restart
82
+ and recalculate their timeouts. """
83
+ try:
84
+ return func(*args, **kwargs)
85
+ except (OSError, IOError, select.error) as e:
86
+ errcode = None
87
+ if hasattr(e, "errno"):
88
+ errcode = e.errno
89
+ raise SelectorError(errcode)
90
+ else:
91
+ def _syscall_wrapper(func, recalc_timeout, *args, **kwargs):
92
+ """ Wrapper function for syscalls that could fail due to EINTR.
93
+ All functions should be retried if there is time left in the timeout
94
+ in accordance with PEP 475. """
95
+ timeout = kwargs.get("timeout", None)
96
+ if timeout is None:
97
+ expires = None
98
+ recalc_timeout = False
99
+ else:
100
+ timeout = float(timeout)
101
+ if timeout < 0.0: # Timeout less than 0 treated as no timeout.
102
+ expires = None
103
+ else:
104
+ expires = monotonic() + timeout
105
+
106
+ args = list(args)
107
+ if recalc_timeout and "timeout" not in kwargs:
108
+ raise ValueError(
109
+ "Timeout must be in args or kwargs to be recalculated")
110
+
111
+ result = _SYSCALL_SENTINEL
112
+ while result is _SYSCALL_SENTINEL:
113
+ try:
114
+ result = func(*args, **kwargs)
115
+ # OSError is thrown by select.select
116
+ # IOError is thrown by select.epoll.poll
117
+ # select.error is thrown by select.poll.poll
118
+ # Aren't we thankful for Python 3.x rework for exceptions?
119
+ except (OSError, IOError, select.error) as e:
120
+ # select.error wasn't a subclass of OSError in the past.
121
+ errcode = None
122
+ if hasattr(e, "errno"):
123
+ errcode = e.errno
124
+ elif hasattr(e, "args"):
125
+ errcode = e.args[0]
126
+
127
+ # Also test for the Windows equivalent of EINTR.
128
+ is_interrupt = (errcode == errno.EINTR or (hasattr(errno, "WSAEINTR") and
129
+ errcode == errno.WSAEINTR))
130
+
131
+ if is_interrupt:
132
+ if expires is not None:
133
+ current_time = monotonic()
134
+ if current_time > expires:
135
+ raise OSError(errno=errno.ETIMEDOUT)
136
+ if recalc_timeout:
137
+ if "timeout" in kwargs:
138
+ kwargs["timeout"] = expires - current_time
139
+ continue
140
+ if errcode:
141
+ raise SelectorError(errcode)
142
+ else:
143
+ raise
144
+ return result
145
+
146
+
147
+ SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
148
+
149
+
150
+ class _SelectorMapping(Mapping):
151
+ """ Mapping of file objects to selector keys """
152
+
153
+ def __init__(self, selector):
154
+ self._selector = selector
155
+
156
+ def __len__(self):
157
+ return len(self._selector._fd_to_key)
158
+
159
+ def __getitem__(self, fileobj):
160
+ try:
161
+ fd = self._selector._fileobj_lookup(fileobj)
162
+ return self._selector._fd_to_key[fd]
163
+ except KeyError:
164
+ raise KeyError("{0!r} is not registered.".format(fileobj))
165
+
166
+ def __iter__(self):
167
+ return iter(self._selector._fd_to_key)
168
+
169
+
170
+ class BaseSelector(object):
171
+ """ Abstract Selector class
172
+
173
+ A selector supports registering file objects to be monitored
174
+ for specific I/O events.
175
+
176
+ A file object is a file descriptor or any object with a
177
+ `fileno()` method. An arbitrary object can be attached to the
178
+ file object which can be used for example to store context info,
179
+ a callback, etc.
180
+
181
+ A selector can use various implementations (select(), poll(), epoll(),
182
+ and kqueue()) depending on the platform. The 'DefaultSelector' class uses
183
+ the most efficient implementation for the current platform.
184
+ """
185
+ def __init__(self):
186
+ # Maps file descriptors to keys.
187
+ self._fd_to_key = {}
188
+
189
+ # Read-only mapping returned by get_map()
190
+ self._map = _SelectorMapping(self)
191
+
192
+ def _fileobj_lookup(self, fileobj):
193
+ """ Return a file descriptor from a file object.
194
+ This wraps _fileobj_to_fd() to do an exhaustive
195
+ search in case the object is invalid but we still
196
+ have it in our map. Used by unregister() so we can
197
+ unregister an object that was previously registered
198
+ even if it is closed. It is also used by _SelectorMapping
199
+ """
200
+ try:
201
+ return _fileobj_to_fd(fileobj)
202
+ except ValueError:
203
+
204
+ # Search through all our mapped keys.
205
+ for key in self._fd_to_key.values():
206
+ if key.fileobj is fileobj:
207
+ return key.fd
208
+
209
+ # Raise ValueError after all.
210
+ raise
211
+
212
+ def register(self, fileobj, events, data=None):
213
+ """ Register a file object for a set of events to monitor. """
214
+ if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
215
+ raise ValueError("Invalid events: {0!r}".format(events))
216
+
217
+ key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
218
+
219
+ if key.fd in self._fd_to_key:
220
+ raise KeyError("{0!r} (FD {1}) is already registered"
221
+ .format(fileobj, key.fd))
222
+
223
+ self._fd_to_key[key.fd] = key
224
+ return key
225
+
226
+ def unregister(self, fileobj):
227
+ """ Unregister a file object from being monitored. """
228
+ try:
229
+ key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
230
+ except KeyError:
231
+ raise KeyError("{0!r} is not registered".format(fileobj))
232
+
233
+ # Getting the fileno of a closed socket on Windows errors with EBADF.
234
+ except socket.error as e: # Platform-specific: Windows.
235
+ if e.errno != errno.EBADF:
236
+ raise
237
+ else:
238
+ for key in self._fd_to_key.values():
239
+ if key.fileobj is fileobj:
240
+ self._fd_to_key.pop(key.fd)
241
+ break
242
+ else:
243
+ raise KeyError("{0!r} is not registered".format(fileobj))
244
+ return key
245
+
246
+ def modify(self, fileobj, events, data=None):
247
+ """ Change a registered file object monitored events and data. """
248
+ # NOTE: Some subclasses optimize this operation even further.
249
+ try:
250
+ key = self._fd_to_key[self._fileobj_lookup(fileobj)]
251
+ except KeyError:
252
+ raise KeyError("{0!r} is not registered".format(fileobj))
253
+
254
+ if events != key.events:
255
+ self.unregister(fileobj)
256
+ key = self.register(fileobj, events, data)
257
+
258
+ elif data != key.data:
259
+ # Use a shortcut to update the data.
260
+ key = key._replace(data=data)
261
+ self._fd_to_key[key.fd] = key
262
+
263
+ return key
264
+
265
+ def select(self, timeout=None):
266
+ """ Perform the actual selection until some monitored file objects
267
+ are ready or the timeout expires. """
268
+ raise NotImplementedError()
269
+
270
+ def close(self):
271
+ """ Close the selector. This must be called to ensure that all
272
+ underlying resources are freed. """
273
+ self._fd_to_key.clear()
274
+ self._map = None
275
+
276
+ def get_key(self, fileobj):
277
+ """ Return the key associated with a registered file object. """
278
+ mapping = self.get_map()
279
+ if mapping is None:
280
+ raise RuntimeError("Selector is closed")
281
+ try:
282
+ return mapping[fileobj]
283
+ except KeyError:
284
+ raise KeyError("{0!r} is not registered".format(fileobj))
285
+
286
+ def get_map(self):
287
+ """ Return a mapping of file objects to selector keys """
288
+ return self._map
289
+
290
+ def _key_from_fd(self, fd):
291
+ """ Return the key associated to a given file descriptor
292
+ Return None if it is not found. """
293
+ try:
294
+ return self._fd_to_key[fd]
295
+ except KeyError:
296
+ return None
297
+
298
+ def __enter__(self):
299
+ return self
300
+
301
+ def __exit__(self, *args):
302
+ self.close()
303
+
304
+
305
+ # Almost all platforms have select.select()
306
+ if hasattr(select, "select"):
307
+ class SelectSelector(BaseSelector):
308
+ """ Select-based selector. """
309
+ def __init__(self):
310
+ super(SelectSelector, self).__init__()
311
+ self._readers = set()
312
+ self._writers = set()
313
+
314
+ def register(self, fileobj, events, data=None):
315
+ key = super(SelectSelector, self).register(fileobj, events, data)
316
+ if events & EVENT_READ:
317
+ self._readers.add(key.fd)
318
+ if events & EVENT_WRITE:
319
+ self._writers.add(key.fd)
320
+ return key
321
+
322
+ def unregister(self, fileobj):
323
+ key = super(SelectSelector, self).unregister(fileobj)
324
+ self._readers.discard(key.fd)
325
+ self._writers.discard(key.fd)
326
+ return key
327
+
328
+ def _select(self, r, w, timeout=None):
329
+ """ Wrapper for select.select because timeout is a positional arg """
330
+ return select.select(r, w, [], timeout)
331
+
332
+ def select(self, timeout=None):
333
+ # Selecting on empty lists on Windows errors out.
334
+ if not len(self._readers) and not len(self._writers):
335
+ return []
336
+
337
+ timeout = None if timeout is None else max(timeout, 0.0)
338
+ ready = []
339
+ r, w, _ = _syscall_wrapper(self._select, True, self._readers,
340
+ self._writers, timeout)
341
+ r = set(r)
342
+ w = set(w)
343
+ for fd in r | w:
344
+ events = 0
345
+ if fd in r:
346
+ events |= EVENT_READ
347
+ if fd in w:
348
+ events |= EVENT_WRITE
349
+
350
+ key = self._key_from_fd(fd)
351
+ if key:
352
+ ready.append((key, events & key.events))
353
+ return ready
354
+
355
+
356
+ if hasattr(select, "poll"):
357
+ class PollSelector(BaseSelector):
358
+ """ Poll-based selector """
359
+ def __init__(self):
360
+ super(PollSelector, self).__init__()
361
+ self._poll = select.poll()
362
+
363
+ def register(self, fileobj, events, data=None):
364
+ key = super(PollSelector, self).register(fileobj, events, data)
365
+ event_mask = 0
366
+ if events & EVENT_READ:
367
+ event_mask |= select.POLLIN
368
+ if events & EVENT_WRITE:
369
+ event_mask |= select.POLLOUT
370
+ self._poll.register(key.fd, event_mask)
371
+ return key
372
+
373
+ def unregister(self, fileobj):
374
+ key = super(PollSelector, self).unregister(fileobj)
375
+ self._poll.unregister(key.fd)
376
+ return key
377
+
378
+ def _wrap_poll(self, timeout=None):
379
+ """ Wrapper function for select.poll.poll() so that
380
+ _syscall_wrapper can work with only seconds. """
381
+ if timeout is not None:
382
+ if timeout <= 0:
383
+ timeout = 0
384
+ else:
385
+ # select.poll.poll() has a resolution of 1 millisecond,
386
+ # round away from zero to wait *at least* timeout seconds.
387
+ timeout = math.ceil(timeout * 1e3)
388
+
389
+ result = self._poll.poll(timeout)
390
+ return result
391
+
392
+ def select(self, timeout=None):
393
+ ready = []
394
+ fd_events = _syscall_wrapper(self._wrap_poll, True, timeout=timeout)
395
+ for fd, event_mask in fd_events:
396
+ events = 0
397
+ if event_mask & ~select.POLLIN:
398
+ events |= EVENT_WRITE
399
+ if event_mask & ~select.POLLOUT:
400
+ events |= EVENT_READ
401
+
402
+ key = self._key_from_fd(fd)
403
+ if key:
404
+ ready.append((key, events & key.events))
405
+
406
+ return ready
407
+
408
+
409
+ if hasattr(select, "epoll"):
410
+ class EpollSelector(BaseSelector):
411
+ """ Epoll-based selector """
412
+ def __init__(self):
413
+ super(EpollSelector, self).__init__()
414
+ self._epoll = select.epoll()
415
+
416
+ def fileno(self):
417
+ return self._epoll.fileno()
418
+
419
+ def register(self, fileobj, events, data=None):
420
+ key = super(EpollSelector, self).register(fileobj, events, data)
421
+ events_mask = 0
422
+ if events & EVENT_READ:
423
+ events_mask |= select.EPOLLIN
424
+ if events & EVENT_WRITE:
425
+ events_mask |= select.EPOLLOUT
426
+ _syscall_wrapper(self._epoll.register, False, key.fd, events_mask)
427
+ return key
428
+
429
+ def unregister(self, fileobj):
430
+ key = super(EpollSelector, self).unregister(fileobj)
431
+ try:
432
+ _syscall_wrapper(self._epoll.unregister, False, key.fd)
433
+ except SelectorError:
434
+ # This can occur when the fd was closed since registry.
435
+ pass
436
+ return key
437
+
438
+ def select(self, timeout=None):
439
+ if timeout is not None:
440
+ if timeout <= 0:
441
+ timeout = 0.0
442
+ else:
443
+ # select.epoll.poll() has a resolution of 1 millisecond
444
+ # but luckily takes seconds so we don't need a wrapper
445
+ # like PollSelector. Just for better rounding.
446
+ timeout = math.ceil(timeout * 1e3) * 1e-3
447
+ timeout = float(timeout)
448
+ else:
449
+ timeout = -1.0 # epoll.poll() must have a float.
450
+
451
+ # We always want at least 1 to ensure that select can be called
452
+ # with no file descriptors registered. Otherwise will fail.
453
+ max_events = max(len(self._fd_to_key), 1)
454
+
455
+ ready = []
456
+ fd_events = _syscall_wrapper(self._epoll.poll, True,
457
+ timeout=timeout,
458
+ maxevents=max_events)
459
+ for fd, event_mask in fd_events:
460
+ events = 0
461
+ if event_mask & ~select.EPOLLIN:
462
+ events |= EVENT_WRITE
463
+ if event_mask & ~select.EPOLLOUT:
464
+ events |= EVENT_READ
465
+
466
+ key = self._key_from_fd(fd)
467
+ if key:
468
+ ready.append((key, events & key.events))
469
+ return ready
470
+
471
+ def close(self):
472
+ self._epoll.close()
473
+ super(EpollSelector, self).close()
474
+
475
+
476
+ if hasattr(select, "kqueue"):
477
+ class KqueueSelector(BaseSelector):
478
+ """ Kqueue / Kevent-based selector """
479
+ def __init__(self):
480
+ super(KqueueSelector, self).__init__()
481
+ self._kqueue = select.kqueue()
482
+
483
+ def fileno(self):
484
+ return self._kqueue.fileno()
485
+
486
+ def register(self, fileobj, events, data=None):
487
+ key = super(KqueueSelector, self).register(fileobj, events, data)
488
+ if events & EVENT_READ:
489
+ kevent = select.kevent(key.fd,
490
+ select.KQ_FILTER_READ,
491
+ select.KQ_EV_ADD)
492
+
493
+ _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
494
+
495
+ if events & EVENT_WRITE:
496
+ kevent = select.kevent(key.fd,
497
+ select.KQ_FILTER_WRITE,
498
+ select.KQ_EV_ADD)
499
+
500
+ _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
501
+
502
+ return key
503
+
504
+ def unregister(self, fileobj):
505
+ key = super(KqueueSelector, self).unregister(fileobj)
506
+ if key.events & EVENT_READ:
507
+ kevent = select.kevent(key.fd,
508
+ select.KQ_FILTER_READ,
509
+ select.KQ_EV_DELETE)
510
+ try:
511
+ _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
512
+ except SelectorError:
513
+ pass
514
+ if key.events & EVENT_WRITE:
515
+ kevent = select.kevent(key.fd,
516
+ select.KQ_FILTER_WRITE,
517
+ select.KQ_EV_DELETE)
518
+ try:
519
+ _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0)
520
+ except SelectorError:
521
+ pass
522
+
523
+ return key
524
+
525
+ def select(self, timeout=None):
526
+ if timeout is not None:
527
+ timeout = max(timeout, 0)
528
+
529
+ max_events = len(self._fd_to_key) * 2
530
+ ready_fds = {}
531
+
532
+ kevent_list = _syscall_wrapper(self._kqueue.control, True,
533
+ None, max_events, timeout)
534
+
535
+ for kevent in kevent_list:
536
+ fd = kevent.ident
537
+ event_mask = kevent.filter
538
+ events = 0
539
+ if event_mask == select.KQ_FILTER_READ:
540
+ events |= EVENT_READ
541
+ if event_mask == select.KQ_FILTER_WRITE:
542
+ events |= EVENT_WRITE
543
+
544
+ key = self._key_from_fd(fd)
545
+ if key:
546
+ if key.fd not in ready_fds:
547
+ ready_fds[key.fd] = (key, events & key.events)
548
+ else:
549
+ old_events = ready_fds[key.fd][1]
550
+ ready_fds[key.fd] = (key, (events | old_events) & key.events)
551
+
552
+ return list(ready_fds.values())
553
+
554
+ def close(self):
555
+ self._kqueue.close()
556
+ super(KqueueSelector, self).close()
557
+
558
+
559
+ if not hasattr(select, 'select'): # Platform-specific: AppEngine
560
+ HAS_SELECT = False
561
+
562
+
563
+ def _can_allocate(struct):
564
+ """ Checks that select structs can be allocated by the underlying
565
+ operating system, not just advertised by the select module. We don't
566
+ check select() because we'll be hopeful that most platforms that
567
+ don't have it available will not advertise it. (ie: GAE) """
568
+ try:
569
+ # select.poll() objects won't fail until used.
570
+ if struct == 'poll':
571
+ p = select.poll()
572
+ p.poll(0)
573
+
574
+ # All others will fail on allocation.
575
+ else:
576
+ getattr(select, struct)().close()
577
+ return True
578
+ except (OSError, AttributeError) as e:
579
+ return False
580
+
581
+
582
+ # Choose the best implementation, roughly:
583
+ # kqueue == epoll > poll > select. Devpoll not supported. (See above)
584
+ # select() also can't accept a FD > FD_SETSIZE (usually around 1024)
585
+ def DefaultSelector():
586
+ """ This function serves as a first call for DefaultSelector to
587
+ detect if the select module is being monkey-patched incorrectly
588
+ by eventlet, greenlet, and preserve proper behavior. """
589
+ global _DEFAULT_SELECTOR
590
+ if _DEFAULT_SELECTOR is None:
591
+ if _can_allocate('kqueue'):
592
+ _DEFAULT_SELECTOR = KqueueSelector
593
+ elif _can_allocate('epoll'):
594
+ _DEFAULT_SELECTOR = EpollSelector
595
+ elif _can_allocate('poll'):
596
+ _DEFAULT_SELECTOR = PollSelector
597
+ elif hasattr(select, 'select'):
598
+ _DEFAULT_SELECTOR = SelectSelector
599
+ else: # Platform-specific: AppEngine
600
+ raise ValueError('Platform does not have a selector')
601
+ return _DEFAULT_SELECTOR()