bcmd 0.4.11__py3-none-any.whl → 0.5.0__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/image.py +148 -1
- bcmd/tasks/lib.py +5 -5
- bcmd/tasks/project.py +5 -3
- bcmd/tasks/venv.py +65 -31
- {bcmd-0.4.11.dist-info → bcmd-0.5.0.dist-info}/METADATA +2 -3
- {bcmd-0.4.11.dist-info → bcmd-0.5.0.dist-info}/RECORD +9 -9
- {bcmd-0.4.11.dist-info → bcmd-0.5.0.dist-info}/WHEEL +1 -1
- {bcmd-0.4.11.dist-info → bcmd-0.5.0.dist-info}/entry_points.txt +0 -0
- {bcmd-0.4.11.dist-info → bcmd-0.5.0.dist-info}/top_level.txt +0 -0
bcmd/tasks/image.py
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import os
|
|
3
|
+
import random
|
|
2
4
|
from enum import StrEnum
|
|
3
5
|
from pathlib import Path
|
|
4
6
|
from typing import Final
|
|
5
7
|
|
|
8
|
+
import httpx
|
|
6
9
|
import typer
|
|
7
|
-
from beni import bcolor, bfile, bpath, btask
|
|
10
|
+
from beni import bcolor, bfile, bhttp, block, bpath, btask
|
|
11
|
+
from beni.bbyte import BytesReader, BytesWriter
|
|
8
12
|
from beni.bfunc import syncCall
|
|
13
|
+
from beni.btype import Null, XPath
|
|
9
14
|
from PIL import Image
|
|
10
15
|
|
|
11
16
|
app: Final = btask.newSubApp('图片工具集')
|
|
@@ -52,3 +57,145 @@ async def convert(
|
|
|
52
57
|
if outputFile != file:
|
|
53
58
|
bpath.remove(file)
|
|
54
59
|
bcolor.printGreen(f'{file} -> {outputFile}')
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# ------------------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
@app.command()
|
|
65
|
+
@syncCall
|
|
66
|
+
async def tiny(
|
|
67
|
+
path: Path = typer.Option(None, '--path', help='指定目录或具体图片文件,默认当前目录'),
|
|
68
|
+
compression: int = typer.Option(60, '--compression', help='如果压缩尺寸小于指定数值则使用原来的图片,单位:%,默认60'),
|
|
69
|
+
isKeepOriginal: bool = typer.Option(False, '--keep-original', help='保留原始图片'),
|
|
70
|
+
):
|
|
71
|
+
|
|
72
|
+
keyList = [
|
|
73
|
+
'MB3QmtvZ8HKRkXcDnxhWCNTXzvx6cNF3',
|
|
74
|
+
'7L7X2CJ35GM1bChSHdT14yZPLx7FlpNk',
|
|
75
|
+
'q8YLcvrXVW2NYcr5mMyzQhsSHF4j7gny',
|
|
76
|
+
]
|
|
77
|
+
random.shuffle(keyList)
|
|
78
|
+
await block.setLimit(_TinyFile.runTiny, len(keyList))
|
|
79
|
+
|
|
80
|
+
# 整理文件列表
|
|
81
|
+
fileList = []
|
|
82
|
+
path = path or Path(os.getcwd())
|
|
83
|
+
if path.is_file():
|
|
84
|
+
fileList.append(path)
|
|
85
|
+
elif path.is_dir():
|
|
86
|
+
fileList = [x for x in bpath.listFile(path, True) if x.suffix[1:].lower() in ['jpg', 'jpeg', 'png']]
|
|
87
|
+
else:
|
|
88
|
+
btask.abort('未找到图片文件', path)
|
|
89
|
+
fileList.sort()
|
|
90
|
+
|
|
91
|
+
# 将文件列表整理成 _TinyFile 对象
|
|
92
|
+
fileList = [_TinyFile(x) for x in fileList]
|
|
93
|
+
await asyncio.gather(*[x.updateInfo() for x in fileList])
|
|
94
|
+
|
|
95
|
+
# 过滤掉已经处理过的图片
|
|
96
|
+
for i in range(len(fileList)):
|
|
97
|
+
file = fileList[i]
|
|
98
|
+
if file.compression == 0:
|
|
99
|
+
# 未处理过的图片,进行图片的压缩处理
|
|
100
|
+
pass
|
|
101
|
+
elif not file.isTiny and file.compression < compression:
|
|
102
|
+
# 之前测试的压缩率不符合要求,不过现在符合了,进行图片的压缩处理
|
|
103
|
+
pass
|
|
104
|
+
else:
|
|
105
|
+
# 要忽略掉的文件
|
|
106
|
+
bcolor.printYellow(f'{file.file}({file.compression}% / 忽略)')
|
|
107
|
+
fileList[i] = Null
|
|
108
|
+
fileList = [x for x in fileList if x]
|
|
109
|
+
|
|
110
|
+
# 开始压缩
|
|
111
|
+
taskList = [
|
|
112
|
+
x.runTiny(keyList[i % len(keyList)], compression, isKeepOriginal)
|
|
113
|
+
for i, x in enumerate(fileList)
|
|
114
|
+
]
|
|
115
|
+
await asyncio.gather(*taskList)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ------------------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class _TinyFile:
|
|
122
|
+
|
|
123
|
+
_endian: Final = '>'
|
|
124
|
+
_sep = BytesWriter(_endian).writeStr('tiny').writeUint(9527).writeUint(709394).toBytes()
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def compression(self):
|
|
128
|
+
return self._compression
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def isTiny(self):
|
|
132
|
+
return self._isTiny
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def file(self):
|
|
136
|
+
return self._file
|
|
137
|
+
|
|
138
|
+
def __init__(self, file: XPath):
|
|
139
|
+
self._file = file
|
|
140
|
+
self._compression: float = 0.0
|
|
141
|
+
self._isTiny: bool = False
|
|
142
|
+
|
|
143
|
+
async def updateInfo(self):
|
|
144
|
+
fileBytes = await bfile.readBytes(self._file)
|
|
145
|
+
self._compression = 0.0
|
|
146
|
+
self._isTiny = False
|
|
147
|
+
blockAry = fileBytes.split(self._sep)
|
|
148
|
+
if len(blockAry) > 1:
|
|
149
|
+
info = BytesReader(self._endian, blockAry[1])
|
|
150
|
+
size = info.readUint()
|
|
151
|
+
if size == len(blockAry[0]):
|
|
152
|
+
self._compression = round(info.readFloat(), 2)
|
|
153
|
+
self._isTiny = info.readBool()
|
|
154
|
+
|
|
155
|
+
async def _flushInfo(self, compression: float, isTiny: bool):
|
|
156
|
+
self._compression = compression
|
|
157
|
+
self._isTiny = isTiny
|
|
158
|
+
content = await self._getPureContent()
|
|
159
|
+
info = (
|
|
160
|
+
BytesWriter(self._endian)
|
|
161
|
+
.writeUint(len(content))
|
|
162
|
+
.writeFloat(compression)
|
|
163
|
+
.writeBool(isTiny)
|
|
164
|
+
.toBytes()
|
|
165
|
+
)
|
|
166
|
+
content += self._sep + info
|
|
167
|
+
await bfile.writeBytes(self._file, content)
|
|
168
|
+
|
|
169
|
+
async def _getPureContent(self):
|
|
170
|
+
content = await bfile.readBytes(self._file)
|
|
171
|
+
content = content.split(self._sep)[0]
|
|
172
|
+
return content
|
|
173
|
+
|
|
174
|
+
@block.limit(1)
|
|
175
|
+
async def runTiny(self, key: str, compression: float, isKeepOriginal: bool):
|
|
176
|
+
content = await self._getPureContent()
|
|
177
|
+
async with httpx.AsyncClient() as client:
|
|
178
|
+
response = await client.post(
|
|
179
|
+
'https://api.tinify.com/shrink',
|
|
180
|
+
auth=('api', key),
|
|
181
|
+
content=content,
|
|
182
|
+
timeout=30,
|
|
183
|
+
)
|
|
184
|
+
response.raise_for_status()
|
|
185
|
+
result = response.json()
|
|
186
|
+
outputCompression = round(result['output']['ratio'] * 100, 2)
|
|
187
|
+
if outputCompression < compression:
|
|
188
|
+
# 下载文件
|
|
189
|
+
url = result['output']['url']
|
|
190
|
+
with bpath.useTempFile() as tempFile:
|
|
191
|
+
await bhttp.download(url, tempFile)
|
|
192
|
+
await _TinyFile(tempFile)._flushInfo(outputCompression, True)
|
|
193
|
+
outputFile = bpath.get(self._file)
|
|
194
|
+
if isKeepOriginal:
|
|
195
|
+
outputFile = outputFile.with_stem(f'{outputFile.stem}_tiny')
|
|
196
|
+
bpath.move(tempFile, outputFile, True)
|
|
197
|
+
bcolor.printGreen(f'{outputFile}({outputCompression}% / 已压缩)')
|
|
198
|
+
else:
|
|
199
|
+
# 不进行压缩
|
|
200
|
+
await self._flushInfo(outputCompression, False)
|
|
201
|
+
bcolor.printMagenta(f'{self._file} ({outputCompression}% / 不处理)')
|
bcmd/tasks/lib.py
CHANGED
|
@@ -46,11 +46,11 @@ async def tidy_dependencies(
|
|
|
46
46
|
@app.command()
|
|
47
47
|
@syncCall
|
|
48
48
|
async def update_version(
|
|
49
|
-
|
|
49
|
+
path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
|
|
50
50
|
isNotCommit: bool = typer.Option(False, '--no-commit', '-d', help='是否提交git'),
|
|
51
51
|
):
|
|
52
52
|
'修改 pyproject.toml 版本号'
|
|
53
|
-
file =
|
|
53
|
+
file = path / 'pyproject.toml'
|
|
54
54
|
btask.assertTrue(file.is_file(), '文件不存在', file)
|
|
55
55
|
data = await bfile.readToml(file)
|
|
56
56
|
version = data['project']['version']
|
|
@@ -77,13 +77,13 @@ async def update_version(
|
|
|
77
77
|
@app.command()
|
|
78
78
|
@syncCall
|
|
79
79
|
async def build(
|
|
80
|
-
|
|
80
|
+
path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
|
|
81
81
|
isKeepBuildFiles: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
|
|
82
82
|
):
|
|
83
83
|
'发布项目'
|
|
84
84
|
u, p = await password.getPypi()
|
|
85
|
-
with _useBuildPath(
|
|
86
|
-
scriptPath = (
|
|
85
|
+
with _useBuildPath(path, isKeepBuildFiles):
|
|
86
|
+
scriptPath = (path / './venv/Scripts')
|
|
87
87
|
os.system(f'{scriptPath / "python.exe"} -m build')
|
|
88
88
|
os.system(f'{scriptPath / "twine.exe"} upload dist/* -u {u} -p {p}')
|
|
89
89
|
|
bcmd/tasks/project.py
CHANGED
|
@@ -2,11 +2,11 @@ from pathlib import Path
|
|
|
2
2
|
from typing import Final
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
|
-
from beni import bcolor, bpath, btask
|
|
5
|
+
from beni import bcolor, binput, bpath, btask
|
|
6
6
|
from beni.bfunc import syncCall
|
|
7
7
|
|
|
8
8
|
from ..common.func import useResources
|
|
9
|
-
from .venv import
|
|
9
|
+
from .venv import add as venvAdd
|
|
10
10
|
|
|
11
11
|
app: Final = btask.app
|
|
12
12
|
|
|
@@ -27,6 +27,8 @@ async def _(
|
|
|
27
27
|
bcolor.printRed('目标路径不是空目录', path)
|
|
28
28
|
return
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
bcolor.printYellow(path)
|
|
31
|
+
await binput.confirm('即将在此路径生成新项目,是否继续?')
|
|
32
|
+
venvAdd(['benimang'], path)
|
|
31
33
|
with useResources('project') as sourceProjectPath:
|
|
32
34
|
bpath.copyOverwrite(sourceProjectPath, path)
|
bcmd/tasks/venv.py
CHANGED
|
@@ -7,37 +7,91 @@ from typing import Final
|
|
|
7
7
|
|
|
8
8
|
import typer
|
|
9
9
|
from beni import bcolor, bexecute, bfile, bhttp, bpath, btask
|
|
10
|
-
from beni.bfunc import syncCall
|
|
10
|
+
from beni.bfunc import syncCall
|
|
11
11
|
from beni.btype import Null
|
|
12
12
|
from prettytable import PrettyTable
|
|
13
13
|
|
|
14
|
-
from ..common import password
|
|
15
14
|
from ..common.func import checkFileOrNotExists, checkPathOrNotExists
|
|
16
|
-
from . import bin
|
|
17
15
|
|
|
18
|
-
app: Final = btask.
|
|
16
|
+
app: Final = btask.newSubApp('venv 相关')
|
|
19
17
|
|
|
20
18
|
|
|
21
19
|
@app.command()
|
|
22
20
|
@syncCall
|
|
23
|
-
async def
|
|
21
|
+
async def add(
|
|
24
22
|
packages: list[str] = typer.Argument(None),
|
|
25
23
|
path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
|
|
26
24
|
isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
):
|
|
26
|
+
'添加指定库'
|
|
27
|
+
await _venv(
|
|
28
|
+
packages,
|
|
29
|
+
path=path,
|
|
30
|
+
isOfficial=isOfficial,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.command()
|
|
35
|
+
@syncCall
|
|
36
|
+
async def update_benimang(
|
|
37
|
+
path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
|
|
38
|
+
):
|
|
39
|
+
'更新 benimang 库'
|
|
40
|
+
await _venv(
|
|
41
|
+
['benimang==now'],
|
|
42
|
+
path=path,
|
|
43
|
+
isOfficial=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command()
|
|
48
|
+
@syncCall
|
|
49
|
+
async def reinstall_base(
|
|
50
|
+
path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
|
|
51
|
+
isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
|
|
52
|
+
):
|
|
53
|
+
'重新安装基础库'
|
|
54
|
+
await _venv(
|
|
55
|
+
path=path,
|
|
56
|
+
isOfficial=isOfficial,
|
|
57
|
+
isUseBase=True,
|
|
58
|
+
isCleanup=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@app.command()
|
|
63
|
+
@syncCall
|
|
64
|
+
async def reinstall_lock(
|
|
65
|
+
path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
|
|
66
|
+
isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
|
|
67
|
+
):
|
|
68
|
+
'重新安装版本固定的库'
|
|
69
|
+
await _venv(
|
|
70
|
+
path=path,
|
|
71
|
+
isOfficial=isOfficial,
|
|
72
|
+
isUseLock=True,
|
|
73
|
+
isCleanup=True,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ------------------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
async def _venv(
|
|
80
|
+
packages: list[str] = [],
|
|
81
|
+
*,
|
|
82
|
+
path: Path = Null,
|
|
83
|
+
isOfficial: bool = False,
|
|
84
|
+
isUseBase: bool = False,
|
|
85
|
+
isUseLock: bool = False,
|
|
86
|
+
isCleanup: bool = False,
|
|
30
87
|
):
|
|
31
88
|
'python 虚拟环境配置'
|
|
32
89
|
btask.assertTrue(not (isUseBase == isUseLock == True), '2个选项只能选择其中一个 --use-base / --use-lock')
|
|
33
90
|
path = path or Path(os.getcwd())
|
|
34
|
-
binPath = path / 'bin'
|
|
35
|
-
binListFile = bpath.get(path, 'bin.list')
|
|
36
91
|
venvPath = bpath.get(path, 'venv')
|
|
37
92
|
checkPathOrNotExists(venvPath)
|
|
38
93
|
venvFile = bpath.get(path, '.venv')
|
|
39
94
|
checkFileOrNotExists(venvFile)
|
|
40
|
-
await _inputQiniuPassword(binListFile, binPath)
|
|
41
95
|
if isCleanup:
|
|
42
96
|
bpath.remove(venvPath)
|
|
43
97
|
btask.assertTrue(not venvPath.exists(), f'无法删除 venv 目录 {venvPath}')
|
|
@@ -69,20 +123,9 @@ async def venv(
|
|
|
69
123
|
basePackages = _mergePackageList(basePackages, packages)
|
|
70
124
|
lockPackages = (await bfile.readText(tempFile)).strip().split('\n')
|
|
71
125
|
await updatePackageList(venvFile, basePackages, lockPackages)
|
|
72
|
-
|
|
73
|
-
# 下载 bin 文件
|
|
74
|
-
if binListFile.exists():
|
|
75
|
-
bin.download(
|
|
76
|
-
names=Null,
|
|
77
|
-
file=binListFile,
|
|
78
|
-
output=binPath,
|
|
79
|
-
)
|
|
80
126
|
bcolor.printGreen('OK')
|
|
81
127
|
|
|
82
128
|
|
|
83
|
-
# ------------------------------------------------------------------------------------
|
|
84
|
-
|
|
85
|
-
|
|
86
129
|
async def _pipInstall(pip: Path, installPackages: list[str], disabled_mirror: bool):
|
|
87
130
|
python = pip.with_stem('python')
|
|
88
131
|
btask.assertTrue(python.is_file(), f'无法找到指定文件 {python}')
|
|
@@ -170,12 +213,3 @@ def _getPackageName(package: str):
|
|
|
170
213
|
if package.startswith('#'):
|
|
171
214
|
package = package.replace('#', '', 1).strip()
|
|
172
215
|
return package
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
async def _inputQiniuPassword(binListFile: Path, binPath: Path) -> None:
|
|
176
|
-
'根据需要输入七牛云密码'
|
|
177
|
-
if binListFile.exists():
|
|
178
|
-
aaSet = set(textToAry(await bfile.readText(binListFile)))
|
|
179
|
-
bbSet = set([x.name for x in bpath.listFile(binPath)])
|
|
180
|
-
if aaSet != bbSet:
|
|
181
|
-
await password.getQiniu()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bcmd
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
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
|
|
9
|
+
Requires-Dist: benimang==0.7.5
|
|
10
10
|
Requires-Dist: build
|
|
11
11
|
Requires-Dist: cryptography
|
|
12
12
|
Requires-Dist: pathspec
|
|
@@ -16,4 +16,3 @@ Requires-Dist: psutil
|
|
|
16
16
|
Requires-Dist: qiniu
|
|
17
17
|
Requires-Dist: twine
|
|
18
18
|
Requires-Dist: typer
|
|
19
|
-
|
|
@@ -14,17 +14,17 @@ bcmd/tasks/code.py,sha256=S_98EXb7QKeS1mxPfmXKu7E91yLuoA45CyXvJ6szzQY,2941
|
|
|
14
14
|
bcmd/tasks/crypto.py,sha256=C2welnYfdI4Tc-W1cwkboGb6bk6NGWO0MRKm4Bzo_9Q,3216
|
|
15
15
|
bcmd/tasks/debian.py,sha256=B9aMIIct3vNqMJr5hTr1GegXVf20H49C27FMvRRGIzI,3004
|
|
16
16
|
bcmd/tasks/download.py,sha256=kQnzEXtduRWtfFFfbUugGbCEbeFWUiKvWrrZ_4RjY-Y,1792
|
|
17
|
-
bcmd/tasks/image.py,sha256=
|
|
17
|
+
bcmd/tasks/image.py,sha256=TN7vMjyNHtv-qBOKklWluEqI4OESp6U3ll3agFTkFc4,7558
|
|
18
18
|
bcmd/tasks/json.py,sha256=WWOyvcZPYaqQgp-Tkm-uIJschNMBKPKtZN3yXz_SC5s,635
|
|
19
|
-
bcmd/tasks/lib.py,sha256=
|
|
19
|
+
bcmd/tasks/lib.py,sha256=_hlMzvXKtiHtDOX4nBcWkoqrpQ8A1PdICa01OBFdhVU,4563
|
|
20
20
|
bcmd/tasks/math.py,sha256=xbl5UdaDMyAjiLodDPleP4Cutrk2S3NOAgurzAgOEAE,2862
|
|
21
21
|
bcmd/tasks/mirror.py,sha256=nAe8NYftMKzht16MFBj7RqXwvVhR6Jh2uuAyJLh87og,1098
|
|
22
|
-
bcmd/tasks/project.py,sha256=
|
|
22
|
+
bcmd/tasks/project.py,sha256=73d5sxOD-Aqc5PwqV8FBzIfbyJjZ-juQad7JV43YZIE,948
|
|
23
23
|
bcmd/tasks/proxy.py,sha256=6ApGO2t61uF9NWaQ-VpsTwR1MoDXKmfQDCWXgcC-3UY,1454
|
|
24
24
|
bcmd/tasks/time.py,sha256=ZiqA1jdgl-TBtFSOxxP51nwv4g9iZItmkFKpf9MKelk,2453
|
|
25
|
-
bcmd/tasks/venv.py,sha256=
|
|
26
|
-
bcmd-0.
|
|
27
|
-
bcmd-0.
|
|
28
|
-
bcmd-0.
|
|
29
|
-
bcmd-0.
|
|
30
|
-
bcmd-0.
|
|
25
|
+
bcmd/tasks/venv.py,sha256=P2bO-ddRG5WWKvdU-JM8YAEAhPAoRdqOrku1Ihp1nFo,7144
|
|
26
|
+
bcmd-0.5.0.dist-info/METADATA,sha256=Lo4gTAZs6wfBwHWcpiBKpnyO6g7ouL9_V-m2-BSEC8I,474
|
|
27
|
+
bcmd-0.5.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
28
|
+
bcmd-0.5.0.dist-info/entry_points.txt,sha256=rHJrP6KEQpB-YaQqDFzEL2v88r03rxSfnzAayRvAqHU,39
|
|
29
|
+
bcmd-0.5.0.dist-info/top_level.txt,sha256=-KrvhhtBcYsm4XhcjQvEcFbBB3VXeep7d3NIfDTrXKQ,5
|
|
30
|
+
bcmd-0.5.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|