From 593b4013c46d3dec518d0040ae478953329dcb9c Mon Sep 17 00:00:00 2001 From: deluxghost Date: Fri, 2 Nov 2018 20:54:33 +0800 Subject: [PATCH] remove pyswagger; add ErrorResponse --- ASF/__init__.py | 2 +- ASF/models.py | 78 +++++++++++++++++++++++++++++++----------------- README.md | 57 ++++++++++++++++++++++++----------- requirements.txt | 2 +- setup.py | 2 +- 5 files changed, 92 insertions(+), 49 deletions(-) diff --git a/ASF/__init__.py b/ASF/__init__.py index 8ebbc57..da2b2ab 100644 --- a/ASF/__init__.py +++ b/ASF/__init__.py @@ -1,3 +1,3 @@ -__version__ = '2.0.0' +__version__ = '2.1.0' from .models import IPC # noqa: F401 diff --git a/ASF/models.py b/ASF/models.py index dbe3232..e6f22fb 100644 --- a/ASF/models.py +++ b/ASF/models.py @@ -1,22 +1,33 @@ +import asyncio + import aiohttp -import pyswagger from . import utils class IPC: - def __init__(self, ipc='http://127.0.0.1:1242/', password='', timeout=5): + def __init__(self, ipc='http://127.0.0.1:1242/', password='', timeout=10): self._ipc = ipc self._password = password self._timeout = timeout async def __aenter__(self): - self._swagger = pyswagger.App.create(utils.build_url(self._ipc, '/swagger/ASF/swagger.json')) - for api in self._swagger.op.values(): + headers = dict() + if self._password: + headers['Authentication'] = self._password + timeout = aiohttp.ClientTimeout(total=self._timeout) + self._session = aiohttp.ClientSession(headers=headers, timeout=timeout) + try: + async with self._session.get(utils.build_url(self._ipc, '/swagger/ASF/swagger.json')) as resp: + self._swagger = await resp.json() + except Exception: + await self._session.close() + raise + for path in self._swagger['paths'].keys(): p = self p_path = '' - for node in api.path.strip(utils.sep).split(utils.sep): + for node in path.strip(utils.sep).split(utils.sep): p_path += f'/{node}' if node.startswith('{') and node.endswith('}'): arg = node[1:-1] @@ -27,11 +38,6 @@ async def __aenter__(self): setattr(p, node, Endpoint(self)) p = getattr(p, node) p._path = p_path - headers = dict() - if self._password: - headers['Authentication'] = self._password - timeout = aiohttp.ClientTimeout(total=self._timeout) - self._session = aiohttp.ClientSession(headers=headers, timeout=timeout) return self async def __aexit__(self, exc_type, exc_val, exc_tb): @@ -57,30 +63,36 @@ async def _request(self, method, body=None, params=None, **kw): url = utils.build_url(self._ipc._ipc, self._path) for k, v in kw.items(): url = url.replace(f'{{{k}}}', utils.quote(v)) - async with session.request(method, url, json=body, params=params) as resp: - try: - json_data = await resp.json() - except Exception: - json_data = None - text = await resp.text() - return ASFResponse(resp, json_data, text) + try: + async with session.request(method, url, json=body, params=params) as resp: + try: + json_data = await resp.json() + except Exception: + json_data = None + text = await resp.text() + return ASFResponse(resp, json_data, text) + except (asyncio.TimeoutError, aiohttp.ClientError) as exc: + return ErrorResponse(url, exc.__class__.__name__) async def ws(self, **kw): session = self._ipc._session url = utils.build_url(self._ipc._ipc, self._path) for k, v in kw.items(): url = url.replace(f'{{{k}}}', utils.quote(v)) - async with session.ws_connect(url) as ws: - async for msg in ws: - if msg.type == aiohttp.WSMsgType.TEXT: - try: - json_data = msg.json() - except Exception: - json_data = None - text = msg.data - yield WSResponse(url, json_data, text) - elif msg.type == aiohttp.WSMsgType.ERROR or msg.type == aiohttp.WSMsgType.CLOSE: - break + try: + async with session.ws_connect(url) as ws: + async for msg in ws: + if msg.type == aiohttp.WSMsgType.TEXT: + try: + json_data = msg.json() + except Exception: + json_data = None + text = msg.data + yield WSResponse(url, json_data, text) + elif msg.type == aiohttp.WSMsgType.ERROR or msg.type == aiohttp.WSMsgType.CLOSE: + break + except (asyncio.TimeoutError, aiohttp.ClientError) as exc: + yield ErrorResponse(url, exc.__class__.__name__) async def get(self, **kw): return await self._request('get', **kw) @@ -135,3 +147,13 @@ def __init__(self, resp, json_data, text): self.Message = self.message self.Result = self.result self.Success = self.success + + +class ErrorResponse: + + def __init__(self, url, message): + self.url = url + self.OK = self.ok = False + self.Message = self.message = message + self.Result = self.result = None + self.Success = self.success = False diff --git a/README.md b/README.md index 0af8372..72ef5d2 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,22 @@ This example shows how to send a command to ASF: import asyncio from ASF import IPC -async def command(cmd): +async def command(asf, cmd): + return await asf.Api.Command['command'].post(command=cmd) + +async def main(): + # The IPC initialization time depends on the network async with IPC(ipc='http://127.0.0.1:1242', password='YOUR IPC PASSWORD') as asf: - resp = await asf.Api.Command['command'].post(command=cmd) - return resp.result + while True: + cmd = input('Enter a command: ') + resp = await command(asf, cmd) + if resp.success: + print(resp.result) + else: + print(f'Error: {resp.message}') -cmd = input('Enter a command: ') loop = asyncio.get_event_loop() -output = loop.run_until_complete(command(cmd)) -print(output) +output = loop.run_until_complete(main()) loop.close() ``` @@ -51,15 +58,16 @@ To get a list of all endpoints of ASF, open your web browser and visit the swagg You can see many endpoints with their path, such as `/Api/Bot/{botNames}`, this endpoint in ASF_IPC is `asf.Api.Bot['botNames']`. +The replacing rules are simple: change `/` to `.` and change `/{variable}` + Some more examples: ```python -async with IPC(...) as asf: - asf.Api.ASF # /Api/ASF - asf.Api.Command['command'] # /Api/Command/{command} - asf.Api.Bot['botNames'].Pause # /Api/Bot/{botNames}/Pause - asf.Api.WWW.GitHub.Releases # /Api/WWW/GitHub/Releases - asf.Api.WWW.GitHub.Releases['version'] # /Api/WWW/GitHub/Releases/{version} +asf.Api.ASF # /Api/ASF +asf.Api.Command['command'] # /Api/Command/{command} +asf.Api.Bot['botNames'].Pause # /Api/Bot/{botNames}/Pause +asf.Api.WWW.GitHub.Releases # /Api/WWW/GitHub/Releases +asf.Api.WWW.GitHub.Releases['version'] # /Api/WWW/GitHub/Releases/{version} ``` ## Send a request @@ -75,11 +83,11 @@ Some examples: ```python # POST /Api/Command/status%20asf -resp = await asf.Api.Command['command'].post(command='status asf') +await asf.Api.Command['command'].post(command='status asf') # GET /Api/WWW/GitHub/Releases?count=10 -resp = await asf.Api.WWW.GitHub.Releases.get(params={'count': 10}) +await asf.Api.WWW.GitHub.Releases.get(params={'count': 10}) # POST /Api/Bot/robot with json body {'BotConfig': ...} -resp = await asf.Api.Bot['botName'].post(body={'BotConfig': ...}, botName='robot') +await asf.Api.Bot['botName'].post(body={'BotConfig': ...}, botName='robot') ``` ## Get a response @@ -97,8 +105,21 @@ If ASF_IPC cannot give a value to some attributes, these attributes will be `Non Example for `/Api/NLog`: ```python -async def get_log(): - async with IPC(ipc='http://127.0.0.1:1242', password='YOUR IPC PASSWORD') as asf: - async for resp in asf.Api.NLog.ws(): # use ws() instead of get(), post()... +import asyncio +from ASF import IPC + +async def get_log(asf): + async for resp in asf.Api.NLog.ws(): # use ws() instead of get(), post()... + if resp.success: print(resp.result) + +async def main(): + async with IPC(ipc='http://127.0.0.1:1242', password='YOUR IPC PASSWORD') as asf: + while True: + await get_log(asf) + +loop = asyncio.get_event_loop() +output = loop.run_until_complete(main()) +loop.close() + ``` diff --git a/requirements.txt b/requirements.txt index 21b0803..a4f69f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ aiohttp -pyswagger +cchardet diff --git a/setup.py b/setup.py index 49de6a4..9a1a14e 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ def find_version(*file_paths): python_requires='>=3.6, <4', install_requires=[ 'aiohttp', - 'pyswagger' + 'cchardet', ], author='deluxghost', author_email='deluxghost@gmail.com',