fast-dev-cli 0.22.0__tar.gz → 0.23.1__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.22.0 → fast_dev_cli-0.23.1}/PKG-INFO +1 -1
- fast_dev_cli-0.23.1/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.22.0 → fast_dev_cli-0.23.1}/fast_dev_cli/cli.py +110 -80
- {fast_dev_cli-0.22.0 → fast_dev_cli-0.23.1}/pyproject.toml +11 -4
- fast_dev_cli-0.22.0/fast_dev_cli/__init__.py +0 -1
- fast_dev_cli-0.22.0/tests/__init__.py +0 -0
- fast_dev_cli-0.22.0/tests/assets/uv-tx.lock +0 -1476
- fast_dev_cli-0.22.0/tests/assets/uv-upload-time.lock +0 -1658
- fast_dev_cli-0.22.0/tests/assets/uv.lock +0 -1476
- fast_dev_cli-0.22.0/tests/conftest.py +0 -59
- fast_dev_cli-0.22.0/tests/test_bump.py +0 -424
- fast_dev_cli-0.22.0/tests/test_deps.py +0 -78
- fast_dev_cli-0.22.0/tests/test_exec.py +0 -51
- fast_dev_cli-0.22.0/tests/test_fast_test.py +0 -115
- fast_dev_cli-0.22.0/tests/test_functions.py +0 -118
- fast_dev_cli-0.22.0/tests/test_help.py +0 -7
- fast_dev_cli-0.22.0/tests/test_lint.py +0 -464
- fast_dev_cli-0.22.0/tests/test_poetry_version_plugin.py +0 -102
- fast_dev_cli-0.22.0/tests/test_pypi.py +0 -118
- fast_dev_cli-0.22.0/tests/test_runserver.py +0 -136
- fast_dev_cli-0.22.0/tests/test_sync.py +0 -240
- fast_dev_cli-0.22.0/tests/test_tag.py +0 -73
- fast_dev_cli-0.22.0/tests/test_upgrade.py +0 -321
- fast_dev_cli-0.22.0/tests/test_upload.py +0 -43
- fast_dev_cli-0.22.0/tests/test_version.py +0 -78
- fast_dev_cli-0.22.0/tests/utils.py +0 -74
- {fast_dev_cli-0.22.0 → fast_dev_cli-0.23.1}/LICENSE +0 -0
- {fast_dev_cli-0.22.0 → fast_dev_cli-0.23.1}/README.md +0 -0
- {fast_dev_cli-0.22.0 → fast_dev_cli-0.23.1}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.22.0 → fast_dev_cli-0.23.1}/fast_dev_cli/py.typed +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.23.1"
|
|
@@ -15,7 +15,6 @@ from pathlib import Path
|
|
|
15
15
|
from typing import TYPE_CHECKING, Any, Literal, cast, get_args, overload
|
|
16
16
|
|
|
17
17
|
import typer
|
|
18
|
-
from click import UsageError
|
|
19
18
|
from typer import Exit, Option, echo, secho
|
|
20
19
|
from typer.models import ArgumentInfo, OptionInfo
|
|
21
20
|
|
|
@@ -98,6 +97,14 @@ def is_windows() -> bool:
|
|
|
98
97
|
return platform.system() == "Windows"
|
|
99
98
|
|
|
100
99
|
|
|
100
|
+
@functools.cache
|
|
101
|
+
def prefer_uv_tool() -> bool:
|
|
102
|
+
if shutil.which("uv") is None:
|
|
103
|
+
return False
|
|
104
|
+
cmd = "uv tool list"
|
|
105
|
+
return Shell(cmd).capture_output() != "No tools installed"
|
|
106
|
+
|
|
107
|
+
|
|
101
108
|
def yellow_warn(msg: str) -> None:
|
|
102
109
|
if is_windows() and (encoding := sys.stdout.encoding) != "utf-8":
|
|
103
110
|
msg = msg.encode(encoding, errors="ignore").decode(encoding)
|
|
@@ -281,7 +288,7 @@ def _get_frontend_version() -> tuple[Path, str] | None:
|
|
|
281
288
|
except ImportError:
|
|
282
289
|
from json import loads as json_loads # type:ignore[assignment]
|
|
283
290
|
content = frontend_version_file.read_bytes()
|
|
284
|
-
metadata: dict[str, str] = json_loads(content) # type:ignore
|
|
291
|
+
metadata: dict[str, str] = json_loads(content) # type:ignore
|
|
285
292
|
try:
|
|
286
293
|
current_version = metadata["version"]
|
|
287
294
|
except (KeyError, TypeError):
|
|
@@ -731,6 +738,12 @@ class Project:
|
|
|
731
738
|
if t in backend:
|
|
732
739
|
cls._tool = t
|
|
733
740
|
return cls._tool
|
|
741
|
+
return cls._get_manage_tool(text, backend, skip_uv)
|
|
742
|
+
|
|
743
|
+
@classmethod
|
|
744
|
+
def _get_manage_tool(
|
|
745
|
+
cls: type[Self], text: str, backend: str, skip_uv: bool
|
|
746
|
+
) -> ToolName | None:
|
|
734
747
|
work_dir: Path | None = None
|
|
735
748
|
uv_lock_exists: bool | None = None
|
|
736
749
|
if skip_uv:
|
|
@@ -755,6 +768,12 @@ class Project:
|
|
|
755
768
|
if uv_lock_exists:
|
|
756
769
|
cls._tool = "uv"
|
|
757
770
|
return cls._tool
|
|
771
|
+
return cls._parse_manage_tool(work_dir, text, backend)
|
|
772
|
+
|
|
773
|
+
@classmethod
|
|
774
|
+
def _parse_manage_tool(
|
|
775
|
+
cls: type[Self], work_dir: Path, text: str, backend: str
|
|
776
|
+
) -> ToolName | None:
|
|
758
777
|
pdm_lock_exists = Path(work_dir, "pdm.lock").exists()
|
|
759
778
|
poetry_lock_exists = Path(work_dir, "poetry.lock").exists()
|
|
760
779
|
match pdm_lock_exists + poetry_lock_exists:
|
|
@@ -1078,7 +1097,7 @@ class GitTag(DryRun):
|
|
|
1078
1097
|
|
|
1079
1098
|
def run(self) -> None:
|
|
1080
1099
|
if self.mark_tag() and not self.dry:
|
|
1081
|
-
echo("You may want to publish package:\n
|
|
1100
|
+
echo("You may want to publish package:\n pdm publish")
|
|
1082
1101
|
|
|
1083
1102
|
|
|
1084
1103
|
@cli.command()
|
|
@@ -1173,17 +1192,21 @@ class LintCode(DryRun):
|
|
|
1173
1192
|
) -> str:
|
|
1174
1193
|
if paths != "." and all(i.endswith(".html") for i in paths.split()):
|
|
1175
1194
|
return f"prettier -w {paths}"
|
|
1176
|
-
|
|
1177
|
-
|
|
1195
|
+
ruff_rules = ["I", "B"]
|
|
1196
|
+
if ruff_check_sim and not load_bool("FASTDEVCLI_NO_SIM"):
|
|
1197
|
+
ruff_rules.append("SIM")
|
|
1198
|
+
if ruff_check_up or load_bool("FASTDEVCLI_UP"):
|
|
1199
|
+
ruff_rules.append("UP")
|
|
1200
|
+
ruff_check = "ruff check --extend-select=" + ",".join(ruff_rules)
|
|
1201
|
+
if (
|
|
1202
|
+
ruff_check_fix
|
|
1203
|
+
and not check_only
|
|
1204
|
+
and (not load_bool("NO_FIX") and not load_bool("FASTDEVCLI_NO_FIX"))
|
|
1205
|
+
):
|
|
1206
|
+
ruff_check += " --fix"
|
|
1178
1207
|
tools = ["ruff format", ruff_check, "mypy"]
|
|
1179
1208
|
if check_only:
|
|
1180
1209
|
tools[0] += " --check"
|
|
1181
|
-
if check_only or load_bool("NO_FIX"):
|
|
1182
|
-
tools[1] = tools[1].replace(" --fix", "")
|
|
1183
|
-
if ruff_check_up or load_bool("FASTDEVCLI_UP"):
|
|
1184
|
-
tools[1] = tools[1].replace(",SIM", ",SIM,UP")
|
|
1185
|
-
if not ruff_check_sim or load_bool("FASTDEVCLI_NO_SIM"):
|
|
1186
|
-
tools[1] = tools[1].replace(",SIM", "")
|
|
1187
1210
|
if skip_mypy or load_bool("SKIP_MYPY") or load_bool("FASTDEVCLI_NO_MYPY"):
|
|
1188
1211
|
# Sometimes mypy is too slow
|
|
1189
1212
|
tools = tools[:-1]
|
|
@@ -1195,23 +1218,11 @@ class LintCode(DryRun):
|
|
|
1195
1218
|
tools[-1] += " --ignore-missing-imports"
|
|
1196
1219
|
if mypy_strict or load_bool("FASTDEVCLI_STRICT"):
|
|
1197
1220
|
tools[-1] += " --strict"
|
|
1198
|
-
|
|
1199
|
-
"{0}{" + str(i) + "} {1}" for i in range(2, len(tools) + 2)
|
|
1200
|
-
)
|
|
1201
|
-
if ruff_exists := cls.check_lint_tool_installed():
|
|
1202
|
-
# `ruff <command>` get the same result with `pdm run ruff <command>`
|
|
1203
|
-
# While `mypy .`(installed global and env not activated),
|
|
1204
|
-
# does not the same as `pdm run mypy .`
|
|
1205
|
-
lint_them = " && ".join(
|
|
1206
|
-
("" if tool.startswith("ruff") else "{0}")
|
|
1207
|
-
+ (
|
|
1208
|
-
"{%d} {1}" % i # noqa: UP031
|
|
1209
|
-
)
|
|
1210
|
-
for i, tool in enumerate(tools, 2)
|
|
1211
|
-
)
|
|
1221
|
+
ruff_exists = cls.check_lint_tool_installed()
|
|
1212
1222
|
prefix = ""
|
|
1213
1223
|
should_run_by_tool = with_prefix
|
|
1214
|
-
|
|
1224
|
+
requires_mypy = any(tool.startswith("mypy") for tool in tools)
|
|
1225
|
+
if requires_mypy and not should_run_by_tool:
|
|
1215
1226
|
if is_venv() and Path(sys.argv[0]).parent != Path.home().joinpath(
|
|
1216
1227
|
".local/bin"
|
|
1217
1228
|
): # Virtual environment activated and fast-dev-cli is installed in it
|
|
@@ -1221,6 +1232,8 @@ class LintCode(DryRun):
|
|
|
1221
1232
|
if shutil.which("pipx") is None:
|
|
1222
1233
|
ensure_pipx = "pip install --user pipx\n pipx ensurepath\n "
|
|
1223
1234
|
command = ensure_pipx + command
|
|
1235
|
+
elif prefer_uv_tool():
|
|
1236
|
+
command = "uv tool install ruff"
|
|
1224
1237
|
yellow_warn(
|
|
1225
1238
|
"You may need to run the following command"
|
|
1226
1239
|
f" to install ruff:\n\n {command}\n"
|
|
@@ -1228,7 +1241,7 @@ class LintCode(DryRun):
|
|
|
1228
1241
|
elif cls.missing_mypy_exec():
|
|
1229
1242
|
should_run_by_tool = True
|
|
1230
1243
|
if check_call('python -c "import fast_dev_cli"'):
|
|
1231
|
-
command =
|
|
1244
|
+
command = "python -m pip install -U mypy"
|
|
1232
1245
|
yellow_warn(
|
|
1233
1246
|
"You may need to run the following command"
|
|
1234
1247
|
f" to install lint tools:\n\n {command}\n"
|
|
@@ -1255,7 +1268,17 @@ class LintCode(DryRun):
|
|
|
1255
1268
|
prefix = bin_dir
|
|
1256
1269
|
if cls.prefer_dmypy(paths, tools, use_dmypy=use_dmypy):
|
|
1257
1270
|
tools[-1] = "dmypy run"
|
|
1258
|
-
cmd
|
|
1271
|
+
cmd = " && ".join(
|
|
1272
|
+
(
|
|
1273
|
+
tool
|
|
1274
|
+
# `ruff <command>` get the same result with `pdm run ruff <command>`.
|
|
1275
|
+
# Other tools should run inside the selected environment.
|
|
1276
|
+
if ruff_exists and tool.startswith("ruff")
|
|
1277
|
+
else prefix + tool
|
|
1278
|
+
)
|
|
1279
|
+
+ f" {paths}"
|
|
1280
|
+
for tool in tools
|
|
1281
|
+
)
|
|
1259
1282
|
if bandit or load_bool("FASTDEVCLI_BANDIT"):
|
|
1260
1283
|
command = prefix + "bandit"
|
|
1261
1284
|
if Path("pyproject.toml").exists():
|
|
@@ -1565,6 +1588,59 @@ def should_use_just() -> bool:
|
|
|
1565
1588
|
return False
|
|
1566
1589
|
|
|
1567
1590
|
|
|
1591
|
+
def _parse_serve_file(uvicorn, filename: str, cmd: str, args: list) -> str:
|
|
1592
|
+
if m := re.search(r"(.*):(\d+)$", filename):
|
|
1593
|
+
h, p = m.group(1), m.group(2)
|
|
1594
|
+
if h and "--host" not in str(args):
|
|
1595
|
+
if h == "0":
|
|
1596
|
+
args.append("--host=0.0.0.0")
|
|
1597
|
+
else:
|
|
1598
|
+
args.append(f"--host={h}")
|
|
1599
|
+
args.append(f"--port={p}")
|
|
1600
|
+
if uvicorn:
|
|
1601
|
+
p = Path("main.py")
|
|
1602
|
+
if p.exists():
|
|
1603
|
+
cmd += " main:app"
|
|
1604
|
+
elif Path("app", p.name).exists():
|
|
1605
|
+
cmd += " app.main:app"
|
|
1606
|
+
elif Path("app.py").exists():
|
|
1607
|
+
cmd += " app:app"
|
|
1608
|
+
return cmd
|
|
1609
|
+
if uvicorn and ((filepath := Path(filename)).is_file() or filepath.suffix == ".py"):
|
|
1610
|
+
filename = filepath.stem + ":app"
|
|
1611
|
+
parent_names = [j for i in filepath.parents if (j := i.name)]
|
|
1612
|
+
if parent_names:
|
|
1613
|
+
filename = ".".join([*parent_names[::-1], filename])
|
|
1614
|
+
cmd += " " + filename
|
|
1615
|
+
return cmd
|
|
1616
|
+
|
|
1617
|
+
|
|
1618
|
+
def _runserver(uvicorn, host, port, file) -> tuple[str, list[str]]:
|
|
1619
|
+
cmd = "uvicorn" if uvicorn else "fastapi dev"
|
|
1620
|
+
args = []
|
|
1621
|
+
if (host := getattr(host, "default", host)) and host not in (
|
|
1622
|
+
"localhost",
|
|
1623
|
+
"127.0.0.1",
|
|
1624
|
+
):
|
|
1625
|
+
args.append(f"--host={host}")
|
|
1626
|
+
no_port_yet = True
|
|
1627
|
+
if file is not None:
|
|
1628
|
+
filename = str(file)
|
|
1629
|
+
try:
|
|
1630
|
+
port = int(filename)
|
|
1631
|
+
except ValueError:
|
|
1632
|
+
cmd = _parse_serve_file(uvicorn, filename, cmd, args)
|
|
1633
|
+
else:
|
|
1634
|
+
if port != 8000:
|
|
1635
|
+
args.append(f"--port={port}")
|
|
1636
|
+
no_port_yet = False
|
|
1637
|
+
if no_port_yet and (port := getattr(port, "default", port)) and str(port) != "8000":
|
|
1638
|
+
args.append(f"--port={port}")
|
|
1639
|
+
if shutil.which("pdm") is not None:
|
|
1640
|
+
cmd = "pdm run " + cmd
|
|
1641
|
+
return cmd, args
|
|
1642
|
+
|
|
1643
|
+
|
|
1568
1644
|
def dev(
|
|
1569
1645
|
port: int | None | OptionInfo,
|
|
1570
1646
|
host: str | None | OptionInfo,
|
|
@@ -1579,56 +1655,7 @@ def dev(
|
|
|
1579
1655
|
args = [i for i in sys.argv[2:] if i != "--dry"]
|
|
1580
1656
|
cmd = "just dev"
|
|
1581
1657
|
else:
|
|
1582
|
-
cmd =
|
|
1583
|
-
args = []
|
|
1584
|
-
if (host := getattr(host, "default", host)) and host not in (
|
|
1585
|
-
"localhost",
|
|
1586
|
-
"127.0.0.1",
|
|
1587
|
-
):
|
|
1588
|
-
args.append(f"--host={host}")
|
|
1589
|
-
no_port_yet = True
|
|
1590
|
-
if file is not None:
|
|
1591
|
-
try:
|
|
1592
|
-
port = int(str(file))
|
|
1593
|
-
except ValueError:
|
|
1594
|
-
if m := re.search(r"(.*):(\d+)$", str(file)):
|
|
1595
|
-
h, p = m.group(1), m.group(2)
|
|
1596
|
-
if h and "--host" not in str(args):
|
|
1597
|
-
if h == "0":
|
|
1598
|
-
args.append("--host=0.0.0.0")
|
|
1599
|
-
else:
|
|
1600
|
-
args.append(f"--host={h}")
|
|
1601
|
-
args.append(f"--port={p}")
|
|
1602
|
-
if uvicorn:
|
|
1603
|
-
p = Path("main.py")
|
|
1604
|
-
if p.exists():
|
|
1605
|
-
cmd += " main:app"
|
|
1606
|
-
elif Path("app", p.name).exists():
|
|
1607
|
-
cmd += " app.main:app"
|
|
1608
|
-
elif Path("app.py").exists():
|
|
1609
|
-
cmd += " app:app"
|
|
1610
|
-
else:
|
|
1611
|
-
if uvicorn and (
|
|
1612
|
-
(filepath := Path(str(file))).is_file()
|
|
1613
|
-
or filepath.suffix == ".py"
|
|
1614
|
-
):
|
|
1615
|
-
file = filepath.stem + ":app"
|
|
1616
|
-
parent_names = [j for i in filepath.parents if (j := i.name)]
|
|
1617
|
-
if parent_names:
|
|
1618
|
-
file = ".".join([*parent_names[::-1], file])
|
|
1619
|
-
cmd += f" {file}"
|
|
1620
|
-
else:
|
|
1621
|
-
if port != 8000:
|
|
1622
|
-
args.append(f"--port={port}")
|
|
1623
|
-
no_port_yet = False
|
|
1624
|
-
if (
|
|
1625
|
-
no_port_yet
|
|
1626
|
-
and (port := getattr(port, "default", port))
|
|
1627
|
-
and str(port) != "8000"
|
|
1628
|
-
):
|
|
1629
|
-
args.append(f"--port={port}")
|
|
1630
|
-
if shutil.which("pdm") is not None:
|
|
1631
|
-
cmd = "pdm run " + cmd
|
|
1658
|
+
cmd, args = _runserver(uvicorn, host, port, file)
|
|
1632
1659
|
if args:
|
|
1633
1660
|
cmd += " " + " ".join(args)
|
|
1634
1661
|
exit_if_run_failed(cmd, dry=dry)
|
|
@@ -1717,7 +1744,7 @@ class MakeDeps(DryRun):
|
|
|
1717
1744
|
if self.should_ensure_pip():
|
|
1718
1745
|
cmd = f"python -m ensurepip && {upgrade} && {cmd}"
|
|
1719
1746
|
elif self.should_upgrade_pip():
|
|
1720
|
-
cmd = "{upgrade} && {cmd}"
|
|
1747
|
+
cmd = f"{upgrade} && {cmd}"
|
|
1721
1748
|
return cmd
|
|
1722
1749
|
|
|
1723
1750
|
|
|
@@ -1743,7 +1770,10 @@ def make_deps(
|
|
|
1743
1770
|
) -> None:
|
|
1744
1771
|
"""Run: ruff check/format to reformat code and then mypy to check"""
|
|
1745
1772
|
if use_uv + use_pdm + use_pip + use_poetry > 1:
|
|
1746
|
-
raise
|
|
1773
|
+
raise typer.BadParameter(
|
|
1774
|
+
"can only choose one",
|
|
1775
|
+
param_hint=("--uv", "--pdm", "--pip", "--poetry"),
|
|
1776
|
+
)
|
|
1747
1777
|
if use_uv:
|
|
1748
1778
|
tool = "uv"
|
|
1749
1779
|
elif use_pdm:
|
|
@@ -29,7 +29,7 @@ dependencies = [
|
|
|
29
29
|
"typer>=0.24.0,<1",
|
|
30
30
|
"tomli >=2.0.1,<3; python_version < '3.11'",
|
|
31
31
|
]
|
|
32
|
-
version = "0.
|
|
32
|
+
version = "0.23.1"
|
|
33
33
|
|
|
34
34
|
[project.urls]
|
|
35
35
|
Homepage = "https://github.com/waketzheng/fast-dev-cli"
|
|
@@ -52,9 +52,7 @@ fast = "fast_dev_cli.cli:main"
|
|
|
52
52
|
|
|
53
53
|
[dependency-groups]
|
|
54
54
|
dev = [
|
|
55
|
-
"twine>=6.1.0",
|
|
56
55
|
"asynctor>=0.10.0",
|
|
57
|
-
"mypy>=1.19.1",
|
|
58
56
|
]
|
|
59
57
|
|
|
60
58
|
[build-system]
|
|
@@ -67,9 +65,18 @@ build-backend = "pdm.backend"
|
|
|
67
65
|
distribution = true
|
|
68
66
|
|
|
69
67
|
[tool.pdm.version]
|
|
70
|
-
source = "file"
|
|
71
68
|
path = "fast_dev_cli/__init__.py"
|
|
72
69
|
|
|
70
|
+
[tool.pdm.build]
|
|
71
|
+
excludes = [
|
|
72
|
+
"./**/.git",
|
|
73
|
+
"./**/.*_cache",
|
|
74
|
+
"examples",
|
|
75
|
+
"scripts",
|
|
76
|
+
"tests",
|
|
77
|
+
"fastdevcli-slim",
|
|
78
|
+
]
|
|
79
|
+
|
|
73
80
|
[tool.pdm.scripts]
|
|
74
81
|
up = "just up {args}"
|
|
75
82
|
tree = "pdm list --tree {args}"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.22.0"
|
|
File without changes
|