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.
Files changed (54) hide show
  1. {renus-2.7.2 → renus-2.7.3}/LICENSE +1 -1
  2. {renus-2.7.2/renus.egg-info → renus-2.7.3}/PKG-INFO +2 -2
  3. renus-2.7.3/README.md +135 -0
  4. {renus-2.7.2 → renus-2.7.3}/renus/core/cache.py +56 -32
  5. {renus-2.7.2 → renus-2.7.3}/renus/core/model.py +4 -2
  6. {renus-2.7.2 → renus-2.7.3}/renus/core/serialize.py +2 -4
  7. {renus-2.7.2 → renus-2.7.3/renus.egg-info}/PKG-INFO +2 -2
  8. {renus-2.7.2 → renus-2.7.3}/setup.py +2 -2
  9. renus-2.7.2/README.md +0 -1
  10. {renus-2.7.2 → renus-2.7.3}/MANIFEST.in +0 -0
  11. {renus-2.7.2 → renus-2.7.3}/renus/__init__.py +0 -0
  12. {renus-2.7.2 → renus-2.7.3}/renus/__main__.py +0 -0
  13. {renus-2.7.2 → renus-2.7.3}/renus/app.py +0 -0
  14. {renus-2.7.2 → renus-2.7.3}/renus/commands/__init__.py +0 -0
  15. {renus-2.7.2 → renus-2.7.3}/renus/commands/app/__init__.py +0 -0
  16. {renus-2.7.2 → renus-2.7.3}/renus/commands/app/run.py +0 -0
  17. {renus-2.7.2 → renus-2.7.3}/renus/commands/app/temp.py +0 -0
  18. {renus-2.7.2 → renus-2.7.3}/renus/commands/copy/__init__.py +0 -0
  19. {renus-2.7.2 → renus-2.7.3}/renus/commands/copy/run.py +0 -0
  20. {renus-2.7.2 → renus-2.7.3}/renus/commands/db/__init__.py +0 -0
  21. {renus-2.7.2 → renus-2.7.3}/renus/commands/db/run.py +0 -0
  22. {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/__init__.py +0 -0
  23. {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/build.py +0 -0
  24. {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/remove.py +0 -0
  25. {renus-2.7.2 → renus-2.7.3}/renus/commands/extension/run.py +0 -0
  26. {renus-2.7.2 → renus-2.7.3}/renus/commands/help.py +0 -0
  27. {renus-2.7.2 → renus-2.7.3}/renus/commands/run.py +0 -0
  28. {renus-2.7.2 → renus-2.7.3}/renus/core/__init__.py +0 -0
  29. {renus-2.7.2 → renus-2.7.3}/renus/core/concurrency.py +0 -0
  30. {renus-2.7.2 → renus-2.7.3}/renus/core/config.py +0 -0
  31. {renus-2.7.2 → renus-2.7.3}/renus/core/cprint.py +0 -0
  32. {renus-2.7.2 → renus-2.7.3}/renus/core/datastructures.py +0 -0
  33. {renus-2.7.2 → renus-2.7.3}/renus/core/exception.py +0 -0
  34. {renus-2.7.2 → renus-2.7.3}/renus/core/formparsers.py +0 -0
  35. {renus-2.7.2 → renus-2.7.3}/renus/core/injection.py +0 -0
  36. {renus-2.7.2 → renus-2.7.3}/renus/core/log.py +0 -0
  37. {renus-2.7.2 → renus-2.7.3}/renus/core/middleware.py +0 -0
  38. {renus-2.7.2 → renus-2.7.3}/renus/core/request.py +0 -0
  39. {renus-2.7.2 → renus-2.7.3}/renus/core/response.py +0 -0
  40. {renus-2.7.2 → renus-2.7.3}/renus/core/routing.py +0 -0
  41. {renus-2.7.2 → renus-2.7.3}/renus/core/schedule.py +0 -0
  42. {renus-2.7.2 → renus-2.7.3}/renus/core/status.py +0 -0
  43. {renus-2.7.2 → renus-2.7.3}/renus/core/validation/__init__.py +0 -0
  44. {renus-2.7.2 → renus-2.7.3}/renus/core/validation/rules.py +0 -0
  45. {renus-2.7.2 → renus-2.7.3}/renus/core/validation/validate.py +0 -0
  46. {renus-2.7.2 → renus-2.7.3}/renus/core/websockets.py +0 -0
  47. {renus-2.7.2 → renus-2.7.3}/renus/util/__init__.py +0 -0
  48. {renus-2.7.2 → renus-2.7.3}/renus/util/helper.py +0 -0
  49. {renus-2.7.2 → renus-2.7.3}/renus.egg-info/SOURCES.txt +0 -0
  50. {renus-2.7.2 → renus-2.7.3}/renus.egg-info/dependency_links.txt +0 -0
  51. {renus-2.7.2 → renus-2.7.3}/renus.egg-info/entry_points.txt +0 -0
  52. {renus-2.7.2 → renus-2.7.3}/renus.egg-info/requires.txt +0 -0
  53. {renus-2.7.2 → renus-2.7.3}/renus.egg-info/top_level.txt +0 -0
  54. {renus-2.7.2 → renus-2.7.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  BSD 3-Clause License
2
2
 
3
- Copyright (c) 2022, https://github.com/smkoBa/renus
3
+ Copyright (c) 2022, https://github.com/kak-smko/renus
4
4
  All rights reserved.
5
5
 
6
6
  Redistribution and use in source and binary forms, with or without
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: renus
3
- Version: 2.7.2
3
+ Version: 2.7.3
4
4
  Summary: Renus Core Framework
5
- Home-page: https://github.com/smkoBa/renus
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,timezone
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
- if self.rd:
37
- v=pickle.dumps(value,pickle.HIGHEST_PROTOCOL)
38
- self.rd.set(self._prefix+key,v,expire)
39
- return
40
- expire = int((datetime.now(timezone.utc) + timedelta(seconds=expire)).timestamp())
41
- path = self._build_name(key, self.depth)
42
- self._create_if_not_exist(path, value, expire)
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
- if self.rd:
46
- r=self.rd.get(self._prefix+key)
47
- return default if r is None else pickle.loads(r)
48
- path = self._build_name(key, self.depth)
49
- return self._read_key(path, default)['value']
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
- if self.rd:
53
- return max(-1,self.rd.ttl(self._prefix+key))
54
- path = self._build_name(key, self.depth)
55
- t= self._read_key(path, default)['expire']
56
- if t==-1:
57
- return -1
58
- return max(-1,int(t- datetime.now(timezone.utc).timestamp()))
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
- if self.rd:
62
- return self.rd.delete(self._prefix+key)
63
-
64
- path = self._build_name(key, self.depth)
65
- return self._delete_file(f"storage/{self.folder_path}{path}")
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
- Log().info(f'cache delete_file: {exc}')
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.utcnow().timestamp()):
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
- except:
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
- return {'value': default, 'expire': -1}
151
+ raise e
128
152
 
129
- if expire < datetime.now(timezone.utc).timestamp():
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
- @staticmethod
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()+'Z'
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.2
3
+ Version: 2.7.3
4
4
  Summary: Renus Core Framework
5
- Home-page: https://github.com/smkoBa/renus
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.2',
5
+ version='2.7.3',
6
6
  description='Renus Core Framework',
7
- url='https://github.com/smkoBa/renus',
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