cloudoll 2.4.0__py3-none-any.whl

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.
@@ -0,0 +1,222 @@
1
+ """
2
+ from https://github.com/erdewit/nest_asyncio/blob/master/nest_asyncio.py
3
+ """
4
+ """Patch asyncio to allow nested event loops."""
5
+
6
+ import asyncio
7
+ import asyncio.events as events
8
+ import os
9
+ import sys
10
+ import threading
11
+ from contextlib import contextmanager, suppress
12
+ from heapq import heappop
13
+
14
+
15
+ def apply(loop=None):
16
+ """Patch asyncio to make its event loop reentrant."""
17
+ _patch_asyncio()
18
+ _patch_policy()
19
+ _patch_tornado()
20
+
21
+ loop = loop or asyncio.get_event_loop()
22
+ _patch_loop(loop)
23
+
24
+
25
+ def _patch_asyncio():
26
+ """Patch asyncio module to use pure Python tasks and futures."""
27
+
28
+ def run(main, *, debug=False):
29
+ loop = asyncio.get_event_loop()
30
+ loop.set_debug(debug)
31
+ task = asyncio.ensure_future(main)
32
+ try:
33
+ return loop.run_until_complete(task)
34
+ finally:
35
+ if not task.done():
36
+ task.cancel()
37
+ with suppress(asyncio.CancelledError):
38
+ loop.run_until_complete(task)
39
+
40
+ def _get_event_loop(stacklevel=3):
41
+ loop = events._get_running_loop()
42
+ if loop is None:
43
+ loop = events.get_event_loop_policy().get_event_loop()
44
+ return loop
45
+
46
+ # Use module level _current_tasks, all_tasks and patch run method.
47
+ if hasattr(asyncio, '_nest_patched'):
48
+ return
49
+ if sys.version_info >= (3, 6, 0):
50
+ asyncio.Task = asyncio.tasks._CTask = asyncio.tasks.Task = \
51
+ asyncio.tasks._PyTask
52
+ asyncio.Future = asyncio.futures._CFuture = asyncio.futures.Future = \
53
+ asyncio.futures._PyFuture
54
+ if sys.version_info < (3, 7, 0):
55
+ asyncio.tasks._current_tasks = asyncio.tasks.Task._current_tasks
56
+ asyncio.all_tasks = asyncio.tasks.Task.all_tasks
57
+ if sys.version_info >= (3, 9, 0):
58
+ events._get_event_loop = events.get_event_loop = \
59
+ asyncio.get_event_loop = _get_event_loop
60
+ asyncio.run = run
61
+ asyncio._nest_patched = True
62
+
63
+
64
+ def _patch_policy():
65
+ """Patch the policy to always return a patched loop."""
66
+
67
+ def get_event_loop(self):
68
+ if self._local._loop is None:
69
+ loop = self.new_event_loop()
70
+ _patch_loop(loop)
71
+ self.set_event_loop(loop)
72
+ return self._local._loop
73
+
74
+ policy = events.get_event_loop_policy()
75
+ policy.__class__.get_event_loop = get_event_loop
76
+
77
+
78
+ def _patch_loop(loop):
79
+ """Patch loop to make it reentrant."""
80
+
81
+ def run_forever(self):
82
+ with manage_run(self), manage_asyncgens(self):
83
+ while True:
84
+ self._run_once()
85
+ if self._stopping:
86
+ break
87
+ self._stopping = False
88
+
89
+ def run_until_complete(self, future):
90
+ with manage_run(self):
91
+ f = asyncio.ensure_future(future, loop=self)
92
+ if f is not future:
93
+ f._log_destroy_pending = False
94
+ while not f.done():
95
+ self._run_once()
96
+ if self._stopping:
97
+ break
98
+ if not f.done():
99
+ raise RuntimeError(
100
+ 'Event loop stopped before Future completed.')
101
+ return f.result()
102
+
103
+ def _run_once(self):
104
+ """
105
+ Simplified re-implementation of asyncio's _run_once that
106
+ runs handles as they become ready.
107
+ """
108
+ ready = self._ready
109
+ scheduled = self._scheduled
110
+ while scheduled and scheduled[0]._cancelled:
111
+ heappop(scheduled)
112
+
113
+ timeout = (
114
+ 0 if ready or self._stopping
115
+ else min(max(
116
+ scheduled[0]._when - self.time(), 0), 86400) if scheduled
117
+ else None)
118
+ event_list = self._selector.select(timeout)
119
+ self._process_events(event_list)
120
+
121
+ end_time = self.time() + self._clock_resolution
122
+ while scheduled and scheduled[0]._when < end_time:
123
+ handle = heappop(scheduled)
124
+ ready.append(handle)
125
+
126
+ for _ in range(len(ready)):
127
+ if not ready:
128
+ break
129
+ handle = ready.popleft()
130
+ if not handle._cancelled:
131
+ # preempt the current task so that that checks in
132
+ # Task.__step do not raise
133
+ curr_task = curr_tasks.pop(self, None)
134
+
135
+ try:
136
+ handle._run()
137
+ finally:
138
+ # restore the current task
139
+ if curr_task is not None:
140
+ curr_tasks[self] = curr_task
141
+
142
+ handle = None
143
+
144
+ @contextmanager
145
+ def manage_run(self):
146
+ """Set up the loop for running."""
147
+ self._check_closed()
148
+ old_thread_id = self._thread_id
149
+ old_running_loop = events._get_running_loop()
150
+ try:
151
+ self._thread_id = threading.get_ident()
152
+ events._set_running_loop(self)
153
+ self._num_runs_pending += 1
154
+ if self._is_proactorloop:
155
+ if self._self_reading_future is None:
156
+ self.call_soon(self._loop_self_reading)
157
+ yield
158
+ finally:
159
+ self._thread_id = old_thread_id
160
+ events._set_running_loop(old_running_loop)
161
+ self._num_runs_pending -= 1
162
+ if self._is_proactorloop:
163
+ if (self._num_runs_pending == 0
164
+ and self._self_reading_future is not None):
165
+ ov = self._self_reading_future._ov
166
+ self._self_reading_future.cancel()
167
+ if ov is not None:
168
+ self._proactor._unregister(ov)
169
+ self._self_reading_future = None
170
+
171
+ @contextmanager
172
+ def manage_asyncgens(self):
173
+ if not hasattr(sys, 'get_asyncgen_hooks'):
174
+ # Python version is too old.
175
+ return
176
+ old_agen_hooks = sys.get_asyncgen_hooks()
177
+ try:
178
+ self._set_coroutine_origin_tracking(self._debug)
179
+ if self._asyncgens is not None:
180
+ sys.set_asyncgen_hooks(
181
+ firstiter=self._asyncgen_firstiter_hook,
182
+ finalizer=self._asyncgen_finalizer_hook)
183
+ yield
184
+ finally:
185
+ self._set_coroutine_origin_tracking(False)
186
+ if self._asyncgens is not None:
187
+ sys.set_asyncgen_hooks(*old_agen_hooks)
188
+
189
+ def _check_running(self):
190
+ """Do not throw exception if loop is already running."""
191
+ pass
192
+
193
+ if hasattr(loop, '_nest_patched'):
194
+ return
195
+ if not isinstance(loop, asyncio.BaseEventLoop):
196
+ raise ValueError('Can\'t patch loop of type %s' % type(loop))
197
+ cls = loop.__class__
198
+ cls.run_forever = run_forever
199
+ cls.run_until_complete = run_until_complete
200
+ cls._run_once = _run_once
201
+ cls._check_running = _check_running
202
+ cls._check_runnung = _check_running # typo in Python 3.7 source
203
+ cls._num_runs_pending = 1 if loop.is_running() else 0
204
+ cls._is_proactorloop = (
205
+ os.name == 'nt' and issubclass(cls, asyncio.ProactorEventLoop))
206
+ if sys.version_info < (3, 7, 0):
207
+ cls._set_coroutine_origin_tracking = cls._set_coroutine_wrapper
208
+ curr_tasks = asyncio.tasks._current_tasks \
209
+ if sys.version_info >= (3, 7, 0) else asyncio.Task._current_tasks
210
+ cls._nest_patched = True
211
+
212
+
213
+ def _patch_tornado():
214
+ """
215
+ If tornado is imported before nest_asyncio, make tornado aware of
216
+ the pure-Python asyncio Future.
217
+ """
218
+ if 'tornado' in sys.modules:
219
+ import tornado.concurrent as tc # type: ignore
220
+ tc.Future = asyncio.Future
221
+ if asyncio.Future not in tc.FUTURES:
222
+ tc.FUTURES += (asyncio.Future,)
@@ -0,0 +1,86 @@
1
+
2
+ import time
3
+ import threading
4
+
5
+
6
+ """
7
+ 雪花算法(全局唯一id)
8
+
9
+ # 最早是Twitter公司在其内部用于分布式环境下生成唯一ID
10
+
11
+ # Twitter's Snowflake algorithm implementation which is used to generate distributed IDs.
12
+
13
+ # https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala
14
+ """
15
+
16
+
17
+ class IdWorker:
18
+ def __init__(self, worker_id, datacenter_id, sequence=0):
19
+ # 位移值
20
+ self.worker_id_bits = 5
21
+ self.datacenter_id_bits = 5
22
+ self.sequence_bits = 12
23
+
24
+ self.max_worker_id = -1 ^ (-1 << self.worker_id_bits)
25
+ self.max_datacenter_id = -1 ^ (-1 << self.datacenter_id_bits)
26
+
27
+ # 参数校验
28
+ if worker_id > self.max_worker_id or worker_id < 0:
29
+ raise ValueError(f'worker_id must be between 0 and {self.max_worker_id}')
30
+ if datacenter_id > self.max_datacenter_id or datacenter_id < 0:
31
+ raise ValueError(f'datacenter_id must be between 0 and {self.max_datacenter_id}')
32
+
33
+ self.worker_id = worker_id
34
+ self.datacenter_id = datacenter_id
35
+ self.sequence = sequence
36
+ self.lock = threading.Lock()
37
+
38
+ # Twitter Epoch (2010-11-04 01:42:54 UTC)
39
+ self.twepoch = 1288834974657
40
+
41
+
42
+
43
+
44
+ self.worker_id_shift = self.sequence_bits
45
+ self.datacenter_id_shift = self.sequence_bits + self.worker_id_bits
46
+ self.timestamp_left_shift = self.sequence_bits + self.worker_id_bits + self.datacenter_id_bits
47
+ self.sequence_mask = -1 ^ (-1 << self.sequence_bits)
48
+
49
+ self.last_timestamp = -1
50
+
51
+ def _time_gen(self):
52
+ return int(time.time() * 1000)
53
+
54
+ def _til_next_millis(self, last_timestamp):
55
+ timestamp = self._time_gen()
56
+ while timestamp <= last_timestamp:
57
+ timestamp = self._time_gen()
58
+ return timestamp
59
+
60
+ def next_id(self):
61
+ with self.lock:
62
+ timestamp = self._time_gen()
63
+
64
+ if timestamp < self.last_timestamp:
65
+ raise Exception(f'Clock moved backwards. Refusing to generate id for {self.last_timestamp - timestamp} milliseconds')
66
+
67
+ if self.last_timestamp == timestamp:
68
+ self.sequence = (self.sequence + 1) & self.sequence_mask
69
+ if self.sequence == 0:
70
+ timestamp = self._til_next_millis(self.last_timestamp)
71
+ else:
72
+ self.sequence = 0
73
+
74
+ self.last_timestamp = timestamp
75
+
76
+ id = ((timestamp - self.twepoch) << self.timestamp_left_shift) | \
77
+ (self.datacenter_id << self.datacenter_id_shift) | \
78
+ (self.worker_id << self.worker_id_shift) | \
79
+ self.sequence
80
+
81
+ return id
82
+
83
+
84
+ # eg:
85
+ # worker = IdWorker(worker_id=1, datacenter_id=1)
86
+ # print(worker.next_id())
@@ -0,0 +1,194 @@
1
+ from typing import AsyncIterator, Optional, Union
2
+ from pathlib import Path
3
+ from ..web import Application, app
4
+ import asyncio
5
+ import os
6
+ import signal
7
+ import sys
8
+ from contextlib import suppress
9
+ from multiprocessing import Process
10
+ from watchfiles import awatch, DefaultFilter
11
+ from ..logging import info, warning, error
12
+ import contextlib
13
+ from typing import Iterator, Optional
14
+ from typing import Optional, Union
15
+ from aiohttp import web
16
+ import traceback
17
+ from .common import check_port_open
18
+
19
+ class CloudollFilter(DefaultFilter):
20
+ def __init__(self, ignore_dirs: tuple = None) -> None:
21
+ self.ignore_dirs = self.ignore_dirs + tuple("logs")
22
+ if ignore_dirs:
23
+ self.ignore_dirs = self.ignore_dirs + ignore_dirs
24
+
25
+ super().__init__()
26
+
27
+
28
+ class WatchTask:
29
+ _app: Application
30
+ _task: "asyncio.Task[None]"
31
+
32
+ def __init__(self, path: Union[Path, str]):
33
+ self._path = path
34
+
35
+ async def start(self, app: Application) -> None:
36
+ self._app = app
37
+ self.stopper = asyncio.Event()
38
+ ignore_dirs = self._config["server"].get("ignore_dirs", [])
39
+ self._awatch = awatch(
40
+ self._path,
41
+ stop_event=self.stopper,
42
+ watch_filter=CloudollFilter(tuple(ignore_dirs)),
43
+ )
44
+ self._task = asyncio.create_task(self._run())
45
+
46
+ async def _run(self) -> None:
47
+ raise NotImplementedError()
48
+
49
+ async def close(self, *args) -> None:
50
+ if self._task:
51
+ self.stopper.set()
52
+ self._task.cancel()
53
+ with suppress(asyncio.CancelledError):
54
+ await self._task
55
+
56
+ async def cleanup_ctx(self, app: Application) -> AsyncIterator[None]:
57
+ await self.start(app)
58
+ yield
59
+ await self.close()
60
+
61
+
62
+ @contextlib.contextmanager
63
+ def set_tty(tty_path: Optional[str]) -> Iterator[None]:
64
+ try:
65
+ if not tty_path:
66
+ # to match OSError from open
67
+ raise OSError()
68
+ with open(tty_path) as tty:
69
+ sys.stdin = tty
70
+ yield
71
+ except OSError:
72
+ # either tty_path is None (windows) or opening it fails (eg. on pycharm)
73
+ yield
74
+
75
+
76
+ def mian_app(tty_path, config, entry, env):
77
+ with set_tty(tty_path):
78
+ if sys.version_info >= (3, 11):
79
+ with asyncio.Runner() as runner:
80
+ app_runner = runner.run(create_main_app(config, entry, env))
81
+ try:
82
+ runner.run(start_main_app(app_runner, config["server"]["port"]))
83
+ runner.get_loop().run_forever()
84
+ except KeyboardInterrupt:
85
+ pass
86
+ finally:
87
+ with suppress(asyncio.TimeoutError, KeyboardInterrupt):
88
+ runner.run(app_runner.cleanup())
89
+ else:
90
+ loop = asyncio.new_event_loop()
91
+ runner = loop.run_until_complete(create_main_app(config, entry, env))
92
+ try:
93
+ loop.run_until_complete(start_main_app(runner, config["server"]["port"]))
94
+ loop.run_forever()
95
+ except KeyboardInterrupt:
96
+ pass
97
+ finally:
98
+ with suppress(asyncio.TimeoutError, KeyboardInterrupt):
99
+ loop.run_until_complete(runner.cleanup())
100
+
101
+
102
+ async def create_main_app(config, entry, env):
103
+ await check_port_open(config["server"]["port"])
104
+ App = app.create(env=env, config=config, entry_model=entry)
105
+ return web.AppRunner(App.app, shutdown_timeout=0.1)
106
+
107
+
108
+ async def start_main_app(runner, port):
109
+ await runner.setup()
110
+ site = web.TCPSite(runner, host="0.0.0.0", port=port)
111
+ info(f"Server running on http://0.0.0.0:{port}")
112
+ info("(Press CTRL+C to quit)")
113
+ await site.start()
114
+
115
+
116
+ class AppTask(WatchTask):
117
+ def __init__(self, watch_path: str, config, entry: None, env: str):
118
+ self._config = config
119
+ self._entry = entry
120
+ self._env = env
121
+ self._reloads = 0
122
+ assert watch_path
123
+ super().__init__(watch_path)
124
+
125
+ async def _run(self) -> None:
126
+ assert self._app is not None
127
+ self._start_dev_server()
128
+ try:
129
+ async for changes in self._awatch:
130
+ self._reloads += 1
131
+ if any(f.endswith(".py") for _, f in changes):
132
+ info(f"{len(changes)} changes, restarting server")
133
+ await self._stop_dev_server()
134
+ self._start_dev_server()
135
+ await asyncio.sleep(1)
136
+ except Exception as exc:
137
+ traceback.print_exc()
138
+
139
+ raise Exception("error running dev server")
140
+
141
+ def _start_dev_server(self) -> None:
142
+ act = "Start" if self._reloads == 0 else "Restart"
143
+ info(f"{act}ing dev server")
144
+
145
+ try:
146
+ tty_path = os.ttyname(sys.stdin.fileno())
147
+ except OSError: # pragma: no branch
148
+ # fileno() always fails with pytest
149
+ tty_path = "/dev/tty"
150
+ except AttributeError:
151
+ # on windows, without a windows machine I've no idea what else to do here
152
+ tty_path = None
153
+
154
+ # for key, value in self._config.items():
155
+ # try:
156
+ # pickle.dumps(value)
157
+ # except Exception as e:
158
+ # print(f"Serialization error on key '{key}': {e} {value}")
159
+
160
+ # import pickle
161
+ # try:
162
+ # pickle.dumps(b) # 检查是否可序列化
163
+ # except Exception as e:
164
+ # print("Serialization error:", e)
165
+ try:
166
+ self._process = Process(
167
+ target=mian_app, args=(tty_path, self._config, self._entry, self._env)
168
+ )
169
+ self._process.start()
170
+ except Exception as e:
171
+ error(f"Error starting server: {e}")
172
+
173
+ async def _stop_dev_server(self) -> None:
174
+ if self._process.is_alive():
175
+ info("stopping server process...")
176
+ if self._process.pid:
177
+ info("sending SIGINT")
178
+ os.kill(self._process.pid, signal.SIGINT)
179
+ self._process.join(5)
180
+ if self._process.exitcode is None:
181
+ warning("process has not terminated, sending SIGKILL")
182
+ self._process.kill()
183
+ self._process.join(1)
184
+ else:
185
+ info("process stopped")
186
+ else:
187
+ warning(
188
+ "server process already dead, exit code: %s", self._process.exitcode
189
+ )
190
+
191
+ async def close(self) -> None:
192
+ self.stopper.set()
193
+ await self._stop_dev_server()
194
+ await asyncio.gather(super().close())
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ __author__ = "chuchur/chuchur.com"
5
+ from typing import Tuple
6
+
7
+ from .settings import get_config
8
+ from . import jwt
9
+ from .core import (
10
+ Application,
11
+ app,
12
+ Response,
13
+ WebSocketResponse,
14
+ WebSocket,
15
+ WebStream,
16
+ WSMsgType,
17
+ View,
18
+ routes,
19
+ get,
20
+ put,
21
+ delete,
22
+ post,
23
+ render_json,
24
+ render_error,
25
+ render_view,
26
+ render,
27
+ middleware,
28
+ redirect,
29
+ )
30
+
31
+ __all__: Tuple[str, ...] = (
32
+ "Application",
33
+ "app",
34
+ "WebSocket",
35
+ "Response",
36
+ "WebSocketResponse",
37
+ "WebStream",
38
+ "WSMsgType",
39
+ "View",
40
+ "routes",
41
+ "get",
42
+ "put",
43
+ "delete",
44
+ "post",
45
+ "render_json",
46
+ "render_error",
47
+ "render_view",
48
+ "render",
49
+ "middleware",
50
+ "redirect",
51
+ "jwt",
52
+ "get_config",
53
+ )