fast-dev-cli 0.21.1__tar.gz → 0.23.0__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.
Files changed (36) hide show
  1. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/PKG-INFO +15 -10
  2. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/README.md +5 -5
  3. fast_dev_cli-0.23.0/fast_dev_cli/__init__.py +1 -0
  4. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/fast_dev_cli/cli.py +128 -51
  5. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/pyproject.toml +23 -47
  6. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_deps.py +26 -2
  7. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_fast_test.py +1 -0
  8. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_functions.py +5 -4
  9. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_lint.py +87 -38
  10. fast_dev_cli-0.23.0/tests/test_runserver.py +136 -0
  11. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_sync.py +14 -8
  12. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_tag.py +1 -1
  13. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_upgrade.py +4 -3
  14. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_upload.py +1 -1
  15. fast_dev_cli-0.21.1/fast_dev_cli/__init__.py +0 -1
  16. fast_dev_cli-0.21.1/pdm_build.py +0 -39
  17. fast_dev_cli-0.21.1/scripts/check.py +0 -30
  18. fast_dev_cli-0.21.1/scripts/deps.py +0 -156
  19. fast_dev_cli-0.21.1/scripts/format.py +0 -13
  20. fast_dev_cli-0.21.1/scripts/test.py +0 -48
  21. fast_dev_cli-0.21.1/tests/test_runserver.py +0 -97
  22. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/LICENSE +0 -0
  23. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/fast_dev_cli/__main__.py +0 -0
  24. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/fast_dev_cli/py.typed +0 -0
  25. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/__init__.py +0 -0
  26. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/assets/uv-tx.lock +0 -0
  27. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/assets/uv-upload-time.lock +0 -0
  28. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/assets/uv.lock +0 -0
  29. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/conftest.py +0 -0
  30. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_bump.py +0 -0
  31. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_exec.py +0 -0
  32. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_help.py +0 -0
  33. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_poetry_version_plugin.py +0 -0
  34. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_pypi.py +0 -0
  35. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/test_version.py +0 -0
  36. {fast_dev_cli-0.21.1 → fast_dev_cli-0.23.0}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.21.1
3
+ Version: 0.23.0
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -23,10 +23,15 @@ Project-URL: Homepage, https://github.com/waketzheng/fast-dev-cli
23
23
  Requires-Python: >=3.10
24
24
  Requires-Dist: typer<1,>=0.24.0
25
25
  Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
26
- Requires-Dist: coverage>=7.5.1
27
- Requires-Dist: pytest>=8.2.0
28
- Provides-Extra: include-optional-dependencies
29
- Requires-Dist: all; extra == "include-optional-dependencies"
26
+ Provides-Extra: standard
27
+ Requires-Dist: coverage>=7.5.1; extra == "standard"
28
+ Requires-Dist: pytest>=8.2.0; extra == "standard"
29
+ Provides-Extra: all
30
+ Requires-Dist: emoji>=2.12.1; extra == "all"
31
+ Requires-Dist: packaging>=20.5; extra == "all"
32
+ Requires-Dist: bandit>=1.7.9; extra == "all"
33
+ Requires-Dist: pytest-mock>=3.14.0; extra == "all"
34
+ Requires-Dist: fast-dev-cli[standard]; extra == "all"
30
35
  Description-Content-Type: text/markdown
31
36
 
32
37
  <p align="center">
@@ -90,8 +95,8 @@ Python 3.10+
90
95
  <div class="termy">
91
96
 
92
97
  ```bash
93
- uv tool install --python 3.14 fastdevcli-slim
94
- # Or: pipx install fastdevcli-slim
98
+ uv tool install fast-dev-cli
99
+ # Or: pipx install fast-dev-cli
95
100
  ```
96
101
  *Will create a `fast` command in ~/.local/bin
97
102
 
@@ -102,7 +107,7 @@ uv tool install --python 3.14 fastdevcli-slim
102
107
  <div class="termy">
103
108
 
104
109
  ```bash
105
- pip install fast-dev-cli
110
+ pip install "fast-dev-cli[standard]"
106
111
  ```
107
112
  *Will install: fast-dev-cli emoji typer packaging pytest coverage*
108
113
 
@@ -118,7 +123,7 @@ fast lint [/path/to/file-or-directory]
118
123
  ```bash
119
124
  fast check
120
125
  ```
121
- - Bump up version in pyproject.toml
126
+ - Bump up version in pyproject.toml(or package.json)
122
127
  ```bash
123
128
  fast bump patch # 0.1.0 -> 0.1.1
124
129
  fast bump minor # 0.1.0 -> 0.2.0
@@ -146,7 +151,7 @@ fast pypi
146
151
  ## Use it without installed
147
152
 
148
153
  ```bash
149
- uvx --from fastdevcli-slim fast
154
+ uvx --from fast-dev-cli fast
150
155
  ```
151
156
 
152
157
  ## Lint/Check by ty instead of mypy
@@ -59,8 +59,8 @@ Python 3.10+
59
59
  <div class="termy">
60
60
 
61
61
  ```bash
62
- uv tool install --python 3.14 fastdevcli-slim
63
- # Or: pipx install fastdevcli-slim
62
+ uv tool install fast-dev-cli
63
+ # Or: pipx install fast-dev-cli
64
64
  ```
65
65
  *Will create a `fast` command in ~/.local/bin
66
66
 
@@ -71,7 +71,7 @@ uv tool install --python 3.14 fastdevcli-slim
71
71
  <div class="termy">
72
72
 
73
73
  ```bash
74
- pip install fast-dev-cli
74
+ pip install "fast-dev-cli[standard]"
75
75
  ```
76
76
  *Will install: fast-dev-cli emoji typer packaging pytest coverage*
77
77
 
@@ -87,7 +87,7 @@ fast lint [/path/to/file-or-directory]
87
87
  ```bash
88
88
  fast check
89
89
  ```
90
- - Bump up version in pyproject.toml
90
+ - Bump up version in pyproject.toml(or package.json)
91
91
  ```bash
92
92
  fast bump patch # 0.1.0 -> 0.1.1
93
93
  fast bump minor # 0.1.0 -> 0.2.0
@@ -115,7 +115,7 @@ fast pypi
115
115
  ## Use it without installed
116
116
 
117
117
  ```bash
118
- uvx --from fastdevcli-slim fast
118
+ uvx --from fast-dev-cli fast
119
119
  ```
120
120
 
121
121
  ## Lint/Check by ty instead of mypy
@@ -0,0 +1 @@
1
+ __version__ = "0.23.0"
@@ -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[assignment]
291
+ metadata: dict[str, str] = json_loads(content) # type:ignore
285
292
  try:
286
293
  current_version = metadata["version"]
287
294
  except (KeyError, TypeError):
@@ -1078,7 +1085,7 @@ class GitTag(DryRun):
1078
1085
 
1079
1086
  def run(self) -> None:
1080
1087
  if self.mark_tag() and not self.dry:
1081
- echo("You may want to publish package:\n poetry publish --build")
1088
+ echo("You may want to publish package:\n pdm publish")
1082
1089
 
1083
1090
 
1084
1091
  @cli.command()
@@ -1173,17 +1180,21 @@ class LintCode(DryRun):
1173
1180
  ) -> str:
1174
1181
  if paths != "." and all(i.endswith(".html") for i in paths.split()):
1175
1182
  return f"prettier -w {paths}"
1176
- cmd = ""
1177
- ruff_check = "ruff check --extend-select=I,B,SIM" + " --fix" * ruff_check_fix
1183
+ ruff_rules = ["I", "B"]
1184
+ if ruff_check_sim and not load_bool("FASTDEVCLI_NO_SIM"):
1185
+ ruff_rules.append("SIM")
1186
+ if ruff_check_up or load_bool("FASTDEVCLI_UP"):
1187
+ ruff_rules.append("UP")
1188
+ ruff_check = "ruff check --extend-select=" + ",".join(ruff_rules)
1189
+ if (
1190
+ ruff_check_fix
1191
+ and not check_only
1192
+ and (not load_bool("NO_FIX") and not load_bool("FASTDEVCLI_NO_FIX"))
1193
+ ):
1194
+ ruff_check += " --fix"
1178
1195
  tools = ["ruff format", ruff_check, "mypy"]
1179
1196
  if check_only:
1180
1197
  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
1198
  if skip_mypy or load_bool("SKIP_MYPY") or load_bool("FASTDEVCLI_NO_MYPY"):
1188
1199
  # Sometimes mypy is too slow
1189
1200
  tools = tools[:-1]
@@ -1195,23 +1206,11 @@ class LintCode(DryRun):
1195
1206
  tools[-1] += " --ignore-missing-imports"
1196
1207
  if mypy_strict or load_bool("FASTDEVCLI_STRICT"):
1197
1208
  tools[-1] += " --strict"
1198
- lint_them = " && ".join(
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
- )
1209
+ ruff_exists = cls.check_lint_tool_installed()
1212
1210
  prefix = ""
1213
1211
  should_run_by_tool = with_prefix
1214
- if not should_run_by_tool and "mypy" in str(tools):
1212
+ requires_mypy = any(tool.startswith("mypy") for tool in tools)
1213
+ if requires_mypy and not should_run_by_tool:
1215
1214
  if is_venv() and Path(sys.argv[0]).parent != Path.home().joinpath(
1216
1215
  ".local/bin"
1217
1216
  ): # Virtual environment activated and fast-dev-cli is installed in it
@@ -1221,6 +1220,8 @@ class LintCode(DryRun):
1221
1220
  if shutil.which("pipx") is None:
1222
1221
  ensure_pipx = "pip install --user pipx\n pipx ensurepath\n "
1223
1222
  command = ensure_pipx + command
1223
+ elif prefer_uv_tool():
1224
+ command = "uv tool install ruff"
1224
1225
  yellow_warn(
1225
1226
  "You may need to run the following command"
1226
1227
  f" to install ruff:\n\n {command}\n"
@@ -1228,7 +1229,7 @@ class LintCode(DryRun):
1228
1229
  elif cls.missing_mypy_exec():
1229
1230
  should_run_by_tool = True
1230
1231
  if check_call('python -c "import fast_dev_cli"'):
1231
- command = 'python -m pip install -U "fast-dev-cli"'
1232
+ command = "python -m pip install -U mypy"
1232
1233
  yellow_warn(
1233
1234
  "You may need to run the following command"
1234
1235
  f" to install lint tools:\n\n {command}\n"
@@ -1255,7 +1256,17 @@ class LintCode(DryRun):
1255
1256
  prefix = bin_dir
1256
1257
  if cls.prefer_dmypy(paths, tools, use_dmypy=use_dmypy):
1257
1258
  tools[-1] = "dmypy run"
1258
- cmd += lint_them.format(prefix, paths, *tools)
1259
+ cmd = " && ".join(
1260
+ (
1261
+ tool
1262
+ # `ruff <command>` get the same result with `pdm run ruff <command>`.
1263
+ # Other tools should run inside the selected environment.
1264
+ if ruff_exists and tool.startswith("ruff")
1265
+ else prefix + tool
1266
+ )
1267
+ + f" {paths}"
1268
+ for tool in tools
1269
+ )
1259
1270
  if bandit or load_bool("FASTDEVCLI_BANDIT"):
1260
1271
  command = prefix + "bandit"
1261
1272
  if Path("pyproject.toml").exists():
@@ -1453,7 +1464,9 @@ class Sync(DryRun):
1453
1464
  if should_remove or not is_venv():
1454
1465
  raise EnvError("There project is not managed by uv/pdm/poetry!")
1455
1466
  return f"python -m pip install -r {self.filename}"
1456
- prefix = "" if is_venv() else f"{tool} run "
1467
+ prefix = ""
1468
+ if not is_venv():
1469
+ prefix = f"{tool} run " + "--no-sync " * (tool == "uv")
1457
1470
  ensure_pip = " {1}python -m ensurepip && {1}python -m pip install -U pip &&"
1458
1471
  export_cmd = "uv export --no-hashes --all-extras --all-groups --frozen"
1459
1472
  if tool in ("poetry", "pdm"):
@@ -1549,30 +1562,86 @@ def upload(
1549
1562
  exit_if_run_failed(cmd, dry=dry)
1550
1563
 
1551
1564
 
1565
+ def should_use_just() -> bool:
1566
+ if shutil.which("just") is None:
1567
+ return False
1568
+ d = Path.cwd()
1569
+ for _ in range(5):
1570
+ f = d / "justfile"
1571
+ if f.exists():
1572
+ text = f.read_text(encoding="utf-8")
1573
+ return any(i.startswith("dev *args:") for i in text.splitlines())
1574
+ if d.joinpath("pyproject.toml").exists():
1575
+ break
1576
+ return False
1577
+
1578
+
1552
1579
  def dev(
1553
1580
  port: int | None | OptionInfo,
1554
1581
  host: str | None | OptionInfo,
1582
+ fastapi: bool | None = None,
1583
+ uvicorn: bool | None = None,
1584
+ prod: bool | None = None,
1585
+ reload: bool | None = None,
1555
1586
  file: str | None | ArgumentInfo = None,
1556
1587
  dry: bool = False,
1557
1588
  ) -> None:
1558
- cmd = "fastapi dev"
1559
- no_port_yet = True
1560
- if file is not None:
1561
- try:
1562
- port = int(str(file))
1563
- except ValueError:
1564
- cmd += f" {file}"
1565
- else:
1566
- if port != 8000:
1567
- cmd += f" --port={port}"
1568
- no_port_yet = False
1569
- if no_port_yet and (port := getattr(port, "default", port)) and str(port) != "8000":
1570
- cmd += f" --port={port}"
1571
- if (host := getattr(host, "default", host)) and host not in (
1572
- "localhost",
1573
- "127.0.0.1",
1574
- ):
1575
- cmd += f" --host={host}"
1589
+ if should_use_just():
1590
+ args = [i for i in sys.argv[2:] if i != "--dry"]
1591
+ cmd = "just dev"
1592
+ else:
1593
+ cmd = "uvicorn" if uvicorn else "fastapi dev"
1594
+ args = []
1595
+ if (host := getattr(host, "default", host)) and host not in (
1596
+ "localhost",
1597
+ "127.0.0.1",
1598
+ ):
1599
+ args.append(f"--host={host}")
1600
+ no_port_yet = True
1601
+ if file is not None:
1602
+ try:
1603
+ port = int(str(file))
1604
+ except ValueError:
1605
+ if m := re.search(r"(.*):(\d+)$", str(file)):
1606
+ h, p = m.group(1), m.group(2)
1607
+ if h and "--host" not in str(args):
1608
+ if h == "0":
1609
+ args.append("--host=0.0.0.0")
1610
+ else:
1611
+ args.append(f"--host={h}")
1612
+ args.append(f"--port={p}")
1613
+ if uvicorn:
1614
+ p = Path("main.py")
1615
+ if p.exists():
1616
+ cmd += " main:app"
1617
+ elif Path("app", p.name).exists():
1618
+ cmd += " app.main:app"
1619
+ elif Path("app.py").exists():
1620
+ cmd += " app:app"
1621
+ else:
1622
+ if uvicorn and (
1623
+ (filepath := Path(str(file))).is_file()
1624
+ or filepath.suffix == ".py"
1625
+ ):
1626
+ file = filepath.stem + ":app"
1627
+ parent_names = [j for i in filepath.parents if (j := i.name)]
1628
+ if parent_names:
1629
+ file = ".".join([*parent_names[::-1], file])
1630
+ cmd += f" {file}"
1631
+ else:
1632
+ if port != 8000:
1633
+ args.append(f"--port={port}")
1634
+ no_port_yet = False
1635
+ if (
1636
+ no_port_yet
1637
+ and (port := getattr(port, "default", port))
1638
+ and str(port) != "8000"
1639
+ ):
1640
+ args.append(f"--port={port}")
1641
+ if shutil.which("pdm") is not None:
1642
+ cmd = "pdm run " + cmd
1643
+ if args:
1644
+ cmd += " " + " ".join(args)
1576
1645
  exit_if_run_failed(cmd, dry=dry)
1577
1646
 
1578
1647
 
@@ -1581,13 +1650,18 @@ def runserver(
1581
1650
  file_or_port: str | None = typer.Argument(default=None),
1582
1651
  port: int | None = Option(None, "-p", "--port"),
1583
1652
  host: str | None = Option(None, "-h", "--host"),
1653
+ fastapi: bool | None = None,
1654
+ uvicorn: bool | None = None,
1655
+ prod: bool | None = None,
1656
+ reload: bool | None = None,
1584
1657
  dry: bool = DryOption,
1585
1658
  ) -> None:
1586
1659
  """Start a fastapi server(only for fastapi>=0.111.0)"""
1660
+ f = functools.partial(dev, port, host, fastapi, uvicorn, prod, reload, dry=dry)
1587
1661
  if getattr(file_or_port, "default", file_or_port):
1588
- dev(port, host, file=file_or_port, dry=dry)
1662
+ f(file=file_or_port)
1589
1663
  else:
1590
- dev(port, host, dry=dry)
1664
+ f()
1591
1665
 
1592
1666
 
1593
1667
  @cli.command(name="exec")
@@ -1654,7 +1728,7 @@ class MakeDeps(DryRun):
1654
1728
  if self.should_ensure_pip():
1655
1729
  cmd = f"python -m ensurepip && {upgrade} && {cmd}"
1656
1730
  elif self.should_upgrade_pip():
1657
- cmd = "{upgrade} && {cmd}"
1731
+ cmd = f"{upgrade} && {cmd}"
1658
1732
  return cmd
1659
1733
 
1660
1734
 
@@ -1680,7 +1754,10 @@ def make_deps(
1680
1754
  ) -> None:
1681
1755
  """Run: ruff check/format to reformat code and then mypy to check"""
1682
1756
  if use_uv + use_pdm + use_pip + use_poetry > 1:
1683
- raise UsageError("`--uv/--pdm/--pip/--poetry` can only choose one!")
1757
+ raise typer.BadParameter(
1758
+ "can only choose one",
1759
+ param_hint=("--uv", "--pdm", "--pip", "--poetry"),
1760
+ )
1684
1761
  if use_uv:
1685
1762
  tool = "uv"
1686
1763
  elif use_pdm:
@@ -1,9 +1,3 @@
1
- [build-system]
2
- requires = [
3
- "pdm-backend",
4
- ]
5
- build-backend = "pdm.backend"
6
-
7
1
  [project]
8
2
  name = "fast-dev-cli"
9
3
  dynamic = []
@@ -34,28 +28,39 @@ classifiers = [
34
28
  dependencies = [
35
29
  "typer>=0.24.0,<1",
36
30
  "tomli >=2.0.1,<3; python_version < '3.11'",
37
- "coverage >=7.5.1",
38
- "pytest >=8.2.0",
39
31
  ]
40
- version = "0.21.1"
32
+ version = "0.23.0"
41
33
 
42
34
  [project.urls]
43
35
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
44
36
 
45
37
  [project.optional-dependencies]
46
- include-optional-dependencies = [
47
- "all",
38
+ standard = [
39
+ "coverage >=7.5.1",
40
+ "pytest >=8.2.0",
41
+ ]
42
+ all = [
43
+ "emoji >=2.12.1",
44
+ "packaging >=20.5",
45
+ "bandit>=1.7.9",
46
+ "pytest-mock >=3.14.0",
47
+ "fast-dev-cli[standard]",
48
48
  ]
49
49
 
50
50
  [project.scripts]
51
- fast = "fast_dev_cli:cli.main"
51
+ 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
56
  ]
58
57
 
58
+ [build-system]
59
+ requires = [
60
+ "pdm-backend",
61
+ ]
62
+ build-backend = "pdm.backend"
63
+
59
64
  [tool.pdm]
60
65
  distribution = true
61
66
 
@@ -63,24 +68,14 @@ distribution = true
63
68
  source = "file"
64
69
  path = "fast_dev_cli/__init__.py"
65
70
 
66
- [tool.pdm.build]
67
- source-includes = [
68
- "scripts/",
69
- ]
70
-
71
- [tool.pdm.dev-dependencies]
72
- dev = [
73
- "-e file:///${PROJECT_ROOT}/#egg=fast-dev-cli[all]",
74
- ]
75
-
76
71
  [tool.pdm.scripts]
77
- up = "pdm update -G :all"
72
+ up = "just up {args}"
78
73
  tree = "pdm list --tree {args}"
79
- deps = "pdm run python scripts/deps.py {args}"
74
+ deps = "just deps {args}"
80
75
  prod = "pdm install --prod --frozen {args}"
81
76
  test = "pdm run fast test {args}"
82
- lint = "pdm run fast lint {args}"
83
- check = "pdm run fast check {args}"
77
+ lint = "just lint {args}"
78
+ check = "just check {args}"
84
79
  tag = "pdm run fast tag {args}"
85
80
  bump = "pdm run fast bump patch --commit {args}"
86
81
 
@@ -99,7 +94,7 @@ composite = [
99
94
 
100
95
  [tool.pdm.scripts.start]
101
96
  composite = [
102
- "pre-commit install",
97
+ "prek install",
103
98
  "deps",
104
99
  ]
105
100
 
@@ -109,25 +104,6 @@ composite = [
109
104
  "ruff check --fix --unsafe-fixes {args}",
110
105
  ]
111
106
 
112
- [tool.waketzheng._internal-slim-build.packages.fastdevcli-slim.project]
113
- name = "fastdevcli-slim"
114
-
115
- [tool.waketzheng._internal-slim-build.packages.fastdevcli-slim.project.scripts]
116
- fast = "fast_dev_cli:cli.main"
117
-
118
- [tool.waketzheng._internal-slim-build.packages.fast-dev-cli]
119
- include-optional-dependencies = [
120
- "standard",
121
- ]
122
-
123
- [tool.waketzheng._internal-slim-build.packages.fast-dev-cli.project.optional-dependencies]
124
- include-optional-dependencies = [
125
- "all",
126
- ]
127
-
128
- [tool.waketzheng._internal-slim-build.packages.fast-dev-cli.project.scripts]
129
- fast = "fast_dev_cli:cli.main"
130
-
131
107
  [tool.pytest.ini_options]
132
108
  testpaths = [
133
109
  "tests",
@@ -1,7 +1,19 @@
1
- from fast_dev_cli.cli import MakeDeps, capture_cmd_output
1
+ import re
2
+
3
+ from fast_dev_cli.cli import MakeDeps, capture_cmd_output, run_and_echo
4
+
5
+ ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
6
+
7
+
8
+ def strip_ansi(text: str) -> str:
9
+ return ANSI_RE.sub("", text)
2
10
 
3
11
 
4
12
  def test_make_deps_class():
13
+ class PipDepsWithoutEnsure(MakeDeps):
14
+ def should_ensure_pip(self) -> bool:
15
+ return False
16
+
5
17
  assert (
6
18
  MakeDeps("uv", prod=False).gen()
7
19
  == "uv sync --inexact --active --all-extras --all-groups"
@@ -27,10 +39,14 @@ def test_make_deps_class():
27
39
  MakeDeps("pip", prod=True).gen()
28
40
  == "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e ."
29
41
  )
42
+ assert (
43
+ PipDepsWithoutEnsure("pip", prod=False).gen()
44
+ == "python -m pip install --upgrade pip && python -m pip install -e . --group dev"
45
+ )
30
46
 
31
47
 
32
48
  def test_fast_deps():
33
- out = capture_cmd_output("fast deps --dry")
49
+ out = capture_cmd_output("fast deps --pdm --dry")
34
50
  assert out == "--> pdm install --frozen -G :all"
35
51
  out = capture_cmd_output("fast deps --uv --prod --dry")
36
52
  assert out == "--> uv sync --inexact --active"
@@ -50,6 +66,14 @@ def test_fast_deps():
50
66
  assert out == "--> uv sync --all-extras --all-groups"
51
67
 
52
68
 
69
+ def test_fast_deps_mutually_exclusive_options():
70
+ cmd = "fast deps --uv --pdm --dry"
71
+ assert run_and_echo(cmd, verbose=False) == 2
72
+ out = strip_ansi(capture_cmd_output(cmd))
73
+ assert "Invalid value for '--uv' / '--pdm' / '--pip' / '--poetry'" in out
74
+ assert "can only choose" in out
75
+
76
+
53
77
  def test_smart_fast_deps(tmp_work_dir, monkeypatch):
54
78
  toml_file = tmp_work_dir.joinpath("pyproject.toml")
55
79
  if toml_file.exists():
@@ -42,6 +42,7 @@ def test_cli_test(mocker, capsys):
42
42
  def test_test_with_pdm_run(mocker: MockerFixture, capsys):
43
43
  mocker.patch("fast_dev_cli.cli.check_call", return_value=False)
44
44
  mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
45
+ mocker.patch("fast_dev_cli.cli.Project.get_manage_tool", return_value="pdm")
45
46
  unitcase(dry=True)
46
47
  assert (
47
48
  '--> pdm run coverage run -m pytest -s && pdm run coverage report --omit="tests/*" -m'
@@ -4,7 +4,8 @@ from io import StringIO
4
4
  from pathlib import Path
5
5
 
6
6
  import pytest
7
- import typer
7
+ from typer import Exit as TyperExit
8
+ from typer import Option
8
9
 
9
10
  from fast_dev_cli.cli import (
10
11
  DryRun,
@@ -85,7 +86,7 @@ def test_run_shell():
85
86
  )
86
87
  with pytest.raises(FileNotFoundError):
87
88
  exit_if_run_failed("in_valid_command", _exit=True, capture_output=True)
88
- with pytest.raises(typer.Exit):
89
+ with pytest.raises(TyperExit):
89
90
  exit_if_run_failed(
90
91
  "in_valid_command", _exit=False, capture_output=True, shell=True
91
92
  )
@@ -111,8 +112,8 @@ def test_get_version_in_poetry_project(tmp_path: Path):
111
112
  def test_ensure_bool():
112
113
  assert _ensure_bool(True) is True
113
114
  assert _ensure_bool(False) is False
114
- opt = typer.Option(False, "--check-only", "-c")
115
+ opt = Option(False, "--check-only", "-c")
115
116
  assert opt
116
117
  assert _ensure_bool(opt) is False
117
- opt = typer.Option(True, "--check-only", "-c")
118
+ opt = Option(True, "--check-only", "-c")
118
119
  assert _ensure_bool(opt) is True