From da6ba57b480410a961c2934dd0dbc00197fda8fc Mon Sep 17 00:00:00 2001 From: bot Date: Tue, 3 Dec 2024 13:19:34 +0000 Subject: [PATCH] Automated update of Base Application package. --- dist/app-1.0.0-py3-none-any.whl | Bin 6935 -> 6897 bytes dist/app-1.0.0.tar.gz | Bin 5657 -> 5502 bytes src/app/__init__.py | 6 +- src/app/__main__.py | 6 +- src/app/app.py | 137 ++++++++++++++++++-------------- src/app/args.py | 23 +++--- src/app/cli.py | 23 +++--- src/app/kim.py | 42 +++++----- src/app/repl.py | 3 +- src/app/server.py | 17 ++-- src/app/tests.py | 13 ++- 11 files changed, 142 insertions(+), 128 deletions(-) diff --git a/dist/app-1.0.0-py3-none-any.whl b/dist/app-1.0.0-py3-none-any.whl index 4fafb46ce70d6e53c49f15d6b5e9d1a759af8063..48bab7c2026108b12047508d2a78d81f117bf158 100644 GIT binary patch delta 5923 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 TWSw8y-;TE+B4i!nUw8fo5Sx2O delta 5964 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 qYG?!ps^P$d6_Y=3(b;c2;gS7yN{;RSD@B+h literal 5657 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 diff --git a/src/app/__init__.py b/src/app/__init__.py index 7e315cc..45ab4ae 100644 --- a/src/app/__init__.py +++ b/src/app/__init__.py @@ -1,9 +1,9 @@ -import logging +import logging logging.basicConfig( level=logging.DEBUG, - format='%(asctime)s - %(levelname)s - %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' + format="%(asctime)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", ) log = logging.getLogger(__name__) diff --git a/src/app/__main__.py b/src/app/__main__.py index e7713f8..2c9f57e 100644 --- a/src/app/__main__.py +++ b/src/app/__main__.py @@ -1,13 +1,11 @@ -from aiohttp import web -from .app import create_app from .args import parse_args from .server import serve + def main(): args = parse_args() serve(args.host, args.port) -if __name__ == '__main__': +if __name__ == "__main__": main() - diff --git a/src/app/app.py b/src/app/app.py index 25277e2..40b8e21 100644 --- a/src/app/app.py +++ b/src/app/app.py @@ -1,29 +1,41 @@ -from aiohttp import web +import json import time -from . import log -import json import uuid + import dataset +from aiohttp import web + +from . import log + def get_timestamp(): from datetime import datetime + now = datetime.now() formatted_datetime = now.strftime("%Y-%m-%d %H:%M:%S") return formatted_datetime + class BaseApplication(web.Application): - def __init__(self, username = None, password=None, cookie_name=None,session=None, *args, **kwargs): + def __init__( + self, + username=None, + password=None, + cookie_name=None, + session=None, + *args, + **kwargs, + ): self.cookie_name = cookie_name or str(uuid.uuid4()) - self.username = username - self.password = password + self.username = username + self.password = password self.session = session or {} - middlewares = kwargs.pop("middlewares",[]) + middlewares = kwargs.pop("middlewares", []) middlewares.append(self.request_middleware) middlewares.append(self.base64_auth_middleware) middlewares.append(self.session_middleware) super().__init__(*args, **kwargs) - async def authenticate(self, username, password): return self.username == username and self.password == password @@ -37,58 +49,60 @@ class BaseApplication(web.Application): return web.Response( status=401, text="Unauthorized", - headers={"WWW-Authenticate": 'Basic realm="Restricted"'} + headers={"WWW-Authenticate": 'Basic realm="Restricted"'}, ) - + try: encoded_credentials = auth_header.split(" ", 1)[1] decoded_credentials = base64.b64decode(encoded_credentials).decode("utf-8") username, password = decoded_credentials.split(":", 1) except (ValueError, base64.binascii.Error): return web.Response(status=400, text="Invalid Authorization Header") - + if not await self.authenticate(username, password): return web.Response( status=401, text="Invalid Credentials", - headers={"WWW-Authenticate": 'Basic realm="Restricted"'} + headers={"WWW-Authenticate": 'Basic realm="Restricted"'}, ) - + return await handler(request) + @web.middleware async def request_middleware(self, request, handler): time_start = time.time() created = get_timestamp() request = await handler(request) time_end = time.time() - await self.insert("http_access",dict( - created=created, - path=request.path, - duration=time_end - time_start, - )) + await self.insert( + "http_access", + { + "created": created, + "path": request.path, + "duration": time_end - time_start, + }, + ) return request @web.middleware - async def session_middleware(self,request, handler): + async def session_middleware(self, request, handler): # Process the request and get the response cookies = request.cookies session_id = cookies.get(self.cookie_name, None) - setattr(request,"session", self.session.get(session_id,{})) + setattr(request, "session", self.session.get(session_id, {})) response = await handler(request) - + if not session_id: session_id = str(uuid.uuid4()) - response.set_cookie( - self.cookie_name, - session_id, - max_age=3600, - httponly=True - ) + response.set_cookie(self.cookie_name, session_id, max_age=3600, httponly=True) return response + class WebDbApplication(BaseApplication): - def __init__(self, db=None, db_web=False,db_path="sqlite:///:memory:",*args, **kwargs): + def __init__( + self, db=None, db_web=False, db_path="sqlite:///:memory:", *args, **kwargs + ): super().__init__(*args, **kwargs) self.db_web = db_web self.db_path = db_path @@ -103,53 +117,57 @@ class WebDbApplication(BaseApplication): self.router.add_post("/delete", self.delete_handler) async def insert_handler(self, request): - obj = await request.json() + await request.json() response = await self.insert(request.get("table"), request.get("data")) return web.json_response(response) async def update_handler(self, request): - obj = await request.json() - response = await self.update(request.get('table'), request.get('data'), request.get('where',{})) + await request.json() + response = await self.update( + request.get("table"), request.get("data"), request.get("where", {}) + ) return web.json_response(response) - + async def upsert_handler(self, request): - obj = await request.json() - response = await self.upsert(request.get('table'), request.get('data'), request.get('keys',[])) + await request.json() + response = await self.upsert( + request.get("table"), request.get("data"), request.get("keys", []) + ) return web.json_response(response) async def find_handler(self, request): - obj = await request.json() - response = await self.find(request.get('table'), requesst.get('where',{})) + await request.json() + response = await self.find(request.get("table"), requesst.get("where", {})) return web.json_response(response) async def find_one_handler(self, request): - obj = await request.json() - response = await self.find_one(request.get('table'), requesst.get('where',{})) + await request.json() + response = await self.find_one(request.get("table"), requesst.get("where", {})) return web.json_response(response) async def delete_handler(self, request): - obj = await request.json() - response = await self.delete(request.get('table'), requesst.get('where',{})) + await request.json() + response = await self.delete(request.get("table"), requesst.get("where", {})) return web.json_response(response) async def set(self, key, value): value = json.dumps(value, default=str) - self.db['kv'].upsert(dict(key=key,value=value),['key']) + self.db["kv"].upsert({"key": key, "value": value}, ["key"]) async def get(self, key, default=None): - record = self.db['kv'].find_one(key=key) + record = self.db["kv"].find_one(key=key) if record: - return json.loads(record.get('value','null')) + return json.loads(record.get("value", "null")) return default async def insert(self, table_name, data): return self.db[table_name].insert(data) async def update(self, table_name, data, where): - return self.db[table_name].update(data,where) + return self.db[table_name].update(data, where) async def upsert(self, table_name, data, keys): - return self.db[table_name].upsert(data,keys or []) + return self.db[table_name].upsert(data, keys or []) async def find(self, table_name, filters): if not filters: @@ -162,17 +180,15 @@ class WebDbApplication(BaseApplication): try: return dict(self.db[table_name].find_one(**filters)) except ValueError: - return None + return None async def delete(self, table_name, where): return self.db[table_name].delete(**where) - - class Application(WebDbApplication): - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.on_startup.append(self.on_startup_task) self.router.add_get("/", self.index_handler) @@ -189,19 +205,22 @@ class Application(WebDbApplication): self.running_since = get_timestamp() async def inc_request_count(self): - request_count = await self.get("root_request_count",0) + request_count = await self.get("root_request_count", 0) request_count += 1 await self.set("root_request_count", request_count) - return request_count + return request_count - async def index_handler(self,request): - - return web.json_response(dict( - request_count=await self.inc_request_count(), - timestamp=get_timestamp(), - uptime=self.uptime, - running_since=self.running_since - ),content_type="application/json") + async def index_handler(self, request): + + return web.json_response( + { + "request_count": await self.inc_request_count(), + "timestamp": get_timestamp(), + "uptime": self.uptime, + "running_since": self.running_since, + }, + content_type="application/json", + ) def create_app(*args, **kwargs): diff --git a/src/app/args.py b/src/app/args.py index 57cb6f0..dff7717 100644 --- a/src/app/args.py +++ b/src/app/args.py @@ -1,36 +1,31 @@ -import argparse +import argparse + def parse_args(): - parser = argparse.ArgumentParser( - description="Async web service" - ) + parser = argparse.ArgumentParser(description="Async web service") parser.add_argument( - '--host', + "--host", type=str, required=False, default="0.0.0.0", - help='Host to serve on. Default: 0.0.0.0.' + help="Host to serve on. Default: 0.0.0.0.", ) parser.add_argument( - '--port', + "--port", type=int, required=False, default=8888, - help='Port to serve on Default: 8888.' + help="Port to serve on Default: 8888.", ) - - parser.add_argument( - '--url', + "--url", type=str, default="http://localhost:8888", required=False, - help='Base URL.' + help="Base URL.", ) return parser.parse_args() - - diff --git a/src/app/cli.py b/src/app/cli.py index d5980af..81179d5 100644 --- a/src/app/cli.py +++ b/src/app/cli.py @@ -1,23 +1,24 @@ import asyncio +import time + from aiohttp import ClientSession + +from . import log from .args import parse_args -import time -from . import log - - async def cli_client(url): while True: sentence = input("> ") async with ClientSession() as session: - async with session.post("http://localhost:8080",json=sentence) as response: + async with session.post("http://localhost:8080", json=sentence) as response: try: print(await response.json()) except Exception as ex: print(ex) print(await response.text()) + async def bench(url): index = 0 while True: @@ -28,13 +29,14 @@ async def bench(url): async with ClientSession() as session: async with session.get(url) as response: print(await response.text()) - #print(await response.json()) + # print(await response.json()) time_end = time.time() - print("Request {}. Duration: {}".format(index,time_end-time_start)) + print(f"Request {index}. Duration: {time_end - time_start}") except Exception as ex: log.exception(ex) await asyncio.sleep(1) + def cli_bench(): args = parse_args() asyncio.run(bench(args.url)) @@ -44,9 +46,6 @@ def main(): args = parse_args() asyncio.run(cli_client(args.url)) -if __name__ == '__main__': + +if __name__ == "__main__": main() - - - - diff --git a/src/app/kim.py b/src/app/kim.py index df61755..2b55ef3 100644 --- a/src/app/kim.py +++ b/src/app/kim.py @@ -1,17 +1,18 @@ #!/usr/bin/python3 -import sys import shutil +import sys + from readchar import readkey -def text_editor(init='', prompt=''): - ''' +def text_editor(init="", prompt=""): + """ Allow user to edit a line of text complete with support for line wraps and a cursor | you can move back and forth with the arrow keys. init = initial text supplied to edit prompt = Decoration presented before the text (not editable and not returned) - ''' + """ term_width = shutil.get_terminal_size()[0] ptr = len(init) @@ -25,26 +26,24 @@ def text_editor(init='', prompt=''): copy = prompt + text.copy() if ptr < len(text): - copy.insert(ptr + len(prompt), '|') + copy.insert(ptr + len(prompt), "|") # Line wraps support: if len(copy) > term_width: cut = len(copy) + 3 - term_width if ptr > len(copy) / 2: - copy = ['<'] * 3 + copy[cut:] + copy = ["<"] * 3 + copy[cut:] else: - copy = copy[:-cut] + ['>'] * 3 - + copy = copy[:-cut] + [">"] * 3 # Display current line - print('\r' * term_width + ''.join(copy), end=' ' * (term_width - len(copy))) - + print("\r" * term_width + "".join(copy), end=" " * (term_width - len(copy))) # Read new character into c if c in (53, 54): # Page up/down bug c = readkey() - if c == '~': + if c == "~": continue else: c = readkey() @@ -52,17 +51,17 @@ def text_editor(init='', prompt=''): if len(c) > 1: # Control Character c = ord(c[-1]) - if c == 68: # Left + if c == 68: # Left ptr -= 1 - elif c == 67: # Right + elif c == 67: # Right ptr += 1 - elif c == 53: # PgDn + elif c == 53: # PgDn ptr -= term_width // 2 - elif c == 54: # PgUp + elif c == 54: # PgUp ptr += term_width // 2 - elif c == 70: # End + elif c == 70: # End ptr = len(text) - elif c == 72: # Home + elif c == 72: # Home ptr = 0 else: print("\nUnknown control character:", c) @@ -77,17 +76,18 @@ def text_editor(init='', prompt=''): num = ord(c) if num in (13, 10): # Enter print() - return ''.join(text) - elif num == 127: # Backspace + return "".join(text) + elif num == 127: # Backspace if text: text.pop(ptr - 1) ptr -= 1 - elif num == 3: # Ctrl-C + elif num == 3: # Ctrl-C sys.exit(1) else: # Insert normal character into text. text.insert(ptr, c) ptr += 1 + if __name__ == "__main__": - print("Result =", text_editor('Edit this text', prompt="Prompt: ")) + print("Result =", text_editor("Edit this text", prompt="Prompt: ")) diff --git a/src/app/repl.py b/src/app/repl.py index 468d723..6ea516d 100644 --- a/src/app/repl.py +++ b/src/app/repl.py @@ -1,4 +1,4 @@ -import code +import code def repl(**kwargs): @@ -8,6 +8,7 @@ def repl(**kwargs): variables.update(kwargs) code.interact(local=variables) + if __name__ == "__main__": repl() diff --git a/src/app/server.py b/src/app/server.py index 647f214..6a7bd90 100644 --- a/src/app/server.py +++ b/src/app/server.py @@ -1,14 +1,17 @@ -from aiohttp import web -from .app import create_app -from . import log -import time +import time -def serve(host="0.0.0.0",port=8888): +from aiohttp import web + +from . import log +from .app import create_app + + +def serve(host="0.0.0.0", port=8888): app = create_app() - log.info("Serving on {}:{}".format(host,port)) + log.info(f"Serving on {host}:{port}") while True: try: - web.run_app(app,host=host,port=port) + web.run_app(app, host=host, port=port) except KeyboardInterrupt: break except Exception as ex: diff --git a/src/app/tests.py b/src/app/tests.py index 914c583..098f1ab 100644 --- a/src/app/tests.py +++ b/src/app/tests.py @@ -1,20 +1,19 @@ -from .app import WebDbApplication import unittest -import asyncio +from .app import WebDbApplication class WebDbApplicationTestCase(unittest.IsolatedAsyncioTestCase): - def setUp(self): self.db = WebDbApplication() async def test_insert(self): - self.assertEqual(await self.db.insert("test",dict(test=True)), 1) - self.assertEqual(len(await self.db.find("test",dict(test=True, id=1))), 1) - + self.assertEqual(await self.db.insert("test", {"test": True}), 1) + self.assertEqual(len(await self.db.find("test", {"test": True, "id": 1})), 1) + async def test_find(self): - print(await self.db.find("test",None)) + print(await self.db.find("test", None)) + # self.assertEqual(len(await self.db.find("test",dict(test=True, id=1))), 1)