bcmd 0.0.66__tar.gz → 0.2.8__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 (44) hide show
  1. bcmd-0.2.8/MANIFEST.in +1 -0
  2. bcmd-0.2.8/PKG-INFO +18 -0
  3. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/main.py +1 -1
  4. bcmd-0.2.8/bcmd/resources/project/.gitignore +3 -0
  5. bcmd-0.2.8/bcmd/resources/project/src/.vscode/launch.json +15 -0
  6. bcmd-0.2.8/bcmd/resources/project/src/.vscode/settings.json +7 -0
  7. bcmd-0.2.8/bcmd/resources/project/src/.vscode/tasks.json +68 -0
  8. bcmd-0.2.8/bcmd/resources/project/src/main.py +1 -0
  9. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/__init__.py +3 -3
  10. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/bin.py +1 -0
  11. bcmd-0.2.8/bcmd/tasks/code.py +100 -0
  12. bcmd-0.2.8/bcmd/tasks/download.py +57 -0
  13. bcmd-0.2.8/bcmd/tasks/image.py +54 -0
  14. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/lib.py +21 -12
  15. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/math.py +1 -48
  16. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/mirror.py +4 -1
  17. bcmd-0.2.8/bcmd/tasks/project.py +34 -0
  18. bcmd-0.2.8/bcmd/tasks/proxy.py +45 -0
  19. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/venv.py +11 -1
  20. bcmd-0.2.8/bcmd.egg-info/PKG-INFO +18 -0
  21. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd.egg-info/SOURCES.txt +8 -3
  22. bcmd-0.2.8/bcmd.egg-info/requires.txt +10 -0
  23. {bcmd-0.0.66 → bcmd-0.2.8}/pyproject.toml +11 -2
  24. bcmd-0.0.66/MANIFEST.in +0 -1
  25. bcmd-0.0.66/PKG-INFO +0 -10
  26. bcmd-0.0.66/bcmd/tasks/download.py +0 -39
  27. bcmd-0.0.66/bcmd/tasks/jwt.py +0 -87
  28. bcmd-0.0.66/bcmd/tasks/proxy.py +0 -27
  29. bcmd-0.0.66/bcmd/tasks/task.py +0 -144
  30. bcmd-0.0.66/bcmd/tasks/temp.py +0 -31
  31. bcmd-0.0.66/bcmd.egg-info/PKG-INFO +0 -10
  32. bcmd-0.0.66/bcmd.egg-info/requires.txt +0 -2
  33. {bcmd-0.0.66 → bcmd-0.2.8}/README.md +0 -0
  34. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/__init__.py +0 -0
  35. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/common/__init__.py +0 -0
  36. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/common/password.py +0 -0
  37. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/crypto.py +0 -0
  38. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/debian.py +0 -0
  39. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/json.py +0 -0
  40. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd/tasks/time.py +0 -0
  41. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd.egg-info/dependency_links.txt +0 -0
  42. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd.egg-info/entry_points.txt +0 -0
  43. {bcmd-0.0.66 → bcmd-0.2.8}/bcmd.egg-info/top_level.txt +0 -0
  44. {bcmd-0.0.66 → bcmd-0.2.8}/setup.cfg +0 -0
bcmd-0.2.8/MANIFEST.in ADDED
@@ -0,0 +1 @@
1
+ recursive-include bcmd/resources *
bcmd-0.2.8/PKG-INFO ADDED
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.1
2
+ Name: bcmd
3
+ Version: 0.2.8
4
+ Summary: Commands for Beni
5
+ Author-email: Beni Mang <benimang@126.com>
6
+ Maintainer-email: Beni Mang <benimang@126.com>
7
+ Keywords: benimang,beni,bcmd
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: benimang==0.6.8
10
+ Requires-Dist: build
11
+ Requires-Dist: cryptography
12
+ Requires-Dist: pathspec
13
+ Requires-Dist: pillow
14
+ Requires-Dist: prettytable
15
+ Requires-Dist: psutil
16
+ Requires-Dist: qiniu==7.13.2
17
+ Requires-Dist: twine
18
+ Requires-Dist: typer
@@ -6,5 +6,5 @@ from .tasks import *
6
6
 
7
7
 
8
8
  def run():
9
- btask.options.lock = ''
9
+ btask.options.lock = 0
10
10
  asyncio.run(btask.main())
@@ -0,0 +1,3 @@
1
+ __pycache__
2
+ *~$*
3
+ venv/
@@ -0,0 +1,15 @@
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+ {
8
+ "name": "Python: main.py",
9
+ "type": "debugpy",
10
+ "request": "launch",
11
+ // "console": "internalConsole",
12
+ "program": "${workspaceFolder}/main.py"
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "files.exclude": {
3
+ ".pytest_cache": true,
4
+ "**/__pycache__": true,
5
+ },
6
+ "python.defaultInterpreterPath": "${workspaceFolder}/../venv/Scripts/python.exe",
7
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "git commit",
6
+ "problemMatcher": [],
7
+ "command": "TortoiseGitProc.exe",
8
+ "args": [
9
+ "/command:commit",
10
+ "/path:${workspaceFolder}/../",
11
+ ],
12
+ },
13
+ {
14
+ "label": "git commit file",
15
+ "problemMatcher": [],
16
+ "command": "TortoiseGitProc.exe",
17
+ "args": [
18
+ "/command:commit",
19
+ "/path:${file}",
20
+ ],
21
+ },
22
+ {
23
+ "label": "git revert",
24
+ "problemMatcher": [],
25
+ "command": "TortoiseGitProc.exe",
26
+ "args": [
27
+ "/command:revert",
28
+ "/path:${workspaceFolder}/../",
29
+ ],
30
+ },
31
+ {
32
+ "label": "git revert file",
33
+ "problemMatcher": [],
34
+ "command": "TortoiseGitProc.exe",
35
+ "args": [
36
+ "/command:revert",
37
+ "/path:${file}",
38
+ ],
39
+ },
40
+ {
41
+ "label": "git sync",
42
+ "problemMatcher": [],
43
+ "command": "TortoiseGitProc.exe",
44
+ "args": [
45
+ "/command:sync",
46
+ "/path:${workspaceFolder}/../",
47
+ ],
48
+ },
49
+ {
50
+ "label": "git log",
51
+ "problemMatcher": [],
52
+ "command": "TortoiseGitProc.exe",
53
+ "args": [
54
+ "/command:log",
55
+ "/path:${workspaceFolder}/../",
56
+ ],
57
+ },
58
+ {
59
+ "label": "git log file",
60
+ "problemMatcher": [],
61
+ "command": "TortoiseGitProc.exe",
62
+ "args": [
63
+ "/command:log",
64
+ "/path:${file}",
65
+ ],
66
+ },
67
+ ],
68
+ }
@@ -0,0 +1 @@
1
+ print('OK')
@@ -1,15 +1,15 @@
1
1
  # type: ignore
2
2
  from . import bin
3
+ from . import code
3
4
  from . import crypto
4
5
  from . import debian
5
6
  from . import download
7
+ from . import image
6
8
  from . import json
7
- from . import jwt
8
9
  from . import lib
9
10
  from . import math
10
11
  from . import mirror
12
+ from . import project
11
13
  from . import proxy
12
- from . import task
13
- from . import temp
14
14
  from . import time
15
15
  from . import venv
@@ -55,6 +55,7 @@ async def getList():
55
55
  bucket = await _getBucket()
56
56
  datas = (await bucket.getFileList(_PREFIX, limit=1000))[0]
57
57
  datas = [x for x in datas if x.key != _PREFIX and x.key.endswith('.zip')]
58
+ datas.sort(key=lambda x: x.time, reverse=True)
58
59
  table = PrettyTable()
59
60
  table.add_column(
60
61
  bcolor.yellow('文件名称'),
@@ -0,0 +1,100 @@
1
+ from pathlib import Path
2
+ from typing import Final
3
+
4
+ import typer
5
+ from beni import bfile, bpath, btask
6
+ from beni.bcolor import printGreen, printMagenta, printYellow
7
+ from beni.bfunc import syncCall
8
+
9
+ app: Final = btask.newSubApp('code 工具')
10
+
11
+
12
+ @app.command()
13
+ @syncCall
14
+ async def tidy_tasks(
15
+ tasks_path: Path = typer.Argument(None, help="tasks 路径"),
16
+ ):
17
+ '整理 task 项目中的 tasks/__init__.py'
18
+ if not tasks_path:
19
+ tasks_path = Path.cwd()
20
+ initFile = tasks_path / '__init__.py'
21
+ btask.check(initFile.is_file(), '文件不存在', initFile)
22
+ files = bpath.listFile(tasks_path)
23
+ files = [x for x in files if not x.name.startswith('_')]
24
+ contents = [f'from . import {x.stem}' for x in files]
25
+ contents.insert(0, '# type: ignore')
26
+ contents.append('')
27
+ content = '\n'.join(contents)
28
+ oldContent = await bfile.readText(initFile)
29
+ if oldContent != content:
30
+ await bfile.writeText(
31
+ initFile,
32
+ content,
33
+ )
34
+ printYellow(initFile)
35
+ printMagenta(content)
36
+ printGreen('OK')
37
+
38
+
39
+ @app.command()
40
+ @syncCall
41
+ async def tidy_modules(
42
+ modules_path: Path = typer.Argument(None, help="modules_path 路径"),
43
+ ):
44
+ '整理 fastapi 项目中的 modules/__init__.py'
45
+ if not modules_path:
46
+ modules_path = Path.cwd()
47
+ importContents: list[str] = []
48
+ managerContents: list[str] = []
49
+
50
+ xxdict: dict[str, set[Path]] = {}
51
+ for file in sorted(modules_path.glob('**/*Manager.py')):
52
+ if file.parent == modules_path:
53
+ subName = '.'
54
+ elif file.parent.parent == modules_path:
55
+ subName = f'.{file.parent.stem}'
56
+ else:
57
+ continue
58
+ xxdict.setdefault(subName, set()).add(file)
59
+ for subName in sorted(xxdict.keys()):
60
+ files = sorted(xxdict[subName])
61
+ importContents.append(f'from {subName} import {", ".join([x.stem for x in files])}')
62
+ managerContents.extend([f' {x.stem},' for x in sorted([y for x in xxdict.values() for y in x])])
63
+
64
+ managerContents = [x for x in managerContents if x]
65
+ contents = [
66
+ '\n'.join(importContents),
67
+ 'managers = [\n' + '\n'.join(managerContents) + '\n]',
68
+ ]
69
+ content = '\n\n'.join(contents) + '\n'
70
+ file = modules_path / '__init__.py'
71
+ printYellow(str(file))
72
+ printMagenta(content)
73
+ await bfile.writeText(file, content)
74
+ printGreen('OK')
75
+
76
+
77
+ @app.command()
78
+ @syncCall
79
+ async def gen_init(
80
+ target_path: Path = typer.Argument(None, help="tasks 路径"),
81
+ ):
82
+ '生成 __init__.py 文件'
83
+ if not target_path:
84
+ target_path = Path.cwd()
85
+
86
+ async def makeInitFiles(p: Path):
87
+ if p.name == '__pycache__':
88
+ return
89
+ if p.name.startswith('.'):
90
+ return
91
+ if target_path != p:
92
+ initFile = p.joinpath('__init__.py')
93
+ if not initFile.exists():
94
+ printYellow('生成文件', initFile)
95
+ await bfile.writeText(initFile, '')
96
+ for x in bpath.listDir(p):
97
+ await makeInitFiles(x)
98
+
99
+ await makeInitFiles(target_path)
100
+ printGreen('OK')
@@ -0,0 +1,57 @@
1
+ import asyncio
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Final
5
+ from urllib.parse import urlparse
6
+ from uuid import uuid4
7
+
8
+ import pyperclip
9
+ import typer
10
+ from beni import bcolor, bfile, bhttp, binput, bpath, btask
11
+ from beni.bfunc import syncCall
12
+
13
+ app: Final = btask.app
14
+
15
+
16
+ @app.command()
17
+ @syncCall
18
+ async def download(
19
+ url_file: Path = typer.Option(None, '--file', '-f', help='需要下载的url文件路径,默认使用剪贴板内容'),
20
+ save_path: Path = typer.Option(None, '--path', '-p', help='下载存放目录,默认当前目录'),
21
+ keep_directory: bool = typer.Option(False, '--keep', '-k', help='保持原始目录结构,默认不保持'),
22
+ ):
23
+ '下载资源资源文件'
24
+ save_path = save_path or Path(os.getcwd())
25
+
26
+ if url_file:
27
+ if not url_file.exists():
28
+ btask.abort('指定文件不存在', url_file)
29
+ content = await bfile.readText(url_file)
30
+ else:
31
+ content = pyperclip.paste()
32
+ urlSet = set([x.strip() for x in content.strip().split('\n') if x.strip()])
33
+
34
+ for i, url in enumerate(urlSet):
35
+ print(f'{i + 1}. {url}')
36
+ print(f'输出目录:{save_path}')
37
+ await binput.confirm('是否确认?')
38
+
39
+ fileSet: set[Path] = set()
40
+
41
+ async def download(url: str):
42
+ urlPath = urlparse(url).path
43
+ if keep_directory:
44
+ file = bpath.get(save_path, '/'.join([x for x in urlPath.split('/') if x]))
45
+ else:
46
+ file = save_path / Path(urlPath).name
47
+ if file in fileSet:
48
+ file = file.with_stem(f'{file.stem}--{uuid4()}')
49
+ fileSet.add(file)
50
+ try:
51
+ bcolor.printGreen(url)
52
+ await bhttp.download(url, file)
53
+ except:
54
+ bcolor.printRed(url)
55
+
56
+ await asyncio.gather(*[download(x) for x in urlSet])
57
+ bcolor.printYellow('Done')
@@ -0,0 +1,54 @@
1
+ import os
2
+ from enum import StrEnum
3
+ from pathlib import Path
4
+ from typing import Final
5
+
6
+ import typer
7
+ from beni import bcolor, bfile, bpath, btask
8
+ from beni.bfunc import syncCall
9
+ from PIL import Image
10
+
11
+ app: Final = btask.newSubApp('图片工具集')
12
+
13
+
14
+ class _OutputType(StrEnum):
15
+ '输出类型'
16
+ normal = '0'
17
+ replace_ = '1'
18
+ crc_replace = '2'
19
+
20
+
21
+ @app.command()
22
+ @syncCall
23
+ async def convert(
24
+ path: Path = typer.Option(None, '--path', '-p', help='指定目录或具体图片文件,默认当前目录'),
25
+ src_format: str = typer.Option('jpg|jpeg|png', '--src-format', '-s', help='如果path是目录,指定源格式,可以指定多个,默认值:jpg|jpeg|png'),
26
+ dst_format: str = typer.Option('webp', '--dst-format', '-d', help='目标格式,只能是单个'),
27
+ rgb: bool = typer.Option(False, '--rgb', help='转换为RGB格式'),
28
+ quality: int = typer.Option(85, '--quality', '-q', help='图片质量,0-100,默认85'),
29
+ output_type: _OutputType = typer.Option(_OutputType.normal, '--output-type', help='输出类型,0:普通输出,1:删除源文件,2:输出文件使用CRC32命名并删除源文件'),
30
+ ):
31
+ '图片格式转换'
32
+ path = path or Path(os.getcwd())
33
+ fileList: list[Path] = []
34
+ if path.is_file():
35
+ fileList.append(path)
36
+ elif path.is_dir():
37
+ extNameList = [x for x in src_format.strip().split('|')]
38
+ fileList = [x for x in bpath.listFile(path, True) if x.suffix[1:].lower() in extNameList]
39
+ if not fileList:
40
+ return bcolor.printRed(f'未找到图片文件({path})')
41
+ for file in fileList:
42
+ with Image.open(file) as img:
43
+ if rgb:
44
+ img = img.convert('RGB')
45
+ with bpath.useTempFile() as tempFile:
46
+ img.save(tempFile, format=dst_format, quality=quality)
47
+ outputFile = file.with_suffix(f'.{dst_format}')
48
+ if output_type == _OutputType.crc_replace:
49
+ outputFile = outputFile.with_stem(await bfile.crc(tempFile))
50
+ bpath.copy(tempFile, outputFile)
51
+ if output_type in [_OutputType.replace_, _OutputType.crc_replace]:
52
+ if outputFile != file:
53
+ bpath.remove(file)
54
+ bcolor.printGreen(f'{file} -> {outputFile}')
@@ -14,7 +14,7 @@ app: Final = btask.newSubApp('lib 工具')
14
14
 
15
15
  @app.command()
16
16
  @syncCall
17
- async def tidy(
17
+ async def tidy_dependencies(
18
18
  workspace_path: Path = typer.Argument(None, help='workspace 路径'),
19
19
  with_version: bool = typer.Option(False, '--with-version', help='是否带版本号')
20
20
  ):
@@ -26,9 +26,12 @@ async def tidy(
26
26
  targetVenvFileName = 'venv.lock' if with_version else 'venv.list'
27
27
  targetVenvFile = bpath.get(workspace_path, f'./../{targetVenvFileName}')
28
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])
29
+ libAry = (await bfile.readText(targetVenvFile)).strip().replace('\r\n', '\n').split('\n')
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,15 @@ async def tidy(
42
45
 
43
46
  @app.command()
44
47
  @syncCall
45
- async def version(
48
+ async def update_version(
46
49
  workspace_path: Path = typer.Argument(None, help='workspace 路径'),
47
50
  disabled_commit: bool = typer.Option(False, '--disabled-commit', '-d', help='是否提交git'),
48
- with_dependencies_version: bool = typer.Option(False, '--with_dependencies_version', help='第三方库是否带版本号')
49
51
  ):
50
- '修改 pyproject.toml 版本号(同时会更新依赖列表)'
52
+ '修改 pyproject.toml 版本号'
51
53
  if not workspace_path:
52
54
  workspace_path = Path.cwd()
53
55
  file = workspace_path / 'pyproject.toml'
54
56
  btask.check(file.is_file(), '文件不存在', file)
55
- isChangeDependencies = tidy(workspace_path, with_dependencies_version)
56
57
  data = await bfile.readToml(file)
57
58
  version = data['project']['version']
58
59
  versionList = [int(x) for x in version.split('.')]
@@ -68,10 +69,7 @@ async def version(
68
69
  await bfile.writeText(file, content)
69
70
  bcolor.printCyan(newVersion)
70
71
  if not disabled_commit:
71
- msg = ''.join([
72
- f'更新版本号 {newVersion}',
73
- '(注意:本次有修改依赖)' if isChangeDependencies else '',
74
- ]).strip()
72
+ msg = f'更新版本号 {newVersion}'
75
73
  os.system(
76
74
  rf'TortoiseGitProc.exe /command:commit /path:{file} /logmsg:"{msg}"'
77
75
  )
@@ -80,7 +78,7 @@ async def version(
80
78
 
81
79
  @app.command()
82
80
  @syncCall
83
- async def publish(
81
+ async def build(
84
82
  src_path: Path = typer.Argument(None, help='src 路径'),
85
83
  keep_build_files: bool = typer.Option(False, '--keep-build-files', '-k', help='是否保留构建文件'),
86
84
  ):
@@ -110,3 +108,14 @@ async def publish(
110
108
  finally:
111
109
  if not keep_build_files:
112
110
  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)])
@@ -1,4 +1,3 @@
1
- from math import nan
2
1
  from typing import Final
3
2
 
4
3
  import typer
@@ -41,52 +40,6 @@ 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(
@@ -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()),
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import platform
3
4
  from enum import StrEnum
4
5
  from typing import Final
5
6
 
@@ -39,9 +40,11 @@ class _MirrorsType(StrEnum):
39
40
  # npm = 'npm'
40
41
 
41
42
 
43
+ _isWindows = platform.system() == 'Windows'
44
+
42
45
  _mirrorsFiles = {
43
46
  _MirrorsType.pip: {
44
- bpath.user('pip/pip.ini'): [
47
+ bpath.user('pip/pip.ini') if _isWindows else bpath.user('.pip/pip.conf'): [
45
48
  '[global]',
46
49
  'index-url = https://mirrors.aliyun.com/pypi/simple',
47
50
  ],
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+ from typing import Final
3
+
4
+ import typer
5
+ from beni import bcolor, bfile, bpath, btask
6
+ from beni.bfunc import syncCall
7
+
8
+ app: Final = btask.newSubApp('project 工具')
9
+
10
+
11
+ @app.command()
12
+ @syncCall
13
+ async def gen_init_py(
14
+ workspace_path: Path = typer.Argument(None, help='workspace 路径'),
15
+ ):
16
+ '将指定目录下的所有文件生成 __init__.py 文件'
17
+
18
+ async def makeInitFiles(p: Path):
19
+ if p.name == '__pycache__':
20
+ return
21
+ if p.name.startswith('.'):
22
+ 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
+
31
+ if not workspace_path:
32
+ workspace_path = Path.cwd()
33
+ await makeInitFiles(workspace_path)
34
+ bcolor.printGreen('OK')
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Final
4
+
5
+ import psutil
6
+ import pyperclip
7
+ import typer
8
+ from beni import bcolor, btask
9
+ from beni.bfunc import syncCall
10
+
11
+ app: Final = btask.app
12
+
13
+
14
+ @app.command()
15
+ @syncCall
16
+ async def proxy(
17
+ port: int = typer.Option(15236, help="代理服务器端口"),
18
+ ):
19
+ '生成终端设置代理服务器的命令'
20
+ processNameAry = []
21
+ process = psutil.Process().parent()
22
+ while process:
23
+ processNameAry.append(process.name())
24
+ process = process.parent()
25
+ msg = ''
26
+ if 'cmd.exe' in processNameAry:
27
+ msg = '\r\n'.join([
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
+ ])
33
+ elif set(['powershell.exe', 'pwsh.exe']) & set(processNameAry):
34
+ msg = '\r\n'.join([
35
+ f'$env:http_proxy="http://localhost:{port}"',
36
+ f'$env:https_proxy="http://localhost:{port}"',
37
+ f'$env:all_proxy="http://localhost:{port}"',
38
+ '',
39
+ ])
40
+ if msg:
41
+ bcolor.printMagenta('\r\n' + msg)
42
+ pyperclip.copy(msg)
43
+ bcolor.printYellow('已复制,可直接粘贴使用')
44
+ else:
45
+ bcolor.printRed(f'不支持当前终端({processNameAry})')
@@ -1,10 +1,11 @@
1
+ import importlib.resources
1
2
  import os
2
3
  import sys
3
4
  from pathlib import Path
4
5
  from typing import Final
5
6
 
6
7
  import typer
7
- from beni import bexecute, bfile, bhttp, binput, bpath, btask
8
+ from beni import bcolor, bexecute, bfile, bhttp, binput, bpath, btask
8
9
  from beni.bfunc import syncCall
9
10
  from beni.btype import Null
10
11
 
@@ -21,6 +22,7 @@ async def venv(
21
22
  packages: list[str] = typer.Argument(None),
22
23
  path: Path = typer.Option(None, '--path', '-p', help='指定路径,默认当前目录'),
23
24
  disabled_mirror: bool = typer.Option(False, '--disabled-mirror', '-d', help='是否禁用镜像'),
25
+ new_project: bool = typer.Option(False, '--new-project', '-n', help='是否新建项目'),
24
26
  quiet: bool = typer.Option(False, '--quiet', '-q', help='是否安静模式'),
25
27
  ):
26
28
  'python 虚拟环境配置'
@@ -65,6 +67,14 @@ async def venv(
65
67
  file=binListFile,
66
68
  output=binPath,
67
69
  )
70
+ # 新建项目
71
+ if new_project:
72
+ with bpath.changePath(path):
73
+ await bexecute.run('beni venv --quiet')
74
+ with importlib.resources.path('bcmd.resources', 'project') as sourceProjectPath:
75
+ for p in bpath.listDir(sourceProjectPath):
76
+ bpath.copy(p, path / p.name)
77
+ bcolor.printGreen('OK')
68
78
 
69
79
 
70
80
  async def pipInstall(pip: Path, file: Path, disabled_mirror: bool):
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.1
2
+ Name: bcmd
3
+ Version: 0.2.8
4
+ Summary: Commands for Beni
5
+ Author-email: Beni Mang <benimang@126.com>
6
+ Maintainer-email: Beni Mang <benimang@126.com>
7
+ Keywords: benimang,beni,bcmd
8
+ Requires-Python: >=3.10
9
+ Requires-Dist: benimang==0.6.8
10
+ Requires-Dist: build
11
+ Requires-Dist: cryptography
12
+ Requires-Dist: pathspec
13
+ Requires-Dist: pillow
14
+ Requires-Dist: prettytable
15
+ Requires-Dist: psutil
16
+ Requires-Dist: qiniu==7.13.2
17
+ Requires-Dist: twine
18
+ Requires-Dist: typer
@@ -11,18 +11,23 @@ bcmd.egg-info/requires.txt
11
11
  bcmd.egg-info/top_level.txt
12
12
  bcmd/common/__init__.py
13
13
  bcmd/common/password.py
14
+ bcmd/resources/project/.gitignore
15
+ bcmd/resources/project/src/main.py
16
+ bcmd/resources/project/src/.vscode/launch.json
17
+ bcmd/resources/project/src/.vscode/settings.json
18
+ bcmd/resources/project/src/.vscode/tasks.json
14
19
  bcmd/tasks/__init__.py
15
20
  bcmd/tasks/bin.py
21
+ bcmd/tasks/code.py
16
22
  bcmd/tasks/crypto.py
17
23
  bcmd/tasks/debian.py
18
24
  bcmd/tasks/download.py
25
+ bcmd/tasks/image.py
19
26
  bcmd/tasks/json.py
20
- bcmd/tasks/jwt.py
21
27
  bcmd/tasks/lib.py
22
28
  bcmd/tasks/math.py
23
29
  bcmd/tasks/mirror.py
30
+ bcmd/tasks/project.py
24
31
  bcmd/tasks/proxy.py
25
- bcmd/tasks/task.py
26
- bcmd/tasks/temp.py
27
32
  bcmd/tasks/time.py
28
33
  bcmd/tasks/venv.py
@@ -0,0 +1,10 @@
1
+ benimang==0.6.8
2
+ build
3
+ cryptography
4
+ pathspec
5
+ pillow
6
+ prettytable
7
+ psutil
8
+ qiniu==7.13.2
9
+ twine
10
+ typer
@@ -3,7 +3,7 @@
3
3
 
4
4
  [project]
5
5
  name = 'bcmd'
6
- version = '0.0.66'
6
+ version = '0.2.8'
7
7
  description = 'Commands for Beni'
8
8
  requires-python = '>=3.10'
9
9
  keywords = ['benimang', 'beni', 'bcmd']
@@ -12,8 +12,17 @@ maintainers = [{ name = 'Beni Mang', email = 'benimang@126.com' }]
12
12
 
13
13
 
14
14
  dependencies = [
15
- 'benimang==0.5.18',
15
+ 'benimang==0.6.8',
16
+ 'build',
17
+ 'cryptography',
16
18
  'pathspec',
19
+ 'pillow',
20
+ 'prettytable',
21
+ 'psutil',
22
+ 'qiniu==7.13.2',
23
+ 'twine',
24
+ 'typer',
25
+ # 'setuptools',
17
26
  ]
18
27
 
19
28
  [project.scripts]
bcmd-0.0.66/MANIFEST.in DELETED
@@ -1 +0,0 @@
1
- recursive-include bcmd/data *
bcmd-0.0.66/PKG-INFO DELETED
@@ -1,10 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: bcmd
3
- Version: 0.0.66
4
- Summary: Commands for Beni
5
- Author-email: Beni Mang <benimang@126.com>
6
- Maintainer-email: Beni Mang <benimang@126.com>
7
- Keywords: benimang,beni,bcmd
8
- Requires-Python: >=3.10
9
- Requires-Dist: benimang==0.5.18
10
- Requires-Dist: pathspec
@@ -1,39 +0,0 @@
1
- import asyncio
2
- import os
3
- from pathlib import Path
4
- from typing import Final
5
-
6
- import pyperclip
7
- import typer
8
- from beni import bcolor, bhttp, binput, bpath, btask
9
- from beni.bfunc import syncCall
10
-
11
- app: Final = btask.newSubApp('下载')
12
-
13
-
14
- @app.command()
15
- @syncCall
16
- async def urls(
17
- path: Path = typer.Option(None, '--path', '-p', help='指定路径,默认当前目录'),
18
- ):
19
- '下载文件'
20
- path = path or Path(os.getcwd())
21
- content = pyperclip.paste()
22
- urlSet = set([x.strip() for x in content.strip().split('\n') if x.strip()])
23
-
24
- for i, url in enumerate(urlSet):
25
- print(f'{i + 1}. {url}')
26
- print(f'输出目录:{path}')
27
- await binput.confirm('是否确认?')
28
-
29
- async def download(url: str):
30
- file = bpath.get(path, '/'.join([x for x in url.replace('://', '/').split('/') if x]))
31
- try:
32
- bcolor.printGreen(url)
33
- await bhttp.download(url, file)
34
- except:
35
- bcolor.printRed(url)
36
-
37
- await asyncio.gather(*[download(x) for x in urlSet])
38
-
39
- bcolor.printYellow('Done')
@@ -1,87 +0,0 @@
1
- import getpass
2
- import json
3
- from typing import Final
4
-
5
- import pyperclip
6
- from beni import bcolor, bjwt, btask
7
- from beni.bfunc import syncCall
8
- from rich.console import Console
9
-
10
- app: Final = btask.newSubApp('JWT密文')
11
-
12
-
13
- @app.command()
14
- @syncCall
15
- async def encode_json():
16
- '生成JSON密文(使用剪贴板内容)'
17
- content = pyperclip.paste()
18
- try:
19
- data = json.loads(content)
20
- except:
21
- return btask.abort('错误:剪贴板内容必须是JSON格式', content)
22
- Console().print_json(data=data, indent=4, ensure_ascii=False, sort_keys=True)
23
- secret, tips = _genSecret()
24
- result = bjwt.encodeJson(data, secret, tips)
25
- pyperclip.copy(result)
26
- print('密文已复制到剪贴板')
27
- bcolor.printYellow(result)
28
-
29
-
30
- @app.command()
31
- @syncCall
32
- async def encode_text():
33
- '生成文本密文(使用剪贴板内容)'
34
- content = pyperclip.paste()
35
- assert content, '剪贴板内容不能为空'
36
- bcolor.printGreen(content)
37
- secret, tips = _genSecret()
38
- result = bjwt.encodeText(content, secret, tips)
39
- pyperclip.copy(result)
40
- print('密文已复制到剪贴板')
41
- bcolor.printYellow(result)
42
-
43
-
44
- @app.command()
45
- @syncCall
46
- async def decode_json():
47
- '还原JSON密文内容(使用剪贴板内容)'
48
- content = pyperclip.paste().strip()
49
- bcolor.printYellow(content)
50
- while True:
51
- try:
52
- password = getpass.getpass('输入密码:')
53
- data = bjwt.decodeJson(content, password)
54
- Console().print_json(data=data, indent=4, ensure_ascii=False, sort_keys=True)
55
- return
56
- except KeyboardInterrupt:
57
- break
58
- except BaseException:
59
- pass
60
-
61
-
62
- @app.command()
63
- @syncCall
64
- async def decode_text():
65
- '还原文本密文内容(使用剪贴板内容)'
66
- content = pyperclip.paste().strip()
67
- bcolor.printYellow(content)
68
- while True:
69
- try:
70
- password = getpass.getpass('输入密码:')
71
- data = bjwt.decodeText(content, password)
72
- bcolor.printGreen(data)
73
- return
74
- except KeyboardInterrupt:
75
- break
76
- except BaseException:
77
- pass
78
-
79
-
80
- def _genSecret():
81
- secret = ''
82
- while not secret:
83
- secret = getpass.getpass('输入密码:')
84
- while secret != getpass.getpass('再次密码:'):
85
- pass
86
- tips = input('密码提示(可选):')
87
- return secret, tips
@@ -1,27 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Final
4
-
5
- import pyperclip
6
- import typer
7
- from beni import bcolor, btask
8
- from beni.bfunc import syncCall
9
-
10
- app: Final = btask.app
11
-
12
-
13
- @app.command()
14
- @syncCall
15
- async def proxy(
16
- port: int = typer.Option(15236, help="代理服务器端口"),
17
- ):
18
- '生成终端设置代理服务器的命令'
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
- ])
25
- bcolor.printMagenta('\r\n' + msg)
26
- pyperclip.copy(msg)
27
- bcolor.printYellow('已复制,可直接粘贴使用')
@@ -1,144 +0,0 @@
1
- from pathlib import Path
2
- from typing import Final
3
-
4
- import pathspec
5
- import typer
6
- from beni import bcolor, bfile, binput, bpath, btask, btime, bzip
7
- from beni.bfunc import syncCall, toAny, tryRun
8
- from beni.bqiniu import QiniuBucket
9
- from beni.btype import Null
10
-
11
- from bcmd.common import password
12
-
13
- from . import bin, venv
14
-
15
- app: Final = btask.newSubApp('BTask 工具')
16
-
17
-
18
- _PREFIX = 'template/'
19
-
20
-
21
- @app.command()
22
- @syncCall
23
- async def template_gen(
24
- tempalte_name: str = typer.Argument(..., help="模板名称"),
25
- project_path: Path = typer.Argument(None, help="用于生成模板的文件夹路径"),
26
- ):
27
- '生成模板'
28
- with bpath.useTempPath() as tempPath:
29
- project_path = bpath.get(project_path)
30
- await _copyTemplateFiles(project_path, tempPath)
31
- bucket = await _getBucket()
32
- fileList, _ = await bucket.getFileList(f'{_PREFIX}{tempalte_name}/', 200)
33
- num = len(fileList)
34
- if num > 100:
35
- bcolor.printYellow(f'当前模板的版本数量已经超过 {num} 个,建议删除一些不常用的版本')
36
- elif num > 180:
37
- btask.abort(f'当前模板的版本数量已经超过 {num} 个,无法继续添加')
38
- with bpath.useTempFile() as tempFile:
39
- bzip.zipFolder(tempFile, tempPath)
40
- nowTime = await btime.networkTime()
41
- key = f'{_PREFIX}{tempalte_name}/{tempalte_name}_{nowTime.strftime("%Y%m%d_%H%M%S")}.zip'
42
- with tryRun():
43
- await bucket.deleteFiles(key)
44
- await bucket.uploadFile(key, tempFile)
45
- bcolor.printGreen('OK')
46
-
47
-
48
- async def _copyTemplateFiles(src: Path, dst: Path):
49
- spec = pathspec.PathSpec.from_lines(
50
- toAny(pathspec.patterns).GitWildMatchPattern,
51
- (await bfile.readText(src / '.gitignore')).splitlines() + ['.git'],
52
- )
53
- files = bpath.listFile(src, True)
54
- files = [x for x in files if not spec.match_file(x.relative_to(src))]
55
- for file in sorted(files):
56
- toFile = bpath.changeRelative(file, src, dst)
57
- bpath.copy(file, toFile)
58
-
59
-
60
- @app.command()
61
- @syncCall
62
- async def create(
63
- tempalte_name: str = typer.Argument(..., help="模板名称"),
64
- project_path: Path = typer.Argument(None, help="项目路径,不填标识在当前目录创建"),
65
- ):
66
- '创建项目'
67
- if not project_path:
68
- project_path = Path.cwd()
69
- project_path = bpath.get(project_path)
70
- if project_path.exists():
71
- await binput.confirm(f'项目路径 {project_path} 已存在,是否覆盖?')
72
- if Path(tempalte_name).is_absolute():
73
- btask.abort(f'模板名称不能为绝对路径:{tempalte_name}')
74
- bucket = await _getBucket()
75
- fileList, _ = await bucket.getFileList(f'{_PREFIX}{tempalte_name}/', 1000)
76
- if not fileList:
77
- btask.abort('模板不存在')
78
- fileList.sort(key=lambda x: x.key)
79
- with bpath.useTempPath(True) as tempPath:
80
- key = fileList[-1].key
81
- bcolor.printGreen(f'正在使用版本 {key}')
82
- await bucket.downloadPrivateFileUnzip(key, tempPath)
83
- for item in bpath.listPath(tempPath):
84
- toItem = project_path / item.name
85
- bpath.copy(item, toItem)
86
- init(project_path)
87
-
88
-
89
- @app.command()
90
- @syncCall
91
- async def init(
92
- project_path: Path = typer.Argument(None, help="项目路径"),
93
- ):
94
- '初始化 BTask 项目,包括 venv 和 bin 操作'
95
- if not project_path:
96
- project_path = Path.cwd()
97
- fileList = bpath.listFile(project_path, True)
98
- for file in fileList:
99
- if file.name == 'venv.list':
100
- targetPath = file.parent
101
- venv.venv(
102
- packages=Null,
103
- path=targetPath,
104
- disabled_mirror=False,
105
- quiet=True,
106
- )
107
- binListFile = targetPath / 'bin.list'
108
- if binListFile.is_file():
109
- bin.download(
110
- names=Null,
111
- file=binListFile,
112
- output=targetPath / 'bin',
113
- )
114
-
115
-
116
- @app.command()
117
- @syncCall
118
- async def tidy(
119
- tasks_path: Path = typer.Argument(None, help="tasks 路径"),
120
- ):
121
- '整理 tasks 文件'
122
- initFile = tasks_path / '__init__.py'
123
- btask.check(initFile.is_file(), '文件不存在', initFile)
124
- files = bpath.listFile(tasks_path)
125
- files = [x for x in files if not x.name.startswith('_')]
126
- contents = [f'from . import {x.stem}' for x in files]
127
- contents.insert(0, '# type: ignore')
128
- contents.append('')
129
- content = '\n'.join(contents)
130
- await bfile.writeText(
131
- initFile,
132
- content,
133
- )
134
- print(content)
135
-
136
-
137
- async def _getBucket():
138
- ak, sk = await password.getQiniu()
139
- return QiniuBucket(
140
- 'pytask',
141
- 'http://qiniu-cdn.pytask.com',
142
- ak,
143
- sk,
144
- )
@@ -1,31 +0,0 @@
1
- from pathlib import Path
2
- from typing import Final
3
-
4
- import typer
5
- from beni import bcolor, bpath, btask
6
- from beni.bfunc import syncCall
7
- from beni.bqiniu import QiniuBucket
8
-
9
- from bcmd.common import password
10
-
11
- app: Final = btask.newSubApp('temp 工具')
12
-
13
-
14
- @app.command()
15
- @syncCall
16
- async def upload_qiniu(
17
- local_path: Path = typer.Argument(..., help="本地路径"),
18
- bucket_name: str = typer.Argument(None, help="七牛云空间名称"),
19
- ):
20
- ak, sk = await password.getQiniu()
21
- bucket = QiniuBucket(
22
- 'pytask-doc',
23
- '',
24
- ak,
25
- sk,
26
- )
27
- for file in bpath.listFile(local_path, True):
28
- key = file.relative_to(local_path).as_posix()
29
- await bucket.uploadFile(key, file)
30
- print(key)
31
- bcolor.printGreen('OK')
@@ -1,10 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: bcmd
3
- Version: 0.0.66
4
- Summary: Commands for Beni
5
- Author-email: Beni Mang <benimang@126.com>
6
- Maintainer-email: Beni Mang <benimang@126.com>
7
- Keywords: benimang,beni,bcmd
8
- Requires-Python: >=3.10
9
- Requires-Dist: benimang==0.5.18
10
- Requires-Dist: pathspec
@@ -1,2 +0,0 @@
1
- benimang==0.5.18
2
- pathspec
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