Skip to content

Commit 0450347

Browse files
✨ Add ManagerInstall for dependency management and update documentation
- Introduce new ManagerInstall class for automated dependency installation - Update README files (English and Chinese) with installation usage examples - Add test case for ManagerInstall in test_etool.py - Bump version to 1.4.1 in pyproject.toml - Update __init__.py to expose ManagerInstall module
1 parent 16dd15d commit 0450347

File tree

7 files changed

+191
-43
lines changed

7 files changed

+191
-43
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,3 +202,14 @@ print(ManagerPassword.generate_pwd_list(ManagerPassword.results['all_letters'] +
202202
print(ManagerPassword.random_pwd(8))
203203
# Randomly generate an 8-digit password (random encryption)
204204
```
205+
206+
### Install Dependencies
207+
208+
```python
209+
from etool import ManagerInstall
210+
ManagerInstall.install(requirements_file="requirements.txt", failed_file="failed_requirements.txt", retry=2)
211+
# Automatically install dependencies, retry 2 times if installation fails, skip installation if successful. The above are default parameters.
212+
# You can also use the default parameters without specifying parameters.
213+
ManagerInstall.install()
214+
```
215+

README_CN.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,3 +204,14 @@ print(ManagerPassword.random_pwd(8))
204204
# 随机生成8位密码(随机加密)
205205
```
206206

207+
### 安装依赖
208+
209+
```bash
210+
from etool import ManagerInstall
211+
ManagerInstall.install(requirements_file="requirements.txt", failed_file="failed_requirements.txt", retry=2)
212+
# 自动安装依赖,如果安装失败,则重试2次, 如果安装成功,则跳过安装.以上为默认缺省参数
213+
# 你也可以不指定参数,使用默认参数
214+
ManagerInstall.install()
215+
```
216+
217+

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "etool"
7-
version = "1.4.0"
7+
version = "1.4.1"
88
authors = [
99
{ name="Allen", email="[email protected]" },
1010
]

src/etool/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from ._other._password import ManagerPassword
55
from ._other._scheduler import ManagerScheduler
6+
from ._other._install import ManagerInstall
67

78
from ._office._image import ManagerImage
89
from ._office._email import ManagerEmail
@@ -17,6 +18,7 @@
1718
"ManagerShare",
1819
"ManagerPassword",
1920
"ManagerScheduler",
21+
"ManagerInstall",
2022
"ManagerImage",
2123
"ManagerEmail",
2224
"ManagerDocx",

src/etool/_other/_install.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import subprocess
2+
3+
class ManagerInstall:
4+
5+
@staticmethod
6+
def install_requirements(
7+
requirements_file="requirements.txt", failed_file="failed_requirements.txt"):
8+
failed_packages = []
9+
# open requirements.txt and read module name
10+
with open(requirements_file, "r") as file:
11+
packages = file.readlines()
12+
13+
# iterate each package name and try to install
14+
for package in packages:
15+
package = package.strip()
16+
17+
if package: # skip empty line
18+
print(f"installing: {package}")
19+
try:
20+
# use subprocess to execute pip install command
21+
result = subprocess.run(
22+
["pip", "install", package],
23+
stdout=subprocess.PIPE,
24+
stderr=subprocess.PIPE,
25+
text=True,
26+
)
27+
28+
# check install result, if return code is not 0, then install failed
29+
if result.returncode != 0:
30+
print(f"install failed: {package}")
31+
32+
failed_packages.append(package)
33+
34+
except Exception as e:
35+
print(f"error: {e}")
36+
failed_packages.append(package)
37+
38+
# write failed packages to failed_requirements.txt
39+
if failed_packages:
40+
cleaned_lines = []
41+
for line in failed_packages:
42+
# remove version part (==后面的内容)
43+
package = line.split("==")[0].strip()
44+
if package: # skip empty line
45+
cleaned_lines.append(package)
46+
with open(failed_file, "w") as f:
47+
f.write("\n".join(cleaned_lines))
48+
print(f"install failed packages to {failed_file}")
49+
return False
50+
else:
51+
print("all packages installed successfully")
52+
return True
53+
54+
55+
@staticmethod
56+
def install(
57+
requirements_file="requirements.txt",
58+
failed_file="failed_requirements.txt",
59+
retry=2,
60+
):
61+
for i in range(retry):
62+
print(f"retry {i+1} of {retry}")
63+
if i == 0:
64+
continue_install = ManagerInstall.install_requirements(
65+
requirements_file, failed_file
66+
)
67+
else:
68+
continue_install = ManagerInstall.install_requirements(
69+
failed_file, failed_file
70+
)
71+
if continue_install:
72+
break
73+
return continue_install
74+
75+
if __name__ == "__main__":
76+
ManagerInstall.install()
77+

tests/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bs4
2+
requests

tests/test_etool.py

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
import os
2-
os.chdir("./tests")
2+
import sys
3+
test_dir = os.path.dirname(os.path.abspath(__file__))
4+
project_root = os.path.dirname(test_dir)
5+
sys.path.insert(0, os.path.join(project_root, "src"))
6+
from etool import *
37
import pytest
4-
from etool import ManagerSpeed, ManagerShare
5-
from etool import (
6-
ManagerImage,
7-
ManagerEmail,
8-
ManagerDocx,
9-
ManagerExcel,
10-
ManagerPdf,
11-
ManagerIpynb,
12-
ManagerQrcode,
13-
)
14-
from etool import ManagerPassword, ManagerScheduler
15-
8+
os.chdir("./tests")
169

1710
def test_speed_manager():
11+
1812
assert ManagerSpeed.network() is not None
13+
1914
assert ManagerSpeed.disk() is not None
15+
2016
assert ManagerSpeed.memory() is not None
2117
assert ManagerSpeed.gpu_memory() is not None
2218

@@ -35,17 +31,20 @@ def test_share_file():
3531
@pytest.mark.skip(reason="发送邮件不宜频繁测试,跳过")
3632
def test_email_manager():
3733
# 假设 send_email 方法返回 True 表示成功
38-
assert ManagerEmail.send_email(
39-
40-
password="********",
41-
message="测试邮件内容",
42-
sender_show="[email protected]",
43-
recipient="[email protected]",
44-
recipient_show="[email protected]",
45-
subject="测试邮件",
46-
file_path="result.docx",
47-
image_path="pic1.webp",
48-
) == "send success"
34+
assert (
35+
ManagerEmail.send_email(
36+
37+
password="********",
38+
message="测试邮件内容",
39+
sender_show="[email protected]",
40+
recipient="[email protected]",
41+
recipient_show="[email protected]",
42+
subject="测试邮件",
43+
file_path="result.docx",
44+
image_path="pic1.webp",
45+
)
46+
== "send success"
47+
)
4948

5049

5150
@pytest.mark.skip(reason="定时发送不宜频繁测试,跳过")
@@ -59,11 +58,11 @@ def func_success():
5958

6059
def func_failure():
6160
print("failure")
61+
6262
# 每2秒执行一次job,成功时执行func_success,失败时执行func_failure
6363
ManagerScheduler.pocwatch(job, 2, func_success, func_failure)
6464

6565

66-
6766
def test_image_manager():
6867
# 假设 merge_LR 和 merge_UD 方法返回合并后的图片路径
6968
assert ManagerImage.merge_LR(["pic1.webp", "pic2.webp"]) is not None
@@ -100,44 +99,90 @@ def test_qrcode_manager():
10099

101100
def test_ipynb_manager():
102101
# 假设 merge_notebooks 和 convert_notebook_to_markdown 方法返回 True 表示成功
103-
assert ManagerIpynb.merge_notebooks('ipynb_dir') is not None
104-
assert ManagerIpynb.convert_notebook_to_markdown('ipynb_dir.ipynb', 'md') is not None
102+
assert ManagerIpynb.merge_notebooks("ipynb_dir") is not None
103+
assert (
104+
ManagerIpynb.convert_notebook_to_markdown("ipynb_dir.ipynb", "md") is not None
105+
)
105106

106107

107108
def test_docx_manager():
108109
# 假设 get_pictures 方法返回提取的图片数量
109-
assert ManagerDocx.replace_words('ex1.docx', '1', '2') is not None
110-
assert ManagerDocx.change_forward('ex1.docx', 'result.docx') is not None
111-
assert ManagerDocx.get_pictures('ex1.docx', 'result') is not None
110+
assert ManagerDocx.replace_words("ex1.docx", "1", "2") is not None
111+
assert ManagerDocx.change_forward("ex1.docx", "result.docx") is not None
112+
assert ManagerDocx.get_pictures("ex1.docx", "result") is not None
113+
112114

113115
def test_excel_manager():
114116
# 假设 excel_format 方法返回 True 表示成功
115-
assert ManagerExcel.excel_format('ex1.xlsx', 'result.xlsx') is not None
117+
assert ManagerExcel.excel_format("ex1.xlsx", "result.xlsx") is not None
118+
116119

117120
@pytest.mark.skip(reason="CICD环境没有office组件,跳过")
118121
def test_pdf_manager():
119122
# doc、xlsx等转换为pdf(转换一个)
120-
ManagerPdf.pdfconverter(os.path.join(os.path.dirname(__file__),'pdf','ex1.docx'),os.path.join(os.path.dirname(__file__),'pdf_out'))
123+
ManagerPdf.pdfconverter(
124+
os.path.join(os.path.dirname(__file__), "pdf", "ex1.docx"),
125+
os.path.join(os.path.dirname(__file__), "pdf_out"),
126+
)
121127
# doc、xlsx等转换为pdf(转换一个目录下的所有文件)
122-
ManagerPdf.pdfconverter(os.path.join(os.path.dirname(__file__),'pdf'),os.path.join(os.path.dirname(__file__),'pdf_out'))
128+
ManagerPdf.pdfconverter(
129+
os.path.join(os.path.dirname(__file__), "pdf"),
130+
os.path.join(os.path.dirname(__file__), "pdf_out"),
131+
)
123132

124133
# 给pdf文件添加水印(一个文件)
125-
ManagerPdf.create_watermarks(os.path.join(os.path.dirname(__file__),'pdf_out','ex1.pdf'),os.path.join(os.path.dirname(__file__),'pdf_out','watermarks.pdf'),os.path.join(os.path.dirname(__file__),'pdf_out_watermark'))
134+
ManagerPdf.create_watermarks(
135+
os.path.join(os.path.dirname(__file__), "pdf_out", "ex1.pdf"),
136+
os.path.join(os.path.dirname(__file__), "pdf_out", "watermarks.pdf"),
137+
os.path.join(os.path.dirname(__file__), "pdf_out_watermark"),
138+
)
126139
# 给pdf文件添加水印(一个目录下的所有文件)
127-
ManagerPdf.create_watermarks(os.path.join(os.path.dirname(__file__),'pdf_out'),os.path.join(os.path.dirname(__file__),'pdf_out','watermarks.pdf'),os.path.join(os.path.dirname(__file__),'pdf_out_watermark'))
140+
ManagerPdf.create_watermarks(
141+
os.path.join(os.path.dirname(__file__), "pdf_out"),
142+
os.path.join(os.path.dirname(__file__), "pdf_out", "watermarks.pdf"),
143+
os.path.join(os.path.dirname(__file__), "pdf_out_watermark"),
144+
)
128145

129146
# 合并pdf文件(一个目录下的所有文件)
130-
ManagerPdf.merge_pdfs(os.path.join(os.path.dirname(__file__),'pdf_out'),os.path.join(os.path.dirname(__file__),'pdf_out','merged.pdf'))
131-
147+
ManagerPdf.merge_pdfs(
148+
os.path.join(os.path.dirname(__file__), "pdf_out"),
149+
os.path.join(os.path.dirname(__file__), "pdf_out", "merged.pdf"),
150+
)
151+
132152
# 拆分pdf文件(按页数)每3页一份
133-
ManagerPdf.split_by_pages(os.path.join(os.path.dirname(__file__),'pdf_out','merged.pdf'),3)
153+
ManagerPdf.split_by_pages(
154+
os.path.join(os.path.dirname(__file__), "pdf_out", "merged.pdf"), 3
155+
)
134156
# 拆分pdf文件(按份数)生成2份
135-
ManagerPdf.split_by_num(os.path.join(os.path.dirname(__file__),'pdf_out','merged.pdf'),2)
157+
ManagerPdf.split_by_num(
158+
os.path.join(os.path.dirname(__file__), "pdf_out", "merged.pdf"), 2
159+
)
136160

137161
# 将pdf ex2插入到pdf ex1的指定页后
138-
ManagerPdf.insert_pdf(os.path.join(os.path.dirname(__file__),'pdf_out','ex1.pdf'),os.path.join(os.path.dirname(__file__),'pdf_out','ex2.pdf'),0,os.path.join(os.path.dirname(__file__),'pdf_out','pdf_insert.pdf'))
162+
ManagerPdf.insert_pdf(
163+
os.path.join(os.path.dirname(__file__), "pdf_out", "ex1.pdf"),
164+
os.path.join(os.path.dirname(__file__), "pdf_out", "ex2.pdf"),
165+
0,
166+
os.path.join(os.path.dirname(__file__), "pdf_out", "pdf_insert.pdf"),
167+
)
139168

140169
# 加密pdf文件
141-
ManagerPdf.encrypt_pdf(os.path.join(os.path.dirname(__file__),'pdf_out','ex1.pdf'),r"1234567890")
170+
ManagerPdf.encrypt_pdf(
171+
os.path.join(os.path.dirname(__file__), "pdf_out", "ex1.pdf"), r"1234567890"
172+
)
142173
# 解密pdf文件
143-
ManagerPdf.decrypt_pdf(os.path.join(os.path.dirname(__file__),'pdf_out','ex1_encrypted.pdf'),r"1234567890")
174+
ManagerPdf.decrypt_pdf(
175+
os.path.join(os.path.dirname(__file__), "pdf_out", "ex1_encrypted.pdf"),
176+
r"1234567890",
177+
)
178+
179+
180+
def test_install_manager():
181+
# 安装依赖
182+
requirements_file = os.path.join(os.path.dirname(__file__), "requirements.txt")
183+
failed_file = os.path.join(os.path.dirname(__file__), "failed_requirements.txt")
184+
continue_install = ManagerInstall.install(
185+
requirements_file=requirements_file, failed_file=failed_file, retry=2
186+
)
187+
assert continue_install is True
188+
# pytest tests/test_etool.py --disable-warnings

0 commit comments

Comments
 (0)