├── .gitignore ├── LICENSE ├── README.md ├── eospy ├── __init__.py ├── api.py ├── exceptions.py ├── http_client.py └── log.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .idea/ 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eospy 2 | Python library(wrapper) of the EOS.IO project. 3 | 4 | As of before the June launch, this library is pretty much in flux. 5 | And there are some methods that have not been tested. Don't hold your breath. 6 | 7 | ## Installation 8 | 9 | You can install this package as usual with pip: 10 | 11 | `pip install eospy` 12 | 13 | ## Example 14 | 15 | ### ChainApi 16 | 17 | ```python 18 | from eospy import ChainApi 19 | 20 | 21 | chain = ChainApi(hosts=['http://hk.party.eosfans.io:8888/', ]) 22 | 23 | # chain = ChainApi.local_network() 24 | 25 | chain.get_info() 26 | chain.get_block('5') 27 | chain.get_account('eostea') 28 | 29 | # More: https://eosio.github.io/eos/group__eosiorpc.html#chainrpc 30 | ``` 31 | 32 | ### WalletApi 33 | 34 | Not tested 35 | 36 | ```python 37 | from eospy import WalletApi 38 | 39 | 40 | wallet = WalletApi(hosts=['http://hk.party.eosfans.io:8888/', ]) 41 | 42 | # or wallet = WalletApi.local_network() 43 | 44 | wallet.create('eostea') 45 | wallet.open('eostea') 46 | 47 | # More: https://eosio.github.io/eos/group__eosiorpc.html#walletrpc 48 | ``` 49 | 50 | ## Exceptions 51 | ```python 52 | from eospy.exceptions import BaseError 53 | 54 | try: 55 | pass 56 | # something 57 | except BaseError as err: 58 | print(err) 59 | 60 | # All exceptions inherit from BaseError 61 | ``` 62 | -------------------------------------------------------------------------------- /eospy/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import WalletApi, ChainApi 2 | 3 | 4 | VERSION = '0.0.1' 5 | 6 | __all__ = ['WalletApi', 'ChainApi', 'VERSION'] 7 | -------------------------------------------------------------------------------- /eospy/api.py: -------------------------------------------------------------------------------- 1 | import json as _json 2 | from .http_client import HTTPClient 3 | 4 | 5 | class ChainApi(HTTPClient): 6 | 7 | @classmethod 8 | def local_network(cls, timeout=5, **kwargs): 9 | return cls(hosts=['http://127.0.0.1:8888'], 10 | timeout=timeout, **kwargs) 11 | 12 | def __init__(self, hosts, timeout=5, **kwargs): 13 | """ 14 | :param hosts: [''] 15 | :param kwargs: 16 | """ 17 | super().__init__(hosts, timeout=timeout, **kwargs) 18 | 19 | def get_info(self): 20 | """ 21 | https://eosio.github.io/eos/group__eosiorpc.html#v1chaingetinfo 22 | :return: 23 | """ 24 | return self._exec( 25 | api='chain', 26 | endpoint='get_info', 27 | method=self.GET, 28 | version='v1' 29 | ) 30 | 31 | def get_block(self, block_num_or_id): 32 | """ 33 | https://eosio.github.io/eos/group__eosiorpc.html#v1chaingetblock 34 | :param block_num_or_id: 35 | :return: 36 | """ 37 | return self._exec( 38 | api='chain', 39 | endpoint='get_block', 40 | method=self.POST, 41 | version='v1', 42 | body={ 43 | 'block_num_or_id': block_num_or_id 44 | } 45 | ) 46 | 47 | def get_account(self, account_name): 48 | """ 49 | https://eosio.github.io/eos/group__eosiorpc.html#v1chaingetaccount 50 | :param account_name: 51 | :return: 52 | """ 53 | return self._exec( 54 | api='chain', 55 | endpoint='get_account', 56 | method=self.POST, 57 | version='v1', 58 | body={ 59 | 'account_name': account_name 60 | } 61 | ) 62 | 63 | def get_code(self, account_name): 64 | """ 65 | :param account_name: 66 | :return: 67 | """ 68 | return self._exec( 69 | api='chain', 70 | endpoint='get_code', 71 | method=self.POST, 72 | version='v1', 73 | body={ 74 | 'account_name': account_name 75 | } 76 | ) 77 | 78 | def get_table_rows(self, scope: str, code: str, table: str, json: bool=True, lower_bound: int=None, 79 | upper_bound: int=None, limit: int=None): 80 | """ 81 | https://eosio.github.io/eos/group__eosiorpc.html#v1chaingetcode 82 | :param scope: 83 | :param code: 84 | :param table: 85 | :param json: 86 | :param lower_bound: 87 | :param upper_bound: 88 | :param limit: 89 | :return: 90 | """ 91 | body = { 92 | 'scope': scope, 93 | 'code': code, 94 | 'table': table, 95 | 'json': json 96 | } 97 | if lower_bound is not None: 98 | body['lower_bound'] = lower_bound 99 | if upper_bound is not None: 100 | body['upper_bound'] = upper_bound 101 | if limit is not None: 102 | body['limit'] = limit 103 | 104 | return self._exec( 105 | api='chain', 106 | endpoint='get_table_rows', 107 | method=self.POST, 108 | version='v1', 109 | body=body 110 | ) 111 | 112 | def abi_json_to_bin(self, code, action, args: dict): 113 | """ 114 | https://eosio.github.io/eos/group__eosiorpc.html#v1chainabijsontobin 115 | :param code: 116 | :param action: 117 | :param args: 118 | :return: 119 | """ 120 | body = { 121 | 'code': code, 122 | 'action': action, 123 | 'args': _json.dumps(args) 124 | } 125 | return self._exec( 126 | api='chain', 127 | endpoint='abi_json_to_bin', 128 | method=self.POST, 129 | version='v1', 130 | body=body 131 | ) 132 | 133 | def abi_bin_to_json(self, code, action, bin_args): 134 | """ 135 | :param code: 136 | :param action: 137 | :param bin_args: 138 | :return: 139 | """ 140 | body = { 141 | 'code': code, 142 | 'action': action, 143 | 'binargs': bin_args 144 | } 145 | return self._exec( 146 | api='chain', 147 | endpoint='abi_bin_to_json', 148 | method=self.POST, 149 | version='v1', 150 | body=body 151 | ) 152 | 153 | def push_transaction(self, ref_block_num: str, ref_block_prefix: str, expiration: str, scope: list, 154 | actions: list, signatures: list, authorizations: list): 155 | """ 156 | :param ref_block_num: 157 | :param ref_block_prefix: 158 | :param expiration: datetime iosformat 159 | :param scope: 160 | :param actions: 161 | :param signatures: 162 | :param authorizations: 163 | :return: 164 | """ 165 | body = { 166 | 'ref_block_num': ref_block_num, 167 | 'ref_block_prefix': ref_block_prefix, 168 | 'expiration': expiration, 169 | 'scope': scope, 170 | 'actions': actions, 171 | "signatures": signatures, 172 | "authorizations": authorizations 173 | } 174 | return self._exec( 175 | api='chain', 176 | endpoint='push_transaction', 177 | method=self.POST, 178 | version='v1', 179 | body=body 180 | ) 181 | 182 | def push_transactions(self, body_list: list): 183 | """ 184 | https://eosio.github.io/eos/group__eosiorpc.html#v1chainpushtransactions 185 | :param body_list: 186 | :return: 187 | 188 | >>> self.push_transactions( 189 | body_list=[{"ref_block_num":"101", 190 | "ref_block_prefix":"4159312339", 191 | "expiration":"2017-09-25T06:28:49", 192 | "scope":["initb","initc"], 193 | "actions":[{"code":"currency","type":"transfer","recipients":["initb","initc"], 194 | "authorization":[{"account":"initb","permission":"active"}],"data":"000000000041934b000000008041934be803000000000000"}], 195 | "signatures":[],"authorizations":[]}, {"ref_block_num":"101","ref_block_prefix":"4159312339", 196 | "expiration":"2017-09-25T06:28:49","scope":["inita","initc"], 197 | "actions":[{"code":"currency","type":"transfer","recipients":["inita","initc"], 198 | "authorization":[{"account":"inita","permission":"active"}],"data":"000000008040934b000000008041934be803000000000000"}], 199 | "signatures":[],"authorizations":[]}]' 200 | 201 | ) 202 | """ 203 | return self._exec( 204 | api='chain', 205 | endpoint='push_transactions', 206 | method=self.POST, 207 | version='v1', 208 | body=body_list 209 | ) 210 | 211 | def get_required_keys(self, transaction: dict, available_keys: list): 212 | """ 213 | https://eosio.github.io/eos/group__eosiorpc.html#v1chaingetrequiredkeys 214 | :param transaction: 215 | :param available_keys: 216 | :return: 217 | """ 218 | body = { 219 | 'transaction': transaction, 220 | 'available_keys': available_keys 221 | } 222 | return self._exec( 223 | api='chain', 224 | endpoint='get_required_keys', 225 | method=self.POST, 226 | version='v1', 227 | body=body 228 | ) 229 | 230 | 231 | class WalletApi(HTTPClient): 232 | def __init__(self, hosts: list, timeout=5, **kwargs): 233 | super().__init__(hosts, timeout, **kwargs) 234 | FutureWarning('Incomplete and not tested') 235 | 236 | @classmethod 237 | def local_network(cls, timeout=5, **kwargs): 238 | return cls(hosts=['http://127.0.0.1:8888'], 239 | timeout=timeout, **kwargs) 240 | 241 | def create(self, wallet_name: str): 242 | """ 243 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletcreate 244 | :param wallet_name: 245 | :return: 246 | """ 247 | return self._exec( 248 | api='wallet', 249 | endpoint='create', 250 | method=self.POST, 251 | version='v1', 252 | body=wallet_name 253 | ) 254 | 255 | def open(self, wallet_name: str): 256 | """ 257 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletopen 258 | :param wallet_name: 259 | :return: 260 | """ 261 | return self._exec( 262 | api='wallet', 263 | endpoint='open', 264 | method=self.POST, 265 | version='v1', 266 | body=wallet_name 267 | ) 268 | 269 | def lock(self, wallet_name: str): 270 | """ 271 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletlock 272 | :param wallet_name: 273 | :return: 274 | """ 275 | return self._exec( 276 | api='wallet', 277 | endpoint='lock', 278 | method=self.POST, 279 | version='v1', 280 | body=wallet_name 281 | ) 282 | 283 | def lock_all(self): 284 | """ 285 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletlockall 286 | :return: 287 | """ 288 | return self._exec( 289 | api='wallet', 290 | endpoint='lock_all', 291 | method=self.POST, 292 | version='v1', 293 | ) 294 | 295 | def unlock(self, wallet_name: str, password: str): 296 | """ 297 | :param wallet_name: 298 | :param password: 299 | :return: 300 | """ 301 | return self._exec( 302 | api='wallet', 303 | endpoint='unlock', 304 | method=self.POST, 305 | version='v1', 306 | body=[wallet_name, password] 307 | ) 308 | 309 | def import_key(self, wallet_name: str, password: str): 310 | """ 311 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletimportkey 312 | :param wallet_name: 313 | :param password: 314 | :return: 315 | """ 316 | return self._exec( 317 | api='wallet', 318 | endpoint='import_key', 319 | method=self.POST, 320 | version='v1', 321 | body=[wallet_name, password] 322 | ) 323 | 324 | def list_wallets(self): 325 | """ 326 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletlist 327 | :return: 328 | """ 329 | return self._exec( 330 | api='wallet', 331 | endpoint='list_wallets', 332 | method=self.GET, 333 | version='v1', 334 | ) 335 | 336 | def list_keys(self): 337 | """ 338 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletlistkeys 339 | :return: 340 | """ 341 | return self._exec( 342 | api='wallet', 343 | endpoint='list_keys', 344 | method=self.GET, 345 | version='v1', 346 | ) 347 | 348 | def get_public_keys(self): 349 | """ 350 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletgetpublickeys 351 | :return: 352 | """ 353 | return self._exec( 354 | api='wallet', 355 | endpoint='get_public_keys', 356 | method=self.GET, 357 | version='v1', 358 | ) 359 | 360 | def set_timeout(self, timeout: int): 361 | """ 362 | https://eosio.github.io/eos/group__eosiorpc.html#v1walletsettimeout 363 | :param timeout: 364 | :return: 365 | """ 366 | return self._exec( 367 | api='wallet', 368 | endpoint='set_timeout', 369 | method=self.POST, 370 | version='v1', 371 | body=str(timeout) 372 | ) 373 | 374 | def sign_transaction(self, transaction_list: list): 375 | """ 376 | 377 | :param transaction_list: 378 | :return: 379 | """ 380 | raise NotImplementedError() 381 | -------------------------------------------------------------------------------- /eospy/exceptions.py: -------------------------------------------------------------------------------- 1 | class BaseError(BaseException): 2 | pass 3 | 4 | 5 | class ParameterError(BaseError): 6 | pass 7 | 8 | 9 | class HostConnectionError(BaseError): 10 | pass 11 | -------------------------------------------------------------------------------- /eospy/http_client.py: -------------------------------------------------------------------------------- 1 | import json 2 | from abc import abstractmethod 3 | from requests.sessions import Session 4 | from urllib.parse import urlparse, ParseResult 5 | from requests.exceptions import BaseHTTPError, RequestException 6 | from .exceptions import ParameterError, HostConnectionError 7 | from .log import logger 8 | 9 | 10 | session = None 11 | 12 | 13 | def get_session() -> Session: 14 | global session 15 | if not session: 16 | session = Session() 17 | return session 18 | 19 | 20 | class HTTPClient: 21 | 22 | GET = 'GET' 23 | POST = 'POST' 24 | 25 | def __init__(self, hosts=None, timeout=5, **kwargs): 26 | """ 27 | :param hosts: [''] 28 | :param kwargs: 29 | """ 30 | self.client = get_session() 31 | if isinstance(hosts, str): 32 | self.hosts = [hosts] 33 | self.hosts = hosts 34 | self.host_index = 0 35 | self.timeout = timeout 36 | self.previous_available_host_index = 0 37 | 38 | @classmethod 39 | @abstractmethod 40 | def local_network(cls): 41 | pass 42 | 43 | def _check_host(self) -> bool: 44 | """ 45 | :return: 46 | """ 47 | if isinstance(self.hosts, list): 48 | for h in self.hosts: 49 | p: ParseResult = urlparse(h) 50 | if not (p.scheme in ['http', 'https'] and p.path == '/'): 51 | return False 52 | elif isinstance(self.hosts, str): 53 | p: ParseResult = urlparse(self.hosts) 54 | if not (p.scheme in ['http', 'https'] and p.path == '/'): 55 | return False 56 | else: 57 | return False 58 | return True 59 | 60 | @property 61 | def current_host(self) -> str: 62 | """ 63 | :return: 64 | """ 65 | return self.hosts[self.host_index] 66 | 67 | def _next_host(self) -> str: 68 | """ 69 | :return: 70 | """ 71 | if self.host_index >= len(self.hosts) - 1: 72 | self.host_index = 0 73 | else: 74 | self.host_index += 1 75 | return self.hosts[self.host_index] 76 | 77 | def _is_last_host(self): 78 | if self.host_index == len(self.hosts) - 1: 79 | return True 80 | return False 81 | 82 | def _exec(self, api: str, endpoint: str, method: str, version: str = 'v1', 83 | body: dict or list or str=None, params: dict=None): 84 | """ 85 | :param api: one of ['chain', 'wallet'] 86 | :param endpoint: 87 | :param body: 88 | :param method: 89 | :return: 90 | """ 91 | url = self.current_host + version + '/' + api + '/' + endpoint 92 | if method.upper() not in [self.GET, self.POST]: 93 | raise ParameterError('Method not supported: {}'.format(method)) 94 | try: 95 | resp = self.client.request(method=method, url=url, 96 | params=params, data=json.dumps(body) if body is not None else None, 97 | timeout=self.timeout) 98 | self.previous_available_host_index = self.host_index 99 | if resp.status_code in [200, 500, 404]: 100 | return resp.json() 101 | return dict() 102 | except (RequestException, BaseHTTPError) as err: 103 | logger.warning(err) 104 | logger.debug(f'Switched to {self._next_host()}') 105 | if self.host_index == self.previous_available_host_index: 106 | raise HostConnectionError('No connectable server') 107 | return self._exec(api, endpoint, method, version, body, params) 108 | -------------------------------------------------------------------------------- /eospy/log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | 4 | logger = logging.getLogger(__name__) 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from eospy import VERSION 3 | 4 | setup( 5 | name='eospy', 6 | version=VERSION, 7 | description='Python library of the EOS.IO project.', 8 | long_description='Python library of the EOS.IO project..', 9 | url='https://github.com/eosfansio/eospy', 10 | author='strahe', 11 | license='MIT', 12 | packages=['eospy'], 13 | install_requires=('requests', ), 14 | zip_safe=False, 15 | keywords=['eos', 'eosio', 'eospy', 'eospython'], 16 | classifiers=[ 17 | 'Development Status :: 3 - Alpha', 18 | 'License :: OSI Approved :: MIT License', 19 | 'Programming Language :: Python :: 3.6', 20 | 'Programming Language :: Python :: 3 :: Only', 21 | ], 22 | ) 23 | --------------------------------------------------------------------------------