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.
Files changed (34) hide show
  1. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/PKG-INFO +16 -1
  2. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/README.md +15 -0
  3. fast_dev_cli-0.18.3/fast_dev_cli/__init__.py +1 -0
  4. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/fast_dev_cli/cli.py +71 -51
  5. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/pyproject.toml +1 -1
  6. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_lint.py +21 -0
  7. fast_dev_cli-0.18.1/fast_dev_cli/__init__.py +0 -1
  8. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/LICENSE +0 -0
  9. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/fast_dev_cli/__main__.py +0 -0
  10. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/fast_dev_cli/py.typed +0 -0
  11. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/pdm_build.py +0 -0
  12. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/check.py +0 -0
  13. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/deps.py +0 -0
  14. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/format.py +0 -0
  15. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/scripts/test.py +0 -0
  16. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/__init__.py +0 -0
  17. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/assets/uv-tx.lock +0 -0
  18. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/assets/uv.lock +0 -0
  19. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/conftest.py +0 -0
  20. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_bump.py +0 -0
  21. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_deps.py +0 -0
  22. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_exec.py +0 -0
  23. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_fast_test.py +0 -0
  24. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_functions.py +0 -0
  25. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_help.py +0 -0
  26. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_poetry_version_plugin.py +0 -0
  27. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_pypi.py +0 -0
  28. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_runserver.py +0 -0
  29. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_sync.py +0 -0
  30. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_tag.py +0 -0
  31. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_upgrade.py +0 -0
  32. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_upload.py +0 -0
  33. {fast_dev_cli-0.18.1 → fast_dev_cli-0.18.3}/tests/test_version.py +0 -0
  34. {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.1
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
- pass
594
- else:
595
- with contextlib.suppress(KeyError, tomllib.TOMLDecodeError):
596
- doc = tomllib.loads(text)
597
- backend = doc["build-system"]["build-backend"]
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
- if isinstance(args := self.args, str):
1044
- args = args.split()
1045
- paths = " ".join(map(str, args)) if args else "."
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, dry=dry, skip_mypy=skip, dmypy=dmypy, bandit=bandit, tool=tool)
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:
@@ -42,7 +42,7 @@ dependencies = [
42
42
  "bumpversion2 >=1.4.3",
43
43
  "pytest >=8.2.0",
44
44
  ]
45
- version = "0.18.1"
45
+ version = "0.18.3"
46
46
 
47
47
  [project.urls]
48
48
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -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