valerius-flow 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- valerius_flow-0.1.0/PKG-INFO +28 -0
- valerius_flow-0.1.0/README.md +9 -0
- valerius_flow-0.1.0/pyproject.toml +27 -0
- valerius_flow-0.1.0/setup.cfg +4 -0
- valerius_flow-0.1.0/valerius/__init__.py +0 -0
- valerius_flow-0.1.0/valerius/cli.py +132 -0
- valerius_flow-0.1.0/valerius/framework.py +102 -0
- valerius_flow-0.1.0/valerius/management.py +19 -0
- valerius_flow-0.1.0/valerius/middlewares/json.py +19 -0
- valerius_flow-0.1.0/valerius/middlewares/logger.py +20 -0
- valerius_flow-0.1.0/valerius_flow.egg-info/PKG-INFO +28 -0
- valerius_flow-0.1.0/valerius_flow.egg-info/SOURCES.txt +13 -0
- valerius_flow-0.1.0/valerius_flow.egg-info/dependency_links.txt +1 -0
- valerius_flow-0.1.0/valerius_flow.egg-info/requires.txt +4 -0
- valerius_flow-0.1.0/valerius_flow.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: valerius_flow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Django-inspired API framework
|
|
5
|
+
Author-email: Joao Goncalves <you@email.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: webob
|
|
16
|
+
Requires-Dist: parse
|
|
17
|
+
Requires-Dist: python-dotenv
|
|
18
|
+
Requires-Dist: gunicorn
|
|
19
|
+
|
|
20
|
+
# Valerius
|
|
21
|
+
|
|
22
|
+
A Django-inspired API framework.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install valerius
|
|
28
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "valerius_flow"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A Django-inspired API framework"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Joao Goncalves", email = "you@email.com" }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Framework :: Django",
|
|
19
|
+
"Intended Audience :: Developers"
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
"webob",
|
|
24
|
+
"parse",
|
|
25
|
+
"python-dotenv",
|
|
26
|
+
"gunicorn"
|
|
27
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def start_app():
|
|
6
|
+
if len(sys.argv) < 3:
|
|
7
|
+
print("Usage: valerius startapp <app_name>")
|
|
8
|
+
return
|
|
9
|
+
|
|
10
|
+
app_name = sys.argv[2]
|
|
11
|
+
class_name = app_name.capitalize()
|
|
12
|
+
|
|
13
|
+
# Templates for the new app
|
|
14
|
+
handlers_content = """class {class_name}Resource:
|
|
15
|
+
def get(self, req, resp):
|
|
16
|
+
resp.text = {{"message": "Welcome to the {app_name} app!"}}
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
routes_content = """from .handlers import {class_name}Resource
|
|
20
|
+
|
|
21
|
+
routes = [
|
|
22
|
+
("/{app_name}", {class_name}Resource),
|
|
23
|
+
]
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Create directory and files
|
|
27
|
+
os.makedirs(app_name, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
# Create __init__.py so it's a package
|
|
30
|
+
with open(os.path.join(app_name, "__init__.py"), "w") as f:
|
|
31
|
+
f.write("")
|
|
32
|
+
|
|
33
|
+
with open(os.path.join(app_name, "handlers.py"), "w") as f:
|
|
34
|
+
f.write(handlers_content.format(class_name=class_name, app_name=app_name))
|
|
35
|
+
|
|
36
|
+
with open(os.path.join(app_name, "routes.py"), "w") as f:
|
|
37
|
+
f.write(routes_content.format(class_name=class_name, app_name=app_name))
|
|
38
|
+
|
|
39
|
+
settings_path = "config/settings/base.py"
|
|
40
|
+
|
|
41
|
+
if os.path.exists(settings_path):
|
|
42
|
+
with open(settings_path, "r") as f:
|
|
43
|
+
lines = f.readlines()
|
|
44
|
+
|
|
45
|
+
with open(settings_path, "w") as f:
|
|
46
|
+
for line in lines:
|
|
47
|
+
f.write(line)
|
|
48
|
+
# Find the INSTALLED_APPS list and inject the new app
|
|
49
|
+
if "INSTALLED_APPS = [" in line:
|
|
50
|
+
f.write(f" '{app_name}.routes',\n")
|
|
51
|
+
|
|
52
|
+
print(f"Registered '{app_name}.routes' in settings.py")
|
|
53
|
+
else:
|
|
54
|
+
print(f"Next: Add '{app_name}.routes' to INSTALLED_APPS manually.")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def start_project():
|
|
58
|
+
if len(sys.argv) < 3:
|
|
59
|
+
print("Usage: valerius startproject <project_name>")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
project_name = sys.argv[2]
|
|
63
|
+
|
|
64
|
+
app_content = """from valerius.framework import App
|
|
65
|
+
|
|
66
|
+
app = App(settings_module='config.settings.base')
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
settings_content = """INSTALLED_APPS = []
|
|
70
|
+
|
|
71
|
+
MIDDLEWARE = [
|
|
72
|
+
'valerius.middlewares.logger.LoggerMiddleware',
|
|
73
|
+
'valerius.middlewares.json.JsonErrorMiddleware',
|
|
74
|
+
]
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
manage_content = """import sys
|
|
78
|
+
import os
|
|
79
|
+
from valerius.management import create_app
|
|
80
|
+
|
|
81
|
+
def main():
|
|
82
|
+
if len(sys.argv) < 2:
|
|
83
|
+
print("Usage: python manage.py [run|startapp]")
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
command = sys.argv[1]
|
|
87
|
+
|
|
88
|
+
if command == "run":
|
|
89
|
+
# We assume the user's entry file is named app.py
|
|
90
|
+
os.system("gunicorn app:app --reload")
|
|
91
|
+
|
|
92
|
+
elif command == "startapp":
|
|
93
|
+
if len(sys.argv) > 2:
|
|
94
|
+
create_app(sys.argv[2])
|
|
95
|
+
else:
|
|
96
|
+
print("Error: Please provide an app name.")
|
|
97
|
+
|
|
98
|
+
else:
|
|
99
|
+
print(f"Unknown command: {command}")
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
main()
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
files = {
|
|
106
|
+
"app.py": app_content,
|
|
107
|
+
"config/__init__.py": "",
|
|
108
|
+
"config/settings/__init__.py": "",
|
|
109
|
+
"config/settings/base.py": settings_content,
|
|
110
|
+
"manage.py": manage_content,
|
|
111
|
+
"requirements/base.txt": "valerius\ngunicorn",
|
|
112
|
+
"env/.env": "DEBUG=True"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Create directory and files
|
|
116
|
+
os.makedirs(project_name, exist_ok=True)
|
|
117
|
+
|
|
118
|
+
for relative_path, content in files.items():
|
|
119
|
+
# 1. Join project folder + relative path
|
|
120
|
+
full_path = os.path.join(project_name, relative_path)
|
|
121
|
+
|
|
122
|
+
# 2. Extract the directory portion (e.g., 'my_project/config/settings')
|
|
123
|
+
directory = os.path.dirname(full_path)
|
|
124
|
+
|
|
125
|
+
# 3. Create the directories if they don't exist
|
|
126
|
+
if directory:
|
|
127
|
+
os.makedirs(directory, exist_ok=True)
|
|
128
|
+
|
|
129
|
+
with open(full_path, "w") as f:
|
|
130
|
+
f.write(content)
|
|
131
|
+
|
|
132
|
+
print(f"Project '{project_name}' created successfully!")
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import inspect
|
|
3
|
+
import importlib
|
|
4
|
+
from parse import parse
|
|
5
|
+
from webob import Request, Response
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class App:
|
|
9
|
+
def __init__(self, settings_module=None):
|
|
10
|
+
self.routes = {}
|
|
11
|
+
self.middleware_classes = []
|
|
12
|
+
|
|
13
|
+
if settings_module:
|
|
14
|
+
self.settings = importlib.import_module(settings_module)
|
|
15
|
+
self.register_apps()
|
|
16
|
+
|
|
17
|
+
def load_settings(self, settings_module):
|
|
18
|
+
settings = importlib.import_module(settings_module)
|
|
19
|
+
self.settings = settings
|
|
20
|
+
middleware_paths = getattr(settings, "MIDDLEWARE", [])
|
|
21
|
+
|
|
22
|
+
for path in middleware_paths:
|
|
23
|
+
self.add_middleware_from_path(path)
|
|
24
|
+
|
|
25
|
+
def add_middleware_from_path(self, path):
|
|
26
|
+
module_path, class_name = path.rsplit(".", 1)
|
|
27
|
+
module = importlib.import_module(module_path)
|
|
28
|
+
middleware_cls = getattr(module, class_name)
|
|
29
|
+
self.middleware_classes.append(middleware_cls)
|
|
30
|
+
|
|
31
|
+
def register_apps(self):
|
|
32
|
+
apps = getattr(self.settings, "INSTALLED_APPS", [])
|
|
33
|
+
|
|
34
|
+
for app_path in apps:
|
|
35
|
+
module = importlib.import_module(app_path)
|
|
36
|
+
app_routes = getattr(module, "routes", [])
|
|
37
|
+
|
|
38
|
+
for path, handler in app_routes:
|
|
39
|
+
self.add_route(path, handler)
|
|
40
|
+
|
|
41
|
+
def add_route(self, path, handler):
|
|
42
|
+
assert path not in self.routes, f"Route {path} already exists."
|
|
43
|
+
self.routes[path] = handler
|
|
44
|
+
|
|
45
|
+
def __call__(self, environ, start_response):
|
|
46
|
+
app = self.handle_request_wsgi
|
|
47
|
+
|
|
48
|
+
for middleware in reversed(self.middleware_classes):
|
|
49
|
+
app = middleware(app)
|
|
50
|
+
|
|
51
|
+
return app(environ, start_response)
|
|
52
|
+
|
|
53
|
+
def handle_request_wsgi(self, environ, start_response):
|
|
54
|
+
request = Request(environ)
|
|
55
|
+
response = self.handle_request(request)
|
|
56
|
+
return response(environ, start_response)
|
|
57
|
+
|
|
58
|
+
def default_response(self, response):
|
|
59
|
+
response.status_code = 404
|
|
60
|
+
response.text = "Not found."
|
|
61
|
+
|
|
62
|
+
def route(self, path):
|
|
63
|
+
assert path not in self.routes, "Such route already exists."
|
|
64
|
+
|
|
65
|
+
def wrapper(handler):
|
|
66
|
+
self.routes[path] = handler
|
|
67
|
+
return handler
|
|
68
|
+
|
|
69
|
+
return wrapper
|
|
70
|
+
|
|
71
|
+
def find_handler(self, request_path):
|
|
72
|
+
for path, handler in self.routes.items():
|
|
73
|
+
parse_result = parse(path, request_path)
|
|
74
|
+
|
|
75
|
+
if parse_result is not None:
|
|
76
|
+
return handler, parse_result.named
|
|
77
|
+
|
|
78
|
+
return None, None
|
|
79
|
+
|
|
80
|
+
def handle_request(self, request):
|
|
81
|
+
response = Response()
|
|
82
|
+
handler, kwargs = self.find_handler(request_path=request.path)
|
|
83
|
+
|
|
84
|
+
if handler is not None:
|
|
85
|
+
if inspect.isclass(handler):
|
|
86
|
+
handler_instance = handler()
|
|
87
|
+
handler = getattr(handler_instance, request.method.lower(), None)
|
|
88
|
+
|
|
89
|
+
if handler is None:
|
|
90
|
+
response.status_code = 405
|
|
91
|
+
response.text = "Method not allowed."
|
|
92
|
+
return response
|
|
93
|
+
|
|
94
|
+
handler(request, response, **kwargs)
|
|
95
|
+
|
|
96
|
+
if hasattr(response, 'json_body'):
|
|
97
|
+
response.body = json.dumps(response.json_body).encode()
|
|
98
|
+
response.content_type = "application/json"
|
|
99
|
+
else:
|
|
100
|
+
self.default_response(response)
|
|
101
|
+
|
|
102
|
+
return response
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def create_app(app_name):
|
|
5
|
+
class_name = app_name.capitalize()
|
|
6
|
+
os.makedirs(app_name, exist_ok=True)
|
|
7
|
+
|
|
8
|
+
app_files = {
|
|
9
|
+
"__init__.py": "",
|
|
10
|
+
"models.py": f"class {class_name}(object):\n pass\n",
|
|
11
|
+
"handlers.py": f"class {class_name}Resource:\n def get(self, req, resp):\n resp.json = {{'message': 'Hello from {app_name}'}}\n",
|
|
12
|
+
"router.py": f"from .handlers import {class_name}Resource\n\nroutes = [\n ('/{app_name}', {class_name}Resource),\n]\n"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
for filename, content in app_files.items():
|
|
16
|
+
with open(os.path.join(app_name, filename), "w") as f:
|
|
17
|
+
f.write(content)
|
|
18
|
+
|
|
19
|
+
print(f"App '{app_name}' created and registered!")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from webob import Response
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class JsonErrorMiddleware:
|
|
5
|
+
def __init__(self, app):
|
|
6
|
+
self.app = app
|
|
7
|
+
|
|
8
|
+
def __call__(self, environ, start_response):
|
|
9
|
+
try:
|
|
10
|
+
return self.app(environ, start_response)
|
|
11
|
+
except Exception as e:
|
|
12
|
+
response = Response()
|
|
13
|
+
response.status_code = 500
|
|
14
|
+
response.json_body = {
|
|
15
|
+
"error": "Internal Server Error",
|
|
16
|
+
"message": str(e)
|
|
17
|
+
}
|
|
18
|
+
response.content_type = "application/json"
|
|
19
|
+
return response(environ, start_response)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LoggerMiddleware:
|
|
5
|
+
def __init__(self, app):
|
|
6
|
+
self.app = app
|
|
7
|
+
|
|
8
|
+
def __call__(self, environ, start_response):
|
|
9
|
+
# 1. Logic BEFORE the request hits the API
|
|
10
|
+
start_time = datetime.now()
|
|
11
|
+
method = environ.get('REQUEST_METHOD')
|
|
12
|
+
path = environ.get('PATH_INFO')
|
|
13
|
+
|
|
14
|
+
# 2. Pass the request to the main API (or the next middleware)
|
|
15
|
+
response = self.app(environ, start_response)
|
|
16
|
+
|
|
17
|
+
# 3. Logic AFTER the response is generated
|
|
18
|
+
duration = datetime.now() - start_time
|
|
19
|
+
|
|
20
|
+
return response
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: valerius_flow
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Django-inspired API framework
|
|
5
|
+
Author-email: Joao Goncalves <you@email.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Framework :: Django
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: webob
|
|
16
|
+
Requires-Dist: parse
|
|
17
|
+
Requires-Dist: python-dotenv
|
|
18
|
+
Requires-Dist: gunicorn
|
|
19
|
+
|
|
20
|
+
# Valerius
|
|
21
|
+
|
|
22
|
+
A Django-inspired API framework.
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install valerius
|
|
28
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
valerius/__init__.py
|
|
4
|
+
valerius/cli.py
|
|
5
|
+
valerius/framework.py
|
|
6
|
+
valerius/management.py
|
|
7
|
+
valerius/middlewares/json.py
|
|
8
|
+
valerius/middlewares/logger.py
|
|
9
|
+
valerius_flow.egg-info/PKG-INFO
|
|
10
|
+
valerius_flow.egg-info/SOURCES.txt
|
|
11
|
+
valerius_flow.egg-info/dependency_links.txt
|
|
12
|
+
valerius_flow.egg-info/requires.txt
|
|
13
|
+
valerius_flow.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
valerius
|