bcmd 0.0.66__py3-none-any.whl → 0.5.2__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/lib.py CHANGED
@@ -1,34 +1,37 @@
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
- from bcmd.common import password
11
+ from ..common import password
12
+ from .venv import getPackageList
11
13
 
12
14
  app: Final = btask.newSubApp('lib 工具')
13
15
 
14
16
 
15
17
  @app.command()
16
18
  @syncCall
17
- async def tidy(
18
- workspace_path: Path = typer.Argument(None, help='workspace 路径'),
19
- with_version: bool = typer.Option(False, '--with-version', help='是否带版本号')
19
+ async def tidy_dependencies(
20
+ workspace_path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
21
+ isWithVersion: bool = typer.Option(False, '--with-version', help='是否带版本号')
20
22
  ):
21
23
  '整理 pyproject.toml 里面的 dependencies'
22
- if not workspace_path:
23
- workspace_path = Path.cwd()
24
24
  pyprojectTomlFile = workspace_path / 'pyproject.toml'
25
- btask.check(pyprojectTomlFile.is_file(), 'pyproject.toml 不存在', pyprojectTomlFile)
26
- targetVenvFileName = 'venv.lock' if with_version else 'venv.list'
27
- targetVenvFile = bpath.get(workspace_path, f'./../{targetVenvFileName}')
28
- btask.check(targetVenvFile.is_file(), '文件不存在', targetVenvFile)
29
- ary = (await bfile.readText(targetVenvFile)).strip().replace('\r\n', '\n').split('\n')
30
- replaceContent = '\n'.join([f" '{x}'," for x in ary])
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
31
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])
32
35
  newContent = re.sub(r'dependencies = \[(.*?)\]', f"dependencies = [\n{replaceContent}\n]", oldContent, 0, re.DOTALL)
33
36
  if oldContent != newContent:
34
37
  await bfile.writeText(pyprojectTomlFile, newContent)
@@ -42,17 +45,13 @@ async def tidy(
42
45
 
43
46
  @app.command()
44
47
  @syncCall
45
- async def version(
46
- workspace_path: Path = typer.Argument(None, help='workspace 路径'),
47
- disabled_commit: bool = typer.Option(False, '--disabled-commit', '-d', help='是否提交git'),
48
- with_dependencies_version: bool = typer.Option(False, '--with_dependencies_version', help='第三方库是否带版本号')
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'),
49
51
  ):
50
- '修改 pyproject.toml 版本号(同时会更新依赖列表)'
51
- if not workspace_path:
52
- workspace_path = Path.cwd()
53
- file = workspace_path / 'pyproject.toml'
54
- btask.check(file.is_file(), '文件不存在', file)
55
- isChangeDependencies = tidy(workspace_path, with_dependencies_version)
52
+ '修改 pyproject.toml 版本号'
53
+ file = path / 'pyproject.toml'
54
+ btask.assertTrue(file.is_file(), '文件不存在', file)
56
55
  data = await bfile.readToml(file)
57
56
  version = data['project']['version']
58
57
  versionList = [int(x) for x in version.split('.')]
@@ -67,11 +66,8 @@ async def version(
67
66
  raise Exception('版本号修改失败,先检查文件中定义的版本号格式是否正常')
68
67
  await bfile.writeText(file, content)
69
68
  bcolor.printCyan(newVersion)
70
- if not disabled_commit:
71
- msg = ''.join([
72
- f'更新版本号 {newVersion}',
73
- '(注意:本次有修改依赖)' if isChangeDependencies else '',
74
- ]).strip()
69
+ if not isNotCommit:
70
+ msg = f'更新版本号 {newVersion}'
75
71
  os.system(
76
72
  rf'TortoiseGitProc.exe /command:commit /path:{file} /logmsg:"{msg}"'
77
73
  )
@@ -80,33 +76,43 @@ async def version(
80
76
 
81
77
  @app.command()
82
78
  @syncCall
83
- async def publish(
84
- src_path: Path = typer.Argument(None, help='src 路径'),
85
- keep_build_files: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
79
+ async def build(
80
+ path: Path = typer.Argument(Path.cwd(), help='workspace 路径'),
81
+ isKeepBuildFiles: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
86
82
  ):
87
83
  '发布项目'
88
-
89
- # 获取用户名和密码
90
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)])
99
+
91
100
 
92
- if not src_path:
93
- src_path = Path.cwd()
94
- src_path = src_path.resolve()
101
+ @contextmanager
102
+ def _useBuildPath(workspacePath: Path, isKeepBuildFiles: bool):
103
+ '整理构建目录,先清空不必要的输出目录,结束后再判断是否需要再清空一次'
95
104
 
96
105
  def removeUnusedPath():
97
- bpath.remove(src_path / 'dist')
98
- paths = bpath.listDir(src_path)
106
+ bpath.remove(workspacePath / 'dist')
107
+ paths = bpath.listDir(workspacePath)
99
108
  for x in paths:
100
109
  if x.name.endswith('.egg-info'):
101
110
  bpath.remove(x)
102
111
 
103
112
  try:
104
- with bpath.changePath(src_path):
113
+ with bpath.changePath(workspacePath):
105
114
  removeUnusedPath()
106
- scriptPath = (src_path / './../venv/Scripts').resolve()
107
- os.system(f'{scriptPath / "pip.exe"} install setuptools -U -i https://mirrors.aliyun.com/pypi/simple')
108
- os.system(f'{scriptPath / "python.exe"} -m build')
109
- os.system(f'{scriptPath / "twine.exe"} upload dist/* -u {u} -p {p}')
115
+ yield
110
116
  finally:
111
- if not keep_build_files:
117
+ if not isKeepBuildFiles:
112
118
  removeUnusedPath()
bcmd/tasks/math.py CHANGED
@@ -1,4 +1,3 @@
1
- from math import nan
2
1
  from typing import Final
3
2
 
4
3
  import typer
@@ -41,59 +40,13 @@ async def scale(
41
40
  print(table.get_string(header=False))
42
41
 
43
42
 
44
- @app.command()
45
- @syncCall
46
- async def increase(
47
- old: float = typer.Argument(nan, help='原始数值'),
48
- new: float = typer.Argument(nan, help='新数值'),
49
- ):
50
- '''
51
- 计算增长率
52
-
53
- 例子:beni math increase 123 456
54
-
55
- 例子(连续对话):beni math increase
56
- '''
57
- if old is not nan and new is not nan:
58
- value = (new - old) / old
59
- if value > 0:
60
- valueStr = bcolor.red(f'{value*100:+,.3f}%')
61
- else:
62
- valueStr = bcolor.green(f'{value*100:+,.3f}%')
63
- table = PrettyTable(
64
- title=bcolor.yellow('计算增长率'),
65
- )
66
- table.add_rows([
67
- ['旧数值', f'{old:,}'],
68
- ['新数值', f'{new:,}'],
69
- ['增长率', valueStr],
70
- ])
71
- print()
72
- print(table.get_string(header=False))
73
- elif old is nan and new is nan:
74
- while True:
75
- print()
76
- value = input('输入2个数值,空格分隔,退出输入 [exit] :').strip()
77
- if value == 'exit':
78
- break
79
- try:
80
- ary = value.split(' ')
81
- ary = [float(x) for x in ary if x]
82
- assert len(ary) == 2, '参数必须要2个数值'
83
- increase(ary[0], ary[1])
84
- except Exception as e:
85
- bcolor.printRed(e)
86
- else:
87
- btask.abort('参数错误,不传参数,或者传入2个数值参数')
88
-
89
-
90
43
  @app.command()
91
44
  @syncCall
92
45
  async def discount(
93
46
  values: list[str] = typer.Argument(..., help='每组数据使用#作为分隔符,注意后面的数据不能为0,例:123#500'),
94
47
  ):
95
48
  '计算折扣,例子:beni math discount 123#500 130#550'
96
- btask.check(len(values) >= 2, '至少需要提供2组数据用作比较')
49
+ btask.assertTrue(len(values) >= 2, '至少需要提供2组数据用作比较')
97
50
 
98
51
  class Data:
99
52
  def __init__(self, value: str):
@@ -135,7 +88,7 @@ async def discount(
135
88
  f'{data.a:,}',
136
89
  f'{data.b:,}',
137
90
  f'{data.v:,.3f}',
138
- f'{data.discount*100:+,.3f}%' if data.discount else '',
91
+ f'{data.discount * 100:+,.3f}%' if data.discount else '',
139
92
  ]
140
93
  table.add_column(
141
94
  chr(65 + counter()),
bcmd/tasks/mirror.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from enum import StrEnum
3
+ import platform
4
4
  from typing import Final
5
5
 
6
6
  import typer
@@ -13,43 +13,34 @@ app: Final = btask.app
13
13
  @app.command()
14
14
  @syncCall
15
15
  async def mirror(
16
- types: list[_MirrorsType] = typer.Argument(None, help="镜像的类型"),
17
16
  disabled: bool = typer.Option(False, '--disabled', '-d', help="是否禁用"),
18
17
  ):
19
18
  '设置镜像'
20
- if not types:
21
- # types = [_MirrorsType.pip, _MirrorsType.npm]
22
- types = [_MirrorsType.pip]
23
- for targetType in types:
24
- data = _mirrorsFiles[targetType]
25
- for file, msgAry in data.items():
26
- if disabled:
27
- bpath.remove(file)
28
- bcolor.printRed('删除文件', file)
29
- else:
30
- print()
31
- bcolor.printYellow(file)
32
- msg = '\n'.join(msgAry)
33
- await bfile.writeText(file, msg)
34
- bcolor.printMagenta(msg)
35
-
36
-
37
- class _MirrorsType(StrEnum):
38
- pip = 'pip'
39
- # npm = 'npm'
40
-
41
-
42
- _mirrorsFiles = {
43
- _MirrorsType.pip: {
44
- bpath.user('pip/pip.ini'): [
45
- '[global]',
46
- 'index-url = https://mirrors.aliyun.com/pypi/simple',
47
- ],
48
- },
49
- # _MirrorsType.npm: {
50
- # bpath.user('.bashrc'): [
51
- # 'registry=https://registry.npm.taobao.org/',
52
- # 'electron_mirror=https://npm.taobao.org/mirrors/electron/',
53
- # ],
54
- # },
55
- }
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 ADDED
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+ from typing import Final
3
+
4
+ import typer
5
+ from beni import bcolor, binput, bpath, btask
6
+ from beni.bfunc import syncCall
7
+
8
+ from ..common.func import useResources
9
+ from .venv import add as venvAdd
10
+
11
+ app: Final = btask.app
12
+
13
+
14
+ @app.command('project')
15
+ @syncCall
16
+ async def _(
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
+ venvAdd(['benimang'], path)
33
+ with useResources('project') as sourceProjectPath:
34
+ bpath.copyOverwrite(sourceProjectPath, path)
bcmd/tasks/proxy.py CHANGED
@@ -2,10 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Final
4
4
 
5
+ import psutil
5
6
  import pyperclip
6
7
  import typer
7
8
  from beni import bcolor, btask
8
- from beni.bfunc import syncCall
9
+ from beni.bfunc import syncCall, textToAry
9
10
 
10
11
  app: Final = btask.app
11
12
 
@@ -13,15 +14,41 @@ app: Final = btask.app
13
14
  @app.command()
14
15
  @syncCall
15
16
  async def proxy(
16
- port: int = typer.Option(15236, help="代理服务器端口"),
17
+ port: int = typer.Argument(15236, help="代理服务器端口"),
17
18
  ):
18
19
  '生成终端设置代理服务器的命令'
19
- msg = '\r\n'.join([
20
- f'set http_proxy=http://localhost:{port}',
21
- f'set https_proxy=http://localhost:{port}',
22
- f'set all_proxy=http://localhost:{port}',
23
- '',
24
- ])
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, '不支持当前终端({processNameAry})')
34
+ lineAry = textToAry(template.format(port))
35
+ msg = '\n'.join(lineAry)
25
36
  bcolor.printMagenta('\r\n' + msg)
26
37
  pyperclip.copy(msg)
27
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(values: list[str] = typer.Argument(None)):
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
- values = values or []
27
- btask.check(len(values) <= 2, '参数过多')
28
- value1: str | None = values[0] if len(values) >= 1 else None
29
- value2: str | None = values[1] if len(values) >= 2 else 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
- color = typer.colors.BRIGHT_RED
46
- typer.secho('参数无效', fg=color)
47
- typer.secho('\n可使用格式: ', fg=color)
48
- msg_ary = str(showtime.__doc__).strip().replace('\n\n', '\n').split('\n')[1:]
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)