fast-dev-cli 0.18.1__tar.gz → 0.18.3__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.
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/PKG-INFO +16 -1
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/README.md +15 -0
- fast_dev_cli-0.18.3/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/fast_dev_cli/cli.py +71 -51
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/pyproject.toml +1 -1
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_lint.py +21 -0
- fast_dev_cli-0.18.1/fast_dev_cli/__init__.py +0 -1
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/LICENSE +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/pdm_build.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/check.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/deps.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/format.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/test.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/__init__.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/assets/uv-tx.lock +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/assets/uv.lock +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/conftest.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_deps.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_exec.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_fast_test.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_help.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_poetry_version_plugin.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_pypi.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_sync.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_upload.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_version.py +0 -0
- {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: fast-dev-cli
|
|
3
|
-
Version: 0.18.
|
|
3
|
+
Version: 0.18.3
|
|
4
4
|
Summary: Python project development tool.
|
|
5
5
|
Author-Email: Waket Zheng <waketzheng@gmail.com>>
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -131,3 +131,18 @@ fast dev
|
|
|
131
131
|
fast upload
|
|
132
132
|
```
|
|
133
133
|
*Note: all command support the `--dry` option*
|
|
134
|
+
|
|
135
|
+
## Use it without installed
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
uvx --from fastdevcli-slim fast
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Install global
|
|
142
|
+
```bash
|
|
143
|
+
pipx install fastdevcli-slim
|
|
144
|
+
```
|
|
145
|
+
Or:
|
|
146
|
+
```bash
|
|
147
|
+
uv tool install fastdevcli-slim
|
|
148
|
+
```
|
|
@@ -95,3 +95,18 @@ fast dev
|
|
|
95
95
|
fast upload
|
|
96
96
|
```
|
|
97
97
|
*Note: all command support the `--dry` option*
|
|
98
|
+
|
|
99
|
+
## Use it without installed
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uvx --from fastdevcli-slim fast
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Install global
|
|
106
|
+
```bash
|
|
107
|
+
pipx install fastdevcli-slim
|
|
108
|
+
```
|
|
109
|
+
Or:
|
|
110
|
+
```bash
|
|
111
|
+
uv tool install fastdevcli-slim
|
|
112
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.18.3"
|
|
@@ -6,6 +6,7 @@ import importlib.metadata as importlib_metadata
|
|
|
6
6
|
import os
|
|
7
7
|
import re
|
|
8
8
|
import shlex
|
|
9
|
+
import shutil
|
|
9
10
|
import subprocess # nosec:B404
|
|
10
11
|
import sys
|
|
11
12
|
from functools import cached_property
|
|
@@ -61,10 +62,20 @@ ToolOption = Option(
|
|
|
61
62
|
)
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
class FastDevCliError(Exception):
|
|
65
|
+
class FastDevCliError(Exception):
|
|
66
|
+
"""Basic exception of this library, all custom exceptions inherit from it"""
|
|
65
67
|
|
|
66
68
|
|
|
67
|
-
class ShellCommandError(FastDevCliError):
|
|
69
|
+
class ShellCommandError(FastDevCliError):
|
|
70
|
+
"""Raise if cmd command returncode is not zero"""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ParseError(FastDevCliError):
|
|
74
|
+
"""Raise this if parse dependence line error"""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class EnvError(FastDevCliError):
|
|
78
|
+
"""Raise when expected to be managed by poetry, but toml file not found."""
|
|
68
79
|
|
|
69
80
|
|
|
70
81
|
def poetry_module_name(name: str) -> str:
|
|
@@ -259,7 +270,7 @@ def _ensure_bool(value: bool | OptionInfo) -> bool:
|
|
|
259
270
|
return value
|
|
260
271
|
|
|
261
272
|
|
|
262
|
-
def _ensure_str(value: str | OptionInfo) -> str:
|
|
273
|
+
def _ensure_str(value: str | OptionInfo | None) -> str:
|
|
263
274
|
if not isinstance(value, str):
|
|
264
275
|
value = getattr(value, "default", "")
|
|
265
276
|
return value
|
|
@@ -535,10 +546,6 @@ def bump() -> None:
|
|
|
535
546
|
return BumpUp(commit, part, no_sync="--no-sync" in args, dry="--dry" in args).run()
|
|
536
547
|
|
|
537
548
|
|
|
538
|
-
class EnvError(Exception):
|
|
539
|
-
"""Raise when expected to be managed by poetry, but toml file not found."""
|
|
540
|
-
|
|
541
|
-
|
|
542
549
|
class Project:
|
|
543
550
|
path_depth = 5
|
|
544
551
|
_tool: ToolName | None = None
|
|
@@ -590,30 +597,33 @@ class Project:
|
|
|
590
597
|
try:
|
|
591
598
|
text = cls.load_toml_text()
|
|
592
599
|
except EnvError:
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
if "poetry" in backend:
|
|
599
|
-
cls._tool = "poetry"
|
|
600
|
-
return cls._tool
|
|
601
|
-
elif "pdm" in backend:
|
|
602
|
-
cls._tool = "pdm"
|
|
603
|
-
work_dir = cls.get_work_dir(allow_cwd=True)
|
|
604
|
-
if not Path(work_dir, "pdm.lock").exists() and (
|
|
605
|
-
"[tool.uv]" in text or Path(work_dir, "uv.lock").exists()
|
|
606
|
-
):
|
|
607
|
-
cls._tool = "uv"
|
|
608
|
-
return cls._tool
|
|
609
|
-
for name in get_args(ToolName):
|
|
610
|
-
if f"[tool.{name}]" in text:
|
|
611
|
-
cls._tool = cast(ToolName, name)
|
|
612
|
-
return cls._tool
|
|
613
|
-
# Poetry 2.0 default to not include the '[tool.poetry]' section
|
|
614
|
-
if cls.is_poetry_v2(text):
|
|
600
|
+
return None
|
|
601
|
+
with contextlib.suppress(KeyError, tomllib.TOMLDecodeError):
|
|
602
|
+
doc = tomllib.loads(text)
|
|
603
|
+
backend = doc["build-system"]["build-backend"]
|
|
604
|
+
if "poetry" in backend:
|
|
615
605
|
cls._tool = "poetry"
|
|
616
606
|
return cls._tool
|
|
607
|
+
work_dir = cls.get_work_dir(allow_cwd=True)
|
|
608
|
+
uv_lock_exists = Path(work_dir, "uv.lock").exists()
|
|
609
|
+
if "pdm" in backend:
|
|
610
|
+
cls._tool = "pdm"
|
|
611
|
+
if not Path(work_dir, "pdm.lock").exists() and (
|
|
612
|
+
uv_lock_exists or "[tool.uv]" in text
|
|
613
|
+
):
|
|
614
|
+
cls._tool = "uv"
|
|
615
|
+
return cls._tool
|
|
616
|
+
elif uv_lock_exists:
|
|
617
|
+
cls._tool = "uv"
|
|
618
|
+
return cls._tool
|
|
619
|
+
for name in get_args(ToolName):
|
|
620
|
+
if f"[tool.{name}]" in text:
|
|
621
|
+
cls._tool = cast(ToolName, name)
|
|
622
|
+
return cls._tool
|
|
623
|
+
# Poetry 2.0 default to not include the '[tool.poetry]' section
|
|
624
|
+
if cls.is_poetry_v2(text):
|
|
625
|
+
cls._tool = "poetry"
|
|
626
|
+
return cls._tool
|
|
617
627
|
return None
|
|
618
628
|
|
|
619
629
|
@staticmethod
|
|
@@ -664,12 +674,6 @@ class Project:
|
|
|
664
674
|
run_and_echo(cmd)
|
|
665
675
|
|
|
666
676
|
|
|
667
|
-
class ParseError(Exception):
|
|
668
|
-
"""Raise this if parse dependence line error"""
|
|
669
|
-
|
|
670
|
-
pass
|
|
671
|
-
|
|
672
|
-
|
|
673
677
|
class UpgradeDependencies(Project, DryRun):
|
|
674
678
|
def __init__(
|
|
675
679
|
self, _exit: bool = False, dry: bool = False, tool: ToolName = "poetry"
|
|
@@ -943,6 +947,10 @@ class LintCode(DryRun):
|
|
|
943
947
|
def check_lint_tool_installed() -> bool:
|
|
944
948
|
return check_call("ruff --version")
|
|
945
949
|
|
|
950
|
+
@staticmethod
|
|
951
|
+
def missing_mypy_exec() -> bool:
|
|
952
|
+
return shutil.which("mypy") is None
|
|
953
|
+
|
|
946
954
|
@staticmethod
|
|
947
955
|
def prefer_dmypy(paths: str, tools: list[str], use_dmypy: bool = False) -> bool:
|
|
948
956
|
return (
|
|
@@ -1006,6 +1014,16 @@ class LintCode(DryRun):
|
|
|
1006
1014
|
".local/bin"
|
|
1007
1015
|
):
|
|
1008
1016
|
if not ruff_exists:
|
|
1017
|
+
should_run_by_tool = True
|
|
1018
|
+
command = "pipx install ruff"
|
|
1019
|
+
if shutil.which("pipx") is None:
|
|
1020
|
+
ensure_pipx = "pip install --user pipx\n pipx ensurepath\n "
|
|
1021
|
+
command = ensure_pipx + command
|
|
1022
|
+
yellow_warn(
|
|
1023
|
+
"You may need to run the following command"
|
|
1024
|
+
f" to install ruff:\n\n {command}\n"
|
|
1025
|
+
)
|
|
1026
|
+
elif "mypy" in str(tools) and cls.missing_mypy_exec():
|
|
1009
1027
|
should_run_by_tool = True
|
|
1010
1028
|
if check_call('python -c "import fast_dev_cli"'):
|
|
1011
1029
|
command = 'python -m pip install -U "fast-dev-cli"'
|
|
@@ -1040,9 +1058,19 @@ class LintCode(DryRun):
|
|
|
1040
1058
|
return cmd
|
|
1041
1059
|
|
|
1042
1060
|
def gen(self) -> str:
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1061
|
+
paths = "."
|
|
1062
|
+
if args := self.args:
|
|
1063
|
+
ps = args.split() if isinstance(args, str) else [str(i) for i in args]
|
|
1064
|
+
if len(ps) == 1:
|
|
1065
|
+
paths = ps[0]
|
|
1066
|
+
if paths != "." and paths.endswith(".") and not Path(paths).exists():
|
|
1067
|
+
for ft in ("py", "html"):
|
|
1068
|
+
name = paths + ft
|
|
1069
|
+
if Path(name).exists():
|
|
1070
|
+
paths = name
|
|
1071
|
+
break
|
|
1072
|
+
else:
|
|
1073
|
+
paths = " ".join(ps)
|
|
1046
1074
|
return self.to_cmd(
|
|
1047
1075
|
paths,
|
|
1048
1076
|
self.check_only,
|
|
@@ -1129,18 +1157,11 @@ def make_style(
|
|
|
1129
1157
|
bandit = _ensure_bool(bandit)
|
|
1130
1158
|
prefix = _ensure_bool(prefix)
|
|
1131
1159
|
tool = _ensure_str(tool)
|
|
1160
|
+
kwargs = {"dry": dry, "skip_mypy": skip, "dmypy": dmypy, "bandit": bandit}
|
|
1132
1161
|
if _ensure_bool(check_only):
|
|
1133
|
-
check(files,
|
|
1162
|
+
check(files, tool=tool, **kwargs)
|
|
1134
1163
|
else:
|
|
1135
|
-
lint(
|
|
1136
|
-
files,
|
|
1137
|
-
dry=dry,
|
|
1138
|
-
skip_mypy=skip,
|
|
1139
|
-
dmypy=dmypy,
|
|
1140
|
-
bandit=bandit,
|
|
1141
|
-
tool=tool,
|
|
1142
|
-
prefix=prefix,
|
|
1143
|
-
)
|
|
1164
|
+
lint(files, prefix=prefix, tool=tool, **kwargs)
|
|
1144
1165
|
|
|
1145
1166
|
|
|
1146
1167
|
@cli.command(name="check")
|
|
@@ -1469,7 +1490,7 @@ def pypi(
|
|
|
1469
1490
|
quiet: bool = False,
|
|
1470
1491
|
) -> None:
|
|
1471
1492
|
"""Change registry of uv.lock to be pypi.org"""
|
|
1472
|
-
if not (p := Path(file or "uv.lock")).exists() and not (
|
|
1493
|
+
if not (p := Path(_ensure_str(file) or "uv.lock")).exists() and not (
|
|
1473
1494
|
(p := Project.get_work_dir() / p.name).exists()
|
|
1474
1495
|
):
|
|
1475
1496
|
yellow_warn(f"{p.name!r} not found!")
|
|
@@ -1493,8 +1514,7 @@ def common(
|
|
|
1493
1514
|
is_eager=True,
|
|
1494
1515
|
help="Show the version of this tool",
|
|
1495
1516
|
),
|
|
1496
|
-
) -> None:
|
|
1497
|
-
pass
|
|
1517
|
+
) -> None: ...
|
|
1498
1518
|
|
|
1499
1519
|
|
|
1500
1520
|
def main() -> None:
|
|
@@ -187,6 +187,14 @@ def test_lint_with_prefix(mocker):
|
|
|
187
187
|
assert "pdm run" in stream.getvalue()
|
|
188
188
|
|
|
189
189
|
|
|
190
|
+
def test_lint_auto_suffix(tmp_work_dir):
|
|
191
|
+
Path("main.py").touch()
|
|
192
|
+
cmd = "fast lint --dry main."
|
|
193
|
+
assert "main.py" in capture_cmd_output(cmd)
|
|
194
|
+
shutil.move("main.py", "main.html")
|
|
195
|
+
assert "main.html" in capture_cmd_output(cmd)
|
|
196
|
+
|
|
197
|
+
|
|
190
198
|
def test_fast_lint_with_uv():
|
|
191
199
|
command = capture_cmd_output("fast lint --tool=uv --prefix --dry")
|
|
192
200
|
assert (
|
|
@@ -245,6 +253,19 @@ def test_lint_without_ruff_installed(mocker, mock_no_dmypy):
|
|
|
245
253
|
with capture_stdout() as stream:
|
|
246
254
|
lint(".", dry=True)
|
|
247
255
|
output = stream.getvalue()
|
|
256
|
+
cmd = "pipx install ruff"
|
|
257
|
+
assert cmd in output
|
|
258
|
+
tip = "You may need to run the following command to install ruff"
|
|
259
|
+
assert tip in output
|
|
260
|
+
assert f"{tip}:\n\n {cmd}" in output
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def test_lint_without_mypy_installed(mocker, mock_no_dmypy):
|
|
264
|
+
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
265
|
+
mocker.patch("fast_dev_cli.cli.LintCode.missing_mypy_exec", return_value=True)
|
|
266
|
+
with capture_stdout() as stream:
|
|
267
|
+
lint(".", dry=True)
|
|
268
|
+
output = stream.getvalue()
|
|
248
269
|
cmd = 'python -m pip install -U "fast-dev-cli"'
|
|
249
270
|
assert cmd in output
|
|
250
271
|
tip = "You may need to run the following command to install lint tools"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.18.1"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|