cloudoll 2.2.37__tar.gz → 2.3.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.
- {cloudoll-2.2.37 → cloudoll-2.3.0}/PKG-INFO +13 -2
- cloudoll-2.3.0/cloudoll/__init__.py +1 -0
- cloudoll-2.3.0/cloudoll/cli.py +76 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/logging/__init__.py +2 -2
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/__init__.py +1 -1
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/model.py +3 -3
- cloudoll-2.3.0/cloudoll/utils/cli_main.py +72 -0
- cloudoll-2.3.0/cloudoll/utils/common.py +51 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/utils/watch.py +38 -26
- cloudoll-2.3.0/cloudoll/web/__init__.py +51 -0
- cloudoll-2.2.37/cloudoll/web/__init__.py → cloudoll-2.3.0/cloudoll/web/core.py +135 -196
- cloudoll-2.3.0/cloudoll/web/http.py +79 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/web/settings.py +4 -4
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll.egg-info/PKG-INFO +13 -2
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll.egg-info/SOURCES.txt +2 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/setup.py +0 -3
- cloudoll-2.2.37/cloudoll/__init__.py +0 -1
- cloudoll-2.2.37/cloudoll/cli.py +0 -137
- cloudoll-2.2.37/cloudoll/utils/common.py +0 -17
- cloudoll-2.2.37/cloudoll/web/http.py +0 -139
- {cloudoll-2.2.37 → cloudoll-2.3.0}/LICENSE +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/README.md +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/__main__.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/error/__init__.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/error/errors.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/mail/__init__.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/mail/smtp.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/base.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/err.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/field.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/mysql.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/parse.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/orm/postgres.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/robot/__init__.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/robot/dingtalk.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/robot/feishu.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/robot/slack.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/utils/__init__.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/utils/m2d.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/utils/patch_loop.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/utils/snowflake.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/web/exception.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/web/html.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll/web/jwt.py +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll.egg-info/dependency_links.txt +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll.egg-info/entry_points.txt +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll.egg-info/requires.txt +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/cloudoll.egg-info/top_level.txt +0 -0
- {cloudoll-2.2.37 → cloudoll-2.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudoll
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: 辅助快速创建可分布的微服务。
|
|
5
5
|
Home-page: https://gitee.com/chuchur/cloudoll-py
|
|
6
6
|
Author: chuchur
|
|
@@ -29,6 +29,17 @@ Requires-Dist: aiohttp_session[secure]
|
|
|
29
29
|
Requires-Dist: PyYAML
|
|
30
30
|
Requires-Dist: click
|
|
31
31
|
Requires-Dist: envyaml
|
|
32
|
+
Dynamic: author
|
|
33
|
+
Dynamic: author-email
|
|
34
|
+
Dynamic: classifier
|
|
35
|
+
Dynamic: description
|
|
36
|
+
Dynamic: description-content-type
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: license
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
Dynamic: requires-dist
|
|
41
|
+
Dynamic: requires-python
|
|
42
|
+
Dynamic: summary
|
|
32
43
|
|
|
33
44
|
|
|
34
45
|
# cloudoll 云端玩具
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.3.0"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import asyncio
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from .logging import print_error
|
|
6
|
+
from . import __version__
|
|
7
|
+
from typing import Any
|
|
8
|
+
from .utils.cli_main import run_app, run_gen
|
|
9
|
+
sys.path.append(os.path.abspath("."))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group()
|
|
13
|
+
@click.version_option(__version__, "-V", "--version", prog_name="cloudoll")
|
|
14
|
+
def cli() -> None:
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
life_cycle = ["on_startup", "on_shutdown", "on_cleanup", "cleanup_ctx"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@cli.command()
|
|
22
|
+
@click.option("-p", "--path", help="Model's relative path,save models or create tables", default="models.py",)
|
|
23
|
+
@click.option("-c", "--create", help="For create model or table, model / table", default="model")
|
|
24
|
+
@click.option("-t", "--table", help="Table's name or Model's name, split by `,` or 'ALL'", required=True,)
|
|
25
|
+
@click.option("-env", "--environment", help="Environment, local / test / prod", default="local")
|
|
26
|
+
@click.option("-db", "--database", help="Database name, pick the database in conf.{env}.yaml", default="mysql",)
|
|
27
|
+
def gen(**config: Any) -> None:
|
|
28
|
+
"""Help to create models or tables."""
|
|
29
|
+
|
|
30
|
+
# click.echo(create)
|
|
31
|
+
async def start():
|
|
32
|
+
try:
|
|
33
|
+
sa = await run_gen(**config)
|
|
34
|
+
except Exception as e:
|
|
35
|
+
# traceback.print_exc()
|
|
36
|
+
print_error('Error: %s', e)
|
|
37
|
+
click.echo("input `cloudoll --help` for more helps.")
|
|
38
|
+
finally:
|
|
39
|
+
await sa.close()
|
|
40
|
+
|
|
41
|
+
asyncio.run(start())
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@cli.command()
|
|
45
|
+
@click.option("-env", "--environment", help="Environment, local / test / prod", default="local")
|
|
46
|
+
@click.option("-p", "--port", help="Server's port", default=None)
|
|
47
|
+
@click.option("-h", "--host", help="Server's host", default=None)
|
|
48
|
+
@click.option("-m", "--mode", help="development or production mode", default='development')
|
|
49
|
+
@click.option("-path", "--path", help="Unix file system path to serve on. Specifying a path will cause hostname and port arguments to be ignored.",)
|
|
50
|
+
@click.option("-e", "--entry", help="Entry point model name. delfault name app", default="app")
|
|
51
|
+
def start(**config: Any) -> None:
|
|
52
|
+
"""Start a services."""
|
|
53
|
+
try:
|
|
54
|
+
active_config = {k: v for k, v in config.items() if v is not None}
|
|
55
|
+
run_app(**active_config)
|
|
56
|
+
except Exception as e:
|
|
57
|
+
print_error('Error: %s', e)
|
|
58
|
+
# traceback.format_exc()
|
|
59
|
+
sys.exit(2)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
_project_existing = click.Path(dir_okay=True)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@cli.command()
|
|
66
|
+
@click.argument("project-name", type=_project_existing, required=True)
|
|
67
|
+
def create(project_name) -> None:
|
|
68
|
+
full_path = os.path.join(os.path.abspath("."), project_name)
|
|
69
|
+
# log = logging.getLogger('cloudoll.create')
|
|
70
|
+
if os.path.exists(full_path):
|
|
71
|
+
print_error(f"Project name `{project_name}` already exist.")
|
|
72
|
+
# return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
cli()
|
|
@@ -42,10 +42,10 @@ class ColorAnsi:
|
|
|
42
42
|
_FORMATS = {
|
|
43
43
|
# 终端输出格式
|
|
44
44
|
# "console_format": "%(log_color)s[%(levelname)s]-%(asctime)s-%(pathname)s-%(module)s-%(funcName)s-[line:%(lineno)d]: %(message)s",
|
|
45
|
-
"console_format": "%(log_color)s%(asctime)s %(levelname)s %(
|
|
45
|
+
"console_format": "%(log_color)s%(asctime)s %(levelname)s %(message)s",
|
|
46
46
|
# 日志输出格式
|
|
47
47
|
# "log_format": "[%(levelname)s]-%(asctime)s-%(pathname)s-%(module)s-%(funcName)s-[line:%(lineno)d]: %(message)s",
|
|
48
|
-
"log_format": "%(asctime)s %(levelname)s %(
|
|
48
|
+
"log_format": "%(asctime)s %(levelname)s %(message)s",
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
_LOG_SIZE = 1024 * 1024 * 1 # 1MB 日志最大1MB
|
|
@@ -44,7 +44,7 @@ async def create_engine(**kw):
|
|
|
44
44
|
url = f"{driver}://{configs['username']}:{configs['password']}@{configs['host']}:{configs['port']}/{configs['db']}"
|
|
45
45
|
return await aioredis.from_url(url, **query)
|
|
46
46
|
else:
|
|
47
|
-
|
|
47
|
+
raise "Not suport this database type."
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class Postgres(MeteBase):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from .field import Field, Function, Expression
|
|
2
|
-
from ..logging import
|
|
2
|
+
from ..logging import warning
|
|
3
3
|
from functools import reduce
|
|
4
4
|
import operator
|
|
5
5
|
import copy
|
|
@@ -35,13 +35,13 @@ class ModelMetaclass(type):
|
|
|
35
35
|
# mappings[k] = v
|
|
36
36
|
if v.primary_key:
|
|
37
37
|
if primary_key:
|
|
38
|
-
|
|
38
|
+
warning(f"Duplicate primary key for {table_name}")
|
|
39
39
|
primary_key = k
|
|
40
40
|
# else:
|
|
41
41
|
fields.append(k)
|
|
42
42
|
|
|
43
43
|
if not primary_key:
|
|
44
|
-
|
|
44
|
+
warning(f"{table_name} Missing primary key")
|
|
45
45
|
|
|
46
46
|
# for k in mappings.keys():
|
|
47
47
|
# attrs.pop(k)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from ..web.settings import get_config
|
|
3
|
+
from ..logging import print_info
|
|
4
|
+
from .common import Object, chainMap
|
|
5
|
+
from .watch import AppTask
|
|
6
|
+
from aiohttp import web
|
|
7
|
+
from ..web import app
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from .m2d import create_models, create_tables
|
|
10
|
+
from ..orm import create_engine
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def run_app(**config_kwargs: Any):
|
|
15
|
+
config = Object(config_kwargs)
|
|
16
|
+
print_info("Current mode: ", config.mode)
|
|
17
|
+
app_config = get_config(config.environment)
|
|
18
|
+
# print(environment, host, port, mode, path, entry)
|
|
19
|
+
# return
|
|
20
|
+
if config.mode == 'production':
|
|
21
|
+
App = app.create(env=config.environment, config=app_config, entry_model=config.entry)
|
|
22
|
+
App.run()
|
|
23
|
+
else:
|
|
24
|
+
aux_app = web.Application(
|
|
25
|
+
logger=None,
|
|
26
|
+
)
|
|
27
|
+
defaults = {"host": "0.0.0.0", "port": 9001, "path": None}
|
|
28
|
+
conf_server = app_config.get("server", {})
|
|
29
|
+
env_server = {"host": config.host, "port": config.port, "path": config.path}
|
|
30
|
+
server = chainMap(defaults, conf_server, env_server)
|
|
31
|
+
app_config["server"] = server
|
|
32
|
+
aux_port = int(server.port) + 1
|
|
33
|
+
task = AppTask(Path(".").resolve(), app_config, entry=config.entry, env=config.environment)
|
|
34
|
+
aux_app.cleanup_ctx.append(task.cleanup_ctx)
|
|
35
|
+
web.run_app(
|
|
36
|
+
aux_app,
|
|
37
|
+
access_log=None,
|
|
38
|
+
host=config.host,
|
|
39
|
+
port=aux_port,
|
|
40
|
+
print=None,
|
|
41
|
+
shutdown_timeout=0.1,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def run_gen(**config_kwargs: Any):
|
|
46
|
+
config = Object(config_kwargs)
|
|
47
|
+
configs = get_config(config.environment)
|
|
48
|
+
db_configs = configs.get("database")
|
|
49
|
+
if db_configs is None:
|
|
50
|
+
raise KeyError(
|
|
51
|
+
f"Can't find the database config in conf.{config.environment}.yaml"
|
|
52
|
+
)
|
|
53
|
+
db_config = db_configs.get(config.database)
|
|
54
|
+
if db_config is None:
|
|
55
|
+
raise KeyError(
|
|
56
|
+
f"Can't find the database config key ->{config.database} in conf.{config.environment}.yaml"
|
|
57
|
+
)
|
|
58
|
+
sa = await create_engine(**db_config)
|
|
59
|
+
if sa.pool is None:
|
|
60
|
+
return
|
|
61
|
+
model_path = os.path.join(os.path.abspath("."), config.path)
|
|
62
|
+
tables = None
|
|
63
|
+
if config.table != "ALL":
|
|
64
|
+
tables = config.table.split(",")
|
|
65
|
+
|
|
66
|
+
if config.create == "model":
|
|
67
|
+
await create_models(sa, config.path, tables=tables)
|
|
68
|
+
print_info(f"Model save at:{model_path}")
|
|
69
|
+
elif config.create == "table":
|
|
70
|
+
if model_path is None:
|
|
71
|
+
raise ValueError("Need package name or model name.")
|
|
72
|
+
await create_tables(sa, model_path, tables=tables)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
class Object(dict):
|
|
3
|
+
# __setattr__ = dict.__setitem__
|
|
4
|
+
# __getattr__ = dict.__getitem__
|
|
5
|
+
def __setattr__(self, key, value):
|
|
6
|
+
self[key] = value
|
|
7
|
+
|
|
8
|
+
def __getattr__(self, key):
|
|
9
|
+
return self.get(key, None)
|
|
10
|
+
|
|
11
|
+
def __delattr__(self, key):
|
|
12
|
+
try:
|
|
13
|
+
del self[key]
|
|
14
|
+
except KeyError:
|
|
15
|
+
raise AttributeError(f"'DotDict' object has no attribute '{key}'")
|
|
16
|
+
|
|
17
|
+
def __getstate__(self):
|
|
18
|
+
'''to fix pickle.dumps '''
|
|
19
|
+
return dict(self)
|
|
20
|
+
|
|
21
|
+
def __setstate__(self, state):
|
|
22
|
+
self.update(state)
|
|
23
|
+
|
|
24
|
+
def chainMap(*dicts):
|
|
25
|
+
merged_dict = Object()
|
|
26
|
+
for d in dicts:
|
|
27
|
+
for key, value in d.items():
|
|
28
|
+
if key not in merged_dict or value is not None:
|
|
29
|
+
merged_dict[key] = value
|
|
30
|
+
return merged_dict
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def check_port_open(port: int, delay: float = 1) -> None:
|
|
34
|
+
loop = asyncio.get_running_loop()
|
|
35
|
+
# the "s = socket.socket; s.bind" approach sometimes says a port is in use when it's not
|
|
36
|
+
# this approach replicates aiohttp so should always give the same answer
|
|
37
|
+
for i in range(5, 0, -1):
|
|
38
|
+
try:
|
|
39
|
+
server = await loop.create_server(
|
|
40
|
+
asyncio.Protocol, host="0.0.0.0", port=port
|
|
41
|
+
)
|
|
42
|
+
except OSError as e:
|
|
43
|
+
if e.errno != EADDRINUSE:
|
|
44
|
+
raise
|
|
45
|
+
warning("port %d is already in use, waiting %d...", port, i)
|
|
46
|
+
await asyncio.sleep(delay)
|
|
47
|
+
else:
|
|
48
|
+
server.close()
|
|
49
|
+
await server.wait_closed()
|
|
50
|
+
return
|
|
51
|
+
raise Exception("The port {} is already is use".format(port))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import AsyncIterator,
|
|
1
|
+
from typing import AsyncIterator, Optional, Union
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from ..web import Application, app
|
|
3
|
+
from ..web import Application, app
|
|
4
4
|
import asyncio
|
|
5
5
|
import os
|
|
6
6
|
import signal
|
|
@@ -8,12 +8,13 @@ import sys
|
|
|
8
8
|
from contextlib import suppress
|
|
9
9
|
from multiprocessing import Process
|
|
10
10
|
from watchfiles import awatch, DefaultFilter
|
|
11
|
-
from ..logging import
|
|
11
|
+
from ..logging import info, warning, error
|
|
12
12
|
import contextlib
|
|
13
|
-
from typing import
|
|
14
|
-
from typing import
|
|
13
|
+
from typing import Iterator, Optional
|
|
14
|
+
from typing import Optional, Union
|
|
15
15
|
from aiohttp import web
|
|
16
|
-
|
|
16
|
+
import traceback
|
|
17
|
+
from .common import check_port_open
|
|
17
18
|
|
|
18
19
|
class CloudollFilter(DefaultFilter):
|
|
19
20
|
def __init__(self, ignore_dirs: tuple = None) -> None:
|
|
@@ -89,9 +90,7 @@ def mian_app(tty_path, config, entry, env):
|
|
|
89
90
|
loop = asyncio.new_event_loop()
|
|
90
91
|
runner = loop.run_until_complete(create_main_app(config, entry, env))
|
|
91
92
|
try:
|
|
92
|
-
loop.run_until_complete(
|
|
93
|
-
start_main_app(runner, config["server"]["port"])
|
|
94
|
-
)
|
|
93
|
+
loop.run_until_complete(start_main_app(runner, config["server"]["port"]))
|
|
95
94
|
loop.run_forever()
|
|
96
95
|
except KeyboardInterrupt:
|
|
97
96
|
pass
|
|
@@ -109,6 +108,8 @@ async def create_main_app(config, entry, env):
|
|
|
109
108
|
async def start_main_app(runner, port):
|
|
110
109
|
await runner.setup()
|
|
111
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)")
|
|
112
113
|
await site.start()
|
|
113
114
|
|
|
114
115
|
|
|
@@ -123,26 +124,23 @@ class AppTask(WatchTask):
|
|
|
123
124
|
|
|
124
125
|
async def _run(self) -> None:
|
|
125
126
|
assert self._app is not None
|
|
127
|
+
self._start_dev_server()
|
|
126
128
|
try:
|
|
127
|
-
self._start_dev_server()
|
|
128
|
-
|
|
129
129
|
async for changes in self._awatch:
|
|
130
130
|
self._reloads += 1
|
|
131
131
|
if any(f.endswith(".py") for _, f in changes):
|
|
132
|
-
|
|
132
|
+
info(f"{len(changes)} changes, restarting server")
|
|
133
133
|
await self._stop_dev_server()
|
|
134
134
|
self._start_dev_server()
|
|
135
135
|
await asyncio.sleep(1)
|
|
136
136
|
except Exception as exc:
|
|
137
|
-
|
|
137
|
+
traceback.print_exc()
|
|
138
|
+
|
|
138
139
|
raise Exception("error running dev server")
|
|
139
140
|
|
|
140
141
|
def _start_dev_server(self) -> None:
|
|
141
142
|
act = "Start" if self._reloads == 0 else "Restart"
|
|
142
|
-
|
|
143
|
-
print_info(
|
|
144
|
-
f"Server Running on http://{self._config['server']['host']}:{self._config['server']['port']}"
|
|
145
|
-
)
|
|
143
|
+
info(f"{act}ing dev server")
|
|
146
144
|
|
|
147
145
|
try:
|
|
148
146
|
tty_path = os.ttyname(sys.stdin.fileno())
|
|
@@ -153,26 +151,40 @@ class AppTask(WatchTask):
|
|
|
153
151
|
# on windows, without a windows machine I've no idea what else to do here
|
|
154
152
|
tty_path = None
|
|
155
153
|
|
|
156
|
-
self.
|
|
157
|
-
|
|
158
|
-
)
|
|
159
|
-
|
|
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}")
|
|
160
172
|
|
|
161
173
|
async def _stop_dev_server(self) -> None:
|
|
162
174
|
if self._process.is_alive():
|
|
163
|
-
|
|
175
|
+
info("stopping server process...")
|
|
164
176
|
if self._process.pid:
|
|
165
|
-
|
|
177
|
+
info("sending SIGINT")
|
|
166
178
|
os.kill(self._process.pid, signal.SIGINT)
|
|
167
179
|
self._process.join(5)
|
|
168
180
|
if self._process.exitcode is None:
|
|
169
|
-
|
|
181
|
+
warning("process has not terminated, sending SIGKILL")
|
|
170
182
|
self._process.kill()
|
|
171
183
|
self._process.join(1)
|
|
172
184
|
else:
|
|
173
|
-
|
|
185
|
+
info("process stopped")
|
|
174
186
|
else:
|
|
175
|
-
|
|
187
|
+
warning(
|
|
176
188
|
"server process already dead, exit code: %s", self._process.exitcode
|
|
177
189
|
)
|
|
178
190
|
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
+
middleware,
|
|
27
|
+
redirect,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__: Tuple[str, ...] = (
|
|
31
|
+
"Application",
|
|
32
|
+
"app",
|
|
33
|
+
"WebSocket",
|
|
34
|
+
"Response",
|
|
35
|
+
"WebSocketResponse",
|
|
36
|
+
"WebStream",
|
|
37
|
+
"WSMsgType",
|
|
38
|
+
"View",
|
|
39
|
+
"routes",
|
|
40
|
+
"get",
|
|
41
|
+
"put",
|
|
42
|
+
"delete",
|
|
43
|
+
"post",
|
|
44
|
+
"render_json",
|
|
45
|
+
"render_error",
|
|
46
|
+
"render_view",
|
|
47
|
+
"middleware",
|
|
48
|
+
"redirect",
|
|
49
|
+
"jwt",
|
|
50
|
+
"get_config",
|
|
51
|
+
)
|