bcmd 0.5.17__tar.gz → 0.6.0__tar.gz

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.

Files changed (40) hide show
  1. {bcmd-0.5.17 → bcmd-0.6.0}/PKG-INFO +7 -1
  2. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/__init__.py +0 -2
  3. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/image.py +2 -2
  4. bcmd-0.6.0/bcmd/tasks/lib.py +59 -0
  5. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/upgrade.py +7 -3
  6. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd.egg-info/PKG-INFO +7 -1
  7. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd.egg-info/SOURCES.txt +0 -2
  8. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd.egg-info/requires.txt +7 -0
  9. {bcmd-0.5.17 → bcmd-0.6.0}/pyproject.toml +16 -7
  10. bcmd-0.5.17/bcmd/tasks/lib.py +0 -118
  11. bcmd-0.5.17/bcmd/tasks/project.py +0 -59
  12. bcmd-0.5.17/bcmd/tasks/venv.py +0 -227
  13. {bcmd-0.5.17 → bcmd-0.6.0}/MANIFEST.in +0 -0
  14. {bcmd-0.5.17 → bcmd-0.6.0}/README.md +0 -0
  15. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/__init__.py +0 -0
  16. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/common/__init__.py +0 -0
  17. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/common/func.py +0 -0
  18. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/common/password.py +0 -0
  19. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/main.py +0 -0
  20. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/resources/project/.gitignore +0 -0
  21. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/resources/project/.vscode/launch.json +0 -0
  22. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/resources/project/.vscode/settings.json +0 -0
  23. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/resources/project/.vscode/tasks.json +0 -0
  24. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/resources/project/main.py +0 -0
  25. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/bin.py +0 -0
  26. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/code.py +0 -0
  27. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/crypto.py +0 -0
  28. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/debian.py +0 -0
  29. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/download.py +0 -0
  30. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/json.py +0 -0
  31. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/math.py +0 -0
  32. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/mirror.py +0 -0
  33. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/pdf.py +0 -0
  34. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/proxy.py +0 -0
  35. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/time.py +0 -0
  36. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd/tasks/wasabi.py +0 -0
  37. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd.egg-info/dependency_links.txt +0 -0
  38. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd.egg-info/entry_points.txt +0 -0
  39. {bcmd-0.5.17 → bcmd-0.6.0}/bcmd.egg-info/top_level.txt +0 -0
  40. {bcmd-0.5.17 → bcmd-0.6.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bcmd
3
- Version: 0.5.17
3
+ Version: 0.6.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>
@@ -17,3 +17,9 @@ Requires-Dist: pymupdf
17
17
  Requires-Dist: qiniu
18
18
  Requires-Dist: twine
19
19
  Requires-Dist: typer
20
+ Provides-Extra: full
21
+ Requires-Dist: img2pdf; extra == "full"
22
+ Requires-Dist: pytest; extra == "full"
23
+ Requires-Dist: pytest-asyncio; extra == "full"
24
+ Requires-Dist: pytest-order; extra == "full"
25
+ Requires-Dist: setuptools; extra == "full"
@@ -10,9 +10,7 @@ from . import lib
10
10
  from . import math
11
11
  from . import mirror
12
12
  from . import pdf
13
- from . import project
14
13
  from . import proxy
15
14
  from . import time
16
15
  from . import upgrade
17
- from . import venv
18
16
  from . import wasabi
@@ -285,14 +285,14 @@ async def merge(
285
285
  # 修改保存参数为 WebP 格式
286
286
  merged_image.save(
287
287
  output_path,
288
- format='WEBP',
288
+ format='JPEG',
289
289
  quality=80, # 质量参数(0-100),推荐 80-90 之间
290
290
  method=6, # 压缩方法(0-6),6 为最佳压缩
291
291
  lossless=False, # 不使用无损压缩(更小的文件体积)
292
292
  )
293
293
 
294
294
  image_files = [x for x in bpath.listFile(path) if x.suffix in ('.png', '.jpg', '.jpeg', '.webp', '.bmp')]
295
- output_image = path / f'merge_{path.name}.webp' # 修改文件扩展名为 webp
295
+ output_image = path / f'merge_{path.name}.jpg' # 修改文件扩展名为 webp
296
296
  if output_image in image_files:
297
297
  if not force:
298
298
  print(output_image)
@@ -0,0 +1,59 @@
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Final
4
+
5
+ import typer
6
+ from beni import bcolor, bfile, bpath, brun, btask
7
+ from beni.bfunc import syncCall
8
+
9
+ from ..common import password
10
+
11
+ app: Final = btask.newSubApp('lib 工具')
12
+
13
+
14
+ @app.command()
15
+ @syncCall
16
+ async def update_version(
17
+ path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
18
+ isNotCommit: bool = typer.Option(False, '--no-commit', '-d', help='是否提交git'),
19
+ ):
20
+ '修改 pyproject.toml 版本号'
21
+ file = path / 'pyproject.toml'
22
+ btask.assertTrue(file.is_file(), '文件不存在', file)
23
+ data = await bfile.readToml(file)
24
+ version = data['project']['version']
25
+ versionList = [int(x) for x in version.split('.')]
26
+ versionList[-1] += 1
27
+ newVersion = '.'.join([str(x) for x in versionList])
28
+ content = await bfile.readText(file)
29
+ if f"version = '{version}'" in content:
30
+ content = content.replace(f"version = '{version}'", f"version = '{newVersion}'")
31
+ elif f'version = "{version}"' in content:
32
+ content = content.replace(f'version = "{version}"', f'version = "{newVersion}"')
33
+ else:
34
+ raise Exception('版本号修改失败,先检查文件中定义的版本号格式是否正常')
35
+ await bfile.writeText(file, content)
36
+ bcolor.printCyan(newVersion)
37
+ if not isNotCommit:
38
+ msg = f'更新版本号 {newVersion}'
39
+ os.system(
40
+ rf'TortoiseGitProc.exe /command:commit /path:{file} /logmsg:"{msg}"'
41
+ )
42
+ bcolor.printGreen('OK')
43
+
44
+
45
+ @app.command()
46
+ @syncCall
47
+ async def build(
48
+ path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
49
+ isKeepBuildFiles: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
50
+ ):
51
+ '发布项目'
52
+ user, pwd = await password.getPypi()
53
+ bpath.remove(path / 'dist')
54
+ bpath.remove(
55
+ *list(path.glob('*.egg-info'))
56
+ )
57
+ with bpath.changePath(path):
58
+ await brun.run(f'uv build')
59
+ await brun.run(f'uv publish -u {user} -p {pwd}')
@@ -1,6 +1,7 @@
1
1
  from typing import Final
2
2
 
3
3
  import pyperclip
4
+ import typer
4
5
  from beni import bcolor, btask
5
6
  from beni.bfunc import syncCall
6
7
 
@@ -9,9 +10,12 @@ app: Final = btask.app
9
10
 
10
11
  @app.command()
11
12
  @syncCall
12
- async def upgrade():
13
- '使用 pipx 官方源更新 bcmd 到最新版本'
14
- cmd = 'pipx upgrade bcmd -i https://pypi.org/simple'
13
+ async def upgrade(
14
+ name: str = typer.Argument('bcmd', help='要更新的包名'),
15
+ ):
16
+ '使用 pipx 官方源更新指定包到最新版本'
17
+
18
+ cmd = f'pipx upgrade {name} -i https://pypi.org/simple'
15
19
  pyperclip.copy(cmd + '\n')
16
20
  bcolor.printGreen(cmd)
17
21
  bcolor.printGreen('已复制到剪贴板(需要手动执行)')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bcmd
3
- Version: 0.5.17
3
+ Version: 0.6.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>
@@ -17,3 +17,9 @@ Requires-Dist: pymupdf
17
17
  Requires-Dist: qiniu
18
18
  Requires-Dist: twine
19
19
  Requires-Dist: typer
20
+ Provides-Extra: full
21
+ Requires-Dist: img2pdf; extra == "full"
22
+ Requires-Dist: pytest; extra == "full"
23
+ Requires-Dist: pytest-asyncio; extra == "full"
24
+ Requires-Dist: pytest-order; extra == "full"
25
+ Requires-Dist: setuptools; extra == "full"
@@ -29,9 +29,7 @@ bcmd/tasks/lib.py
29
29
  bcmd/tasks/math.py
30
30
  bcmd/tasks/mirror.py
31
31
  bcmd/tasks/pdf.py
32
- bcmd/tasks/project.py
33
32
  bcmd/tasks/proxy.py
34
33
  bcmd/tasks/time.py
35
34
  bcmd/tasks/upgrade.py
36
- bcmd/tasks/venv.py
37
35
  bcmd/tasks/wasabi.py
@@ -9,3 +9,10 @@ pymupdf
9
9
  qiniu
10
10
  twine
11
11
  typer
12
+
13
+ [full]
14
+ img2pdf
15
+ pytest
16
+ pytest-asyncio
17
+ pytest-order
18
+ setuptools
@@ -3,14 +3,13 @@
3
3
 
4
4
  [project]
5
5
  name = 'bcmd'
6
- version = '0.5.17'
6
+ version = '0.6.0'
7
7
  description = 'Commands for Beni'
8
8
  requires-python = '>=3.10'
9
9
  keywords = ['benimang', 'beni', 'bcmd']
10
10
  authors = [{ name = 'Beni Mang', email = 'benimang@126.com' }]
11
11
  maintainers = [{ name = 'Beni Mang', email = 'benimang@126.com' }]
12
12
 
13
-
14
13
  dependencies = [
15
14
  'benimang==0.7.14',
16
15
  'build',
@@ -23,12 +22,22 @@ dependencies = [
23
22
  'qiniu',
24
23
  'twine',
25
24
  'typer',
26
- # 'img2pdf',
27
- # 'pytest',
28
- # 'pytest-asyncio',
29
- # 'pytest-order',
30
- # 'setuptools',
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ full = [
29
+ 'img2pdf',
30
+ 'pytest',
31
+ 'pytest-asyncio',
32
+ 'pytest-order',
33
+ 'setuptools',
31
34
  ]
32
35
 
33
36
  [project.scripts]
34
37
  beni = 'bcmd.main:run'
38
+
39
+ # 使用默认镜像地址
40
+ [[tool.uv.index]]
41
+ url = "https://mirrors.aliyun.com/pypi/simple"
42
+ default = true
43
+
@@ -1,118 +0,0 @@
1
- import os
2
- import re
3
- from contextlib import contextmanager
4
- from pathlib import Path
5
- from typing import Final
6
-
7
- import typer
8
- from beni import bcolor, bfile, bpath, btask
9
- from beni.bfunc import syncCall, textToAry
10
-
11
- from ..common import password
12
- from .venv import getPackageList
13
-
14
- app: Final = btask.newSubApp('lib 工具')
15
-
16
-
17
- @app.command()
18
- @syncCall
19
- async def tidy_dependencies(
20
- workspace_path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
21
- isWithVersion: bool = typer.Option(False, '--with-version', help='是否带版本号')
22
- ):
23
- '整理 pyproject.toml 里面的 dependencies'
24
- pyprojectTomlFile = workspace_path / 'pyproject.toml'
25
- btask.assertTrue(pyprojectTomlFile.is_file(), 'pyproject.toml 不存在', pyprojectTomlFile)
26
- venvFile = bpath.get(workspace_path, f'.venv')
27
- btask.assertTrue(venvFile.is_file(), '.venv 不存在', venvFile)
28
- basePackages, lockPackages = await getPackageList(venvFile)
29
- libAry = lockPackages if isWithVersion else basePackages
30
- oldContent = await bfile.readText(pyprojectTomlFile)
31
- ignoreLibAry = _getIgnoreLibAry(oldContent)
32
- ignoreLibAry = sorted(list(set(ignoreLibAry) & set(libAry)))
33
- libAry = sorted(list(set(libAry) - set(ignoreLibAry)))
34
- replaceContent = '\n'.join([f" '{x}'," for x in libAry]) + '\n' + '\n'.join([f" # '{x}'," for x in ignoreLibAry])
35
- newContent = re.sub(r'dependencies = \[(.*?)\n\]', f"dependencies = [\n{replaceContent}\n]", oldContent, 0, re.DOTALL)
36
- if oldContent != newContent:
37
- await bfile.writeText(pyprojectTomlFile, newContent)
38
- bcolor.printYellow(pyprojectTomlFile)
39
- bcolor.printMagenta(newContent)
40
- return True
41
- else:
42
- bcolor.printGreen('无需修改依赖')
43
- return False
44
-
45
-
46
- @app.command()
47
- @syncCall
48
- async def update_version(
49
- path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
50
- isNotCommit: bool = typer.Option(False, '--no-commit', '-d', help='是否提交git'),
51
- ):
52
- '修改 pyproject.toml 版本号'
53
- file = path / 'pyproject.toml'
54
- btask.assertTrue(file.is_file(), '文件不存在', file)
55
- data = await bfile.readToml(file)
56
- version = data['project']['version']
57
- versionList = [int(x) for x in version.split('.')]
58
- versionList[-1] += 1
59
- newVersion = '.'.join([str(x) for x in versionList])
60
- content = await bfile.readText(file)
61
- if f"version = '{version}'" in content:
62
- content = content.replace(f"version = '{version}'", f"version = '{newVersion}'")
63
- elif f'version = "{version}"' in content:
64
- content = content.replace(f'version = "{version}"', f'version = "{newVersion}"')
65
- else:
66
- raise Exception('版本号修改失败,先检查文件中定义的版本号格式是否正常')
67
- await bfile.writeText(file, content)
68
- bcolor.printCyan(newVersion)
69
- if not isNotCommit:
70
- msg = f'更新版本号 {newVersion}'
71
- os.system(
72
- rf'TortoiseGitProc.exe /command:commit /path:{file} /logmsg:"{msg}"'
73
- )
74
- bcolor.printGreen('OK')
75
-
76
-
77
- @app.command()
78
- @syncCall
79
- async def build(
80
- path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
81
- isKeepBuildFiles: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
82
- ):
83
- '发布项目'
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 = \[(.*?)\n\]', 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)])
99
-
100
-
101
- @contextmanager
102
- def _useBuildPath(workspacePath: Path, isKeepBuildFiles: bool):
103
- '整理构建目录,先清空不必要的输出目录,结束后再判断是否需要再清空一次'
104
-
105
- def removeUnusedPath():
106
- bpath.remove(workspacePath / 'dist')
107
- paths = bpath.listDir(workspacePath)
108
- for x in paths:
109
- if x.name.endswith('.egg-info'):
110
- bpath.remove(x)
111
-
112
- try:
113
- with bpath.changePath(workspacePath):
114
- removeUnusedPath()
115
- yield
116
- finally:
117
- if not isKeepBuildFiles:
118
- removeUnusedPath()
@@ -1,59 +0,0 @@
1
- from pathlib import Path
2
- from typing import Final
3
-
4
- import typer
5
- from beni import bcolor, binput, bpath, brun, btask
6
- from beni.bfunc import syncCall
7
-
8
- from ..common.func import useResources
9
- from . import venv
10
-
11
- app: Final = btask.newSubApp('项目相关')
12
-
13
-
14
- @app.command()
15
- @syncCall
16
- async def create_py(
17
- path: Path = typer.Option(Path.cwd(), '--path', help='workspace 路径'),
18
- ):
19
- '生成新项目'
20
-
21
- # 检查目标路径是否合法
22
- if path.exists():
23
- if not path.is_dir():
24
- bcolor.printRed('目标路径不是一个目录', path)
25
- return
26
- elif list(bpath.get(path).glob('*')):
27
- bcolor.printRed('目标路径不是空目录', path)
28
- return
29
-
30
- bcolor.printYellow(path)
31
- await binput.confirm('即将在此路径生成新项目,是否继续?')
32
- venv.add(['benimang==now'], path)
33
- with useResources('project') as sourceProjectPath:
34
- bpath.copyOverwrite(sourceProjectPath, path)
35
-
36
-
37
- @app.command()
38
- @syncCall
39
- async def install(
40
- path: Path = typer.Option(Path.cwd(), '--path', help='初始化项目的路径'),
41
- deep: int = typer.Option(3, '--deep', help='探索路径深度(默认:1)'),
42
- ):
43
- '初始化项目(python项目执行beni venv install-lock / nodejs项目执行 pnpm install)'
44
-
45
- async def checkPath(currentPath: Path, currentDeep: int):
46
- for file in bpath.listFile(currentPath):
47
- if file.name == '.venv':
48
- with bpath.changePath(file.parent):
49
- await brun.run('beni venv install-lock', isPrint=True)
50
- return
51
- elif file.name == 'package.json':
52
- with bpath.changePath(file.parent):
53
- await brun.run('pnpm install', isPrint=True)
54
- return
55
- if currentDeep < deep:
56
- for folder in bpath.listPath(currentPath):
57
- await checkPath(folder, currentDeep + 1)
58
-
59
- await checkPath(path, 0)
@@ -1,227 +0,0 @@
1
- import os
2
- import platform
3
- import re
4
- import sys
5
- from pathlib import Path
6
- from typing import Final
7
-
8
- import typer
9
- from beni import bcolor, bexecute, bfile, bhttp, bpath, brun, btask
10
- from beni.bfunc import syncCall
11
- from beni.btype import Null
12
- from prettytable import PrettyTable
13
-
14
- from ..common.func import checkFileOrNotExists, checkPathOrNotExists
15
-
16
- app: Final = btask.newSubApp('venv 相关')
17
-
18
-
19
- @app.command()
20
- @syncCall
21
- async def add(
22
- packages: list[str] = typer.Argument(None),
23
- path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
24
- isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
25
- ):
26
- '添加指定库'
27
- await _venv(
28
- packages,
29
- path=path,
30
- isOfficial=isOfficial,
31
- )
32
-
33
-
34
- @app.command()
35
- @syncCall
36
- async def install_benimang(
37
- path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
38
- ):
39
- '更新 benimang 库,强制使用官方源'
40
- path = path or Path(os.getcwd())
41
- pip = getPipFile(path)
42
- await brun.run(f'{pip} install benimang -U -i https://pypi.org/simple', isPrint=True)
43
- await _venv(
44
- ['benimang==now'],
45
- path=path,
46
- )
47
-
48
-
49
- @app.command()
50
- @syncCall
51
- async def install_base(
52
- path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
53
- isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
54
- isCleanup: bool = typer.Option(False, '--cleanup', help='是否清空venv目录后重新安装'),
55
- ):
56
- '安装基础库'
57
- await _venv(
58
- path=path,
59
- isOfficial=isOfficial,
60
- isUseBase=True,
61
- isCleanup=isCleanup,
62
- )
63
-
64
-
65
- @app.command()
66
- @syncCall
67
- async def install_lock(
68
- path: Path = typer.Option(None, '--path', help='指定路径,默认当前目录'),
69
- isOfficial: bool = typer.Option(False, '--official', help='是否使用官方地址安装(https://pypi.org/simple)'),
70
- isCleanup: bool = typer.Option(False, '--cleanup', help='是否清空venv目录后重新安装'),
71
- ):
72
- '安装指定版本的库'
73
- await _venv(
74
- path=path,
75
- isOfficial=isOfficial,
76
- isUseLock=True,
77
- isCleanup=isCleanup,
78
- )
79
-
80
-
81
- # ------------------------------------------------------------------------------------
82
-
83
- async def _venv(
84
- packages: list[str] = [],
85
- *,
86
- path: Path = Null,
87
- isOfficial: bool = False,
88
- isUseBase: bool = False,
89
- isUseLock: bool = False,
90
- isCleanup: bool = False,
91
- ):
92
- 'python 虚拟环境配置'
93
- btask.assertTrue(not (isUseBase == isUseLock == True), '2个选项只能选择其中一个 --use-base / --use-lock')
94
- path = path or Path(os.getcwd())
95
- venvPath = getVenvPath(path)
96
- checkPathOrNotExists(venvPath)
97
- venvFile = bpath.get(path, '.venv')
98
- checkFileOrNotExists(venvFile)
99
- if isCleanup:
100
- bpath.remove(venvPath)
101
- btask.assertTrue(not venvPath.exists(), f'无法删除 venv 目录 {venvPath}')
102
- packages = packages or []
103
- for i in range(len(packages)):
104
- package = packages[i]
105
- if package.endswith('==now'):
106
- ary = package.split('==')
107
- packages[i] = f'{ary[0]}=={await _getPackageLatestVersion(ary[0])}'
108
- if not venvPath.exists():
109
- await bexecute.run(f'python -m venv {venvPath}')
110
- if not venvFile.exists():
111
- await bfile.writeText(venvFile, '')
112
- basePackages, lockPackages = await getPackageList(venvFile)
113
- if isUseBase:
114
- installPackages = _mergePackageList(basePackages, packages)
115
- elif isUseLock:
116
- installPackages = _mergePackageList(lockPackages, packages)
117
- else:
118
- installPackages = _mergePackageList(lockPackages or basePackages, packages)
119
- installPackages = sorted(list(set(installPackages)))
120
- pip = getPipFile(path)
121
- await _pipInstall(pip, installPackages, isOfficial)
122
- with bpath.useTempFile() as tempFile:
123
- await bexecute.run(f'{pip} freeze > {tempFile}')
124
- basePackages = _mergePackageList(basePackages, packages)
125
- lockPackages = (await bfile.readText(tempFile)).replace('\r\n', '\n').strip().split('\n')
126
- await updatePackageList(venvFile, basePackages, lockPackages)
127
- bcolor.printGreen('OK')
128
-
129
-
130
- async def _pipInstall(pip: Path, installPackages: list[str], disabled_mirror: bool):
131
- python = pip.with_stem('python')
132
- btask.assertTrue(python.is_file(), f'无法找到指定文件 {python}')
133
- btask.assertTrue(pip.is_file(), f'无法找到指定文件 {pip}')
134
- indexUrl = '-i https://pypi.org/simple' if disabled_mirror else ''
135
- with bpath.useTempFile() as file:
136
- await bfile.writeText(file, '\n'.join(installPackages))
137
- table = PrettyTable()
138
- table.add_column(
139
- bcolor.yellow('#'),
140
- [x + 1 for x in range(len(installPackages))],
141
- )
142
- table.add_column(
143
- bcolor.yellow('安装库'),
144
- [x for x in installPackages],
145
- 'l',
146
- )
147
- print(table.get_string())
148
-
149
- btask.assertTrue(
150
- not await bexecute.run(f'{python} -m pip install --upgrade pip {indexUrl}'),
151
- '更新 pip 失败',
152
- )
153
- btask.assertTrue(
154
- not await bexecute.run(f'{pip} install -r {file} {indexUrl}'),
155
- '执行失败',
156
- )
157
-
158
-
159
- async def _getPackageDict(venvFile: Path):
160
- content = await bfile.readText(venvFile)
161
- pattern = r'\[\[ (.*?) \]\]\n(.*?)(?=\n\[\[|\Z)'
162
- matches: list[tuple[str, str]] = re.findall(pattern, content.strip(), re.DOTALL)
163
- return {match[0]: [line.strip() for line in match[1].strip().split('\n') if line.strip()] for match in matches}
164
-
165
-
166
- _baseName: Final[str] = 'venv'
167
-
168
-
169
- def _getLockName():
170
- systemName = platform.system()
171
- return f'{_baseName}-{systemName}'
172
-
173
-
174
- async def getPackageList(venvFile: Path):
175
- result = await _getPackageDict(venvFile)
176
- lockName = _getLockName()
177
- return result.get(_baseName, []), result.get(lockName, [])
178
-
179
-
180
- async def updatePackageList(venvFile: Path, packages: list[str], lockPackages: list[str]):
181
- packageDict = await _getPackageDict(venvFile)
182
- lockName = _getLockName()
183
- packages.sort(key=lambda x: x.lower())
184
- lockPackages.sort(key=lambda x: x.lower())
185
- packageDict[_baseName] = packages
186
- packageDict[lockName] = lockPackages
187
- content = '\n\n\n'.join([f'\n[[ {key} ]]\n{'\n'.join(value)}' for key, value in packageDict.items()]).strip()
188
- await bfile.writeText(venvFile, content)
189
-
190
-
191
- async def _getPackageLatestVersion(package: str):
192
- '获取指定包的最新版本'
193
- data = await bhttp.getJson(
194
- f'https://pypi.org/pypi/{package}/json'
195
- )
196
- return data['info']['version']
197
-
198
-
199
- def _mergePackageList(basePackages: list[str], addPackages: list[str]):
200
- basePackagesDict = {_getPackageName(x): x for x in basePackages}
201
- addPackagesDict = {_getPackageName(x): x for x in addPackages}
202
- packagesDict = basePackagesDict | addPackagesDict
203
- return sorted([x for x in packagesDict.values()])
204
-
205
-
206
- def _getPackageName(package: str):
207
- if '==' in package:
208
- package = package.split('==')[0]
209
- elif '>' in package:
210
- package = package.split('>')[0]
211
- elif '<' in package:
212
- package = package.split('<')[0]
213
- package = package.strip()
214
- if package.startswith('#'):
215
- package = package.replace('#', '', 1).strip()
216
- return package
217
-
218
-
219
- def getVenvPath(path: Path):
220
- return bpath.get(path, 'venv')
221
-
222
-
223
- def getPipFile(path: Path):
224
- if sys.platform.startswith('win'):
225
- return bpath.get(getVenvPath(path), 'Scripts/pip.exe')
226
- else:
227
- return bpath.get(getVenvPath(path), 'bin/pip')
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes