renus 2.7.2__tar.gz → 2.7.3__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.
- {renus-2.7.2 → renus-2.7.3}/LICENSE +1 -1
- {renus-2.7.2/renus.egg-info → renus-2.7.3}/PKG-INFO +2 -2
- renus-2.7.3/README.md +135 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/cache.py +56 -32
- {renus-2.7.2 → renus-2.7.3}/renus/core/model.py +4 -2
- {renus-2.7.2 → renus-2.7.3}/renus/core/serialize.py +2 -4
- {renus-2.7.2 → renus-2.7.3/renus.egg-info}/PKG-INFO +2 -2
- {renus-2.7.2 → renus-2.7.3}/setup.py +2 -2
- renus-2.7.2/README.md +0 -1
- {renus-2.7.2 → renus-2.7.3}/MANIFEST.in +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/__main__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/app.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/app/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/app/run.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/app/temp.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/copy/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/copy/run.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/db/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/db/run.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/build.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/remove.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/run.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/help.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/commands/run.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/concurrency.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/config.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/cprint.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/datastructures.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/exception.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/formparsers.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/injection.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/log.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/middleware.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/request.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/response.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/routing.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/schedule.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/status.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/validation/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/validation/rules.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/validation/validate.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/core/websockets.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/util/__init__.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus/util/helper.py +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus.egg-info/SOURCES.txt +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus.egg-info/dependency_links.txt +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus.egg-info/entry_points.txt +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus.egg-info/requires.txt +0 -0
- {renus-2.7.2 → renus-2.7.3}/renus.egg-info/top_level.txt +0 -0
- {renus-2.7.2 → renus-2.7.3}/setup.cfg +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: renus
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.3
|
|
4
4
|
Summary: Renus Core Framework
|
|
5
|
-
Home-page: https://github.com/
|
|
5
|
+
Home-page: https://github.com/kak-smko/renus
|
|
6
6
|
Author: Smko Bayazidi
|
|
7
7
|
Author-email: ba.smko@gmail.com
|
|
8
8
|
License: BSD
|
renus-2.7.3/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
|
|
2
|
+
# Renus
|
|
3
|
+
|
|
4
|
+
**Renus** is a lightweight ASGI framework/toolkit, which is ideal for building async web services in Python. It is production-ready and designed for large-scale projects while maintaining a lightweight footprint and high performance.
|
|
5
|
+
|
|
6
|
+
## ✨ Features
|
|
7
|
+
|
|
8
|
+
- 🚀 **High Performance**: Great overall performance against independent benchmarks.
|
|
9
|
+
- 🪶 **Lightweight**: Low-complexity HTTP web framework with few hard dependencies.
|
|
10
|
+
- 🔄 **Async Support**: Fully compatible with `asyncio` and `trio` backends.
|
|
11
|
+
- 🔌 **WebSocket Support**: Built-in support for WebSockets.
|
|
12
|
+
- 🛠️ **Background Tasks**: Handle in-process background tasks easily.
|
|
13
|
+
- 🔄 **Lifespan Events**: Manage startup and shutdown events for resource management.
|
|
14
|
+
- 📦 **Built-in Utilities**: CORS, GZip, Static Files, Streaming responses, Caching, Cryptography, Scheduling, and more.
|
|
15
|
+
- ✅ **Reliable**: 100% test coverage.
|
|
16
|
+
- 📝 **Type Safe**: 100% type annotated codebase.
|
|
17
|
+
|
|
18
|
+
## 📦 Installation
|
|
19
|
+
|
|
20
|
+
Install Renus using pip:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install renus
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 🚀 Quick Start
|
|
27
|
+
|
|
28
|
+
Here is a simple example to get you started with Renus:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from renus.app import App
|
|
32
|
+
from renus.core.response import TextResponse, JsonResponse
|
|
33
|
+
from renus.core.routing import Router
|
|
34
|
+
|
|
35
|
+
# 1. Define route handlers
|
|
36
|
+
def home():
|
|
37
|
+
return TextResponse('Hello, world!')
|
|
38
|
+
|
|
39
|
+
def user(username):
|
|
40
|
+
return JsonResponse({'name': username})
|
|
41
|
+
|
|
42
|
+
async def websocket(ws):
|
|
43
|
+
await ws.accept()
|
|
44
|
+
await ws.send_text('Hello, world!')
|
|
45
|
+
await ws.close()
|
|
46
|
+
|
|
47
|
+
# 2. Define lifespan events (startup and shutdown)
|
|
48
|
+
async def lifespan(app):
|
|
49
|
+
print('Startup', app)
|
|
50
|
+
yield
|
|
51
|
+
print('Shutdown')
|
|
52
|
+
|
|
53
|
+
# 3. Setup routing
|
|
54
|
+
r = Router()
|
|
55
|
+
r.get('/', func=home)
|
|
56
|
+
r.get('/user/{username}', func=user)
|
|
57
|
+
r.ws('/ws', func=websocket)
|
|
58
|
+
|
|
59
|
+
# 4. Initialize the app
|
|
60
|
+
app = App(lifespan=lifespan)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Running the Application
|
|
64
|
+
|
|
65
|
+
You can run your application using an ASGI server like `uvicorn`:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
# main.py
|
|
69
|
+
import uvicorn
|
|
70
|
+
from renus.app import App
|
|
71
|
+
import routes.index # Import your routes module
|
|
72
|
+
|
|
73
|
+
app = App()
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
uvicorn.run(
|
|
77
|
+
"main:app",
|
|
78
|
+
host="127.0.0.1",
|
|
79
|
+
port=4000,
|
|
80
|
+
reload=True,
|
|
81
|
+
log_level="info",
|
|
82
|
+
headers=[
|
|
83
|
+
("X-Frame-Options", 'DENY'),
|
|
84
|
+
("Access-Control-Allow-Headers", '*'),
|
|
85
|
+
("Access-Control-Allow-Origin", '*')
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Run it with:
|
|
91
|
+
```bash
|
|
92
|
+
python main.py
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 📚 Documentation
|
|
96
|
+
|
|
97
|
+
For full documentation, advanced features, and API references, please visit the official documentation:
|
|
98
|
+
👉 **[Renus Documentation](https://codenus.com/en/renus)**
|
|
99
|
+
|
|
100
|
+
### Advanced Features
|
|
101
|
+
Renus comes packed with advanced features out of the box to help you build complex applications:
|
|
102
|
+
- **Caching**: Efficient data caching mechanisms.
|
|
103
|
+
- **Lifespan**: Manage application lifecycle events.
|
|
104
|
+
- **Cryptography**: Built-in cryptographic utilities.
|
|
105
|
+
- **Background Tasks**: Handle long-running tasks in the background.
|
|
106
|
+
- **Dependency Injection**: Powerful injection system.
|
|
107
|
+
- **Logging**: Integrated logging system.
|
|
108
|
+
- **Scheduling**: Schedule tasks to run at specific intervals.
|
|
109
|
+
- **Thread Pool**: Manage concurrent operations efficiently.
|
|
110
|
+
|
|
111
|
+
## 🤝 Contributing
|
|
112
|
+
|
|
113
|
+
Thank you for your interest in contributing to the Renus framework! We welcome all contributions from the community, whether they're bug reports, feature requests, documentation improvements, or code contributions.
|
|
114
|
+
|
|
115
|
+
### Coding Standards
|
|
116
|
+
- Follow **PEP 8** style guidelines.
|
|
117
|
+
- Use **type hints** for all new code (PEP 484).
|
|
118
|
+
- Keep lines under **88 characters** (Black's default line length).
|
|
119
|
+
|
|
120
|
+
### Testing
|
|
121
|
+
- Write unit tests for all new features using `pytest`.
|
|
122
|
+
- Include docstrings for all public methods and classes.
|
|
123
|
+
|
|
124
|
+
### Issue Reporting
|
|
125
|
+
When reporting bugs or requesting features:
|
|
126
|
+
- Check existing issues to avoid duplicates.
|
|
127
|
+
- Provide detailed reproduction steps for bugs.
|
|
128
|
+
- Include Python version, OS, and framework version.
|
|
129
|
+
- For feature requests, explain the use case and benefits.
|
|
130
|
+
|
|
131
|
+
Please read our [Contributing Guidelines](https://codenus.com/en/renus/3) for more details.
|
|
132
|
+
|
|
133
|
+
## 📄 License
|
|
134
|
+
|
|
135
|
+
*BSD 3-Clause License*
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import threading
|
|
3
|
+
import fcntl
|
|
2
4
|
import pickle
|
|
3
|
-
from datetime import datetime, timedelta
|
|
5
|
+
from datetime import UTC, datetime, timedelta
|
|
4
6
|
from hashlib import sha3_256
|
|
5
7
|
|
|
6
8
|
from renus.core.config import Config
|
|
@@ -17,6 +19,7 @@ class Cache:
|
|
|
17
19
|
depth = 1
|
|
18
20
|
folder_path = 'cache'
|
|
19
21
|
typ = Config('app').get('cacheDriver', 'file')
|
|
22
|
+
_lock = threading.RLock()
|
|
20
23
|
if typ=='redis':
|
|
21
24
|
assert rds is not None, "'redis' must be installed for Cache"
|
|
22
25
|
rd=rds
|
|
@@ -33,36 +36,52 @@ class Cache:
|
|
|
33
36
|
:param value: value that save by key
|
|
34
37
|
:param expire: seconds to expire from now
|
|
35
38
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
with self._lock:
|
|
40
|
+
if self.rd:
|
|
41
|
+
v=pickle.dumps(value,pickle.HIGHEST_PROTOCOL)
|
|
42
|
+
self.rd.set(self._prefix+key,v,expire)
|
|
43
|
+
return
|
|
44
|
+
expire = datetime.now(UTC) + timedelta(seconds=expire)
|
|
45
|
+
path = self._build_name(key, self.depth)
|
|
46
|
+
self._create_if_not_exist(path, value, expire)
|
|
47
|
+
|
|
48
|
+
def increment(self, key: str, value, expire: int = 60):
|
|
49
|
+
with self._lock:
|
|
50
|
+
if self.rd:
|
|
51
|
+
r=self.rd.get(self._prefix+key)
|
|
52
|
+
value= value if r is None else (pickle.loads(r)+value)
|
|
53
|
+
v=pickle.dumps(value,pickle.HIGHEST_PROTOCOL)
|
|
54
|
+
self.rd.set(self._prefix+key,v,expire)
|
|
55
|
+
return
|
|
56
|
+
expire = datetime.now(UTC) + timedelta(seconds=expire)
|
|
57
|
+
value=self.get(key,value-1)+1
|
|
58
|
+
path = self._build_name(key, self.depth)
|
|
59
|
+
self._create_if_not_exist(path, value, expire)
|
|
43
60
|
|
|
44
61
|
def get(self, key: str, default=None):
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
with self._lock:
|
|
63
|
+
if self.rd:
|
|
64
|
+
r=self.rd.get(self._prefix+key)
|
|
65
|
+
return default if r is None else pickle.loads(r)
|
|
66
|
+
path = self._build_name(key, self.depth)
|
|
67
|
+
return self._read_key(path, default)['value']
|
|
50
68
|
|
|
51
69
|
def expire(self, key, default=None):
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
with self._lock:
|
|
71
|
+
if self.rd:
|
|
72
|
+
return max(-1,self.rd.ttl(self._prefix+key))
|
|
73
|
+
path = self._build_name(key, self.depth)
|
|
74
|
+
t= self._read_key(path, default)['expire']
|
|
75
|
+
if t==-1:
|
|
76
|
+
return -1
|
|
77
|
+
return max(-1,int(t.timestamp()-datetime.now(UTC).timestamp()))
|
|
59
78
|
|
|
60
79
|
def delete(self, key):
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
80
|
+
with self._lock:
|
|
81
|
+
if self.rd:
|
|
82
|
+
return self.rd.delete(self._prefix+key)
|
|
83
|
+
path = self._build_name(key, self.depth)
|
|
84
|
+
return self._delete_file(f"storage/{self.folder_path}{path}")
|
|
66
85
|
|
|
67
86
|
def delete_expired(self):
|
|
68
87
|
filename = f'./storage/{self.folder_path}/'
|
|
@@ -86,7 +105,8 @@ class Cache:
|
|
|
86
105
|
except Exception as exc:
|
|
87
106
|
if n < 3:
|
|
88
107
|
return self._delete_file(path, n + 1)
|
|
89
|
-
|
|
108
|
+
else:
|
|
109
|
+
raise exc
|
|
90
110
|
return True
|
|
91
111
|
|
|
92
112
|
def _check_pass(self, line):
|
|
@@ -94,7 +114,7 @@ class Cache:
|
|
|
94
114
|
if len(res) < 2:
|
|
95
115
|
return True
|
|
96
116
|
expired, path = res
|
|
97
|
-
if (datetime.strptime(expired, '%Y-%m-%d %H:%M:%S').timestamp() < datetime.
|
|
117
|
+
if (datetime.strptime(expired, '%Y-%m-%d %H:%M:%S').timestamp() < datetime.now(UTC).timestamp()):
|
|
98
118
|
self._delete_file(f'storage/{self.folder_path}{path}')
|
|
99
119
|
return False
|
|
100
120
|
return True
|
|
@@ -105,11 +125,15 @@ class Cache:
|
|
|
105
125
|
try:
|
|
106
126
|
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
|
107
127
|
with open(filename, 'wb') as output:
|
|
128
|
+
fcntl.flock(output.fileno(), fcntl.LOCK_EX)
|
|
108
129
|
pickle.dump(expire, output, pickle.HIGHEST_PROTOCOL)
|
|
109
130
|
pickle.dump(value, output, pickle.HIGHEST_PROTOCOL)
|
|
110
|
-
|
|
131
|
+
fcntl.flock(output.fileno(), fcntl.LOCK_UN)
|
|
132
|
+
except Exception as e:
|
|
111
133
|
if n < 3:
|
|
112
134
|
self._create_if_not_exist(path, value, expire, n + 1)
|
|
135
|
+
else:
|
|
136
|
+
raise e
|
|
113
137
|
|
|
114
138
|
def _read_key(self, path, default=None, n=0):
|
|
115
139
|
filename = f'storage/{self.folder_path}{path}'
|
|
@@ -120,13 +144,13 @@ class Cache:
|
|
|
120
144
|
with open(filename, 'rb') as input:
|
|
121
145
|
expire = pickle.load(input)
|
|
122
146
|
value = pickle.load(input)
|
|
123
|
-
except Exception:
|
|
147
|
+
except Exception as e:
|
|
124
148
|
if n < 3:
|
|
125
149
|
return self._read_key(path, default, n + 1)
|
|
126
150
|
else:
|
|
127
|
-
|
|
151
|
+
raise e
|
|
128
152
|
|
|
129
|
-
if expire < datetime.now(
|
|
153
|
+
if expire.timestamp() < datetime.now(UTC).timestamp():
|
|
130
154
|
self._delete_file(filename)
|
|
131
155
|
return {'value': default, 'expire': -1}
|
|
132
156
|
return {'value': value, 'expire': expire}
|
|
@@ -177,4 +201,4 @@ def cache_func(time: int, prefix=''):
|
|
|
177
201
|
|
|
178
202
|
return wrapper
|
|
179
203
|
|
|
180
|
-
return dec
|
|
204
|
+
return dec
|
|
@@ -25,6 +25,9 @@ class ReModel:
|
|
|
25
25
|
elif isinstance(v, list):
|
|
26
26
|
v = self._process_list_items(v, field_type)
|
|
27
27
|
|
|
28
|
+
elif isinstance(v, datetime):
|
|
29
|
+
v = v.replace(tzinfo=timezone.utc)
|
|
30
|
+
|
|
28
31
|
setattr(self, k, v)
|
|
29
32
|
|
|
30
33
|
def _process_list_items(self, items, field_type):
|
|
@@ -366,8 +369,7 @@ class ModelBase(Generic[M]):
|
|
|
366
369
|
self.visible_fields = fields
|
|
367
370
|
return self
|
|
368
371
|
|
|
369
|
-
|
|
370
|
-
def boot_event(typ: str, old, new, session):
|
|
372
|
+
def boot_event(self,typ: str, old, new, session):
|
|
371
373
|
pass
|
|
372
374
|
|
|
373
375
|
@staticmethod
|
|
@@ -19,10 +19,8 @@ _DECIMAL_RE = re.compile(r'^-?\d+\.\d+$')
|
|
|
19
19
|
|
|
20
20
|
class jsonEncoder(json.JSONEncoder):
|
|
21
21
|
def default(self, o):
|
|
22
|
-
if isinstance(o, datetime.datetime):
|
|
23
|
-
return o.isoformat()
|
|
24
|
-
elif isinstance(o, datetime.date):
|
|
25
|
-
return o.isoformat() + 'T00:00:00Z'
|
|
22
|
+
if isinstance(o, (datetime.datetime, datetime.date)):
|
|
23
|
+
return o.isoformat()
|
|
26
24
|
elif isinstance(o, datetime.time):
|
|
27
25
|
r = o.isoformat()
|
|
28
26
|
if o.microsecond:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: renus
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.3
|
|
4
4
|
Summary: Renus Core Framework
|
|
5
|
-
Home-page: https://github.com/
|
|
5
|
+
Home-page: https://github.com/kak-smko/renus
|
|
6
6
|
Author: Smko Bayazidi
|
|
7
7
|
Author-email: ba.smko@gmail.com
|
|
8
8
|
License: BSD
|
|
@@ -2,9 +2,9 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name='renus',
|
|
5
|
-
version='2.7.
|
|
5
|
+
version='2.7.3',
|
|
6
6
|
description='Renus Core Framework',
|
|
7
|
-
url='https://github.com/
|
|
7
|
+
url='https://github.com/kak-smko/renus',
|
|
8
8
|
author='Smko Bayazidi',
|
|
9
9
|
author_email='ba.smko@gmail.com',
|
|
10
10
|
license='BSD',
|
renus-2.7.2/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# renus
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|