bcmd 0.6.6__py3-none-any.whl → 0.6.7__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.

Potentially problematic release.


This version of bcmd might be problematic. Click here for more details.

bcmd/tasks/proxy.py ADDED
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Final
4
+
5
+ import psutil
6
+ import pyperclip
7
+ import typer
8
+ from beni import bcolor, btask
9
+ from beni.bfunc import syncCall, textToAry
10
+
11
+ app: Final = btask.app
12
+
13
+
14
+ @app.command()
15
+ @syncCall
16
+ async def proxy(
17
+ port: int = typer.Argument(15236, help="代理服务器端口"),
18
+ ):
19
+ '生成终端设置代理服务器的命令'
20
+ processNameAry: list[str] = []
21
+ process = psutil.Process().parent()
22
+ while process:
23
+ processNameAry.append(process.name())
24
+ process = process.parent()
25
+ template = ''
26
+
27
+ # 针对不同的终端使用不同的模板
28
+ if 'cmd.exe' in processNameAry:
29
+ template = cmdTemplate
30
+ elif set(['powershell.exe', 'pwsh.exe']) & set(processNameAry):
31
+ template = powerShellTemplate
32
+
33
+ btask.assertTrue(template, f'不支持当前终端({processNameAry})')
34
+ lineAry = textToAry(template.format(port))
35
+ msg = '\n'.join(lineAry)
36
+ bcolor.printMagenta('\r\n' + msg)
37
+ msg += '\n' # 多增加一个换行,直接粘贴的时候相当于最后一行也执行完
38
+ pyperclip.copy(msg)
39
+ bcolor.printYellow('已复制,可直接粘贴使用')
40
+
41
+
42
+ # ------------------------------------------------------------------------------------
43
+
44
+
45
+ cmdTemplate = '''
46
+ set http_proxy=http://localhost:{0}
47
+ set https_proxy=http://localhost:{0}
48
+ set all_proxy=http://localhost:{0}
49
+ '''
50
+
51
+ powerShellTemplate = '''
52
+ $env:http_proxy="http://localhost:{0}"
53
+ $env:https_proxy="http://localhost:{0}"
54
+ $env:all_proxy="http://localhost:{0}"
55
+ '''
bcmd/tasks/time.py ADDED
@@ -0,0 +1,81 @@
1
+ import time
2
+ from datetime import datetime as Datetime
3
+ from datetime import timezone
4
+ from typing import Final
5
+ from zoneinfo import ZoneInfo
6
+
7
+ import typer
8
+ from beni import bcolor, btask
9
+ from beni.bfunc import syncCall, textToAry
10
+ from beni.btype import Null
11
+
12
+ app: Final = btask.app
13
+
14
+
15
+ @app.command('time')
16
+ @syncCall
17
+ async def showtime(
18
+ args: list[str] = typer.Argument(None),
19
+ ):
20
+ '''
21
+ 格式化时间戳\n
22
+ beni time\n
23
+ beni time 1632412740\n
24
+ beni time 1632412740.1234\n
25
+ beni time 2021-9-23\n
26
+ beni time 2021-9-23 09:47:00\n
27
+ '''
28
+ args = args or []
29
+ btask.assertTrue(len(args) <= 2, '参数过多')
30
+ value1: str | None = args[0] if len(args) >= 1 else None
31
+ value2: str | None = args[1] if len(args) >= 2 else None
32
+ timestamp: float = Null
33
+ if not value1:
34
+ timestamp = time.time()
35
+ else:
36
+ try:
37
+ timestamp = float(value1)
38
+ except:
39
+ try:
40
+ if value2:
41
+ timestamp = Datetime.strptime(f'{value1} {value2}', '%Y-%m-%d %H:%M:%S').timestamp()
42
+ else:
43
+ timestamp = Datetime.strptime(f'{value1}', '%Y-%m-%d').timestamp()
44
+ except:
45
+ pass
46
+ if not timestamp:
47
+ bcolor.printRed('参数无效\n')
48
+ bcolor.printRed('使用示例:')
49
+ msgAry = textToAry(str(showtime.__doc__))[1:]
50
+ bcolor.printRed('\n'.join(msgAry))
51
+ return
52
+ print()
53
+ bcolor.printMagenta(timestamp)
54
+ print()
55
+ # localtime = time.localtime(timestamp)
56
+ # tzname = time.tzname[(time.daylight and localtime.tm_isdst) and 1 or 0]
57
+ # bcolor.printx(time.strftime('%Y-%m-%d %H:%M:%S %z', localtime), tzname, colors=[Fore.YELLOW])
58
+ # print()
59
+ datetime_utc = Datetime.fromtimestamp(timestamp, tz=timezone.utc)
60
+ tzname_list = [
61
+ 'Australia/Sydney',
62
+ 'Asia/Tokyo',
63
+ 'Asia/Shanghai',
64
+ 'Asia/Kolkata',
65
+ 'Africa/Cairo',
66
+ 'Europe/London',
67
+ 'America/Sao_Paulo',
68
+ 'America/New_York',
69
+ 'America/Chicago',
70
+ 'America/Los_Angeles',
71
+ ]
72
+ for tzname in tzname_list:
73
+ datetime_tz = datetime_utc.astimezone(ZoneInfo(tzname))
74
+ dstStr = ''
75
+ dst = datetime_tz.dst()
76
+ if dst:
77
+ dstStr = f'(DST+{dst})'
78
+ if tzname == 'Asia/Shanghai':
79
+ bcolor.printYellow(f'{datetime_tz} {tzname} {dstStr}')
80
+ else:
81
+ print(f'{datetime_tz} {tzname} {dstStr}')
bcmd/tasks/upgrade.py ADDED
@@ -0,0 +1,22 @@
1
+ from typing import Final
2
+
3
+ import pyperclip
4
+ import typer
5
+ from beni import bcolor, btask
6
+ from beni.bfunc import syncCall
7
+
8
+ app: Final = btask.app
9
+
10
+
11
+ @app.command()
12
+ @syncCall
13
+ async def upgrade(
14
+ name: str = typer.Argument('bcmdx', help='要更新的包名'),
15
+ ):
16
+ '使用 pipx 官方源更新指定包到最新版本'
17
+
18
+ cmd = f'pipx upgrade {name} -i https://pypi.org/simple'
19
+ pyperclip.copy(cmd + '\n')
20
+ bcolor.printGreen(cmd)
21
+ bcolor.printGreen('已复制到剪贴板(需要手动执行)')
22
+ bcolor.printGreen('OK')
bcmd/tasks/wasabi.py ADDED
@@ -0,0 +1,94 @@
1
+ import getpass
2
+ import stat
3
+ from pathlib import Path
4
+ from typing import Final
5
+
6
+ import typer
7
+ from beni import bcolor, bcrypto, bfile, bpath, btask, bzip
8
+ from beni.bfunc import shuffleSequence, syncCall
9
+ from beni.binput import genPassword
10
+
11
+ app: Final = btask.newSubApp('Wasabi 工具')
12
+
13
+ SEP = f'{chr(852)}{chr(322)}{chr(470)}'.encode()
14
+ MAX_ENCRYPT_SIZE = 199 * 1024
15
+
16
+
17
+ @app.command()
18
+ @syncCall
19
+ async def unzip(
20
+ target: Path = typer.Argument(Path.cwd(), help='加密文件'),
21
+ password: str = typer.Option('', '--password', '-p', help='密码'),
22
+ ):
23
+ '解压缩加密文件成目录'
24
+ assert target.is_file(), f'不是文件 {target}'
25
+ password = password or getpass.getpass('请输入密码: ')
26
+ with bpath.useTempFile() as tempFile:
27
+ data = await bfile.readBytes(target)
28
+ if SEP not in data:
29
+ data = bcrypto.decrypt(data, password)
30
+ else:
31
+ partA, partB = data.split(SEP)
32
+ partA = bcrypto.decrypt(partA, password)
33
+ data = partA + partB
34
+ data = shuffleSequence(data)
35
+ await bfile.writeBytes(tempFile, data)
36
+ tempPath = target.with_suffix('.tmp')
37
+ await bzip.sevenUnzip(tempFile, tempPath)
38
+
39
+ # 调整文件权限,完全擦除
40
+ target.chmod(stat.S_IWRITE)
41
+ await bpath.removeSecure(target)
42
+
43
+ bpath.move(tempPath, target)
44
+ bcolor.printGreen('OK')
45
+
46
+
47
+ @app.command()
48
+ @syncCall
49
+ async def zip(
50
+ target: Path = typer.Argument(Path.cwd(), help='输出目录'),
51
+ password: str = typer.Option('', '--password', '-p', help='密码'),
52
+ ):
53
+ '将目录压缩成加密文件'
54
+ target = target.absolute()
55
+ assert target.is_dir(), f'不是目录 {target}'
56
+ password = password or genPassword()
57
+ with bpath.useTempFile() as tempFile:
58
+ await bzip.sevenZipFolder(tempFile, target)
59
+ data = await bfile.readBytes(tempFile)
60
+ bpath.remove(tempFile) # 为了安全所以立即删除
61
+ data = shuffleSequence(data)
62
+ if len(data) < MAX_ENCRYPT_SIZE:
63
+ data = bcrypto.encrypt(data, password)
64
+ else:
65
+ partA, partB = data[:MAX_ENCRYPT_SIZE], data[MAX_ENCRYPT_SIZE:]
66
+ partA = bcrypto.encrypt(partA, password)
67
+ data = partA + SEP + partB
68
+ tempZipFile = target.with_suffix('.tmp')
69
+ await bfile.writeBytes(tempZipFile, data)
70
+
71
+ # 调整目录权限,完全擦除
72
+ target.chmod(stat.S_IWRITE)
73
+ for file in target.glob('**/*'):
74
+ file.chmod(stat.S_IWRITE)
75
+ await bpath.removeSecure(target)
76
+
77
+ bpath.move(tempZipFile, target)
78
+
79
+ bcolor.printGreen('OK')
80
+
81
+
82
+ @app.command()
83
+ @syncCall
84
+ async def change_pass(
85
+ file: Path = typer.Argument(Path.cwd(), help='加密文件'),
86
+ password: str = typer.Option('', '--password', '-p', help='密码'),
87
+ new_password: str = typer.Option('', '--new-password', '-n', help='新密码'),
88
+ ):
89
+ with bpath.useTempPath() as tempPath:
90
+ target = tempPath / file.name
91
+ bpath.copy(file, target)
92
+ unzip(target, password)
93
+ zip(target, new_password)
94
+ bpath.copy(target, file)
bcmd/utils/__init__.py ADDED
File without changes
bcmd/utils/tkUtil.py ADDED
@@ -0,0 +1,243 @@
1
+ import tkinter as tk
2
+ from tkinter import filedialog
3
+ from tkinter.scrolledtext import ScrolledText
4
+ from typing import Any, Callable, Literal, TypeVar, Union
5
+ from uuid import uuid4
6
+
7
+ RADIO_NOTHING = uuid4().hex
8
+ TkVar = TypeVar('TkVar', bound=Union[tk.StringVar, tk.IntVar, tk.DoubleVar, tk.BooleanVar])
9
+
10
+
11
+ class TkForm(tk.Tk):
12
+
13
+ _rowIndex = -1
14
+ _initList: list[Callable[..., None]] = []
15
+ _varList: list[tk.Variable] = [] # 用来存储 var 变量,避免传出去,外面没有接收导致界面异常
16
+
17
+ def __init__(self):
18
+ super().__init__()
19
+ self.resizable(False, False)
20
+ self.bind("<Map>", self._onInit)
21
+
22
+ def _onInit(self, evt: tk.Event):
23
+ if evt.widget == self:
24
+ for callback in self._initList:
25
+ callback()
26
+ self._initList.clear()
27
+
28
+ def addInitHandler(self, handler: Callable[..., None]):
29
+ self._initList.append(handler)
30
+
31
+ def _initVar(self, var: TkVar) -> TkVar:
32
+ self._varList.append(var)
33
+ return var
34
+
35
+ def run(self):
36
+ self.center()
37
+ self.mainloop()
38
+
39
+ def center(self):
40
+ self.withdraw() # 先隐藏窗口,避免闪动
41
+ self.update_idletasks() # 确保获取正确的窗口尺寸
42
+ width = self.winfo_width() # 获取窗口宽度
43
+ height = self.winfo_height() # 获取窗口高度
44
+ screen_width = self.winfo_screenwidth() # 屏幕宽度
45
+ screen_height = self.winfo_screenheight() # 屏幕高度
46
+ x = (screen_width - width) // 2 # 水平居中
47
+ y = (screen_height - height) // 2 # 垂直居中
48
+ self.geometry(f"+{x}+{y}") # 设置窗口位置
49
+ self.deiconify() # 恢复显示窗口
50
+
51
+ def addRow(self, desc: str, widget: tk.Widget):
52
+ self._rowIndex += 1
53
+ tk.Label(text=desc).grid(row=self._rowIndex, column=0, padx=10, pady=5, sticky='e')
54
+ widget.grid(row=self._rowIndex, column=1, padx=10, pady=5, sticky='w')
55
+
56
+ def addRowFrame(self):
57
+ self._rowIndex += 1
58
+ frame = tk.Frame(self)
59
+ frame.grid(row=self._rowIndex, column=0, columnspan=2, padx=10, pady=5)
60
+ return frame
61
+
62
+ def addRowFrameWithDesc(self, desc: str):
63
+ self._rowIndex += 1
64
+ tk.Label(text=desc).grid(row=self._rowIndex, column=0, padx=10, pady=5, sticky='e')
65
+ frame = tk.Frame(self)
66
+ frame.grid(row=self._rowIndex, column=1, padx=10, pady=5, sticky='w')
67
+ return frame
68
+
69
+ def addLabel(
70
+ self,
71
+ desc: str,
72
+ text: str
73
+ ):
74
+ self.addRow(desc, tk.Label(text=text))
75
+
76
+ def addBtn(
77
+ self,
78
+ label: str,
79
+ command: Callable[..., None],
80
+ *,
81
+ width: int = 20,
82
+ focus: bool = False
83
+ ):
84
+ frame = self.addRowFrame()
85
+ btn = tk.Button(frame, text=label, width=width, command=command)
86
+ btn.pack(side="left", expand=True, padx=15)
87
+ if focus:
88
+ self._initFocus(btn)
89
+
90
+ def addRadioBtnList(
91
+ self,
92
+ desc: str,
93
+ selectionList: list[str],
94
+ *,
95
+ selectedIndex: int | None = None,
96
+ focusIndex: int | None = None,
97
+ onChanged: Callable[[str], None] | None = None,
98
+ ):
99
+ frame = tk.Frame()
100
+ self.addRow(desc, frame)
101
+ var = tk.StringVar(value=selectionList[selectedIndex] if selectedIndex is not None else RADIO_NOTHING)
102
+ radioBtnList: list[tk.Radiobutton] = []
103
+ for version in selectionList:
104
+ radioBtn = tk.Radiobutton(frame, text=version, variable=var, value=version)
105
+ radioBtn.pack(side="left", padx=(0, 15))
106
+ setWidgetClickFocus(radioBtn)
107
+ radioBtnList.append(radioBtn)
108
+ if focusIndex is not None:
109
+ self._initFocus(radioBtnList[focusIndex])
110
+ if onChanged:
111
+ var.trace_add('write', lambda *args: onChanged(var.get())) # type: ignore
112
+ self.addInitHandler(
113
+ lambda: onChanged(var.get())
114
+ )
115
+ return var
116
+
117
+ def addEntry(
118
+ self,
119
+ desc: str,
120
+ var: tk.StringVar,
121
+ *,
122
+ width: int = 60,
123
+ focus: bool = False,
124
+ justify: Literal['left', 'right', 'center'] = tk.LEFT,
125
+ password: bool = False,
126
+ command: Callable[..., Any] | None = None,
127
+ ):
128
+ self._initVar(var)
129
+ entry = tk.Entry(self, width=width, justify=justify, textvariable=var)
130
+ entry.icursor(tk.END)
131
+ self.addRow(desc, entry)
132
+ if password:
133
+ entry.config(show='*')
134
+ if focus:
135
+ self._initFocus(entry)
136
+ if command:
137
+ entry.bind('<Return>', lambda event: command())
138
+ return entry
139
+
140
+ def addChoisePath(
141
+ self,
142
+ desc: str,
143
+ var: tk.StringVar,
144
+ *,
145
+ width: int = 47,
146
+ focus: bool = False,
147
+ isDir: bool = False,
148
+ ):
149
+ self._initVar(var)
150
+ frame = self.addRowFrameWithDesc(desc)
151
+ entry = tk.Entry(frame, width=width, textvariable=var)
152
+ entry.icursor(tk.END)
153
+ entry.pack(side="left")
154
+ btn = tk.Button(frame, text=f'选择{'目录' if isDir else '文件'} ...', width=10, command=lambda: onBtn())
155
+ btn.pack(side="left", padx=(10, 0))
156
+ if focus:
157
+ self._initFocus(btn)
158
+
159
+ def onBtn():
160
+ if isDir:
161
+ var.set(filedialog.askdirectory())
162
+ else:
163
+ var.set(filedialog.askopenfilename())
164
+
165
+ def addScrolledText(
166
+ self,
167
+ desc: str,
168
+ var: tk.StringVar,
169
+ *,
170
+ width: int = 60,
171
+ height: int = 3,
172
+ focus: bool = False,
173
+ ):
174
+ self._initVar(var)
175
+ scrolledText = ScrolledText(self, width=width, height=height)
176
+ scrolledText.insert(tk.END, var.get())
177
+ self.addRow(desc, scrolledText)
178
+
179
+ def on_text_change(*args: Any):
180
+ new_value = scrolledText.get("1.0", tk.END)
181
+ if new_value != var.get():
182
+ var.set(new_value)
183
+
184
+ scrolledText.bind("<KeyRelease>", on_text_change)
185
+
186
+ def on_tab(event: tk.Event):
187
+ widget = event.widget.tk_focusNext()
188
+ assert widget
189
+ widget.focus_set()
190
+ return "break"
191
+
192
+ scrolledText.bind("<Tab>", on_tab)
193
+
194
+ if focus:
195
+ self._initFocus(scrolledText)
196
+ return scrolledText
197
+
198
+ def addCheckBox(
199
+ self,
200
+ desc: str,
201
+ text: str,
202
+ value: bool = False,
203
+ ):
204
+ var = tk.BooleanVar(value=value)
205
+ self._initVar(var)
206
+ check_btn = tk.Checkbutton(text=text, variable=var)
207
+ self.addRow(desc, check_btn)
208
+ self.addInitHandler(
209
+ lambda: var.set(var.get())
210
+ )
211
+ setWidgetClickFocus(check_btn)
212
+ return var
213
+
214
+ def addCheckBoxList(
215
+ self,
216
+ desc: str,
217
+ dataList: list[tuple[str, bool]],
218
+ ):
219
+ varDict: dict[str, tk.BooleanVar] = {}
220
+ frame = tk.Frame(self)
221
+ self.addRow(desc, frame)
222
+ for label, value in dataList:
223
+ varDict[label] = tk.BooleanVar(value=value)
224
+ checkbox = tk.Checkbutton(frame, text=label, variable=varDict[label])
225
+ checkbox.pack(side="left", expand=True, padx=(0, 15))
226
+ setWidgetClickFocus(checkbox)
227
+ self.addInitHandler(
228
+ lambda: varDict[label].set(varDict[label].get())
229
+ )
230
+ return varDict
231
+
232
+ def _initFocus(self, widget: tk.Widget):
233
+ self.addInitHandler(
234
+ lambda: widget.focus_set()
235
+ )
236
+
237
+
238
+ def setWidgetEnabled(widget: tk.Widget, value: bool):
239
+ widget['state'] = tk.NORMAL if value else tk.DISABLED
240
+
241
+
242
+ def setWidgetClickFocus(widget: tk.Widget):
243
+ widget.bind("<Button-1>", lambda args: args.widget.focus_set())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bcmd
3
- Version: 0.6.6
3
+ Version: 0.6.7
4
4
  Summary: Commands for Beni
5
5
  Author-email: Beni Mang <benimang@126.com>
6
6
  Maintainer-email: Beni Mang <benimang@126.com>
@@ -0,0 +1,33 @@
1
+ bcmd/__init__.py,sha256=GP_60-6vImXqdMfC5vc4xlscWajx4OYmnlNXASWn19w,147
2
+ bcmd/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ bcmd/common/func.py,sha256=MB_ChA4aRzEjhMhyFmZ-h8aOZePzk0poYcTYRJUQxmI,509
4
+ bcmd/common/secret.py,sha256=clemEVTCl3shhRBgjKZfrtTdyODz5U-wOyHfUXPegG4,1692
5
+ bcmd/resources/project/main.py,sha256=xdskz_sf05fYA1SRMFCIxDjx8SnegxTbCmHpW86ItLs,11
6
+ bcmd/tasks/__init__.py,sha256=lcVrTgdEph3tKBwwCPVyEOHp-1A3zdFA_6OWLrOo19M,314
7
+ bcmd/tasks/bin.py,sha256=DufZGRX7b2zclSaZM-zUPGwOiycN9RsV8KxF8tfSyqs,3240
8
+ bcmd/tasks/code.py,sha256=IUs_ClZuSsBk2gavlitC8mkRrQQX9rvNDgR8cFxduBA,3992
9
+ bcmd/tasks/crypto.py,sha256=LKvgsMPLvsi1wlt66TinYiN-oV2IPAfaN9y7hWaVpHs,2951
10
+ bcmd/tasks/debian.py,sha256=B9aMIIct3vNqMJr5hTr1GegXVf20H49C27FMvRRGIzI,3004
11
+ bcmd/tasks/download.py,sha256=XdZYKi8zQTNYWEgUxeTNDqPgP7IGYJkMmlDDC9u93Vk,2315
12
+ bcmd/tasks/image.py,sha256=OQxrtTkPnJ_FZ6uenxdNlynhxwrAVy1P_68QAnSBYB0,14238
13
+ bcmd/tasks/json.py,sha256=WWOyvcZPYaqQgp-Tkm-uIJschNMBKPKtZN3yXz_SC5s,635
14
+ bcmd/tasks/lib.py,sha256=ODF21sTjQ687X5AXGOvJjjw6dGNSnflwSz4wDlIbYVM,2861
15
+ bcmd/tasks/math.py,sha256=xbl5UdaDMyAjiLodDPleP4Cutrk2S3NOAgurzAgOEAE,2862
16
+ bcmd/tasks/mirror.py,sha256=nAe8NYftMKzht16MFBj7RqXwvVhR6Jh2uuAyJLh87og,1098
17
+ bcmd/tasks/pdf.py,sha256=fkHRgxqzrRxdb4_-9pL9wp2roqAHJPS_dVqAGJvRUsM,1504
18
+ bcmd/tasks/proxy.py,sha256=xvxN5PClUnc5LQpmq2Wug7_LUVpJboMWLXBvL9lX7EM,1552
19
+ bcmd/tasks/time.py,sha256=ZiqA1jdgl-TBtFSOxxP51nwv4g9iZItmkFKpf9MKelk,2453
20
+ bcmd/tasks/upgrade.py,sha256=z9Ein8U_Co7fsijPxyDFM8tXBFCp4bWATImwwwvC5ho,536
21
+ bcmd/tasks/wasabi.py,sha256=xWFAxprSIlBqDDMGaNXZFb-SahnW1d_R9XxSKRYIhnM,3110
22
+ bcmd/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ bcmd/utils/tkUtil.py,sha256=zhOFQB0SN-ZPSXgJ0-pyHeVldf_u8VNnODz7pQRbLkw,7709
24
+ test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ test/conftest.py,sha256=grlPunlsvrkt_8QPckmF4POiKUPVxIxm2TPAh_ZB-zs,405
26
+ test/test_pdf.py,sha256=7yYlfydyhy2dmVYdTA5Vir2AI8TUdzEi55fL-AqJmio,1533
27
+ test/test_proxy.py,sha256=UMF2hFFGUEbJR1jT2mO_wdo-7Rfp0NDqIdTRnOmwtjY,164
28
+ test/test_wasabi.py,sha256=qqXG1Kb9hKH6t624R173j6LagkgmejN0CFYt7kL0nNs,1066
29
+ bcmd-0.6.7.dist-info/METADATA,sha256=XVbdxqxA2VLOsMucxKpgGgVgslDi1nBM4QtLoCSDYvU,812
30
+ bcmd-0.6.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
+ bcmd-0.6.7.dist-info/entry_points.txt,sha256=mriCeYh3wksKcqq3-LtzyFkSCIdN1uZc1IJwom-SW1s,34
32
+ bcmd-0.6.7.dist-info/top_level.txt,sha256=fYY6tRrJ_G7tn24RXAG0M5ZKbcuaQznodfX1toFPSKs,10
33
+ bcmd-0.6.7.dist-info/RECORD,,
test/__init__.py ADDED
File without changes
test/conftest.py ADDED
@@ -0,0 +1,17 @@
1
+ # '''
2
+ # 快捷键(默认)
3
+ # CTRL+; A 执行全部单元测试
4
+ # CTRL+; E 只执行上次出错的用例
5
+ # CTRL+; C 清除结果
6
+ # CTRL+; CTRL+A 调试全部单元测试
7
+ # CTRL+; CTRL+E 只调试上次出错的用例
8
+ # '''
9
+
10
+ import pytest_asyncio
11
+
12
+ from bcmd.tasks import *
13
+
14
+
15
+ @pytest_asyncio.fixture(scope='session', autouse=True) # type: ignore
16
+ def prepareSession():
17
+ yield
test/test_pdf.py ADDED
@@ -0,0 +1,47 @@
1
+ from contextlib import asynccontextmanager
2
+ from pathlib import Path
3
+ from typing import Literal
4
+
5
+ import img2pdf
6
+ import pytest
7
+ from beni import bfile, bpath, btask
8
+ from PIL import Image
9
+
10
+
11
+ @pytest.mark.asyncio
12
+ async def test_pdf_output_images():
13
+ async with _createTempPdfFile() as pdfFile:
14
+ result = btask.testCall('pdf', 'output-images', '--target', pdfFile.as_posix())
15
+ assert result.exit_code == 0
16
+ outputImagesPath = pdfFile.parent / f'{pdfFile.stem}-PDF图片文件'
17
+ assert outputImagesPath.is_dir()
18
+ assert len(list(outputImagesPath.glob('*.png'))) == 1
19
+ assert len(list(outputImagesPath.glob('*.jpeg'))) == 1
20
+
21
+
22
+ @asynccontextmanager
23
+ async def _createTempPdfFile():
24
+
25
+ def create_color_block_image(
26
+ file: Path,
27
+ color: tuple[int, int, int],
28
+ size: tuple[int, int] = (500, 500),
29
+ image_format: Literal['JPEG', 'PNG'] = 'JPEG'
30
+ ):
31
+ image = Image.new('RGB', size, color)
32
+ image.save(file, format=image_format)
33
+ image.close()
34
+ return file
35
+
36
+ with bpath.useTempPath(True) as tempPath:
37
+ # 创建色块图片
38
+ fileList = [
39
+ create_color_block_image(tempPath / 'blue.png', (0, 0, 255), image_format='PNG'),
40
+ create_color_block_image(tempPath / 'red.jpeg', (255, 0, 0), image_format='JPEG'),
41
+ ]
42
+
43
+ # 生成PDF文件
44
+ pdfFile = tempPath / 'output.pdf'
45
+ await bfile.writeBytes(pdfFile, img2pdf.convert(fileList)) # type: ignore
46
+
47
+ yield pdfFile
test/test_proxy.py ADDED
@@ -0,0 +1,8 @@
1
+ import pytest
2
+ from beni import btask
3
+
4
+
5
+ @pytest.mark.asyncio
6
+ async def test_proxy():
7
+ result = btask.testCall('proxy', '--help')
8
+ assert result.exit_code == 0
test/test_wasabi.py ADDED
@@ -0,0 +1,28 @@
1
+ from uuid import uuid4
2
+
3
+ import pytest
4
+ from beni import bfile, bpath, btask
5
+
6
+
7
+ @pytest.mark.asyncio
8
+ async def test_wasabi():
9
+ with bpath.useTempPath() as tempPath:
10
+ target = tempPath / uuid4().hex
11
+ file = target / uuid4().hex
12
+ content = uuid4().hex
13
+ password = uuid4().hex
14
+ await bfile.writeText(file, content)
15
+
16
+ result = btask.testCall('wasabi', 'zip', target.as_posix(), '--password', password)
17
+ assert result.exit_code == 0
18
+ assert target.is_file(), '生成加密文件失败'
19
+
20
+ newPassword = uuid4().hex
21
+ result = btask.testCall('wasabi', 'change-pass', target.as_posix(), '--password', password, '--new-password', newPassword)
22
+ assert result.exit_code == 0
23
+ assert target.is_file(), '修改密码失败'
24
+
25
+ result = btask.testCall('wasabi', 'unzip', target.as_posix(), '--password', newPassword)
26
+ assert result.exit_code == 0
27
+ assert target.is_dir(), '解密文件失败'
28
+ assert await bfile.readText(file) == content, '解密文件内容不一致'
@@ -1,6 +0,0 @@
1
- bcmd/__init__.py,sha256=GP_60-6vImXqdMfC5vc4xlscWajx4OYmnlNXASWn19w,147
2
- bcmd-0.6.6.dist-info/METADATA,sha256=4bllONvfMpWoucDX_cdkz3rqND0yPbWBrtIupX99lHI,812
3
- bcmd-0.6.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
4
- bcmd-0.6.6.dist-info/entry_points.txt,sha256=mriCeYh3wksKcqq3-LtzyFkSCIdN1uZc1IJwom-SW1s,34
5
- bcmd-0.6.6.dist-info/top_level.txt,sha256=-KrvhhtBcYsm4XhcjQvEcFbBB3VXeep7d3NIfDTrXKQ,5
6
- bcmd-0.6.6.dist-info/RECORD,,
File without changes