fast-dev-cli 0.24.3__tar.gz → 0.25.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.24.3 → fast_dev_cli-0.25.1}/PKG-INFO +1 -1
- fast_dev_cli-0.25.1/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.24.3 → fast_dev_cli-0.25.1}/fast_dev_cli/cli.py +133 -81
- {fast_dev_cli-0.24.3 → fast_dev_cli-0.25.1}/pyproject.toml +4 -1
- fast_dev_cli-0.24.3/fast_dev_cli/__init__.py +0 -1
- {fast_dev_cli-0.24.3 → fast_dev_cli-0.25.1}/LICENSE +0 -0
- {fast_dev_cli-0.24.3 → fast_dev_cli-0.25.1}/README.md +0 -0
- {fast_dev_cli-0.24.3 → fast_dev_cli-0.25.1}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.24.3 → fast_dev_cli-0.25.1}/fast_dev_cli/py.typed +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.25.1"
|
|
@@ -114,13 +114,19 @@ def yellow_warn(msg: str) -> None:
|
|
|
114
114
|
def load_bool(name: str, default: bool = False, *, verbose: bool = True) -> bool:
|
|
115
115
|
if not (v := os.getenv(name)):
|
|
116
116
|
return default
|
|
117
|
+
if (b := _convert_bool(v)) is not None:
|
|
118
|
+
return b
|
|
119
|
+
if verbose:
|
|
120
|
+
secho(f"WARNING: can not convert value({v!r}) of {name} to bool!")
|
|
121
|
+
return default
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _convert_bool(v: str) -> bool | None:
|
|
117
125
|
if (lower := v.lower()) in ("0", "false", "f", "off", "no", "n"):
|
|
118
126
|
return False
|
|
119
127
|
elif lower in ("1", "true", "t", "on", "yes", "y"):
|
|
120
128
|
return True
|
|
121
|
-
|
|
122
|
-
secho(f"WARNING: can not convert value({v!r}) of {name} to bool!")
|
|
123
|
-
return default
|
|
129
|
+
return None
|
|
124
130
|
|
|
125
131
|
|
|
126
132
|
def is_venv() -> bool:
|
|
@@ -141,7 +147,8 @@ class Shell:
|
|
|
141
147
|
) -> subprocess.CompletedProcess[str]:
|
|
142
148
|
if isinstance(cmd, str):
|
|
143
149
|
kw.setdefault("shell", True)
|
|
144
|
-
|
|
150
|
+
check = kw.pop("check", False)
|
|
151
|
+
return subprocess.run(cmd, check=check, **kw) # nosec:B603
|
|
145
152
|
|
|
146
153
|
@property
|
|
147
154
|
def command(self) -> list[str] | str:
|
|
@@ -260,13 +267,12 @@ def read_version_from_file(
|
|
|
260
267
|
for line in all_lines:
|
|
261
268
|
if pattern.match(line):
|
|
262
269
|
return _parse_version(line, pattern)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
return "0.0.0"
|
|
270
|
+
pattern = re.compile(r"VERSION\s*=")
|
|
271
|
+
for line in all_lines:
|
|
272
|
+
if pattern.match(line):
|
|
273
|
+
return _parse_version(line, pattern)
|
|
274
|
+
secho(f"WARNING: can not find version pattern in {version_file}!")
|
|
275
|
+
return "0.0.0"
|
|
270
276
|
|
|
271
277
|
|
|
272
278
|
def _get_frontend_version() -> tuple[Path, str] | None:
|
|
@@ -325,9 +331,9 @@ def get_current_version(
|
|
|
325
331
|
if package_name is None:
|
|
326
332
|
try:
|
|
327
333
|
work_dir = Project.get_work_dir()
|
|
328
|
-
except EnvError
|
|
334
|
+
except EnvError:
|
|
329
335
|
if (res := _get_frontend_version()) is None:
|
|
330
|
-
raise
|
|
336
|
+
raise
|
|
331
337
|
current_version = res[1]
|
|
332
338
|
if check_version:
|
|
333
339
|
return False, current_version
|
|
@@ -468,7 +474,7 @@ class BumpUp(DryRun):
|
|
|
468
474
|
toml_text: str | None = None,
|
|
469
475
|
work_dir: Path | None = None,
|
|
470
476
|
package_name: str | None = None,
|
|
471
|
-
context: dict | None = None,
|
|
477
|
+
context: dict[str, Any] | None = None,
|
|
472
478
|
) -> str:
|
|
473
479
|
if context is None:
|
|
474
480
|
if toml_text is None:
|
|
@@ -528,11 +534,11 @@ class BumpUp(DryRun):
|
|
|
528
534
|
ds: list[Path] = []
|
|
529
535
|
if package_name is not None:
|
|
530
536
|
packages.insert(0, (package_name, ""))
|
|
531
|
-
for
|
|
532
|
-
ds.append(cwd /
|
|
533
|
-
ds.append(cwd / "src" /
|
|
537
|
+
for pack_name, source_dir in packages:
|
|
538
|
+
ds.append(cwd / pack_name)
|
|
539
|
+
ds.append(cwd / "src" / pack_name)
|
|
534
540
|
if source_dir and source_dir != "src":
|
|
535
|
-
ds.append(cwd / source_dir /
|
|
541
|
+
ds.append(cwd / source_dir / pack_name)
|
|
536
542
|
module_name = poetry_module_name(cwd.name)
|
|
537
543
|
ds.extend([cwd / module_name, cwd / "src" / module_name, cwd])
|
|
538
544
|
for d in ds:
|
|
@@ -756,7 +762,7 @@ class Project:
|
|
|
756
762
|
if skip_uv:
|
|
757
763
|
for name in ("pdm", "poetry"):
|
|
758
764
|
if f"[tool.{name}]" in text:
|
|
759
|
-
cls._tool =
|
|
765
|
+
cls._tool = name
|
|
760
766
|
return cls._tool
|
|
761
767
|
work_dir = cls.get_work_dir(allow_cwd=True)
|
|
762
768
|
for name in ("pdm", "poetry"):
|
|
@@ -791,11 +797,11 @@ class Project:
|
|
|
791
797
|
if backend:
|
|
792
798
|
for t in ("pdm", "poetry"):
|
|
793
799
|
if t in backend:
|
|
794
|
-
cls._tool =
|
|
800
|
+
cls._tool = t
|
|
795
801
|
return cls._tool
|
|
796
802
|
for name in ("pdm", "poetry"):
|
|
797
803
|
if f"[tool.{name}]" in text:
|
|
798
|
-
cls._tool =
|
|
804
|
+
cls._tool = name
|
|
799
805
|
return cls._tool
|
|
800
806
|
if x == 2:
|
|
801
807
|
cls._tool = (
|
|
@@ -919,7 +925,7 @@ class UpgradeDependencies(Project, DryRun):
|
|
|
919
925
|
elif v == "[":
|
|
920
926
|
echo(f"Skip complex dependence: {line}")
|
|
921
927
|
return True
|
|
922
|
-
elif v.startswith(">"
|
|
928
|
+
elif v.startswith((">", "<")) or v[0].isdigit():
|
|
923
929
|
echo(f"Ignore bigger/smaller/equal: {line}")
|
|
924
930
|
return True
|
|
925
931
|
return False
|
|
@@ -1230,50 +1236,54 @@ class LintCode(DryRun):
|
|
|
1230
1236
|
should_run_by_tool = with_prefix
|
|
1231
1237
|
requires_mypy = any(tool.startswith("mypy") for tool in tools)
|
|
1232
1238
|
global_mypy = False
|
|
1233
|
-
if requires_mypy
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
if
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
elif global_mypy:
|
|
1258
|
-
should_run_by_tool = True
|
|
1259
|
-
elif cls.missing_mypy_exec():
|
|
1260
|
-
should_run_by_tool = True
|
|
1261
|
-
if check_call('python -c "import fast_dev_cli"'):
|
|
1262
|
-
command = "python -m pip install -U mypy"
|
|
1239
|
+
if requires_mypy:
|
|
1240
|
+
# TODO: move this long logic to a single function
|
|
1241
|
+
local_bin = Path.home().joinpath(".local/bin")
|
|
1242
|
+
if local_bin.joinpath("mypy").exists():
|
|
1243
|
+
global_mypy = True
|
|
1244
|
+
mypy_opt = "--python-executable=.venv/bin/python"
|
|
1245
|
+
for i, t in enumerate(tools):
|
|
1246
|
+
if t.startswith("mypy"):
|
|
1247
|
+
if mypy_opt not in t:
|
|
1248
|
+
tools[i] = t + " " + mypy_opt
|
|
1249
|
+
break
|
|
1250
|
+
if not should_run_by_tool:
|
|
1251
|
+
if is_venv() and Path(sys.argv[0]).parent != local_bin:
|
|
1252
|
+
# Virtual environment activated and fast-dev-cli is installed in it
|
|
1253
|
+
if not ruff_exists:
|
|
1254
|
+
should_run_by_tool = True
|
|
1255
|
+
command = "pipx install ruff"
|
|
1256
|
+
if shutil.which("pipx") is None:
|
|
1257
|
+
ensure_pipx = (
|
|
1258
|
+
"pip install --user pipx\n pipx ensurepath\n "
|
|
1259
|
+
)
|
|
1260
|
+
command = ensure_pipx + command
|
|
1261
|
+
elif prefer_uv_tool():
|
|
1262
|
+
command = "uv tool install ruff"
|
|
1263
1263
|
yellow_warn(
|
|
1264
1264
|
"You may need to run the following command"
|
|
1265
|
-
f" to install
|
|
1265
|
+
f" to install ruff:\n\n {command}\n"
|
|
1266
1266
|
)
|
|
1267
|
-
|
|
1268
|
-
root = Project.get_work_dir(allow_cwd=True)
|
|
1269
|
-
if py := shutil.which("python"):
|
|
1270
|
-
try:
|
|
1271
|
-
Path(py).relative_to(root)
|
|
1272
|
-
except ValueError:
|
|
1273
|
-
# Virtual environment not activated
|
|
1267
|
+
elif global_mypy:
|
|
1274
1268
|
should_run_by_tool = True
|
|
1275
|
-
|
|
1276
|
-
|
|
1269
|
+
elif cls.missing_mypy_exec():
|
|
1270
|
+
should_run_by_tool = True
|
|
1271
|
+
if check_call('python -c "import fast_dev_cli"'):
|
|
1272
|
+
command = "python -m pip install -U mypy"
|
|
1273
|
+
yellow_warn(
|
|
1274
|
+
"You may need to run the following command"
|
|
1275
|
+
f" to install lint tools:\n\n {command}\n"
|
|
1276
|
+
)
|
|
1277
|
+
elif tool == ToolOption.default:
|
|
1278
|
+
root = Project.get_work_dir(allow_cwd=True)
|
|
1279
|
+
if py := shutil.which("python"):
|
|
1280
|
+
try:
|
|
1281
|
+
Path(py).relative_to(root)
|
|
1282
|
+
except ValueError:
|
|
1283
|
+
# Virtual environment not activated
|
|
1284
|
+
should_run_by_tool = True
|
|
1285
|
+
else:
|
|
1286
|
+
should_run_by_tool = True
|
|
1277
1287
|
if should_run_by_tool and tool:
|
|
1278
1288
|
if tool == ToolOption.default:
|
|
1279
1289
|
tool = Project.get_manage_tool() or ""
|
|
@@ -1419,6 +1429,14 @@ def check(
|
|
|
1419
1429
|
).run()
|
|
1420
1430
|
|
|
1421
1431
|
|
|
1432
|
+
def _should_bandit() -> bool:
|
|
1433
|
+
if v := os.getenv("FASTDEVCLI_BANDIT"):
|
|
1434
|
+
return _convert_bool(v) or True
|
|
1435
|
+
toml_text = Project.load_toml_text()
|
|
1436
|
+
lines = toml_text.splitlines()
|
|
1437
|
+
return any(i.startswith("[tool.bandit") for i in lines)
|
|
1438
|
+
|
|
1439
|
+
|
|
1422
1440
|
@cli.command(name="lint")
|
|
1423
1441
|
def make_style(
|
|
1424
1442
|
files: list[str] | None = typer.Argument(default=None), # noqa:B008
|
|
@@ -1440,6 +1458,9 @@ def make_style(
|
|
|
1440
1458
|
strict: bool = Option(False, help="Whether run mypy with --strict"),
|
|
1441
1459
|
ty: bool = Option(False, help="Whether use ty instead of mypy"),
|
|
1442
1460
|
fix: bool | None = Option(None, help="Whether ruff check with --fix"),
|
|
1461
|
+
auto_bandit: bool | None = Option(
|
|
1462
|
+
None, help="Whether to run bandit if `[tool.bandit]` in pyproject.toml"
|
|
1463
|
+
),
|
|
1443
1464
|
) -> None:
|
|
1444
1465
|
"""Run: ruff check/format to reformat code and then mypy to check"""
|
|
1445
1466
|
if getattr(files, "default", files) is None:
|
|
@@ -1448,7 +1469,9 @@ def make_style(
|
|
|
1448
1469
|
files = [files]
|
|
1449
1470
|
skip = _ensure_bool(skip_mypy)
|
|
1450
1471
|
dmypy = _ensure_bool(use_dmypy)
|
|
1451
|
-
bandit = _ensure_bool(bandit)
|
|
1472
|
+
bandit = _ensure_bool(bandit) or (
|
|
1473
|
+
auto_bandit is not None and _ensure_bool(auto_bandit) and _should_bandit()
|
|
1474
|
+
)
|
|
1452
1475
|
tool = _ensure_str(tool) or ""
|
|
1453
1476
|
up = _ensure_bool(up)
|
|
1454
1477
|
sim = _ensure_bool(sim)
|
|
@@ -1581,18 +1604,34 @@ class Publish:
|
|
|
1581
1604
|
twine = "python -m build && twine upload"
|
|
1582
1605
|
|
|
1583
1606
|
@classmethod
|
|
1584
|
-
def gen(cls) -> str:
|
|
1585
|
-
if tool
|
|
1607
|
+
def gen(cls, tool: str | None, verbose: bool) -> str:
|
|
1608
|
+
if tool == "auto":
|
|
1609
|
+
tool = Project.get_manage_tool() or ""
|
|
1610
|
+
if tool:
|
|
1611
|
+
if tool == "uv":
|
|
1612
|
+
envs = ["UV_PUBLISH_INDEX", "UV_PUBLISH_URL", "UV_PUBLISH_TOKEN"]
|
|
1613
|
+
if not any(os.getenv(i) for i in envs):
|
|
1614
|
+
if verbose:
|
|
1615
|
+
yellow_warn(f"Skip uv publish as envs ({envs}) not set")
|
|
1616
|
+
return "uv build"
|
|
1617
|
+
elif tool == "pdm":
|
|
1618
|
+
env = "PDM_PUBLISH_REPO"
|
|
1619
|
+
if not os.getenv(env):
|
|
1620
|
+
if verbose:
|
|
1621
|
+
yellow_warn(f"Skip pdm publish as env ({env}) not set")
|
|
1622
|
+
return "pdm build"
|
|
1586
1623
|
return cls.CommandEnum[tool]
|
|
1587
1624
|
return cls.CommandEnum.twine
|
|
1588
1625
|
|
|
1589
1626
|
|
|
1590
1627
|
@cli.command()
|
|
1591
1628
|
def upload(
|
|
1629
|
+
verbose: bool = False,
|
|
1630
|
+
tool: str = ToolOption,
|
|
1592
1631
|
dry: bool = DryOption,
|
|
1593
1632
|
) -> None:
|
|
1594
1633
|
"""Shortcut for package publish"""
|
|
1595
|
-
cmd = Publish.gen()
|
|
1634
|
+
cmd = Publish.gen(_ensure_str(tool), verbose)
|
|
1596
1635
|
exit_if_run_failed(cmd, dry=dry)
|
|
1597
1636
|
|
|
1598
1637
|
|
|
@@ -1630,7 +1669,9 @@ def _prefer_just_dev(f: Path) -> bool:
|
|
|
1630
1669
|
return False
|
|
1631
1670
|
|
|
1632
1671
|
|
|
1633
|
-
def _parse_serve_file(
|
|
1672
|
+
def _parse_serve_file(
|
|
1673
|
+
uvicorn: bool | None, filename: str, cmd: str, args: list[str]
|
|
1674
|
+
) -> str:
|
|
1634
1675
|
if m := re.search(r"(.*):(\d+)$", filename):
|
|
1635
1676
|
h, p = m.group(1), m.group(2)
|
|
1636
1677
|
if h and "--host" not in str(args):
|
|
@@ -1657,7 +1698,12 @@ def _parse_serve_file(uvicorn, filename: str, cmd: str, args: list) -> str:
|
|
|
1657
1698
|
return cmd
|
|
1658
1699
|
|
|
1659
1700
|
|
|
1660
|
-
def _runserver(
|
|
1701
|
+
def _runserver(
|
|
1702
|
+
uvicorn: bool | None,
|
|
1703
|
+
host: OptionInfo | str | None,
|
|
1704
|
+
port: OptionInfo | int | None,
|
|
1705
|
+
file: ArgumentInfo | str | None,
|
|
1706
|
+
) -> tuple[str, list[str]]:
|
|
1661
1707
|
cmd = "uvicorn" if uvicorn else "fastapi dev"
|
|
1662
1708
|
args = []
|
|
1663
1709
|
if (host := getattr(host, "default", host)) and host not in (
|
|
@@ -1690,10 +1736,11 @@ def dev(
|
|
|
1690
1736
|
uvicorn: bool | None = None,
|
|
1691
1737
|
prod: bool | None = None,
|
|
1692
1738
|
reload: bool | None = None,
|
|
1739
|
+
just: bool | None = None,
|
|
1693
1740
|
file: str | None | ArgumentInfo = None,
|
|
1694
1741
|
dry: bool = False,
|
|
1695
1742
|
) -> None:
|
|
1696
|
-
if should_use_just():
|
|
1743
|
+
if just is not False and should_use_just():
|
|
1697
1744
|
args = [i for i in sys.argv[2:] if i != "--dry"]
|
|
1698
1745
|
cmd = "just dev"
|
|
1699
1746
|
else:
|
|
@@ -1712,10 +1759,13 @@ def runserver(
|
|
|
1712
1759
|
uvicorn: bool | None = None,
|
|
1713
1760
|
prod: bool | None = None,
|
|
1714
1761
|
reload: bool | None = None,
|
|
1762
|
+
just: bool | None = None,
|
|
1715
1763
|
dry: bool = DryOption,
|
|
1716
1764
|
) -> None:
|
|
1717
1765
|
"""Start a fastapi server(only for fastapi>=0.111.0)"""
|
|
1718
|
-
f = functools.partial(
|
|
1766
|
+
f = functools.partial(
|
|
1767
|
+
dev, port, host, fastapi, uvicorn, prod, reload, just, dry=dry
|
|
1768
|
+
)
|
|
1719
1769
|
if getattr(file_or_port, "default", file_or_port):
|
|
1720
1770
|
f(file=file_or_port)
|
|
1721
1771
|
else:
|
|
@@ -1734,7 +1784,7 @@ def run_by_subprocess(cmd: str, dry: bool = DryOption) -> None:
|
|
|
1734
1784
|
):
|
|
1735
1785
|
echo(f"Command not found: {command}")
|
|
1736
1786
|
raise Exit(1) from None
|
|
1737
|
-
raise
|
|
1787
|
+
raise
|
|
1738
1788
|
else:
|
|
1739
1789
|
if rc:
|
|
1740
1790
|
raise Exit(rc)
|
|
@@ -1804,15 +1854,17 @@ class MakeDeps(DryRun):
|
|
|
1804
1854
|
tool_section = doc["tool"]
|
|
1805
1855
|
uv_package = tool_section.get("uv", {}).get("package")
|
|
1806
1856
|
if uv_package is not None:
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1857
|
+
if not uv_package:
|
|
1858
|
+
return ""
|
|
1859
|
+
else:
|
|
1860
|
+
match doc["build-system"]["build-backend"]:
|
|
1861
|
+
case "pdm.backend":
|
|
1862
|
+
if not tool_section.get("pdm", {}).get("distribution", True):
|
|
1863
|
+
return ""
|
|
1864
|
+
case x if x.startswith("poetry"):
|
|
1865
|
+
if not tool_section.get("poetry", {}).get("package-mode", True):
|
|
1866
|
+
return ""
|
|
1867
|
+
return cast(str, doc["project"]["name"])
|
|
1816
1868
|
return ""
|
|
1817
1869
|
|
|
1818
1870
|
def _gen(self) -> str:
|
|
@@ -1928,7 +1980,7 @@ class UvPypi(DryRun):
|
|
|
1928
1980
|
|
|
1929
1981
|
@staticmethod
|
|
1930
1982
|
def get_target_content(
|
|
1931
|
-
text: str, verbose: bool, target_registry, target_host: str
|
|
1983
|
+
text: str, verbose: bool, target_registry: str, target_host: str
|
|
1932
1984
|
) -> str | None:
|
|
1933
1985
|
registry_pattern = r'(registry = ")(.*?)"'
|
|
1934
1986
|
registry_urls = {i[1] for i in re.findall(registry_pattern, text)}
|
|
@@ -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.25.1"
|
|
33
33
|
|
|
34
34
|
[project.urls]
|
|
35
35
|
Homepage = "https://github.com/waketzheng/fast-dev-cli"
|
|
@@ -184,10 +184,13 @@ extend-select = [
|
|
|
184
184
|
[tool.ruff.lint.per-file-ignores]
|
|
185
185
|
"test_*.py" = [
|
|
186
186
|
"E501",
|
|
187
|
+
"BLE001",
|
|
188
|
+
"PLW1510",
|
|
187
189
|
]
|
|
188
190
|
"scripts/*.py" = [
|
|
189
191
|
"UP009",
|
|
190
192
|
"UP032",
|
|
193
|
+
"PLW1510",
|
|
191
194
|
]
|
|
192
195
|
"fast_dev_cli/cli.py" = [
|
|
193
196
|
"UP007",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.24.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|