fasttask-manager 0.2.1__py2.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.
- fasttask_manager/__init__.py +1 -0
- fasttask_manager/create_project.py +40 -0
- fasttask_manager/manager.py +88 -0
- fasttask_manager/project/Dockerfile +12 -0
- fasttask_manager/project/docker-compose.yml +32 -0
- fasttask_manager/project/req.txt +0 -0
- fasttask_manager/project/setting.py +21 -0
- fasttask_manager/project/tasks/get_circle_area.py +25 -0
- fasttask_manager/project/tasks/get_hypotenuse.py +28 -0
- fasttask_manager/project/tasks/packages/__init__.py +0 -0
- fasttask_manager/project/tasks/packages/tools.py +10 -0
- fasttask_manager-0.2.1.dist-info/LICENSE +21 -0
- fasttask_manager-0.2.1.dist-info/METADATA +53 -0
- fasttask_manager-0.2.1.dist-info/RECORD +16 -0
- fasttask_manager-0.2.1.dist-info/WHEEL +6 -0
- fasttask_manager-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .manager import Manager
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_int_input_or_default(name, default):
|
|
6
|
+
input_str = input(f"{name} (default:{default}):")
|
|
7
|
+
return int(input_str) if input_str else default
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def read_file(file):
|
|
11
|
+
with open(file, encoding="utf-8") as f:
|
|
12
|
+
return f.read()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def write_file(content, file):
|
|
16
|
+
with open(file, "w", encoding="utf-8") as f:
|
|
17
|
+
f.write(content)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def replace_file_content(file, replace_dict):
|
|
21
|
+
content = read_file(file)
|
|
22
|
+
for k, v in replace_dict.items():
|
|
23
|
+
content = content.replace("{" + k + "}", str(v))
|
|
24
|
+
write_file(content, file)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def create_project():
|
|
28
|
+
project_name = input("project name:")
|
|
29
|
+
port = get_int_input_or_default("port", 80)
|
|
30
|
+
fasttask_path = os.path.abspath(os.path.dirname(__file__))
|
|
31
|
+
|
|
32
|
+
shutil.copytree(os.path.join(fasttask_path, "project"), f"{project_name}")
|
|
33
|
+
|
|
34
|
+
replace_dict = {"project_name": project_name, "port": port}
|
|
35
|
+
|
|
36
|
+
replace_file_content(f"{project_name}/docker-compose.yml", replace_dict)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
create_project()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import requests
|
|
3
|
+
from retry import retry
|
|
4
|
+
from logging import Logger, StreamHandler
|
|
5
|
+
from requests.auth import HTTPBasicAuth
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Manager:
|
|
9
|
+
def __init__(self, host: str, task_name: str, protocol: str = "http", port: int = 80, check_gap: int = 15,
|
|
10
|
+
tries: int = 5, delay: int = 3, logger: Logger = None, log_prefix: str = "",
|
|
11
|
+
auth_user: str = "", auth_passwd: str = "", url_base_path: str = "", req_timeout=30) -> None:
|
|
12
|
+
|
|
13
|
+
self.task_name = task_name
|
|
14
|
+
self.protocol = protocol
|
|
15
|
+
self.host = host
|
|
16
|
+
self.port = port
|
|
17
|
+
self.url = f"{self.protocol}://{self.host}:{self.port}{url_base_path}"
|
|
18
|
+
self.tries = tries
|
|
19
|
+
self.delay = delay
|
|
20
|
+
self.check_gap = check_gap
|
|
21
|
+
self.logger = logger
|
|
22
|
+
self.log_prefix = f"{log_prefix}{self.task_name}:"
|
|
23
|
+
self.auth = HTTPBasicAuth(auth_user, auth_passwd)
|
|
24
|
+
self.req_timeout = req_timeout
|
|
25
|
+
if self.logger:
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
self.logger = Logger(task_name)
|
|
29
|
+
self.logger.addHandler(StreamHandler())
|
|
30
|
+
|
|
31
|
+
def _req(self, path, data: dict = None, method="p", file: str = None, raw_resp: bool = False):
|
|
32
|
+
@retry(tries=self.tries, delay=self.delay)
|
|
33
|
+
def req():
|
|
34
|
+
params = {
|
|
35
|
+
"url": f"{self.url}{path}",
|
|
36
|
+
"auth": self.auth,
|
|
37
|
+
"files": None if not file else {
|
|
38
|
+
'file': open(file, 'rb')
|
|
39
|
+
},
|
|
40
|
+
"timeout": self.req_timeout,
|
|
41
|
+
}
|
|
42
|
+
if method == "p":
|
|
43
|
+
r = requests.post(json=data, **params)
|
|
44
|
+
elif method == "g":
|
|
45
|
+
r = requests.get(params=data, **params)
|
|
46
|
+
|
|
47
|
+
r.raise_for_status()
|
|
48
|
+
return r if raw_resp else r.json()
|
|
49
|
+
return req()
|
|
50
|
+
|
|
51
|
+
def run(self, params: dict) -> dict:
|
|
52
|
+
return self._req(path=f"/run/{self.task_name}", data=params)
|
|
53
|
+
|
|
54
|
+
def create_task(self, params: dict) -> dict:
|
|
55
|
+
self.logger.info(f"{self.log_prefix}: task creating...")
|
|
56
|
+
return self._req(path=f"/create/{self.task_name}", data=params)
|
|
57
|
+
|
|
58
|
+
def check(self, result_id: str) -> dict:
|
|
59
|
+
resp = self._req(path=f"/check/{self.task_name}", data={"result_id": result_id}, method="g")
|
|
60
|
+
self.logger.info(f"{self.log_prefix}: check task: {resp['state']}")
|
|
61
|
+
return resp
|
|
62
|
+
|
|
63
|
+
def upload(self, file_path) -> str:
|
|
64
|
+
return self._req("/upload", method="p", file=file_path)["file_name"]
|
|
65
|
+
|
|
66
|
+
def download(self, file_name, local_path):
|
|
67
|
+
r = self._req("/download", data={"file_name": file_name}, method="g", raw_resp=True)
|
|
68
|
+
with open(local_path, "wb") as f:
|
|
69
|
+
for chunk in r.iter_content(chunk_size=512):
|
|
70
|
+
f.write(chunk)
|
|
71
|
+
|
|
72
|
+
def create_and_wait_result(self, params: dict) -> dict:
|
|
73
|
+
start = time.time()
|
|
74
|
+
resp = self.create_task(params)
|
|
75
|
+
|
|
76
|
+
self.logger.info(f"{self.log_prefix} cost: {time.time() - start} create_task resp: {resp}")
|
|
77
|
+
|
|
78
|
+
while True:
|
|
79
|
+
resp = self.check(result_id=resp["id"])
|
|
80
|
+
if resp["state"] == "FAILURE":
|
|
81
|
+
self.logger.info(f"{self.log_prefix} cost: {time.time()-start}")
|
|
82
|
+
raise Exception(f"task :{resp['result']}")
|
|
83
|
+
|
|
84
|
+
elif resp["state"] == "SUCCESS":
|
|
85
|
+
self.logger.info(f"{self.log_prefix} cost: {time.time()-start}")
|
|
86
|
+
return resp["result"]
|
|
87
|
+
|
|
88
|
+
time.sleep(self.check_gap)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
version: "3.9"
|
|
2
|
+
|
|
3
|
+
services:
|
|
4
|
+
{project_name}:
|
|
5
|
+
image: {project_name}
|
|
6
|
+
restart: always
|
|
7
|
+
command: sh run.sh
|
|
8
|
+
|
|
9
|
+
container_name: {project_name}
|
|
10
|
+
|
|
11
|
+
ports:
|
|
12
|
+
# 在single_node 与 distributed_master 需要映射api端口
|
|
13
|
+
- "{port}:80"
|
|
14
|
+
# 在distributed_master需要映射任务队列端口
|
|
15
|
+
# - "9000:6379"
|
|
16
|
+
|
|
17
|
+
volumes:
|
|
18
|
+
- ./files:/fasttask/files
|
|
19
|
+
|
|
20
|
+
environment:
|
|
21
|
+
# single_node: 单一节点提供接口+执行任务
|
|
22
|
+
- node_type=single_node
|
|
23
|
+
|
|
24
|
+
# # distributed_master: 分布式部署部署的主节点 提供api与任务队列后端
|
|
25
|
+
# - node_type=distributed_master
|
|
26
|
+
# - task_queue_passwd=passwd
|
|
27
|
+
|
|
28
|
+
# # distributed_worker: 分布式部署部署的从节点 负责cerery任务执行环境,需要配置distributed_master的主机地址, 任务队列端口与密码
|
|
29
|
+
# - node_type=distributed_worker
|
|
30
|
+
# - master_host=0.0.0.0
|
|
31
|
+
# - task_queue_port=9000
|
|
32
|
+
# - task_queue_passwd=passwd
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# fasttask setting
|
|
2
|
+
project_title = "⚡{project_name}⚡"
|
|
3
|
+
project_summary = "{project_name} Project"
|
|
4
|
+
project_description = "this is a fasttask project!"
|
|
5
|
+
project_version = "ver: 🐕"
|
|
6
|
+
|
|
7
|
+
api_docs = True
|
|
8
|
+
api_redoc = True
|
|
9
|
+
|
|
10
|
+
file_download = True
|
|
11
|
+
file_upload = True
|
|
12
|
+
user_to_passwd = {
|
|
13
|
+
"john wick": "john_passwd"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# celery setting use namespeace "celery"
|
|
18
|
+
# ref: https://docs.celeryq.dev/en/stable/userguide/configuration.html
|
|
19
|
+
# celery_result_expires = 3600
|
|
20
|
+
# celery_worker_pool = "prefork"
|
|
21
|
+
# celery_worker_concurrency = 4
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from celery_app import app
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
from tasks.packages.tools import sleep_random
|
|
5
|
+
from typing import Union
|
|
6
|
+
from math import pi
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Params(BaseModel):
|
|
10
|
+
r: Union[float, int]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Result(BaseModel):
|
|
14
|
+
area: Union[float, int]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.task
|
|
18
|
+
def get_circle_area(r):
|
|
19
|
+
if r <= 0:
|
|
20
|
+
raise ValueError("r must > 0")
|
|
21
|
+
print("running...")
|
|
22
|
+
sleep_random()
|
|
23
|
+
result = Result(area=pi * r**2)
|
|
24
|
+
|
|
25
|
+
return result.model_dump()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
|
|
4
|
+
from packages.tools import xx, sleep_random
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Params(BaseModel):
|
|
8
|
+
a: Union[float, int]
|
|
9
|
+
b: Union[float, int]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Result(BaseModel):
|
|
13
|
+
hypotenuse: Union[float, int]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_hypotenuse(a, b):
|
|
17
|
+
if a <= 0 or b <= 0:
|
|
18
|
+
raise ValueError("side length must > 0")
|
|
19
|
+
print("running...")
|
|
20
|
+
sleep_random()
|
|
21
|
+
result = Result(hypotenuse=(xx(a) + xx(b))**0.5)
|
|
22
|
+
return result.model_dump()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == '__main__':
|
|
26
|
+
with open("files/a.txt", "w") as f:
|
|
27
|
+
f.write("a")
|
|
28
|
+
print(get_hypotenuse(3, 4))
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [year] [fullname]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: fasttask_manager
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: fasttask's manager
|
|
5
|
+
Home-page: https://github.com/iridesc/fasttask_manager
|
|
6
|
+
Author: Irid
|
|
7
|
+
Author-email: irid.zzy@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: retry
|
|
14
|
+
Requires-Dist: requests
|
|
15
|
+
|
|
16
|
+
manager for [fasttask](https://github.com/iridesc/fasttask)
|
|
17
|
+
|
|
18
|
+
### create project
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
python -m fasttask_manager.create_project
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### create a Manager
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
m = Manager("127.0.0.1", task_name="get_hypotenuse", port=8080)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
### run a task
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
result = m.run(params)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### create a task and check result later
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
result_id = m.create_task(params)
|
|
43
|
+
# do something...
|
|
44
|
+
# do something...
|
|
45
|
+
# do something...
|
|
46
|
+
result = m.check(result_id)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### create a task and wait for result
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
result = m.create_and_wait_result(params)
|
|
53
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fasttask_manager/__init__.py,sha256=iNQjDvmAgl5g0EihQQFT-VmOEFJAvZ5xHLTB2-eUjjI,29
|
|
2
|
+
fasttask_manager/create_project.py,sha256=vg3xxwxBcc4PagqHkVj_-Himei2hgCJL0OQAqtsQNaU,1068
|
|
3
|
+
fasttask_manager/manager.py,sha256=Z-FI5DYhViSOS5f7FWZY-nuddqHxYOEbWRm1QgxZNqw,3499
|
|
4
|
+
fasttask_manager/project/Dockerfile,sha256=ilF9ey6nhDTfVEbe0eh8bnRZYWKZTwh8CGlR19h9CQI,213
|
|
5
|
+
fasttask_manager/project/docker-compose.yml,sha256=LjOQ8TgEiCoUaIeYhIxpC_GByesZO0grnzXruUH4sSs,992
|
|
6
|
+
fasttask_manager/project/req.txt,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
fasttask_manager/project/setting.py,sha256=dqrslQfMZjxy0BZRh38VWPztKC998LdQq6RXdbcQjN4,520
|
|
8
|
+
fasttask_manager/project/tasks/get_circle_area.py,sha256=8f6YovtLcCTWamdVp5GY8ixt7li3pW_6-qYFLeVjZHA,483
|
|
9
|
+
fasttask_manager/project/tasks/get_hypotenuse.py,sha256=O6Ex07yLEuJzsLywuVBTz9LADIdeF4hpPBwFrZStcM8,618
|
|
10
|
+
fasttask_manager/project/tasks/packages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
fasttask_manager/project/tasks/packages/tools.py,sha256=U7udJMeDtPIFdEUe4giiLh1-hOTEQnzg7lhUEOOgwKI,129
|
|
12
|
+
fasttask_manager-0.2.1.dist-info/LICENSE,sha256=Qv2ilebwoUtMJnRsZwRy729xS5JZQzLauJ0tQzkAkTA,1088
|
|
13
|
+
fasttask_manager-0.2.1.dist-info/METADATA,sha256=_yHUcbAYxNOeNBO74MpDSBgofXT32eqVpiutcDkS6S8,1012
|
|
14
|
+
fasttask_manager-0.2.1.dist-info/WHEEL,sha256=OpXWERl2xLPRHTvd2ZXo_iluPEQd8uSbYkJ53NAER_Y,109
|
|
15
|
+
fasttask_manager-0.2.1.dist-info/top_level.txt,sha256=pTXhlmzuBDPvkJ-u2YjLVC6Zyws1EglgsA26PryFZqs,17
|
|
16
|
+
fasttask_manager-0.2.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fasttask_manager
|