bcmd 0.5.2__py3-none-any.whl → 0.5.4__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,118 +1,118 @@
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 = \[(.*?)\]', 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 = \[(.*?)\]', 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
+ 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 = \[(.*?)\]', 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 = \[(.*?)\]', 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()
bcmd/tasks/math.py CHANGED
@@ -1,97 +1,97 @@
1
- from typing import Final
2
-
3
- import typer
4
- from beni import bcolor, btask
5
- from beni.bfunc import Counter, syncCall, toFloat
6
- from prettytable import PrettyTable
7
-
8
- app: Final = btask.newSubApp('math 工具集')
9
-
10
-
11
- @app.command()
12
- @syncCall
13
- async def scale(
14
- a: float = typer.Argument(..., help='原始数值'),
15
- b: float = typer.Argument(..., help='原始数值'),
16
- c: str = typer.Argument(..., help='数值 或 ?'),
17
- d: str = typer.Argument(..., help='数值 或 ?'),
18
- ):
19
- '按比例计算数值,例子:beni math scale 1 2 3 ?'
20
- if not ((c == '?') != (d == '?')):
21
- return bcolor.printRed('参数C和参数D必须有且仅有一个为?')
22
- print()
23
- table = PrettyTable(
24
- title=bcolor.yellow('按比例计算数值'),
25
- )
26
- if c == '?':
27
- dd = toFloat(d)
28
- cc = a * dd / b
29
- table.add_rows([
30
- ['A', a, bcolor.magenta(str(cc)), bcolor.magenta('C')],
31
- ['B', b, dd, 'D'],
32
- ])
33
- elif d == '?':
34
- cc = toFloat(c)
35
- dd = b * cc / a
36
- table.add_rows([
37
- ['A', a, cc, 'C'],
38
- ['B', b, bcolor.magenta(str(dd)), bcolor.magenta('D')],
39
- ])
40
- print(table.get_string(header=False))
41
-
42
-
43
- @app.command()
44
- @syncCall
45
- async def discount(
46
- values: list[str] = typer.Argument(..., help='每组数据使用#作为分隔符,注意后面的数据不能为0,例:123#500'),
47
- ):
48
- '计算折扣,例子:beni math discount 123#500 130#550'
49
- btask.assertTrue(len(values) >= 2, '至少需要提供2组数据用作比较')
50
-
51
- class Data:
52
- def __init__(self, value: str):
53
- try:
54
- ary = [x.strip() for x in value.strip().split('#')]
55
- self.a = float(ary[0])
56
- self.b = float(ary[1])
57
- self.v = self.a / self.b
58
- self.discount = 0.0
59
- except:
60
- btask.abort(f'数据格式错误', value)
61
-
62
- datas = [Data(x) for x in values]
63
- table = PrettyTable(
64
- title=bcolor.yellow('计算折扣'),
65
- )
66
- vAry = [x.v for x in datas]
67
- minV = min(vAry)
68
- maxV = max(vAry)
69
- for data in datas:
70
- data.discount = -(maxV - data.v) / maxV
71
- table.add_column(
72
- '',
73
- [
74
- '前数据',
75
- '后数据',
76
- '单价',
77
- '折扣',
78
- ],
79
- )
80
- counter = Counter(-1)
81
- for data in datas:
82
- colorFunc = bcolor.white
83
- if data.v == minV:
84
- colorFunc = bcolor.green
85
- elif data.v == maxV:
86
- colorFunc = bcolor.red
87
- columns = [
88
- f'{data.a:,}',
89
- f'{data.b:,}',
90
- f'{data.v:,.3f}',
91
- f'{data.discount * 100:+,.3f}%' if data.discount else '',
92
- ]
93
- table.add_column(
94
- chr(65 + counter()),
95
- [colorFunc(x) for x in columns],
96
- )
97
- print(table.get_string())
1
+ from typing import Final
2
+
3
+ import typer
4
+ from beni import bcolor, btask
5
+ from beni.bfunc import Counter, syncCall, toFloat
6
+ from prettytable import PrettyTable
7
+
8
+ app: Final = btask.newSubApp('math 工具集')
9
+
10
+
11
+ @app.command()
12
+ @syncCall
13
+ async def scale(
14
+ a: float = typer.Argument(..., help='原始数值'),
15
+ b: float = typer.Argument(..., help='原始数值'),
16
+ c: str = typer.Argument(..., help='数值 或 ?'),
17
+ d: str = typer.Argument(..., help='数值 或 ?'),
18
+ ):
19
+ '按比例计算数值,例子:beni math scale 1 2 3 ?'
20
+ if not ((c == '?') != (d == '?')):
21
+ return bcolor.printRed('参数C和参数D必须有且仅有一个为?')
22
+ print()
23
+ table = PrettyTable(
24
+ title=bcolor.yellow('按比例计算数值'),
25
+ )
26
+ if c == '?':
27
+ dd = toFloat(d)
28
+ cc = a * dd / b
29
+ table.add_rows([
30
+ ['A', a, bcolor.magenta(str(cc)), bcolor.magenta('C')],
31
+ ['B', b, dd, 'D'],
32
+ ])
33
+ elif d == '?':
34
+ cc = toFloat(c)
35
+ dd = b * cc / a
36
+ table.add_rows([
37
+ ['A', a, cc, 'C'],
38
+ ['B', b, bcolor.magenta(str(dd)), bcolor.magenta('D')],
39
+ ])
40
+ print(table.get_string(header=False))
41
+
42
+
43
+ @app.command()
44
+ @syncCall
45
+ async def discount(
46
+ values: list[str] = typer.Argument(..., help='每组数据使用#作为分隔符,注意后面的数据不能为0,例:123#500'),
47
+ ):
48
+ '计算折扣,例子:beni math discount 123#500 130#550'
49
+ btask.assertTrue(len(values) >= 2, '至少需要提供2组数据用作比较')
50
+
51
+ class Data:
52
+ def __init__(self, value: str):
53
+ try:
54
+ ary = [x.strip() for x in value.strip().split('#')]
55
+ self.a = float(ary[0])
56
+ self.b = float(ary[1])
57
+ self.v = self.a / self.b
58
+ self.discount = 0.0
59
+ except:
60
+ btask.abort(f'数据格式错误', value)
61
+
62
+ datas = [Data(x) for x in values]
63
+ table = PrettyTable(
64
+ title=bcolor.yellow('计算折扣'),
65
+ )
66
+ vAry = [x.v for x in datas]
67
+ minV = min(vAry)
68
+ maxV = max(vAry)
69
+ for data in datas:
70
+ data.discount = -(maxV - data.v) / maxV
71
+ table.add_column(
72
+ '',
73
+ [
74
+ '前数据',
75
+ '后数据',
76
+ '单价',
77
+ '折扣',
78
+ ],
79
+ )
80
+ counter = Counter(-1)
81
+ for data in datas:
82
+ colorFunc = bcolor.white
83
+ if data.v == minV:
84
+ colorFunc = bcolor.green
85
+ elif data.v == maxV:
86
+ colorFunc = bcolor.red
87
+ columns = [
88
+ f'{data.a:,}',
89
+ f'{data.b:,}',
90
+ f'{data.v:,.3f}',
91
+ f'{data.discount * 100:+,.3f}%' if data.discount else '',
92
+ ]
93
+ table.add_column(
94
+ chr(65 + counter()),
95
+ [colorFunc(x) for x in columns],
96
+ )
97
+ print(table.get_string())
bcmd/tasks/mirror.py CHANGED
@@ -1,46 +1,46 @@
1
- from __future__ import annotations
2
-
3
- import platform
4
- from typing import Final
5
-
6
- import typer
7
- from beni import bcolor, bfile, bpath, btask
8
- from beni.bfunc import syncCall
9
-
10
- app: Final = btask.app
11
-
12
-
13
- @app.command()
14
- @syncCall
15
- async def mirror(
16
- disabled: bool = typer.Option(False, '--disabled', '-d', help="是否禁用"),
17
- ):
18
- '设置镜像'
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
- '''
1
+ from __future__ import annotations
2
+
3
+ import platform
4
+ from typing import Final
5
+
6
+ import typer
7
+ from beni import bcolor, bfile, bpath, btask
8
+ from beni.bfunc import syncCall
9
+
10
+ app: Final = btask.app
11
+
12
+
13
+ @app.command()
14
+ @syncCall
15
+ async def mirror(
16
+ disabled: bool = typer.Option(False, '--disabled', '-d', help="是否禁用"),
17
+ ):
18
+ '设置镜像'
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
@@ -1,34 +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)
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==now'], path)
33
+ with useResources('project') as sourceProjectPath:
34
+ bpath.copyOverwrite(sourceProjectPath, path)