🚧 moar progress
This commit is contained in:
parent
fe359538e1
commit
cd9c2401d6
23
example.py
Normal file
23
example.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from spiderweb import WebServer
|
||||||
|
from spiderweb.response import HttpResponse, JsonResponse, TemplateResponse, RedirectResponse
|
||||||
|
|
||||||
|
app = WebServer(templates_dirs=["templates"])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def index(request):
|
||||||
|
return TemplateResponse(request, "test.html", context={"value": "TEST!"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/redirect")
|
||||||
|
def redirect(request):
|
||||||
|
return RedirectResponse("/")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# app.add_route("/", index)
|
||||||
|
try:
|
||||||
|
app.start()
|
||||||
|
print("Currently serving on", app.uri())
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
app.stop()
|
184
poetry.lock
generated
184
poetry.lock
generated
@ -1,5 +1,63 @@
|
|||||||
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "black"
|
||||||
|
version = "24.8.0"
|
||||||
|
description = "The uncompromising code formatter."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"},
|
||||||
|
{file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"},
|
||||||
|
{file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"},
|
||||||
|
{file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"},
|
||||||
|
{file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"},
|
||||||
|
{file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"},
|
||||||
|
{file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"},
|
||||||
|
{file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"},
|
||||||
|
{file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"},
|
||||||
|
{file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"},
|
||||||
|
{file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"},
|
||||||
|
{file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"},
|
||||||
|
{file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"},
|
||||||
|
{file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"},
|
||||||
|
{file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"},
|
||||||
|
{file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"},
|
||||||
|
{file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"},
|
||||||
|
{file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"},
|
||||||
|
{file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"},
|
||||||
|
{file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"},
|
||||||
|
{file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"},
|
||||||
|
{file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = ">=8.0.0"
|
||||||
|
mypy-extensions = ">=0.4.3"
|
||||||
|
packaging = ">=22.0"
|
||||||
|
pathspec = ">=0.9.0"
|
||||||
|
platformdirs = ">=2"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
colorama = ["colorama (>=0.4.3)"]
|
||||||
|
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||||
|
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||||
|
uvloop = ["uvloop (>=0.15.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click"
|
||||||
|
version = "8.1.7"
|
||||||
|
description = "Composable command line interface toolkit"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"},
|
||||||
|
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@ -22,6 +80,103 @@ files = [
|
|||||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jinja2"
|
||||||
|
version = "3.1.4"
|
||||||
|
description = "A very fast and expressive template engine."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
|
||||||
|
{file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
MarkupSafe = ">=2.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
i18n = ["Babel (>=2.7)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markupsafe"
|
||||||
|
version = "2.1.5"
|
||||||
|
description = "Safely add untrusted strings to HTML/XML markup."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"},
|
||||||
|
{file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"},
|
||||||
|
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mypy-extensions"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "Type system extensions for programs checked with the mypy type checker."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.5"
|
||||||
|
files = [
|
||||||
|
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||||
|
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "24.1"
|
version = "24.1"
|
||||||
@ -33,6 +188,17 @@ files = [
|
|||||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathspec"
|
||||||
|
version = "0.12.1"
|
||||||
|
description = "Utility library for gitignore style pattern matching of file paths."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||||
|
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peewee"
|
name = "peewee"
|
||||||
version = "3.17.6"
|
version = "3.17.6"
|
||||||
@ -43,6 +209,22 @@ files = [
|
|||||||
{file = "peewee-3.17.6.tar.gz", hash = "sha256:cea5592c6f4da1592b7cff8eaf655be6648a1f5857469e30037bf920c03fb8fb"},
|
{file = "peewee-3.17.6.tar.gz", hash = "sha256:cea5592c6f4da1592b7cff8eaf655be6648a1f5857469e30037bf920c03fb8fb"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platformdirs"
|
||||||
|
version = "4.2.2"
|
||||||
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
|
||||||
|
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||||
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||||
|
type = ["mypy (>=1.8)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pluggy"
|
name = "pluggy"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@ -108,4 +290,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.11"
|
python-versions = "^3.11"
|
||||||
content-hash = "8c6e729478f063b2dfe51e54aae69b0577ea66599cf77201928dbdcc5fc3f688"
|
content-hash = "f7533dcd984fcec36a80d3523af159bc6ebe71edb3c315d8bfd01690d910fb76"
|
||||||
|
@ -8,10 +8,12 @@ readme = "README.md"
|
|||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.11"
|
python = "^3.11"
|
||||||
peewee = "^3.17.6"
|
peewee = "^3.17.6"
|
||||||
|
jinja2 = "^3.1.4"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
ruff = "^0.5.5"
|
ruff = "^0.5.5"
|
||||||
pytest = "^8.3.2"
|
pytest = "^8.3.2"
|
||||||
|
black = "^24.8.0"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core"]
|
requires = ["poetry-core"]
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
from spiderweb.main import route, WebServer # noqa: F401
|
@ -6,7 +6,9 @@ def http403(request):
|
|||||||
|
|
||||||
|
|
||||||
def http404(request):
|
def http404(request):
|
||||||
return JsonResponse(data={"error": f"Route {request.url} not found"}, status_code=404)
|
return JsonResponse(
|
||||||
|
data={"error": f"Route {request.url} not found"}, status_code=404
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def http500(request):
|
def http500(request):
|
||||||
|
@ -5,6 +5,7 @@ class SpiderwebException(Exception):
|
|||||||
|
|
||||||
class SpiderwebNetworkException(SpiderwebException):
|
class SpiderwebNetworkException(SpiderwebException):
|
||||||
"""Something has gone wrong with the network stack."""
|
"""Something has gone wrong with the network stack."""
|
||||||
|
|
||||||
def __init__(self, code, msg=None, desc=None):
|
def __init__(self, code, msg=None, desc=None):
|
||||||
self.code = code
|
self.code = code
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
|
@ -5,27 +5,37 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import traceback
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
import urllib.parse as urlparse
|
import urllib.parse as urlparse
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
from typing import Callable, Any
|
from typing import Callable, Any
|
||||||
|
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
from spiderweb.converters import * # noqa: F403
|
from spiderweb.converters import * # noqa: F403
|
||||||
from spiderweb.default_responses import http403, http404, http500
|
from spiderweb.default_responses import http403, http404, http500 # noqa: F401
|
||||||
from spiderweb.exceptions import APIError, ConfigError, ParseError, GeneralException, NoResponseError
|
from spiderweb.exceptions import (
|
||||||
|
APIError,
|
||||||
|
ConfigError,
|
||||||
|
ParseError,
|
||||||
|
GeneralException,
|
||||||
|
NoResponseError,
|
||||||
|
)
|
||||||
from spiderweb.request import Request
|
from spiderweb.request import Request
|
||||||
from spiderweb.response import HttpResponse, JsonResponse
|
from spiderweb.response import HttpResponse, JsonResponse, TemplateResponse
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def api_route(path):
|
def route(path):
|
||||||
def outer(func):
|
def outer(func):
|
||||||
if not hasattr(func, "_routes"):
|
if not hasattr(func, "_routes"):
|
||||||
setattr(func, "_routes", [])
|
setattr(func, "_routes", [])
|
||||||
func._routes += [path]
|
func._routes += [path]
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return outer
|
return outer
|
||||||
|
|
||||||
|
|
||||||
@ -64,24 +74,30 @@ def convert_match_to_dict(match: dict):
|
|||||||
|
|
||||||
|
|
||||||
class WebServer(HTTPServer):
|
class WebServer(HTTPServer):
|
||||||
def __init__(self, addr: str, port: int, custom_handler: Callable = None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
addr: str = None,
|
||||||
|
port: int = None,
|
||||||
|
custom_handler: Callable = None,
|
||||||
|
templates_dirs: list[str] = None,
|
||||||
|
middleware: list[str] = None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create a new server on address, port. Port can be zero.
|
Create a new server on address, port. Port can be zero.
|
||||||
|
|
||||||
> from simple_rpc_server import WebServer, APIError, api_route
|
> from simple_rpc_server import WebServer, APIError, route
|
||||||
|
|
||||||
Create your handlers by inheriting from WebServer and tagging them with
|
Create your handlers by inheriting from WebServer and tagging them with
|
||||||
@api_route("/path"). Alternately, you can use the WebServer() directly
|
@route("/path"). Alternately, you can use the WebServer() directly
|
||||||
by calling `add_handler("path", function)`.
|
by calling `add_handler("path", function)`.
|
||||||
|
|
||||||
Raise network errors by raising `APIError(code, message, description=None)`.
|
|
||||||
|
|
||||||
Return responses by simply returning a dict() or str() object.
|
|
||||||
|
|
||||||
Parameter to handlers is a dict().
|
|
||||||
|
|
||||||
Query arguments are shoved into the dict via urllib.parse_qs.
|
|
||||||
"""
|
"""
|
||||||
|
addr = addr if addr else "localhost"
|
||||||
|
port = port if port else 7777
|
||||||
|
self.templates_dirs = templates_dirs
|
||||||
|
if self.templates_dirs:
|
||||||
|
self.env = Environment(loader=FileSystemLoader(self.templates_dirs))
|
||||||
|
else:
|
||||||
|
self.env = None
|
||||||
server_address = (addr, port)
|
server_address = (addr, port)
|
||||||
self.__addr = addr
|
self.__addr = addr
|
||||||
|
|
||||||
@ -90,21 +106,54 @@ class WebServer(HTTPServer):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
self.handler_class = custom_handler if custom_handler else HandlerClass
|
self.handler_class = custom_handler if custom_handler else HandlerClass
|
||||||
|
self.handler_class.env = self.env
|
||||||
|
|
||||||
# routed methods map into handler
|
# routed methods map into handler
|
||||||
for method in type(self).__dict__.values():
|
for method in type(self).__dict__.values():
|
||||||
if hasattr(method, "_routes"):
|
if hasattr(method, "_routes"):
|
||||||
for route in method._routes:
|
for route in method._routes:
|
||||||
self.add_route(route, method)
|
self.add_route(route, method)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
super().__init__(server_address, self.handler_class)
|
super().__init__(server_address, self.handler_class)
|
||||||
except OSError:
|
except OSError:
|
||||||
raise GeneralException("Port already in use.")
|
raise GeneralException("Port already in use.")
|
||||||
|
|
||||||
|
def check_for_route_duplicates(self, path: str):
|
||||||
|
if convert_path(path) in self.handler_class._routes:
|
||||||
|
raise ConfigError(f"Route '{path}' already exists.")
|
||||||
|
|
||||||
def add_route(self, path: str, method: Callable):
|
def add_route(self, path: str, method: Callable):
|
||||||
|
"""Add a route to the server."""
|
||||||
|
if not hasattr(self.handler_class, "_routes"):
|
||||||
|
setattr(self.handler_class, "_routes", [])
|
||||||
|
self.check_for_route_duplicates(path)
|
||||||
self.handler_class._routes[convert_path(path)] = method
|
self.handler_class._routes[convert_path(path)] = method
|
||||||
|
|
||||||
|
def route(self, path) -> Callable:
|
||||||
|
"""
|
||||||
|
Decorator for adding a route to a view.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
app = WebServer()
|
||||||
|
|
||||||
|
@app.route("/hello")
|
||||||
|
def index(request):
|
||||||
|
return HttpResponse(content="Hello, world!")
|
||||||
|
|
||||||
|
:param path: str
|
||||||
|
:return: Callable
|
||||||
|
"""
|
||||||
|
|
||||||
|
def outer(func):
|
||||||
|
if not hasattr(self.handler_class, "_routes"):
|
||||||
|
setattr(self.handler_class, "_routes", [])
|
||||||
|
self.check_for_route_duplicates(path)
|
||||||
|
self.handler_class._routes[convert_path(path)] = func
|
||||||
|
return func
|
||||||
|
|
||||||
|
return outer
|
||||||
|
|
||||||
def port(self):
|
def port(self):
|
||||||
"""Return current port."""
|
"""Return current port."""
|
||||||
return self.socket.getsockname()[1]
|
return self.socket.getsockname()[1]
|
||||||
@ -113,9 +162,10 @@ class WebServer(HTTPServer):
|
|||||||
"""Return current IP address."""
|
"""Return current IP address."""
|
||||||
return self.socket.getsockname()[0]
|
return self.socket.getsockname()[0]
|
||||||
|
|
||||||
def uri(self, path):
|
def uri(self, path=None):
|
||||||
"""Make a URI pointing at myself."""
|
"""Make a URI pointing at myself."""
|
||||||
if path[0] == "/":
|
path = path if path else ""
|
||||||
|
if path.startswith("/"):
|
||||||
path = path[1:]
|
path = path[1:]
|
||||||
return "http://" + self.__addr + ":" + str(self.port()) + "/" + path
|
return "http://" + self.__addr + ":" + str(self.port()) + "/" + path
|
||||||
|
|
||||||
@ -130,7 +180,7 @@ class WebServer(HTTPServer):
|
|||||||
print("Stopping server!")
|
print("Stopping server!")
|
||||||
return
|
return
|
||||||
|
|
||||||
def shutdown(self):
|
def stop(self):
|
||||||
super().shutdown()
|
super().shutdown()
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
|
|
||||||
@ -146,7 +196,7 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
body="",
|
body="",
|
||||||
method=self.command,
|
method=self.command,
|
||||||
headers=self.headers,
|
headers=self.headers,
|
||||||
path=self.path
|
path=self.path,
|
||||||
)
|
)
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
@ -212,13 +262,17 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
raise NoResponseError(f"View {handler} returned None.")
|
raise NoResponseError(f"View {handler} returned None.")
|
||||||
if isinstance(resp, dict):
|
if isinstance(resp, dict):
|
||||||
self.fire_response(JsonResponse(data=resp))
|
self.fire_response(JsonResponse(data=resp))
|
||||||
|
if isinstance(resp, TemplateResponse):
|
||||||
|
if hasattr(self, "env"): # injected from above
|
||||||
|
resp.set_template_loader(self.env)
|
||||||
|
self.fire_response(resp)
|
||||||
except APIError:
|
except APIError:
|
||||||
raise
|
raise
|
||||||
except ConnectionAbortedError as e:
|
except ConnectionAbortedError as e:
|
||||||
log.error(f"GET {self.path} : {e}")
|
log.error(f"GET {self.path} : {e}")
|
||||||
except Exception as e:
|
except Exception:
|
||||||
log.error(e.__traceback__)
|
log.error(traceback.format_exc())
|
||||||
self.fire_response(self.get_error_route(500)(self, request))
|
self.fire_response(self.get_error_route(500)(request))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise APIError(404)
|
raise APIError(404)
|
||||||
|
@ -18,12 +18,15 @@ class SpiderwebMiddleware:
|
|||||||
and the response will be returned immediately. `process_response` will not be called.
|
and the response will be returned immediately. `process_response` will not be called.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def process_request(self, request: Request) -> HttpResponse | None:
|
def process_request(self, request: Request) -> HttpResponse | None:
|
||||||
# example of a middleware that sets a flag on the request
|
# example of a middleware that sets a flag on the request
|
||||||
request.spiderweb = True
|
request.spiderweb = True
|
||||||
|
|
||||||
def process_response(self, request: Request, response: HttpResponse) -> HttpResponse | None:
|
def process_response(
|
||||||
|
self, request: Request, response: HttpResponse
|
||||||
|
) -> HttpResponse | None:
|
||||||
# example of a middleware that sets a header on the resp
|
# example of a middleware that sets a header on the resp
|
||||||
if hasattr(request, 'spiderweb'):
|
if hasattr(request, "spiderweb"):
|
||||||
response.headers['X-Spiderweb'] = 'true'
|
response.headers["X-Spiderweb"] = "true"
|
||||||
return response
|
return response
|
||||||
|
@ -2,7 +2,16 @@ import json
|
|||||||
|
|
||||||
|
|
||||||
class Request:
|
class Request:
|
||||||
def __init__(self, content=None, body=None, method=None, headers=None, path=None, url=None, query_params=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
content=None,
|
||||||
|
body=None,
|
||||||
|
method=None,
|
||||||
|
headers=None,
|
||||||
|
path=None,
|
||||||
|
url=None,
|
||||||
|
query_params=None,
|
||||||
|
):
|
||||||
self.content: str = content
|
self.content: str = content
|
||||||
self.body: str = body
|
self.body: str = body
|
||||||
self.method: str = method
|
self.method: str = method
|
||||||
@ -13,5 +22,3 @@ class Request:
|
|||||||
|
|
||||||
def json(self):
|
def json(self):
|
||||||
return json.loads(self.content)
|
return json.loads(self.content)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,25 +1,36 @@
|
|||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from spiderweb.exceptions import GeneralException
|
||||||
|
from spiderweb.request import Request
|
||||||
|
|
||||||
|
|
||||||
class HttpResponse:
|
class HttpResponse:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
content: str = None,
|
body: str = None,
|
||||||
data: dict[str, Any] = None,
|
data: dict[str, Any] = None,
|
||||||
|
context: dict[str, Any] = None,
|
||||||
status_code: int = 200,
|
status_code: int = 200,
|
||||||
headers=None,
|
headers=None,
|
||||||
):
|
):
|
||||||
self.content = content
|
self.body = body
|
||||||
self.data = data
|
self.data = data
|
||||||
|
self.context = context if context else {}
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
self.headers = headers if headers else {}
|
self.headers = headers if headers else {}
|
||||||
|
self.headers["Content-Type"] = "text/html; charset=utf-8"
|
||||||
|
self.headers["Server"] = "Spiderweb"
|
||||||
|
self.headers["Date"] = datetime.datetime.now(tz=datetime.UTC).strftime(
|
||||||
|
"%a, %d %b %Y %H:%M:%S GMT"
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.content
|
return self.body
|
||||||
|
|
||||||
def render(self) -> str:
|
def render(self) -> str:
|
||||||
raise NotImplemented
|
return str(self.body)
|
||||||
|
|
||||||
|
|
||||||
class JsonResponse(HttpResponse):
|
class JsonResponse(HttpResponse):
|
||||||
@ -32,8 +43,27 @@ class JsonResponse(HttpResponse):
|
|||||||
|
|
||||||
|
|
||||||
class RedirectResponse(HttpResponse):
|
class RedirectResponse(HttpResponse):
|
||||||
...
|
def __init__(self, location: str, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.status_code = 302
|
||||||
|
self.headers["Location"] = location
|
||||||
|
|
||||||
|
|
||||||
class TemplateResponse(HttpResponse):
|
class TemplateResponse(HttpResponse):
|
||||||
...
|
def __init__(self, request: Request, template=None, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.context["request"] = request
|
||||||
|
self.template = template
|
||||||
|
self.loader = None
|
||||||
|
self._template = None
|
||||||
|
if not template:
|
||||||
|
raise GeneralException("TemplateResponse requires a template.")
|
||||||
|
|
||||||
|
def render(self) -> str:
|
||||||
|
if self.loader is None:
|
||||||
|
raise GeneralException("TemplateResponse requires a template loader.")
|
||||||
|
self._template = self.loader.get_template(self.template)
|
||||||
|
return self._template.render(**self.context)
|
||||||
|
|
||||||
|
def set_template_loader(self, env):
|
||||||
|
self.loader = env
|
||||||
|
0
spiderweb/tests/test_spiderweb.py
Normal file
0
spiderweb/tests/test_spiderweb.py
Normal file
5
templates/test.html
Normal file
5
templates/test.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<h1>FART</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This is a test of the {{ value }} template.
|
||||||
|
</p>
|
Loading…
Reference in New Issue
Block a user