bcmd 0.4.7__py3-none-any.whl → 0.4.12__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/common/func.py +10 -2
- bcmd/tasks/bin.py +11 -15
- bcmd/tasks/code.py +12 -16
- bcmd/tasks/crypto.py +3 -0
- bcmd/tasks/download.py +2 -2
- bcmd/tasks/lib.py +37 -40
- bcmd/tasks/math.py +1 -1
- bcmd/tasks/mirror.py +28 -40
- bcmd/tasks/project.py +18 -20
- bcmd/tasks/proxy.py +31 -22
- bcmd/tasks/time.py +12 -12
- bcmd/tasks/venv.py +18 -24
- {bcmd-0.4.7.dist-info → bcmd-0.4.12.dist-info}/METADATA +3 -3
- bcmd-0.4.12.dist-info/RECORD +30 -0
- {bcmd-0.4.7.dist-info → bcmd-0.4.12.dist-info}/WHEEL +1 -1
- bcmd-0.4.7.dist-info/RECORD +0 -30
- {bcmd-0.4.7.dist-info → bcmd-0.4.12.dist-info}/entry_points.txt +0 -0
- {bcmd-0.4.7.dist-info → bcmd-0.4.12.dist-info}/top_level.txt +0 -0
bcmd/common/func.py
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import importlib.resources
|
|
2
|
+
from contextlib import contextmanager
|
|
1
3
|
from pathlib import Path
|
|
2
4
|
|
|
3
5
|
from beni import btask
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def checkFileOrNotExists(file: Path):
|
|
7
|
-
btask.
|
|
9
|
+
btask.assertTrue(file.is_file() or not file.exists(), f'必须是文件 {file}')
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def checkPathOrNotExists(folder: Path):
|
|
11
|
-
btask.
|
|
13
|
+
btask.assertTrue(folder.is_dir() or not folder.exists(), f'必须是目录 {folder}')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@contextmanager
|
|
17
|
+
def useResources(name: str):
|
|
18
|
+
with importlib.resources.path('bcmd.resources', name) as target:
|
|
19
|
+
yield target
|
bcmd/tasks/bin.py
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import os
|
|
2
1
|
from datetime import datetime
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
from typing import Final
|
|
5
4
|
|
|
6
5
|
import typer
|
|
7
6
|
from beni import bcolor, bfile, bpath, btask, bzip
|
|
8
|
-
from beni.bfunc import syncCall
|
|
7
|
+
from beni.bfunc import syncCall, textToAry
|
|
9
8
|
from beni.bqiniu import QiniuBucket
|
|
10
9
|
from beni.btype import Null
|
|
11
10
|
from prettytable import PrettyTable
|
|
12
11
|
|
|
13
|
-
from
|
|
12
|
+
from ..common import password
|
|
14
13
|
|
|
15
14
|
app: Final = btask.newSubApp('bin 工具')
|
|
16
15
|
|
|
@@ -20,24 +19,18 @@ _PREFIX = 'bin/'
|
|
|
20
19
|
@app.command()
|
|
21
20
|
@syncCall
|
|
22
21
|
async def download(
|
|
23
|
-
names: str = typer.Argument(None, help="
|
|
22
|
+
names: list[str] = typer.Argument(None, help="支持多个"),
|
|
24
23
|
file: Path = typer.Option(None, '--file', '-f', help="文件形式指定参数,行为单位"),
|
|
25
|
-
output: Path = typer.Option(
|
|
24
|
+
output: Path = typer.Option(Path.cwd(), '--output', '-o', help="本地保存路径"),
|
|
26
25
|
):
|
|
27
26
|
'从七牛云下载执行文件'
|
|
28
27
|
bucket: QiniuBucket = Null
|
|
29
|
-
if not output:
|
|
30
|
-
output = Path(os.curdir)
|
|
31
|
-
output = output.resolve()
|
|
32
|
-
targetList: list[str] = []
|
|
33
|
-
if names:
|
|
34
|
-
targetList.extend(names.split(','))
|
|
35
28
|
if file:
|
|
36
29
|
content = await bfile.readText(Path(file))
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for target in
|
|
30
|
+
names.extend(
|
|
31
|
+
textToAry(content)
|
|
32
|
+
)
|
|
33
|
+
for target in names:
|
|
41
34
|
binFile = output / target
|
|
42
35
|
if binFile.exists():
|
|
43
36
|
bcolor.printYellow(f'已存在 {binFile}')
|
|
@@ -98,6 +91,9 @@ async def remove(
|
|
|
98
91
|
bcolor.printGreen('OK')
|
|
99
92
|
|
|
100
93
|
|
|
94
|
+
# ------------------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
|
|
101
97
|
async def _getBucket():
|
|
102
98
|
ak, sk = await password.getQiniu()
|
|
103
99
|
return QiniuBucket(
|
bcmd/tasks/code.py
CHANGED
|
@@ -12,13 +12,12 @@ app: Final = btask.newSubApp('code 工具')
|
|
|
12
12
|
@app.command()
|
|
13
13
|
@syncCall
|
|
14
14
|
async def tidy_tasks(
|
|
15
|
-
tasks_path: Path = typer.Argument(
|
|
15
|
+
tasks_path: Path = typer.Argument(Path.cwd(), help="tasks 路径"),
|
|
16
16
|
):
|
|
17
17
|
'整理 task 项目中的 tasks/__init__.py'
|
|
18
|
-
|
|
19
|
-
tasks_path = Path.cwd()
|
|
18
|
+
|
|
20
19
|
initFile = tasks_path / '__init__.py'
|
|
21
|
-
btask.
|
|
20
|
+
btask.assertTrue(initFile.is_file(), '文件不存在', initFile)
|
|
22
21
|
files = bpath.listFile(tasks_path)
|
|
23
22
|
files = [x for x in files if not x.name.startswith('_')]
|
|
24
23
|
contents = [f'from . import {x.stem}' for x in files]
|
|
@@ -39,11 +38,10 @@ async def tidy_tasks(
|
|
|
39
38
|
@app.command()
|
|
40
39
|
@syncCall
|
|
41
40
|
async def tidy_modules(
|
|
42
|
-
modules_path: Path = typer.Argument(
|
|
41
|
+
modules_path: Path = typer.Argument(Path.cwd(), help="modules_path 路径"),
|
|
43
42
|
):
|
|
44
43
|
'整理 fastapi 项目中的 modules/__init__.py'
|
|
45
|
-
|
|
46
|
-
modules_path = Path.cwd()
|
|
44
|
+
|
|
47
45
|
importContents: list[str] = []
|
|
48
46
|
managerContents: list[str] = []
|
|
49
47
|
|
|
@@ -76,25 +74,23 @@ async def tidy_modules(
|
|
|
76
74
|
|
|
77
75
|
@app.command()
|
|
78
76
|
@syncCall
|
|
79
|
-
async def
|
|
80
|
-
|
|
77
|
+
async def gen_init_py(
|
|
78
|
+
workspace_path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
|
|
81
79
|
):
|
|
82
|
-
'
|
|
83
|
-
if not target_path:
|
|
84
|
-
target_path = Path.cwd()
|
|
80
|
+
'递归生成 __init__.py 文件'
|
|
85
81
|
|
|
86
82
|
async def makeInitFiles(p: Path):
|
|
87
83
|
if p.name == '__pycache__':
|
|
88
84
|
return
|
|
89
85
|
if p.name.startswith('.'):
|
|
90
86
|
return
|
|
91
|
-
if
|
|
92
|
-
initFile = p
|
|
87
|
+
if workspace_path != p:
|
|
88
|
+
initFile = p / '__init__.py'
|
|
93
89
|
if not initFile.exists():
|
|
94
|
-
printYellow(
|
|
90
|
+
printYellow(initFile)
|
|
95
91
|
await bfile.writeText(initFile, '')
|
|
96
92
|
for x in bpath.listDir(p):
|
|
97
93
|
await makeInitFiles(x)
|
|
98
94
|
|
|
99
|
-
await makeInitFiles(
|
|
95
|
+
await makeInitFiles(workspace_path)
|
|
100
96
|
printGreen('OK')
|
bcmd/tasks/crypto.py
CHANGED
bcmd/tasks/download.py
CHANGED
|
@@ -8,7 +8,7 @@ from uuid import uuid4
|
|
|
8
8
|
import pyperclip
|
|
9
9
|
import typer
|
|
10
10
|
from beni import bcolor, bfile, bhttp, binput, bpath, btask
|
|
11
|
-
from beni.bfunc import syncCall
|
|
11
|
+
from beni.bfunc import syncCall, textToAry
|
|
12
12
|
|
|
13
13
|
app: Final = btask.app
|
|
14
14
|
|
|
@@ -29,7 +29,7 @@ async def download(
|
|
|
29
29
|
content = await bfile.readText(url_file)
|
|
30
30
|
else:
|
|
31
31
|
content = pyperclip.paste()
|
|
32
|
-
urlSet = set(
|
|
32
|
+
urlSet = set(textToAry(content))
|
|
33
33
|
|
|
34
34
|
for i, url in enumerate(urlSet):
|
|
35
35
|
print(f'{i + 1}. {url}')
|
bcmd/tasks/lib.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import re
|
|
3
|
+
from contextlib import contextmanager
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Final
|
|
5
6
|
|
|
6
7
|
import typer
|
|
7
8
|
from beni import bcolor, bfile, bpath, btask
|
|
8
|
-
from beni.bfunc import syncCall
|
|
9
|
+
from beni.bfunc import syncCall, textToAry
|
|
9
10
|
|
|
10
11
|
from ..common import password
|
|
11
12
|
from .venv import getPackageList
|
|
@@ -16,18 +17,16 @@ app: Final = btask.newSubApp('lib 工具')
|
|
|
16
17
|
@app.command()
|
|
17
18
|
@syncCall
|
|
18
19
|
async def tidy_dependencies(
|
|
19
|
-
workspace_path: Path = typer.Argument(
|
|
20
|
-
|
|
20
|
+
workspace_path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
|
|
21
|
+
isWithVersion: bool = typer.Option(False, '--with-version', help='是否带版本号')
|
|
21
22
|
):
|
|
22
23
|
'整理 pyproject.toml 里面的 dependencies'
|
|
23
|
-
if not workspace_path:
|
|
24
|
-
workspace_path = Path.cwd()
|
|
25
24
|
pyprojectTomlFile = workspace_path / 'pyproject.toml'
|
|
26
|
-
btask.
|
|
25
|
+
btask.assertTrue(pyprojectTomlFile.is_file(), 'pyproject.toml 不存在', pyprojectTomlFile)
|
|
27
26
|
venvFile = bpath.get(workspace_path, f'.venv')
|
|
28
|
-
btask.
|
|
27
|
+
btask.assertTrue(venvFile.is_file(), '.venv 不存在', venvFile)
|
|
29
28
|
basePackages, lockPackages = await getPackageList(venvFile)
|
|
30
|
-
libAry = lockPackages if
|
|
29
|
+
libAry = lockPackages if isWithVersion else basePackages
|
|
31
30
|
oldContent = await bfile.readText(pyprojectTomlFile)
|
|
32
31
|
ignoreLibAry = _getIgnoreLibAry(oldContent)
|
|
33
32
|
ignoreLibAry = sorted(list(set(ignoreLibAry) & set(libAry)))
|
|
@@ -47,14 +46,12 @@ async def tidy_dependencies(
|
|
|
47
46
|
@app.command()
|
|
48
47
|
@syncCall
|
|
49
48
|
async def update_version(
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
|
|
50
|
+
isNotCommit: bool = typer.Option(False, '--no-commit', '-d', help='是否提交git'),
|
|
52
51
|
):
|
|
53
52
|
'修改 pyproject.toml 版本号'
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
file = workspace_path / 'pyproject.toml'
|
|
57
|
-
btask.check(file.is_file(), '文件不存在', file)
|
|
53
|
+
file = path / 'pyproject.toml'
|
|
54
|
+
btask.assertTrue(file.is_file(), '文件不存在', file)
|
|
58
55
|
data = await bfile.readToml(file)
|
|
59
56
|
version = data['project']['version']
|
|
60
57
|
versionList = [int(x) for x in version.split('.')]
|
|
@@ -69,7 +66,7 @@ async def update_version(
|
|
|
69
66
|
raise Exception('版本号修改失败,先检查文件中定义的版本号格式是否正常')
|
|
70
67
|
await bfile.writeText(file, content)
|
|
71
68
|
bcolor.printCyan(newVersion)
|
|
72
|
-
if not
|
|
69
|
+
if not isNotCommit:
|
|
73
70
|
msg = f'更新版本号 {newVersion}'
|
|
74
71
|
os.system(
|
|
75
72
|
rf'TortoiseGitProc.exe /command:commit /path:{file} /logmsg:"{msg}"'
|
|
@@ -80,42 +77,42 @@ async def update_version(
|
|
|
80
77
|
@app.command()
|
|
81
78
|
@syncCall
|
|
82
79
|
async def build(
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
|
|
81
|
+
isKeepBuildFiles: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
|
|
85
82
|
):
|
|
86
83
|
'发布项目'
|
|
87
|
-
|
|
88
|
-
# 获取用户名和密码
|
|
89
84
|
u, p = await password.getPypi()
|
|
85
|
+
with _useBuildPath(path, isKeepBuildFiles):
|
|
86
|
+
scriptPath = (path / './venv/Scripts')
|
|
87
|
+
os.system(f'{scriptPath / "python.exe"} -m build')
|
|
88
|
+
os.system(f'{scriptPath / "twine.exe"} upload dist/* -u {u} -p {p}')
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ------------------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _getIgnoreLibAry(content: str) -> list[str]:
|
|
95
|
+
'获取pyproject.toml中屏蔽的第三方库'
|
|
96
|
+
content = re.findall(r'dependencies = \[(.*?)\]', content, re.DOTALL)[0]
|
|
97
|
+
ary = textToAry(content)
|
|
98
|
+
return sorted([x[1:].replace('"', '').replace("'", '').replace(',', '').strip() for x in filter(lambda x: x.startswith('#'), ary)])
|
|
90
99
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
|
|
101
|
+
@contextmanager
|
|
102
|
+
def _useBuildPath(workspacePath: Path, isKeepBuildFiles: bool):
|
|
103
|
+
'整理构建目录,先清空不必要的输出目录,结束后再判断是否需要再清空一次'
|
|
94
104
|
|
|
95
105
|
def removeUnusedPath():
|
|
96
|
-
bpath.remove(
|
|
97
|
-
paths = bpath.listDir(
|
|
106
|
+
bpath.remove(workspacePath / 'dist')
|
|
107
|
+
paths = bpath.listDir(workspacePath)
|
|
98
108
|
for x in paths:
|
|
99
109
|
if x.name.endswith('.egg-info'):
|
|
100
110
|
bpath.remove(x)
|
|
101
111
|
|
|
102
112
|
try:
|
|
103
|
-
with bpath.changePath(
|
|
113
|
+
with bpath.changePath(workspacePath):
|
|
104
114
|
removeUnusedPath()
|
|
105
|
-
|
|
106
|
-
os.system(f'{scriptPath / "python.exe"} -m build')
|
|
107
|
-
os.system(f'{scriptPath / "twine.exe"} upload dist/* -u {u} -p {p}')
|
|
115
|
+
yield
|
|
108
116
|
finally:
|
|
109
|
-
if not
|
|
117
|
+
if not isKeepBuildFiles:
|
|
110
118
|
removeUnusedPath()
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# -------------------------------------------------
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def _getIgnoreLibAry(content: str) -> list[str]:
|
|
117
|
-
'获取pyproject.toml中屏蔽的第三方库'
|
|
118
|
-
content = re.findall(r'dependencies = \[(.*?)\]', content, re.DOTALL)[0]
|
|
119
|
-
ary = content.strip().split('\n')
|
|
120
|
-
ary = [x.strip() for x in ary if x]
|
|
121
|
-
return sorted([x[1:].replace('"', '').replace("'", '').replace(',', '').strip() for x in filter(lambda x: x.startswith('#'), ary)])
|
bcmd/tasks/math.py
CHANGED
|
@@ -46,7 +46,7 @@ async def discount(
|
|
|
46
46
|
values: list[str] = typer.Argument(..., help='每组数据使用#作为分隔符,注意后面的数据不能为0,例:123#500'),
|
|
47
47
|
):
|
|
48
48
|
'计算折扣,例子:beni math discount 123#500 130#550'
|
|
49
|
-
btask.
|
|
49
|
+
btask.assertTrue(len(values) >= 2, '至少需要提供2组数据用作比较')
|
|
50
50
|
|
|
51
51
|
class Data:
|
|
52
52
|
def __init__(self, value: str):
|
bcmd/tasks/mirror.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import platform
|
|
4
|
-
from enum import StrEnum
|
|
5
4
|
from typing import Final
|
|
6
5
|
|
|
7
6
|
import typer
|
|
@@ -14,45 +13,34 @@ app: Final = btask.app
|
|
|
14
13
|
@app.command()
|
|
15
14
|
@syncCall
|
|
16
15
|
async def mirror(
|
|
17
|
-
types: list[_MirrorsType] = typer.Argument(None, help="镜像的类型"),
|
|
18
16
|
disabled: bool = typer.Option(False, '--disabled', '-d', help="是否禁用"),
|
|
19
17
|
):
|
|
20
18
|
'设置镜像'
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'index-url = https://mirrors.aliyun.com/pypi/simple',
|
|
50
|
-
],
|
|
51
|
-
},
|
|
52
|
-
# _MirrorsType.npm: {
|
|
53
|
-
# bpath.user('.bashrc'): [
|
|
54
|
-
# 'registry=https://registry.npm.taobao.org/',
|
|
55
|
-
# 'electron_mirror=https://npm.taobao.org/mirrors/electron/',
|
|
56
|
-
# ],
|
|
57
|
-
# },
|
|
58
|
-
}
|
|
19
|
+
|
|
20
|
+
# 根据不同的系统平台
|
|
21
|
+
match platform.system():
|
|
22
|
+
case 'Windows':
|
|
23
|
+
file = bpath.user('pip/pip.ini')
|
|
24
|
+
case 'Linux':
|
|
25
|
+
file = bpath.user('.pip/pip.conf')
|
|
26
|
+
case _:
|
|
27
|
+
btask.abort('暂时不支持该平台', platform.system())
|
|
28
|
+
return
|
|
29
|
+
|
|
30
|
+
if disabled:
|
|
31
|
+
bpath.remove(file)
|
|
32
|
+
bcolor.printRed('删除文件', file)
|
|
33
|
+
else:
|
|
34
|
+
content = _content.strip()
|
|
35
|
+
await bfile.writeText(file, content)
|
|
36
|
+
bcolor.printYellow(file)
|
|
37
|
+
bcolor.printMagenta(content)
|
|
38
|
+
bcolor.printGreen('OK')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ------------------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
_content = '''
|
|
44
|
+
[global]
|
|
45
|
+
index-url = https://mirrors.aliyun.com/pypi/simple
|
|
46
|
+
'''
|
bcmd/tasks/project.py
CHANGED
|
@@ -2,33 +2,31 @@ from pathlib import Path
|
|
|
2
2
|
from typing import Final
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
|
-
from beni import bcolor,
|
|
5
|
+
from beni import bcolor, bpath, btask
|
|
6
6
|
from beni.bfunc import syncCall
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
from ..common.func import useResources
|
|
9
|
+
from .venv import venv
|
|
9
10
|
|
|
11
|
+
app: Final = btask.app
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
|
|
14
|
+
@app.command('project')
|
|
12
15
|
@syncCall
|
|
13
|
-
async def
|
|
14
|
-
|
|
16
|
+
async def _(
|
|
17
|
+
path: Path = typer.Option(Path.cwd(), '--path', help='workspace 路径'),
|
|
15
18
|
):
|
|
16
|
-
'
|
|
19
|
+
'生成新项目'
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
# 检查目标路径是否合法
|
|
22
|
+
if path.exists():
|
|
23
|
+
if not path.is_dir():
|
|
24
|
+
bcolor.printRed('目标路径不是一个目录', path)
|
|
20
25
|
return
|
|
21
|
-
|
|
26
|
+
elif list(bpath.get(path).glob('*')):
|
|
27
|
+
bcolor.printRed('目标路径不是空目录', path)
|
|
22
28
|
return
|
|
23
|
-
if workspace_path != p:
|
|
24
|
-
initFile = p / '__init__.py'
|
|
25
|
-
if not initFile.exists():
|
|
26
|
-
bcolor.printYellow(initFile)
|
|
27
|
-
await bfile.writeText(initFile, '')
|
|
28
|
-
for x in bpath.listDir(p):
|
|
29
|
-
await makeInitFiles(x)
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
bcolor.printGreen('OK')
|
|
30
|
+
venv(['benimang'], path, False, False, False, False)
|
|
31
|
+
with useResources('project') as sourceProjectPath:
|
|
32
|
+
bpath.copyOverwrite(sourceProjectPath, path)
|
bcmd/tasks/proxy.py
CHANGED
|
@@ -6,7 +6,7 @@ import psutil
|
|
|
6
6
|
import pyperclip
|
|
7
7
|
import typer
|
|
8
8
|
from beni import bcolor, btask
|
|
9
|
-
from beni.bfunc import syncCall
|
|
9
|
+
from beni.bfunc import syncCall, textToAry
|
|
10
10
|
|
|
11
11
|
app: Final = btask.app
|
|
12
12
|
|
|
@@ -14,32 +14,41 @@ app: Final = btask.app
|
|
|
14
14
|
@app.command()
|
|
15
15
|
@syncCall
|
|
16
16
|
async def proxy(
|
|
17
|
-
port: int = typer.
|
|
17
|
+
port: int = typer.Argument(15236, help="代理服务器端口"),
|
|
18
18
|
):
|
|
19
19
|
'生成终端设置代理服务器的命令'
|
|
20
|
-
processNameAry:list[str] = []
|
|
20
|
+
processNameAry: list[str] = []
|
|
21
21
|
process = psutil.Process().parent()
|
|
22
22
|
while process:
|
|
23
23
|
processNameAry.append(process.name())
|
|
24
24
|
process = process.parent()
|
|
25
|
-
|
|
25
|
+
template = ''
|
|
26
|
+
|
|
27
|
+
# 针对不同的终端使用不同的模板
|
|
26
28
|
if 'cmd.exe' in processNameAry:
|
|
27
|
-
|
|
28
|
-
f'set http_proxy=http://localhost:{port}',
|
|
29
|
-
f'set https_proxy=http://localhost:{port}',
|
|
30
|
-
f'set all_proxy=http://localhost:{port}',
|
|
31
|
-
'',
|
|
32
|
-
])
|
|
29
|
+
template = cmdTemplate
|
|
33
30
|
elif set(['powershell.exe', 'pwsh.exe']) & set(processNameAry):
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
template = powerShellTemplate
|
|
32
|
+
|
|
33
|
+
btask.assertTrue(template, '不支持当前终端({processNameAry})')
|
|
34
|
+
lineAry = textToAry(template.format(port))
|
|
35
|
+
msg = '\n'.join(lineAry)
|
|
36
|
+
bcolor.printMagenta('\r\n' + msg)
|
|
37
|
+
pyperclip.copy(msg)
|
|
38
|
+
bcolor.printYellow('已复制,可直接粘贴使用')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ------------------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
cmdTemplate = '''
|
|
45
|
+
set http_proxy=http://localhost:{0}
|
|
46
|
+
set https_proxy=http://localhost:{0}
|
|
47
|
+
set all_proxy=http://localhost:{0}
|
|
48
|
+
'''
|
|
49
|
+
|
|
50
|
+
powerShellTemplate = '''
|
|
51
|
+
$env:http_proxy="http://localhost:{0}"
|
|
52
|
+
$env:https_proxy="http://localhost:{0}"
|
|
53
|
+
$env:all_proxy="http://localhost:{0}"
|
|
54
|
+
'''
|
bcmd/tasks/time.py
CHANGED
|
@@ -6,7 +6,7 @@ from zoneinfo import ZoneInfo
|
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
8
|
from beni import bcolor, btask
|
|
9
|
-
from beni.bfunc import syncCall
|
|
9
|
+
from beni.bfunc import syncCall, textToAry
|
|
10
10
|
from beni.btype import Null
|
|
11
11
|
|
|
12
12
|
app: Final = btask.app
|
|
@@ -14,7 +14,9 @@ app: Final = btask.app
|
|
|
14
14
|
|
|
15
15
|
@app.command('time')
|
|
16
16
|
@syncCall
|
|
17
|
-
async def showtime(
|
|
17
|
+
async def showtime(
|
|
18
|
+
args: list[str] = typer.Argument(None),
|
|
19
|
+
):
|
|
18
20
|
'''
|
|
19
21
|
格式化时间戳\n
|
|
20
22
|
beni time\n
|
|
@@ -23,10 +25,10 @@ async def showtime(values: list[str] = typer.Argument(None)):
|
|
|
23
25
|
beni time 2021-9-23\n
|
|
24
26
|
beni time 2021-9-23 09:47:00\n
|
|
25
27
|
'''
|
|
26
|
-
|
|
27
|
-
btask.
|
|
28
|
-
value1: str | None =
|
|
29
|
-
value2: str | None =
|
|
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
|
|
30
32
|
timestamp: float = Null
|
|
31
33
|
if not value1:
|
|
32
34
|
timestamp = time.time()
|
|
@@ -42,12 +44,10 @@ async def showtime(values: list[str] = typer.Argument(None)):
|
|
|
42
44
|
except:
|
|
43
45
|
pass
|
|
44
46
|
if not timestamp:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
msg_ary = [x.strip() for x in msg_ary]
|
|
50
|
-
typer.secho('\n'.join(msg_ary), fg=color)
|
|
47
|
+
bcolor.printRed('参数无效\n')
|
|
48
|
+
bcolor.printRed('使用示例:')
|
|
49
|
+
msgAry = textToAry(str(showtime.__doc__))[1:]
|
|
50
|
+
bcolor.printRed('\n'.join(msgAry))
|
|
51
51
|
return
|
|
52
52
|
print()
|
|
53
53
|
bcolor.printMagenta(timestamp)
|
bcmd/tasks/venv.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import importlib.resources
|
|
2
1
|
import os
|
|
3
2
|
import platform
|
|
4
3
|
import re
|
|
@@ -8,13 +7,12 @@ from typing import Final
|
|
|
8
7
|
|
|
9
8
|
import typer
|
|
10
9
|
from beni import bcolor, bexecute, bfile, bhttp, bpath, btask
|
|
11
|
-
from beni.bfunc import syncCall
|
|
10
|
+
from beni.bfunc import syncCall, textToAry
|
|
12
11
|
from beni.btype import Null
|
|
13
12
|
from prettytable import PrettyTable
|
|
14
13
|
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
|
|
14
|
+
from ..common import password
|
|
15
|
+
from ..common.func import checkFileOrNotExists, checkPathOrNotExists
|
|
18
16
|
from . import bin
|
|
19
17
|
|
|
20
18
|
app: Final = btask.app
|
|
@@ -26,14 +24,12 @@ async def venv(
|
|
|
26
24
|
packages: list[str] = typer.Argument(None),
|
|
27
25
|
path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
|
|
28
26
|
isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
|
|
29
|
-
isNewProject: bool = typer.Option(False, '--new-project', help='是否新建项目'),
|
|
30
27
|
isUseBase: bool = typer.Option(False, '--use-base', help='是否强制使用基础库去安装'),
|
|
31
|
-
isReBase: bool = typer.Option(False, '--re-base', help='是否先清空venv目录再使用基础库去安装'),
|
|
32
28
|
isUseLock: bool = typer.Option(False, '--use-lock', help='是否使用锁定库安装'),
|
|
33
|
-
|
|
29
|
+
isCleanup: bool = typer.Option(False, '--cleanup', help='是否先清空 venv 目录再执行'),
|
|
34
30
|
):
|
|
35
31
|
'python 虚拟环境配置'
|
|
36
|
-
btask.
|
|
32
|
+
btask.assertTrue(not (isUseBase == isUseLock == True), '2个选项只能选择其中一个 --use-base / --use-lock')
|
|
37
33
|
path = path or Path(os.getcwd())
|
|
38
34
|
binPath = path / 'bin'
|
|
39
35
|
binListFile = bpath.get(path, 'bin.list')
|
|
@@ -42,9 +38,9 @@ async def venv(
|
|
|
42
38
|
venvFile = bpath.get(path, '.venv')
|
|
43
39
|
checkFileOrNotExists(venvFile)
|
|
44
40
|
await _inputQiniuPassword(binListFile, binPath)
|
|
45
|
-
if
|
|
41
|
+
if isCleanup:
|
|
46
42
|
bpath.remove(venvPath)
|
|
47
|
-
btask.
|
|
43
|
+
btask.assertTrue(not venvPath.exists(), f'无法删除 venv 目录 {venvPath}')
|
|
48
44
|
packages = packages or []
|
|
49
45
|
for i in range(len(packages)):
|
|
50
46
|
package = packages[i]
|
|
@@ -56,9 +52,9 @@ async def venv(
|
|
|
56
52
|
if not venvFile.exists():
|
|
57
53
|
await bfile.writeText(venvFile, '')
|
|
58
54
|
basePackages, lockPackages = await getPackageList(venvFile)
|
|
59
|
-
if isUseBase
|
|
55
|
+
if isUseBase:
|
|
60
56
|
installPackages = _mergePackageList(basePackages, packages)
|
|
61
|
-
elif isUseLock
|
|
57
|
+
elif isUseLock:
|
|
62
58
|
installPackages = _mergePackageList(lockPackages, packages)
|
|
63
59
|
else:
|
|
64
60
|
installPackages = _mergePackageList(lockPackages or basePackages, packages)
|
|
@@ -81,18 +77,16 @@ async def venv(
|
|
|
81
77
|
file=binListFile,
|
|
82
78
|
output=binPath,
|
|
83
79
|
)
|
|
84
|
-
# 新建项目
|
|
85
|
-
if isNewProject:
|
|
86
|
-
with importlib.resources.path('bcmd.resources', 'project') as sourceProjectPath:
|
|
87
|
-
for p in bpath.listPath(sourceProjectPath):
|
|
88
|
-
bpath.copy(p, path / p.name)
|
|
89
80
|
bcolor.printGreen('OK')
|
|
90
81
|
|
|
91
82
|
|
|
83
|
+
# ------------------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
|
|
92
86
|
async def _pipInstall(pip: Path, installPackages: list[str], disabled_mirror: bool):
|
|
93
87
|
python = pip.with_stem('python')
|
|
94
|
-
btask.
|
|
95
|
-
btask.
|
|
88
|
+
btask.assertTrue(python.is_file(), f'无法找到指定文件 {python}')
|
|
89
|
+
btask.assertTrue(pip.is_file(), f'无法找到指定文件 {pip}')
|
|
96
90
|
indexUrl = '-i https://pypi.org/simple' if disabled_mirror else ''
|
|
97
91
|
with bpath.useTempFile() as file:
|
|
98
92
|
await bfile.writeText(file, '\n'.join(installPackages))
|
|
@@ -108,11 +102,11 @@ async def _pipInstall(pip: Path, installPackages: list[str], disabled_mirror: bo
|
|
|
108
102
|
)
|
|
109
103
|
print(table.get_string())
|
|
110
104
|
|
|
111
|
-
btask.
|
|
105
|
+
btask.assertTrue(
|
|
112
106
|
not await bexecute.run(f'{python} -m pip install --upgrade pip {indexUrl}'),
|
|
113
107
|
'更新 pip 失败',
|
|
114
108
|
)
|
|
115
|
-
btask.
|
|
109
|
+
btask.assertTrue(
|
|
116
110
|
not await bexecute.run(f'{pip} install -r {file} {indexUrl}'),
|
|
117
111
|
'执行失败',
|
|
118
112
|
)
|
|
@@ -146,7 +140,7 @@ async def updatePackageList(venvFile: Path, packages: list[str], lockPackages: l
|
|
|
146
140
|
lockPackages.sort(key=lambda x: x.lower())
|
|
147
141
|
packageDict[_baseName] = packages
|
|
148
142
|
packageDict[lockName] = lockPackages
|
|
149
|
-
content = '\n'.join([f'\n[[ {key} ]]\n{'\n'.join(value)}' for key, value in packageDict.items()]).strip()
|
|
143
|
+
content = '\n\n\n'.join([f'\n[[ {key} ]]\n{'\n'.join(value)}' for key, value in packageDict.items()]).strip()
|
|
150
144
|
await bfile.writeText(venvFile, content)
|
|
151
145
|
|
|
152
146
|
|
|
@@ -181,7 +175,7 @@ def _getPackageName(package: str):
|
|
|
181
175
|
async def _inputQiniuPassword(binListFile: Path, binPath: Path) -> None:
|
|
182
176
|
'根据需要输入七牛云密码'
|
|
183
177
|
if binListFile.exists():
|
|
184
|
-
aaSet = set(
|
|
178
|
+
aaSet = set(textToAry(await bfile.readText(binListFile)))
|
|
185
179
|
bbSet = set([x.name for x in bpath.listFile(binPath)])
|
|
186
180
|
if aaSet != bbSet:
|
|
187
181
|
await password.getQiniu()
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bcmd
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.12
|
|
4
4
|
Summary: Commands for Beni
|
|
5
5
|
Author-email: Beni Mang <benimang@126.com>
|
|
6
6
|
Maintainer-email: Beni Mang <benimang@126.com>
|
|
7
7
|
Keywords: benimang,beni,bcmd
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
|
-
Requires-Dist: benimang ==0.
|
|
9
|
+
Requires-Dist: benimang ==0.7.5
|
|
10
10
|
Requires-Dist: build
|
|
11
11
|
Requires-Dist: cryptography
|
|
12
12
|
Requires-Dist: pathspec
|
|
13
13
|
Requires-Dist: pillow
|
|
14
14
|
Requires-Dist: prettytable
|
|
15
15
|
Requires-Dist: psutil
|
|
16
|
-
Requires-Dist: qiniu
|
|
16
|
+
Requires-Dist: qiniu
|
|
17
17
|
Requires-Dist: twine
|
|
18
18
|
Requires-Dist: typer
|
|
19
19
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
bcmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
bcmd/main.py,sha256=1HRCHLt_jeF6ImgjG_MX9N2x9H1f6FyqTX7UADzedfA,131
|
|
3
|
+
bcmd/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
bcmd/common/func.py,sha256=MehbfuWFHTOsihhYxVFj0_U6-hjMTfLh3n-oMrpyyKo,508
|
|
5
|
+
bcmd/common/password.py,sha256=25fA1h9ttZuUobnZ_nA0Ouhmk43etBfGeM40dgxJnFY,1347
|
|
6
|
+
bcmd/resources/project/.gitignore,sha256=m8wh9WahP29_Ci866EEuj07Wfn0wnkomj7wldbxd29E,26
|
|
7
|
+
bcmd/resources/project/main.py,sha256=xdskz_sf05fYA1SRMFCIxDjx8SnegxTbCmHpW86ItLs,11
|
|
8
|
+
bcmd/resources/project/.vscode/launch.json,sha256=Wpghb9lW9Y1wtrjqlTbyjeejDuU8BQJmBjwsLyPRh1g,478
|
|
9
|
+
bcmd/resources/project/.vscode/settings.json,sha256=Ze0dt3KkKU1IiXMRiQfjbcKdSsS7XTD9PkaQn3wgEWs,290
|
|
10
|
+
bcmd/resources/project/.vscode/tasks.json,sha256=gouhpkrqiPz7v65Jw1Rz-BCYU3sSdmphzXIYCzVnoe0,1783
|
|
11
|
+
bcmd/tasks/__init__.py,sha256=XDE4eW0mPkUCSK9tyg1DQRGLA7A9DuTmjq5MyUiPoXs,294
|
|
12
|
+
bcmd/tasks/bin.py,sha256=B_e-HYOc2Cohk-1PbKHyKn3RhI8TAUfI2EBRoFEHiTM,2961
|
|
13
|
+
bcmd/tasks/code.py,sha256=S_98EXb7QKeS1mxPfmXKu7E91yLuoA45CyXvJ6szzQY,2941
|
|
14
|
+
bcmd/tasks/crypto.py,sha256=C2welnYfdI4Tc-W1cwkboGb6bk6NGWO0MRKm4Bzo_9Q,3216
|
|
15
|
+
bcmd/tasks/debian.py,sha256=B9aMIIct3vNqMJr5hTr1GegXVf20H49C27FMvRRGIzI,3004
|
|
16
|
+
bcmd/tasks/download.py,sha256=kQnzEXtduRWtfFFfbUugGbCEbeFWUiKvWrrZ_4RjY-Y,1792
|
|
17
|
+
bcmd/tasks/image.py,sha256=OSqShLb_lwa77aQOnRNksXNemtuAnQDGg-VfiDy9fEM,2310
|
|
18
|
+
bcmd/tasks/json.py,sha256=WWOyvcZPYaqQgp-Tkm-uIJschNMBKPKtZN3yXz_SC5s,635
|
|
19
|
+
bcmd/tasks/lib.py,sha256=_hlMzvXKtiHtDOX4nBcWkoqrpQ8A1PdICa01OBFdhVU,4563
|
|
20
|
+
bcmd/tasks/math.py,sha256=xbl5UdaDMyAjiLodDPleP4Cutrk2S3NOAgurzAgOEAE,2862
|
|
21
|
+
bcmd/tasks/mirror.py,sha256=nAe8NYftMKzht16MFBj7RqXwvVhR6Jh2uuAyJLh87og,1098
|
|
22
|
+
bcmd/tasks/project.py,sha256=5Ui79TM8_RiTquGF_nKxextE0wMhaWHpITJ4mpMHn7E,846
|
|
23
|
+
bcmd/tasks/proxy.py,sha256=6ApGO2t61uF9NWaQ-VpsTwR1MoDXKmfQDCWXgcC-3UY,1454
|
|
24
|
+
bcmd/tasks/time.py,sha256=ZiqA1jdgl-TBtFSOxxP51nwv4g9iZItmkFKpf9MKelk,2453
|
|
25
|
+
bcmd/tasks/venv.py,sha256=pGIoW2cgmK5nrhxN5MKgHTwDxPO7U2VH1fR_D-d1pcY,6684
|
|
26
|
+
bcmd-0.4.12.dist-info/METADATA,sha256=HW-K23e3MRAfMrqejwARheWl8XjTWXyM_lGxyg2RM6o,478
|
|
27
|
+
bcmd-0.4.12.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
|
|
28
|
+
bcmd-0.4.12.dist-info/entry_points.txt,sha256=rHJrP6KEQpB-YaQqDFzEL2v88r03rxSfnzAayRvAqHU,39
|
|
29
|
+
bcmd-0.4.12.dist-info/top_level.txt,sha256=-KrvhhtBcYsm4XhcjQvEcFbBB3VXeep7d3NIfDTrXKQ,5
|
|
30
|
+
bcmd-0.4.12.dist-info/RECORD,,
|
bcmd-0.4.7.dist-info/RECORD
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
bcmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
bcmd/main.py,sha256=1HRCHLt_jeF6ImgjG_MX9N2x9H1f6FyqTX7UADzedfA,131
|
|
3
|
-
bcmd/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
bcmd/common/func.py,sha256=B5IFOcI0pfqKUNg91ry-vP-WZm9S22x7yzha1QMn2kM,296
|
|
5
|
-
bcmd/common/password.py,sha256=25fA1h9ttZuUobnZ_nA0Ouhmk43etBfGeM40dgxJnFY,1347
|
|
6
|
-
bcmd/resources/project/.gitignore,sha256=m8wh9WahP29_Ci866EEuj07Wfn0wnkomj7wldbxd29E,26
|
|
7
|
-
bcmd/resources/project/main.py,sha256=xdskz_sf05fYA1SRMFCIxDjx8SnegxTbCmHpW86ItLs,11
|
|
8
|
-
bcmd/resources/project/.vscode/launch.json,sha256=Wpghb9lW9Y1wtrjqlTbyjeejDuU8BQJmBjwsLyPRh1g,478
|
|
9
|
-
bcmd/resources/project/.vscode/settings.json,sha256=Ze0dt3KkKU1IiXMRiQfjbcKdSsS7XTD9PkaQn3wgEWs,290
|
|
10
|
-
bcmd/resources/project/.vscode/tasks.json,sha256=gouhpkrqiPz7v65Jw1Rz-BCYU3sSdmphzXIYCzVnoe0,1783
|
|
11
|
-
bcmd/tasks/__init__.py,sha256=XDE4eW0mPkUCSK9tyg1DQRGLA7A9DuTmjq5MyUiPoXs,294
|
|
12
|
-
bcmd/tasks/bin.py,sha256=rdag8IJv081CKflnJKo0IkVbi5wqBGowrl6gLMtP6Eg,3133
|
|
13
|
-
bcmd/tasks/code.py,sha256=MEfzE879dplraLtZ59EZu94HrPkEohHCo7PNEoF_xmM,3089
|
|
14
|
-
bcmd/tasks/crypto.py,sha256=IhWtFyfLLUb9xuVdPcvr-D5lZciLDtwxQDoz5SEL7Fg,3127
|
|
15
|
-
bcmd/tasks/debian.py,sha256=B9aMIIct3vNqMJr5hTr1GegXVf20H49C27FMvRRGIzI,3004
|
|
16
|
-
bcmd/tasks/download.py,sha256=0TYdoeEkXL--GTZ8ZSnSNzh8pC42kZhrTu6WVY5e7Fo,1824
|
|
17
|
-
bcmd/tasks/image.py,sha256=OSqShLb_lwa77aQOnRNksXNemtuAnQDGg-VfiDy9fEM,2310
|
|
18
|
-
bcmd/tasks/json.py,sha256=WWOyvcZPYaqQgp-Tkm-uIJschNMBKPKtZN3yXz_SC5s,635
|
|
19
|
-
bcmd/tasks/lib.py,sha256=6-1WGYUzBtr80K7zuQETLiQKpB9EfXvPxiL22a8u5v8,4583
|
|
20
|
-
bcmd/tasks/math.py,sha256=M7-mmyQPx1UW7JiU1knY5Ty0hBw9zv9L4NNJf9eEjZ4,2857
|
|
21
|
-
bcmd/tasks/mirror.py,sha256=-ztGkkxVk81npIo4cpmyLdHa1w4ZFdiJ3mv5WIBMI5Y,1556
|
|
22
|
-
bcmd/tasks/project.py,sha256=ESWyRvRu4tesoYrlBtYMrQvQoxzMnFkI-jTN2hsYruI,939
|
|
23
|
-
bcmd/tasks/proxy.py,sha256=mdiBR2vah5qKt9o7dXE7rg8Lz_A6GheVJDts0m1gmSs,1324
|
|
24
|
-
bcmd/tasks/time.py,sha256=nSIVYov2LsGdxsZAtC91UKXcUtVAqR9o-JmzeuevFhA,2586
|
|
25
|
-
bcmd/tasks/venv.py,sha256=Metps2tKu_wyZfezMTvQPInAWgQV6FyRW9l4-WS9lk8,7160
|
|
26
|
-
bcmd-0.4.7.dist-info/METADATA,sha256=PLqFGXVEpVWeo8M0qwIik_e5dHmkL40BxC1CvIV6aPo,486
|
|
27
|
-
bcmd-0.4.7.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
28
|
-
bcmd-0.4.7.dist-info/entry_points.txt,sha256=rHJrP6KEQpB-YaQqDFzEL2v88r03rxSfnzAayRvAqHU,39
|
|
29
|
-
bcmd-0.4.7.dist-info/top_level.txt,sha256=-KrvhhtBcYsm4XhcjQvEcFbBB3VXeep7d3NIfDTrXKQ,5
|
|
30
|
-
bcmd-0.4.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|