Compare commits
5 Commits
0.0.1.post
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 27c576db21 | |||
|
|
6311d90a2d | ||
|
|
7e13d49feb | ||
|
|
9b3e847908 | ||
|
|
3afebdba33 |
14
Jenkinsfile
vendored
14
Jenkinsfile
vendored
@@ -426,9 +426,17 @@ pipeline {
|
||||
}
|
||||
post {
|
||||
always {
|
||||
dir("gitrepo") {
|
||||
publishCoverage adapters: [cobertura(mergeToOneReport: true, path: "helpers-results/cl_types_check/cobertura.xml")]
|
||||
junit 'helpers-results/cl_types_check/junit.xml'
|
||||
dir("gitrepo") {
|
||||
//publish coverage
|
||||
recordCoverage( sourceDirectories: [[path: 'src']],
|
||||
tools: [[parser: 'COBERTURA', pattern: 'helpers-results/cl_types_check/cobertura.xml']],
|
||||
id: 'COBERTURA', name: 'COBERTURA Coverage',
|
||||
sourceCodeRetention: 'EVERY_BUILD',)
|
||||
|
||||
//add type check to junit result set
|
||||
junit 'helpers-results/cl_types_check/junit.xml'
|
||||
|
||||
//publish html reports files
|
||||
publishHTML([
|
||||
reportDir: "helpers-results/cl_quality_check",
|
||||
reportFiles: "report.html",
|
||||
|
||||
16
README.md
16
README.md
@@ -12,21 +12,23 @@
|
||||
|
||||
A RESTful API library built on top of pydantic & uvicorn to make service API from a data model.
|
||||
|
||||
/!\ early in-progress project for internal use ATM.
|
||||
/!\\ early in-progress project for internal use ATM.
|
||||
|
||||
Feel free to contribute.
|
||||
|
||||
Features:
|
||||
- use annotation
|
||||
Features (available):
|
||||
- type annotation used
|
||||
- support containers (dict)
|
||||
- support plugins (for hook and biding)
|
||||
- user authentification (WIP)
|
||||
- ACL (WIP)
|
||||
- python internal model instance (with possible serialization/auto-save on-disk)
|
||||
- user auth
|
||||
- ACL
|
||||
- daemon mode
|
||||
|
||||
Features(planned):
|
||||
- group support
|
||||
- python internal model instance (with possible serialization/auto-save on-disk)
|
||||
|
||||
Limitations:
|
||||
- no nested reads / writes
|
||||
- weak unitest (atm)
|
||||
|
||||
Checkout [Latest Documentation](https://chacha.ddns.net/mkdocs-web/chacha/pyrestresource/master/latest/).
|
||||
@@ -48,7 +48,6 @@ include-package-data = true
|
||||
where = ["src"]
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"pyrestresource.data" = ["*.*"]
|
||||
"pyrestresource" = ["py.typed"]
|
||||
|
||||
# [[tool.mypy.overrides]]
|
||||
@@ -56,10 +55,13 @@ where = ["src"]
|
||||
# ignore_missing_imports = true
|
||||
|
||||
[tool.coverage.run]
|
||||
cover_pylib = false
|
||||
branch = true
|
||||
data_file="helpers-results/cl_unit_test_raw_coverage/.coverage"
|
||||
# debug = ["config","multiproc","process"]
|
||||
parallel = true
|
||||
concurrency = [
|
||||
'thread',
|
||||
'multiprocessing'
|
||||
'thread'
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -68,12 +70,12 @@ Documentation = "https://chacha.ddns.net/mkdocs-web/chacha/pyrestresource/mast
|
||||
Tracker = "https://chacha.ddns.net/gitea/chacha/pyrestresource/issues"
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = ["chacha_cicd_helper@git+https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper.git@master"]
|
||||
coverage-check = ["chacha_cicd_helper@git+https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper.git@master"]
|
||||
complexity-check = ["chacha_cicd_helper@git+https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper.git@master"]
|
||||
quality-check = ["chacha_cicd_helper@git+https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper.git@master"]
|
||||
type-check = ["chacha_cicd_helper@git+https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper.git@master"]
|
||||
doc-gen = ["chacha_cicd_helper@git+https://chacha.ddns.net/gitea/chacha/chacha_cicd_helper.git@master"]
|
||||
test = ["chacha_cicd_helper"]
|
||||
coverage-check = ["chacha_cicd_helper"]
|
||||
complexity-check = ["chacha_cicd_helper"]
|
||||
quality-check = ["chacha_cicd_helper"]
|
||||
type-check = ["chacha_cicd_helper"]
|
||||
doc-gen = ["chacha_cicd_helper"]
|
||||
|
||||
# [project.scripts]
|
||||
# my-script = "my_package.module:function"
|
||||
|
||||
23
test/ThreadedUvicorn.py
Normal file
23
test/ThreadedUvicorn.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from uvicorn import Config, Server
|
||||
from threading import Thread
|
||||
import asyncio
|
||||
|
||||
|
||||
class ThreadedUvicorn:
|
||||
def __init__(self, config: Config):
|
||||
self.server = Server(config)
|
||||
self.thread = Thread(daemon=True, target=self.server.run)
|
||||
|
||||
def start(self):
|
||||
self.thread.start()
|
||||
asyncio.run(self.wait_for_started())
|
||||
|
||||
async def wait_for_started(self):
|
||||
while not self.server.started:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
def stop(self):
|
||||
if self.thread.is_alive():
|
||||
self.server.should_exit = True
|
||||
while self.thread.is_alive():
|
||||
continue
|
||||
@@ -5,3 +5,5 @@
|
||||
#
|
||||
# You should have received a copy of the license along with this
|
||||
# work. If not, see <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
|
||||
|
||||
from .ThreadedUvicorn import ThreadedUvicorn
|
||||
|
||||
@@ -32,6 +32,8 @@ from src.pyrestresource import (
|
||||
)
|
||||
|
||||
|
||||
from test import ThreadedUvicorn
|
||||
|
||||
testdir_path = Path(__file__).parent.resolve()
|
||||
chdir(testdir_path.parent.resolve())
|
||||
|
||||
@@ -93,26 +95,18 @@ def find_free_port():
|
||||
return "localhost", s.getsockname()[1]
|
||||
|
||||
|
||||
def launch_server(ip, port):
|
||||
init_classes()
|
||||
uvicorn.run(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True)
|
||||
|
||||
|
||||
class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
chdir(testdir_path.parent.resolve())
|
||||
|
||||
def test_login_two_users(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
|
||||
@@ -206,19 +200,15 @@ class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
self.assertEqual(response.json(), "A TEST SET VALUE 2")
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
def test_login(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -260,19 +250,15 @@ class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
self.assertEqual(response.json(), "TestUser")
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
def test_change_host(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s1 = requests.Session()
|
||||
s1.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -378,20 +364,16 @@ class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
self.assertEqual(response.json(), "__ANNONYMOUS__")
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s1.close()
|
||||
s2.close()
|
||||
server.stop()
|
||||
|
||||
def test_login_wrong_pwd(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -493,19 +475,15 @@ class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
self.assertDictEqual(s.cookies.get_dict(), {})
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
def test_access_resourceACL(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -570,19 +548,15 @@ class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
self.assertEqual(response.json(), "TEST SET VALUE 2")
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
def test_access_fieldACL(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -647,5 +621,5 @@ class Test_RestAPI_LOGIN_Web(unittest.TestCase):
|
||||
self.assertEqual(response.json(), "TEST SET VALUE 2")
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
@@ -484,7 +484,7 @@ class Test_RestAPI_PERFO(unittest.TestCase):
|
||||
init_classes()
|
||||
self.testapp = RootApp()
|
||||
|
||||
# @unittest.skip
|
||||
@unittest.skip
|
||||
def test_perf_dict(self):
|
||||
print(f"LIB INTERNAL PERF TEST")
|
||||
n_loop = 10000
|
||||
|
||||
@@ -13,6 +13,7 @@ import requests
|
||||
from contextlib import closing
|
||||
from multiprocessing import Process
|
||||
from requests.adapters import HTTPAdapter
|
||||
import coverage
|
||||
|
||||
print(__name__)
|
||||
print(__package__)
|
||||
@@ -29,6 +30,8 @@ from src.pyrestresource import (
|
||||
)
|
||||
from pprint import pprint
|
||||
|
||||
from test import ThreadedUvicorn
|
||||
|
||||
testdir_path = Path(__file__).parent.resolve()
|
||||
chdir(testdir_path.parent.resolve())
|
||||
|
||||
@@ -120,25 +123,16 @@ def find_free_port():
|
||||
return "localhost", s.getsockname()[1]
|
||||
|
||||
|
||||
def launch_server(ip, port):
|
||||
init_classes()
|
||||
uvicorn.run(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True)
|
||||
|
||||
|
||||
class Test_RestAPI_WebServer(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
chdir(testdir_path.parent.resolve())
|
||||
|
||||
def test_nomal_AllCmd_games(self):
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -272,23 +266,19 @@ class Test_RestAPI_WebServer(unittest.TestCase):
|
||||
data = response.json()
|
||||
self.assertTrue(len(data) == 0)
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
# @unittest.skip
|
||||
@unittest.skip
|
||||
def test_perf_dict(self):
|
||||
print(f"SOCKET PERF TEST")
|
||||
n_loop = 10000
|
||||
|
||||
ip, port = find_free_port()
|
||||
proc = Process(
|
||||
target=launch_server,
|
||||
args=(
|
||||
ip,
|
||||
port,
|
||||
),
|
||||
)
|
||||
proc.start()
|
||||
init_classes()
|
||||
|
||||
server = ThreadedUvicorn(uvicorn.Config(f"{__loader__.name}:RootApp", port=port, host="0.0.0.0", log_level="warning", factory=True))
|
||||
server.start()
|
||||
sleep(1)
|
||||
s = requests.Session()
|
||||
s.mount("http://", HTTPAdapter(max_retries=0))
|
||||
@@ -366,5 +356,5 @@ class Test_RestAPI_WebServer(unittest.TestCase):
|
||||
print(f"PUT/GET 2nd level (value) dict: {int(n_loop/(end-start))} Req/s")
|
||||
|
||||
finally:
|
||||
proc.terminate()
|
||||
s.close()
|
||||
server.stop()
|
||||
|
||||
Reference in New Issue
Block a user