diff --git a/src/snek/app.py b/src/snek/app.py
index 78c0e2b..ab19f42 100644
--- a/src/snek/app.py
+++ b/src/snek/app.py
@@ -1,9 +1,10 @@
import pathlib
+from types import SimpleNamespace
from aiohttp import web
from app.app import Application as BaseApplication
+
from snek.docs.app import Application as DocsApplication
-from app.cache import time_cache_async
from snek.mapper import get_mappers
from snek.service import get_services
from snek.system import http
@@ -17,7 +18,6 @@ from snek.view.login_form import LoginFormView
from snek.view.register import RegisterView
from snek.view.register_form import RegisterFormView
from snek.view.web import WebView
-from types import SimpleNamespace
class Application(BaseApplication):
@@ -51,7 +51,7 @@ class Application(BaseApplication):
self.router.add_view("/about.md", AboutMDView)
self.router.add_view("/docs.html", DocsHTMLView)
self.router.add_view("/docs.md", DocsMDView)
-
+
self.router.add_view("/web.html", WebView)
self.router.add_view("/login.html", LoginView)
self.router.add_view("/login.json", LoginFormView)
@@ -60,7 +60,10 @@ class Application(BaseApplication):
self.router.add_get("/http-get", self.handle_http_get)
self.router.add_get("/http-photo", self.handle_http_photo)
- self.add_subapp("/docs", DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs")))
+ self.add_subapp(
+ "/docs",
+ DocsApplication(path=pathlib.Path(__file__).parent.joinpath("docs")),
+ )
async def handle_test(self, request):
@@ -79,9 +82,8 @@ class Application(BaseApplication):
return web.Response(
body=path.read_bytes(), headers={"Content-Type": "image/png"}
)
-
-
- #@time_cache_async(60)
+
+ # @time_cache_async(60)
async def render_template(self, template, request, context=None):
return await super().render_template(template, request, context)
diff --git a/src/snek/form/login.py b/src/snek/form/login.py
index 0d97d41..3d6d9a7 100644
--- a/src/snek/form/login.py
+++ b/src/snek/form/login.py
@@ -1,24 +1,27 @@
-from snek.system.form import Form, HTMLElement,FormInputElement,FormButtonElement
+from snek.system.form import Form, FormButtonElement, FormInputElement, HTMLElement
+
class LoginForm(Form):
title = HTMLElement(tag="h1", text="Login")
username = FormInputElement(
- name="username",
+ name="username",
required=True,
min_length=2,
max_length=20,
regex=r"^[a-zA-Z0-9_]+$",
place_holder="Username",
- type="text"
+ type="text",
+ )
+ password = FormInputElement(
+ name="password",
+ required=True,
+ regex=r"^[a-zA-Z0-9_.+-]{6,}",
+ type="password",
+ place_holder="Password",
)
- password = FormInputElement(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]{6,}",type="password",place_holder="Password")
action = FormButtonElement(
- name="action",
- value="submit",
- text="Login",
- type="button"
+ name="action", value="submit", text="Login", type="button"
)
-
diff --git a/src/snek/form/register.py b/src/snek/form/register.py
index 4252bf1..1384b8f 100644
--- a/src/snek/form/register.py
+++ b/src/snek/form/register.py
@@ -1,4 +1,5 @@
-from snek.system.form import Form, HTMLElement,FormInputElement,FormButtonElement
+from snek.system.form import Form, FormButtonElement, FormInputElement, HTMLElement
+
class UsernameField(FormInputElement):
@@ -9,32 +10,35 @@ class UsernameField(FormInputElement):
result.append("Username is not available.")
return result
+
class RegisterForm(Form):
title = HTMLElement(tag="h1", text="Register")
username = UsernameField(
- name="username",
+ name="username",
required=True,
min_length=2,
max_length=20,
regex=r"^[a-zA-Z0-9_]+$",
place_holder="Username",
- type="text"
+ type="text",
)
email = FormInputElement(
name="email",
required=False,
regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
place_holder="Email address",
- type="email"
+ type="email",
+ )
+ password = FormInputElement(
+ name="password",
+ required=True,
+ regex=r"^[a-zA-Z0-9_.+-]{6,}",
+ type="password",
+ place_holder="Password",
)
- password = FormInputElement(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]{6,}",type="password",place_holder="Password")
action = FormButtonElement(
- name="action",
- value="submit",
- text="Register",
- type="button"
+ name="action", value="submit", text="Register", type="button"
)
-
diff --git a/src/snek/mapper/__init__.py b/src/snek/mapper/__init__.py
index dc9e047..2b9b79f 100644
--- a/src/snek/mapper/__init__.py
+++ b/src/snek/mapper/__init__.py
@@ -1,12 +1,12 @@
-import functools
+import functools
+
from snek.mapper.user import UserMapper
-@functools.cache
-def get_mappers(app=None):
- return dict(
- user=UserMapper(app=app)
- )
+@functools.cache
+def get_mappers(app=None):
+ return {"user": UserMapper(app=app)}
+
def get_mapper(name, app=None):
- return get_mappers(app=app)[name]
\ No newline at end of file
+ return get_mappers(app=app)[name]
diff --git a/src/snek/mapper/user.py b/src/snek/mapper/user.py
index 642028c..c388abc 100644
--- a/src/snek/mapper/user.py
+++ b/src/snek/mapper/user.py
@@ -1,6 +1,7 @@
-from snek.system.mapper import BaseMapper
from snek.model.user import UserModel
+from snek.system.mapper import BaseMapper
+
class UserMapper(BaseMapper):
table_name = "user"
- model_class = UserModel
\ No newline at end of file
+ model_class = UserModel
diff --git a/src/snek/model/__init__.py b/src/snek/model/__init__.py
index 52af21a..081ae15 100644
--- a/src/snek/model/__init__.py
+++ b/src/snek/model/__init__.py
@@ -1,12 +1,12 @@
-from snek.model.user import UserModel
-import functools
+import functools
+
+from snek.model.user import UserModel
+
@functools.cache
def get_models():
- return dict(
- user=UserModel
+ return {"user": UserModel}
- )
def get_model(name):
return get_models()[name]
diff --git a/src/snek/model/user.py b/src/snek/model/user.py
index 254b6c9..adb236b 100644
--- a/src/snek/model/user.py
+++ b/src/snek/model/user.py
@@ -1,9 +1,10 @@
-from snek.system.model import BaseModel,ModelField
+from snek.system.model import BaseModel, ModelField
+
class UserModel(BaseModel):
-
+
username = ModelField(
- name="username",
+ name="username",
required=True,
min_length=2,
max_length=20,
@@ -12,8 +13,6 @@ class UserModel(BaseModel):
email = ModelField(
name="email",
required=False,
- regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
+ regex=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$",
)
- password = ModelField(name="password",required=True,regex=r"^[a-zA-Z0-9_.+-]{6,}")
-
-
+ password = ModelField(name="password", required=True, regex=r"^[a-zA-Z0-9_.+-]{6,}")
diff --git a/src/snek/service/__init__.py b/src/snek/service/__init__.py
index 4038f70..60fec76 100644
--- a/src/snek/service/__init__.py
+++ b/src/snek/service/__init__.py
@@ -1,12 +1,13 @@
-from snek.service.user import UserService
-import functools
+import functools
+
+from snek.service.user import UserService
+
@functools.cache
def get_services(app):
- return dict(
- user = UserService(app=app)
+ return {"user": UserService(app=app)}
+
- )
def get_service(name, app=None):
- return get_services(app=app)[name]
\ No newline at end of file
+ return get_services(app=app)[name]
diff --git a/src/snek/service/user.py b/src/snek/service/user.py
index a2b0cb1..5124640 100644
--- a/src/snek/service/user.py
+++ b/src/snek/service/user.py
@@ -1,5 +1,6 @@
-from snek.system.service import BaseService
-from snek.system import security
+from snek.system import security
+from snek.system.service import BaseService
+
class UserService(BaseService):
mapper_name = "user"
@@ -12,6 +13,5 @@ class UserService(BaseService):
model.username = username
model.password = await security.hash(password)
if await self.save(model):
- return model
+ return model
raise Exception(f"Failed to create user: {model.errors}.")
-
\ No newline at end of file
diff --git a/src/snek/system/cache.py b/src/snek/system/cache.py
index 2992803..5e275d9 100644
--- a/src/snek/system/cache.py
+++ b/src/snek/system/cache.py
@@ -1,8 +1,8 @@
-
-import functools
+import functools
cache = functools.cache
+
def async_cache(func):
cache = {}
@@ -14,4 +14,4 @@ def async_cache(func):
cache[args] = result
return result
- return wrapper
\ No newline at end of file
+ return wrapper
diff --git a/src/snek/system/form.py b/src/snek/system/form.py
index 82091b6..f4cf2d3 100644
--- a/src/snek/system/form.py
+++ b/src/snek/system/form.py
@@ -5,17 +5,17 @@
# This code uses the `snek.system.model` library for managing model fields.
# MIT License
-#
+#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
-#
+#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
-#
+#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -26,8 +26,19 @@
from snek.system import model
+
class HTMLElement(model.ModelField):
- def __init__(self, id=None, tag="div", name=None, html=None, class_name=None, text=None, *args, **kwargs):
+ def __init__(
+ self,
+ id=None,
+ tag="div",
+ name=None,
+ html=None,
+ class_name=None,
+ text=None,
+ *args,
+ **kwargs,
+ ):
self.tag = tag
self.text = text
self.id = id
@@ -37,16 +48,18 @@ class HTMLElement(model.ModelField):
async def to_json(self):
result = await super().to_json()
- result['text'] = self.text
- result['id'] = self.id
- result['html'] = self.html
- result['class_name'] = self.class_name
- result['tag'] = self.tag
+ result["text"] = self.text
+ result["id"] = self.id
+ result["html"] = self.html
+ result["class_name"] = self.class_name
+ result["tag"] = self.tag
return result
+
class FormElement(HTMLElement):
pass
+
class FormInputElement(FormElement):
def __init__(self, type="text", place_holder=None, *args, **kwargs):
super().__init__(tag="input", *args, **kwargs)
@@ -59,25 +72,27 @@ class FormInputElement(FormElement):
data["type"] = self.type
return data
+
class FormButtonElement(FormElement):
def __init__(self, tag="button", *args, **kwargs):
super().__init__(tag=tag, *args, **kwargs)
+
class Form(model.BaseModel):
@property
def html_elements(self):
return [element for element in self.fields if isinstance(element, HTMLElement)]
def set_user_data(self, data):
- return super().set_user_data(data.get('fields'))
+ return super().set_user_data(data.get("fields"))
async def to_json(self, encode=False):
elements = await super().to_json()
html_elements = {}
for element in elements.keys():
- if element == 'is_valid':
+ if element == "is_valid":
# is_valid is async get property so we can't do getattr on it
- continue
+ continue
field = getattr(self, element)
if isinstance(field, HTMLElement):
try:
@@ -85,8 +100,12 @@ class Form(model.BaseModel):
except KeyError:
pass
- is_valid = all(field['is_valid'] for field in html_elements.values())
- return dict(fields=html_elements, is_valid=is_valid, errors=await self.errors)
+ is_valid = all(field["is_valid"] for field in html_elements.values())
+ return {
+ "fields": html_elements,
+ "is_valid": is_valid,
+ "errors": await self.errors,
+ }
@property
async def errors(self):
@@ -98,4 +117,4 @@ class Form(model.BaseModel):
@property
async def is_valid(self):
# This is not good, but timebox to resolve issue exceeded.
- return False
\ No newline at end of file
+ return False
diff --git a/src/snek/system/http.py b/src/snek/system/http.py
index b5e8b4f..cd8a9b1 100644
--- a/src/snek/system/http.py
+++ b/src/snek/system/http.py
@@ -11,10 +11,10 @@
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
-#
+#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
-#
+#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -24,17 +24,17 @@
# SOFTWARE.
-from aiohttp import web
-import aiohttp
-from app.cache import time_cache_async
-from bs4 import BeautifulSoup
-from urllib.parse import urljoin
+import asyncio
import pathlib
import uuid
-import imgkit
-import asyncio
import zlib
-import io
+from urllib.parse import urljoin
+
+import aiohttp
+import imgkit
+from app.cache import time_cache_async
+from bs4 import BeautifulSoup
+
async def crc32(data):
try:
@@ -43,6 +43,7 @@ async def crc32(data):
pass
return "crc32" + str(zlib.crc32(data))
+
async def get_file(name, suffix=".cache"):
name = await crc32(name)
path = pathlib.Path(".").joinpath("cache")
@@ -50,17 +51,19 @@ async def get_file(name, suffix=".cache"):
path.mkdir(parents=True, exist_ok=True)
return path.joinpath(name + suffix)
+
async def public_touch(name=None):
path = pathlib.Path(".").joinpath(str(uuid.uuid4()) + name)
path.open("wb").close()
return path
+
async def create_site_photo(url):
loop = asyncio.get_event_loop()
if not url.startswith("https"):
url = "https://" + url
output_path = await get_file("site-screenshot-" + url, ".png")
-
+
if output_path.exists():
return output_path
output_path.touch()
@@ -71,21 +74,23 @@ async def create_site_photo(url):
return await loop.run_in_executor(None, make_photo)
+
async def repair_links(base_url, html_content):
soup = BeautifulSoup(html_content, "html.parser")
- for tag in soup.find_all(['a', 'img', 'link']):
- if tag.has_attr('href') and not tag['href'].startswith("http"):
- tag['href'] = urljoin(base_url, tag['href'])
- if tag.has_attr('src') and not tag['src'].startswith("http"):
- tag['src'] = urljoin(base_url, tag['src'])
+ for tag in soup.find_all(["a", "img", "link"]):
+ if tag.has_attr("href") and not tag["href"].startswith("http"):
+ tag["href"] = urljoin(base_url, tag["href"])
+ if tag.has_attr("src") and not tag["src"].startswith("http"):
+ tag["src"] = urljoin(base_url, tag["src"])
return soup.prettify()
+
async def is_html_content(content: bytes):
try:
- content = content.decode(errors='ignore')
+ content = content.decode(errors="ignore")
except:
pass
- marks = [' BaseModel:
+ async def get(self, uid: str = None, **kwargs) -> BaseModel:
if uid:
- kwargs['uid'] = uid
- model = self.new()
+ kwargs["uid"] = uid
+ self.new()
record = self.table.find_one(**kwargs)
- return await self.model_class.from_record(mapper=self,record=record)
+ return await self.model_class.from_record(mapper=self, record=record)
async def exists(self, **kwargs):
return self.table.exists(**kwargs)
@@ -40,19 +39,19 @@ class BaseMapper:
async def count(self, **kwargs) -> int:
return self.table.count(**kwargs)
- async def save(self, model:BaseModel) -> bool:
+ async def save(self, model: BaseModel) -> bool:
record = await model.record
- if not record.get('uid'):
+ if not record.get("uid"):
raise Exception(f"Attempt to save without uid: {record}.")
- return self.table.upsert(record,['uid'])
+ return self.table.upsert(record, ["uid"])
async def find(self, **kwargs) -> typing.AsyncGenerator:
if not kwargs.get("_limit"):
kwargs["_limit"] = self.default_limit
for record in self.table.find(**kwargs):
- yield await self.model_class.from_record(mapper=self,record=record)
-
- async def delete(self, kwargs=None)-> int:
+ yield await self.model_class.from_record(mapper=self, record=record)
+
+ async def delete(self, kwargs=None) -> int:
if not kwargs or not isinstance(kwargs, dict):
raise Exception("Can't execute delete with no filter.")
return self.table.delete(**kwargs)
diff --git a/src/snek/system/markdown.py b/src/snek/system/markdown.py
index fcffe40..23d0656 100644
--- a/src/snek/system/markdown.py
+++ b/src/snek/system/markdown.py
@@ -1,62 +1,65 @@
-
# Original source: https://brandonjay.dev/posts/2021/render-markdown-html-in-python-with-jinja2
from types import SimpleNamespace
-from mistune import escape
-from mistune import Markdown
-from mistune import HTMLRenderer
-from pygments import highlight
-from pygments.lexers import get_lexer_by_name
-from pygments.formatters import html
-from pygments.styles import get_style_by_name
-import functools
+
from app.cache import time_cache_async
+from mistune import HTMLRenderer, Markdown
+from pygments import highlight
+from pygments.formatters import html
+from pygments.lexers import get_lexer_by_name
+
class MarkdownRenderer(HTMLRenderer):
_allow_harmful_protocols = True
+
def __init__(self, app, template):
- self.template = template
-
- self.app = app
- self.env = self.app.jinja2_env
- formatter = html.HtmlFormatter()
- self.env.globals['highlight_styles'] = formatter.get_style_defs()
- def _escape(self,str):
- return str ##escape(str)
- def block_code(self, code, lang=None,info=None):
+ self.template = template
+
+ self.app = app
+ self.env = self.app.jinja2_env
+ formatter = html.HtmlFormatter()
+ self.env.globals["highlight_styles"] = formatter.get_style_defs()
+
+ def _escape(self, str):
+ return str ##escape(str)
+
+ def block_code(self, code, lang=None, info=None):
if not lang:
lang = info
if not lang:
return f"
{code}
"
- #return '\n%s
\n' % escape(code)
+ # return '\n%s
\n' % escape(code)
lexer = get_lexer_by_name(lang, stripall=True)
formatter = html.HtmlFormatter(lineseparator="
")
return highlight(code, lexer, formatter)
+
def render(self):
markdown_string = self.app.template_path.joinpath(self.template).read_text()
- renderer = MarkdownRenderer(self.app,self.template)
+ renderer = MarkdownRenderer(self.app, self.template)
markdown = Markdown(renderer=renderer)
return markdown(markdown_string)
-
def render_markdown_sync(app, markdown_string):
- renderer = MarkdownRenderer(app,None)
+ renderer = MarkdownRenderer(app, None)
markdown = Markdown(renderer=renderer)
return markdown(markdown_string)
+
@time_cache_async(120)
async def render_markdown(app, markdown_string):
- return render_markdown_sync(app,markdown_string)
+ return render_markdown_sync(app, markdown_string)
-from jinja2 import nodes, TemplateSyntaxError
+
+from jinja2 import TemplateSyntaxError, nodes
from jinja2.ext import Extension
from jinja2.nodes import Const
+
# Source: https://ron.sh/how-to-write-a-jinja2-extension/
class MarkdownExtension(Extension):
- tags = {'markdown'}
+ tags = {"markdown"}
def __init__(self, environment):
self.app = SimpleNamespace(jinja2_env=environment)
@@ -64,13 +67,15 @@ class MarkdownExtension(Extension):
def parse(self, parser):
line_number = next(parser.stream).lineno
- md_file = [Const('')]
- body = ''
+ md_file = [Const("")]
+ body = ""
try:
md_file = [parser.parse_expression()]
except TemplateSyntaxError:
- body = parser.parse_statements(['name:endmarkdown'], drop_needle=True)
- return nodes.CallBlock(self.call_method('_to_html', md_file), [], [], body).set_lineno(line_number)
+ body = parser.parse_statements(["name:endmarkdown"], drop_needle=True)
+ return nodes.CallBlock(
+ self.call_method("_to_html", md_file), [], [], body
+ ).set_lineno(line_number)
def _to_html(self, md_file, caller):
- return render_markdown_sync(self.app,caller())
\ No newline at end of file
+ return render_markdown_sync(self.app, caller())
diff --git a/src/snek/system/middleware.py b/src/snek/system/middleware.py
index 7fe457f..69fe378 100644
--- a/src/snek/system/middleware.py
+++ b/src/snek/system/middleware.py
@@ -8,12 +8,14 @@
from aiohttp import web
+
@web.middleware
async def no_cors_middleware(request, handler):
response = await handler(request)
response.headers.pop("Access-Control-Allow-Origin", None)
return response
+
@web.middleware
async def cors_allow_middleware(request, handler):
response = await handler(request)
@@ -22,12 +24,15 @@ async def cors_allow_middleware(request, handler):
response.headers["Access-Control-Allow-Headers"] = "*"
return response
+
@web.middleware
async def cors_middleware(request, handler):
if request.method == "OPTIONS":
response = web.Response()
response.headers["Access-Control-Allow-Origin"] = "*"
- response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
+ response.headers["Access-Control-Allow-Methods"] = (
+ "GET, POST, PUT, DELETE, OPTIONS"
+ )
response.headers["Access-Control-Allow-Headers"] = "*"
return response
@@ -35,4 +40,4 @@ async def cors_middleware(request, handler):
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
response.headers["Access-Control-Allow-Headers"] = "*"
- return response
\ No newline at end of file
+ return response
diff --git a/src/snek/system/model.py b/src/snek/system/model.py
index 0d700ff..b41f4ba 100644
--- a/src/snek/system/model.py
+++ b/src/snek/system/model.py
@@ -25,12 +25,12 @@
# SOFTWARE.
+import copy
+import json
import re
import uuid
-import json
-from datetime import datetime, timezone
from collections import OrderedDict
-import copy
+from datetime import datetime, timezone
TIMESTAMP_REGEX = r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6}\+\d{2}:\d{2}$"
@@ -44,12 +44,21 @@ def add_attrs(**kwargs):
for key, value in kwargs.items():
setattr(func, key, value)
return func
+
return decorator
-def validate_attrs(required=False, min_length=None, max_length=None, regex=None, **kwargs):
+def validate_attrs(
+ required=False, min_length=None, max_length=None, regex=None, **kwargs
+):
def decorator(func):
- return add_attrs(required=required, min_length=min_length, max_length=max_length, regex=regex, **kwargs)(func)
+ return add_attrs(
+ required=required,
+ min_length=min_length,
+ max_length=max_length,
+ regex=regex,
+ **kwargs,
+ )(func)
class Validator:
@@ -70,7 +79,21 @@ class Validator:
def custom_validation(self):
return True
- def __init__(self, required=False, min_num=None, max_num=None, min_length=None, max_length=None, regex=None, value=None, kind=None, help_text=None, app=None, model=None, **kwargs):
+ def __init__(
+ self,
+ required=False,
+ min_num=None,
+ max_num=None,
+ min_length=None,
+ max_length=None,
+ regex=None,
+ value=None,
+ kind=None,
+ help_text=None,
+ app=None,
+ model=None,
+ **kwargs,
+ ):
self.index = Validator._index
Validator._index += 1
self.app = app
@@ -103,9 +126,13 @@ class Validator:
if self.max_num is not None and self.value > self.max_num:
error_list.append(f"Field should be maximal {self.max_num}.")
if self.min_length is not None and len(self.value) < self.min_length:
- error_list.append(f"Field should be minimal {self.min_length} characters long.")
+ error_list.append(
+ f"Field should be minimal {self.min_length} characters long."
+ )
if self.max_length is not None and len(self.value) > self.max_length:
- error_list.append(f"Field should be maximal {self.max_length} characters long.")
+ error_list.append(
+ f"Field should be maximal {self.max_length} characters long."
+ )
if self.regex and self.value and not re.match(self.regex, self.value):
error_list.append("Invalid value.")
if self.kind and not isinstance(self.value, self.kind):
@@ -141,7 +168,7 @@ class Validator:
"help_text": self.help_text,
"errors": errors,
"is_valid": is_valid,
- "index": self.index
+ "index": self.index,
}
@@ -156,7 +183,7 @@ class ModelField(Validator):
async def to_json(self):
result = await super().to_json()
- result['name'] = self.name
+ result["name"] = self.name
return result
@@ -193,30 +220,39 @@ class UUIDField(ModelField):
class BaseModel:
uid = UUIDField(name="uid", required=True)
- created_at = CreatedField(name="created_at", required=True, regex=TIMESTAMP_REGEX, place_holder="Created at")
- updated_at = UpdatedField(name="updated_at", regex=TIMESTAMP_REGEX, place_holder="Updated at")
- deleted_at = DeletedField(name="deleted_at", regex=TIMESTAMP_REGEX, place_holder="Deleted at")
+ created_at = CreatedField(
+ name="created_at",
+ required=True,
+ regex=TIMESTAMP_REGEX,
+ place_holder="Created at",
+ )
+ updated_at = UpdatedField(
+ name="updated_at", regex=TIMESTAMP_REGEX, place_holder="Updated at"
+ )
+ deleted_at = DeletedField(
+ name="deleted_at", regex=TIMESTAMP_REGEX, place_holder="Deleted at"
+ )
- @classmethod
+ @classmethod
async def from_record(cls, record, mapper):
model = cls.__new__()
- model.mapper = mapper
+ model.mapper = mapper
model.record = record
return model
- @property
+ @property
def mapper(self):
- return self._mapper
+ return self._mapper
- @mapper.setter
+ @mapper.setter
def mapper(self, value):
- self._mapper = value
+ self._mapper = value
- @property
+ @property
def record(self):
return {field.name: field.value for field in self.fields}
-
- @record.setter
+
+ @record.setter
def record(self, value):
for key, value in self._record.items():
field = self.fields.get(key)
@@ -233,10 +269,12 @@ class BaseModel:
if isinstance(obj, Validator):
self.__dict__[key] = copy.deepcopy(obj)
- self.__dict__[key].value = kwargs.pop(key, self.__dict__[key].initial_value)
+ self.__dict__[key].value = kwargs.pop(
+ key, self.__dict__[key].initial_value
+ )
self.fields[key] = self.__dict__[key]
self.fields[key].model = self
- self.fields[key].app = kwargs.get('app')
+ self.fields[key].app = kwargs.get("app")
def __setitem__(self, key, value):
obj = self.__dict__.get(key)
@@ -254,16 +292,13 @@ class BaseModel:
field = self.fields.get(key)
if not field:
continue
- if value.get('name'):
- value = value.get('value')
+ if value.get("name"):
+ value = value.get("value")
field.value = value
-
-
@property
async def is_valid(self):
return all([await field.is_valid for field in self.fields.values()])
-
def __getitem__(self, key):
obj = self.__dict__.get(key)
@@ -282,20 +317,22 @@ class BaseModel:
obj = await self.to_json()
record = {}
for key, value in obj.items():
- if not isinstance(value, dict) or not 'value' in value:
+ if not isinstance(value, dict) or "value" not in value:
continue
if getattr(self, key).save:
- record[key] = value.get('value')
+ record[key] = value.get("value")
return record
async def to_json(self, encode=False):
- model_data = OrderedDict({
- "uid": self.uid.value,
- "created_at": self.created_at.value,
- "updated_at": self.updated_at.value,
- "deleted_at": self.deleted_at.value,
- "is_valid": await self.is_valid
- })
+ model_data = OrderedDict(
+ {
+ "uid": self.uid.value,
+ "created_at": self.created_at.value,
+ "updated_at": self.updated_at.value,
+ "deleted_at": self.deleted_at.value,
+ "is_valid": await self.is_valid,
+ }
+ )
for key, value in self.fields.items():
if key == "record":
@@ -325,4 +362,4 @@ class FormElement(ModelField):
data = await super().to_json()
data["name"] = self.name
data["place_holder"] = self.place_holder
- return data
\ No newline at end of file
+ return data
diff --git a/src/snek/system/security.py b/src/snek/system/security.py
index b319f54..5449c50 100644
--- a/src/snek/system/security.py
+++ b/src/snek/system/security.py
@@ -1,12 +1,13 @@
-import hashlib
+import hashlib
DEFAULT_SALT = b"snekker-de-snek-"
-async def hash(data,salt=DEFAULT_SALT):
+
+async def hash(data, salt=DEFAULT_SALT):
try:
data = data.encode(errors="ignore")
except AttributeError:
- pass
+ pass
try:
salt = salt.encode(errors="ignore")
except AttributeError:
@@ -16,5 +17,6 @@ async def hash(data,salt=DEFAULT_SALT):
obj = hashlib.sha256(salted)
return obj.hexdigest()
-async def verify(string:str, hashed:str):
- return await hash(string) == hashed
+
+async def verify(string: str, hashed: str):
+ return await hash(string) == hashed
diff --git a/src/snek/system/service.py b/src/snek/system/service.py
index d970b5f..1f9d601 100644
--- a/src/snek/system/service.py
+++ b/src/snek/system/service.py
@@ -1,24 +1,22 @@
-
-
-
from snek.mapper import get_mapper
-from snek.system.mapper import BaseMapper
from snek.model.user import UserModel
+from snek.system.mapper import BaseMapper
+
class BaseService:
- mapper_name:BaseMapper = None
+ mapper_name: BaseMapper = None
def __init__(self, app):
- self.app = app
+ self.app = app
if self.mapper_name:
self.mapper = get_mapper(self.mapper_name, app=self.app)
else:
- self.mapper = None
+ self.mapper = None
async def exists(self, **kwargs):
return await self.count(**kwargs) > 0
-
+
async def count(self, **kwargs):
return await self.mapper.count(**kwargs)
@@ -27,14 +25,13 @@ class BaseService:
async def get(self, **kwargs):
return await self.mapper.get(**kwargs)
-
- async def save(self, model:UserModel):
+
+ async def save(self, model: UserModel):
# if model.is_valid: You Know why not
- return await self.mapper.save(model) and True
-
-
+ return await self.mapper.save(model) and True
+
async def find(self, **kwargs):
return await self.mapper.find(**kwargs)
-
+
async def delete(self, **kwargs):
- return await self.mapper.delete(**kwargs)
\ No newline at end of file
+ return await self.mapper.delete(**kwargs)
diff --git a/src/snek/system/view.py b/src/snek/system/view.py
index 3b53f33..1cf5329 100644
--- a/src/snek/system/view.py
+++ b/src/snek/system/view.py
@@ -1,13 +1,14 @@
from aiohttp import web
-from snek.system.markdown import render_markdown
+from snek.system.markdown import render_markdown
+
class BaseView(web.View):
-
- @property
+
+ @property
def app(self):
return self.request.app
-
+
@property
def db(self):
return self.app.db
@@ -17,31 +18,36 @@ class BaseView(web.View):
async def render_template(self, template_name, context=None):
if template_name.endswith(".md"):
- response = await self.request.app.render_template(template_name,self.request,context)
+ response = await self.request.app.render_template(
+ template_name, self.request, context
+ )
body = await render_markdown(self.app, response.body.decode())
- return web.Response(body=body,content_type="text/html")
- return await self.request.app.render_template(template_name, self.request,context)
-
+ return web.Response(body=body, content_type="text/html")
+ return await self.request.app.render_template(
+ template_name, self.request, context
+ )
+
+
class BaseFormView(BaseView):
- form = None
+ form = None
async def get(self):
form = self.form(app=self.app)
-
+
return await self.json_response(await form.to_json())
-
+
async def post(self):
form = self.form(app=self.app)
post = await self.request.json()
- form.set_user_data(post['form'])
+ form.set_user_data(post["form"])
result = await form.to_json()
- if post.get('action') == 'validate':
+ if post.get("action") == "validate":
# Pass
pass
- if post.get('action') == 'submit' and result['is_valid']:
+ if post.get("action") == "submit" and result["is_valid"]:
await self.submit(form)
- return await self.json_response(result)
+ return await self.json_response(result)
- async def submit(self,model=None):
+ async def submit(self, model=None):
print("Submit sucess")
diff --git a/src/snek/templates/docs.md b/src/snek/templates/docs.md
index 2cf60cb..a42b7d5 100644
--- a/src/snek/templates/docs.md
+++ b/src/snek/templates/docs.md
@@ -9,8 +9,7 @@ Currently only some details about the internal API are available.
# of the snek.system.security module.
new_user_object = await app.service.user.register(
- username="retoor",
- password="retoorded"
+ username="retoor", password="retoorded"
)
```
@@ -23,15 +22,16 @@ var1 = security.encrypt("data")
var2 = security.encrypt(b"data")
# Is correct:
-assert(var1 == var2)
+assert var1 == var2
```
## How to create a basic HTML / Markdown view
```python
-from snek.system.view import BaseView
+from snek.system.view import BaseView
+
class IndexView(BaseView):
-
+
async def get(self):
# The render function supports markdown.
# It will render with syntax highlighting.
@@ -40,11 +40,12 @@ class IndexView(BaseView):
```
## How to create a FormView
```python
-from snek.system.view import BaseFormView
from snek.form.register import RegisterForm
+from snek.system.view import BaseFormView
+
class RegisterFormView(BaseFormView):
-
+
form = RegisterForm
```
## How to register a class view
diff --git a/src/snek/view/about.py b/src/snek/view/about.py
index 593d5a9..762fc8e 100644
--- a/src/snek/view/about.py
+++ b/src/snek/view/about.py
@@ -1,5 +1,3 @@
-
-
from snek.system.view import BaseView
@@ -7,8 +5,9 @@ class AboutHTMLView(BaseView):
async def get(self):
return await self.render_template("about.html")
-
+
+
class AboutMDView(BaseView):
async def get(self):
- return await self.render_template("about.md")
\ No newline at end of file
+ return await self.render_template("about.md")
diff --git a/src/snek/view/docs.py b/src/snek/view/docs.py
index e69d754..519a0eb 100644
--- a/src/snek/view/docs.py
+++ b/src/snek/view/docs.py
@@ -1,6 +1,3 @@
-
-
-
from snek.system.view import BaseView
@@ -8,8 +5,9 @@ class DocsHTMLView(BaseView):
async def get(self):
return await self.render_template("docs.html")
-
+
+
class DocsMDView(BaseView):
async def get(self):
- return await self.render_template("docs.md")
\ No newline at end of file
+ return await self.render_template("docs.md")
diff --git a/src/snek/view/index.py b/src/snek/view/index.py
index c7861fa..bd91dc8 100644
--- a/src/snek/view/index.py
+++ b/src/snek/view/index.py
@@ -1,5 +1,6 @@
from snek.system.view import BaseView
+
class IndexView(BaseView):
async def get(self):
diff --git a/src/snek/view/login.py b/src/snek/view/login.py
index ffedc79..6566df9 100644
--- a/src/snek/view/login.py
+++ b/src/snek/view/login.py
@@ -1,13 +1,18 @@
from snek.form.register import RegisterForm
from snek.system.view import BaseView
+
class LoginView(BaseView):
async def get(self):
- return await self.render_template("login.html") #web.json_response({"form": RegisterForm().to_json()})
-
+ return await self.render_template(
+ "login.html"
+ ) # web.json_response({"form": RegisterForm().to_json()})
+
async def post(self):
form = RegisterForm()
form.set_user_data(await self.request.post())
print(form.is_valid())
- return await self.render_template("login.html", self.request) #web.json_response({"form": RegisterForm().to_json()})
+ return await self.render_template(
+ "login.html", self.request
+ ) # web.json_response({"form": RegisterForm().to_json()})
diff --git a/src/snek/view/login_form.py b/src/snek/view/login_form.py
index e9b6eac..576ddc6 100644
--- a/src/snek/view/login_form.py
+++ b/src/snek/view/login_form.py
@@ -1,5 +1,6 @@
-from snek.system.view import BaseFormView
from snek.form.login import LoginForm
+from snek.system.view import BaseFormView
+
class LoginFormView(BaseFormView):
- form = LoginForm
\ No newline at end of file
+ form = LoginForm
diff --git a/src/snek/view/register.py b/src/snek/view/register.py
index e3b3038..1186959 100644
--- a/src/snek/view/register.py
+++ b/src/snek/view/register.py
@@ -1,6 +1,7 @@
from snek.system.view import BaseView
+
class RegisterView(BaseView):
async def get(self):
- return await self.render_template("register.html")
\ No newline at end of file
+ return await self.render_template("register.html")
diff --git a/src/snek/view/register_form.py b/src/snek/view/register_form.py
index 8cb6567..4c30169 100644
--- a/src/snek/view/register_form.py
+++ b/src/snek/view/register_form.py
@@ -1,9 +1,12 @@
from snek.form.register import RegisterForm
from snek.system.view import BaseFormView
+
class RegisterFormView(BaseFormView):
form = RegisterForm
async def submit(self, form):
- result = await self.app.services.user.register(form.email.value,form.username.value,form.password.value)
- print("SUBMITTED:",result)
\ No newline at end of file
+ result = await self.app.services.user.register(
+ form.email.value, form.username.value, form.password.value
+ )
+ print("SUBMITTED:", result)
diff --git a/src/snek/view/web.py b/src/snek/view/web.py
index b06563a..d42fcec 100644
--- a/src/snek/view/web.py
+++ b/src/snek/view/web.py
@@ -1,6 +1,7 @@
from snek.system.view import BaseView
+
class WebView(BaseView):
async def get(self):
- return await self.render_template("web.html")
\ No newline at end of file
+ return await self.render_template("web.html")