fast-dev-cli 0.18.0__tar.gz → 0.18.2__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.0 → fast_dev_cli-0.18.2}/PKG-INFO +1 -1
  2. fast_dev_cli-0.18.2/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/fast_dev_cli/cli.py +59 -48
  4. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/pyproject.toml +1 -1
  5. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_lint.py +13 -0
  6. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_pypi.py +14 -1
  7. fast_dev_cli-0.18.0/fast_dev_cli/__init__.py +0 -1
  8. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/LICENSE +0 -0
  9. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/README.md +0 -0
  10. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/fast_dev_cli/__main__.py +0 -0
  11. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/fast_dev_cli/py.typed +0 -0
  12. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/pdm_build.py +0 -0
  13. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/scripts/check.py +0 -0
  14. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/scripts/deps.py +0 -0
  15. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/scripts/format.py +0 -0
  16. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/scripts/test.py +0 -0
  17. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/__init__.py +0 -0
  18. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/assets/uv-tx.lock +0 -0
  19. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/assets/uv.lock +0 -0
  20. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/conftest.py +0 -0
  21. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_bump.py +0 -0
  22. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_deps.py +0 -0
  23. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_exec.py +0 -0
  24. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_fast_test.py +0 -0
  25. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_functions.py +0 -0
  26. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_help.py +0 -0
  27. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_poetry_version_plugin.py +0 -0
  28. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_runserver.py +0 -0
  29. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_sync.py +0 -0
  30. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_tag.py +0 -0
  31. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_upgrade.py +0 -0
  32. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_upload.py +0 -0
  33. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/tests/test_version.py +0 -0
  34. {fast_dev_cli-0.18.0 → fast_dev_cli-0.18.2}/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.0
3
+ Version: 0.18.2
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -0,0 +1 @@
1
+ __version__ = "0.18.2"
@@ -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"'
@@ -1129,18 +1147,11 @@ def make_style(
1129
1147
  bandit = _ensure_bool(bandit)
1130
1148
  prefix = _ensure_bool(prefix)
1131
1149
  tool = _ensure_str(tool)
1150
+ kwargs = {"dry": dry, "skip_mypy": skip, "dmypy": dmypy, "bandit": bandit}
1132
1151
  if _ensure_bool(check_only):
1133
- check(files, dry=dry, skip_mypy=skip, dmypy=dmypy, bandit=bandit, tool=tool)
1152
+ check(files, tool=tool, **kwargs)
1134
1153
  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
- )
1154
+ lint(files, prefix=prefix, tool=tool, **kwargs)
1144
1155
 
1145
1156
 
1146
1157
  @cli.command(name="check")
@@ -1463,12 +1474,13 @@ class UvPypi(DryRun):
1463
1474
 
1464
1475
  @cli.command()
1465
1476
  def pypi(
1477
+ file: Optional[str] = typer.Argument(default=None),
1466
1478
  dry: bool = DryOption,
1467
1479
  verbose: bool = False,
1468
1480
  quiet: bool = False,
1469
1481
  ) -> None:
1470
1482
  """Change registry of uv.lock to be pypi.org"""
1471
- if not (p := Path("uv.lock")).exists() and not (
1483
+ if not (p := Path(_ensure_str(file) or "uv.lock")).exists() and not (
1472
1484
  (p := Project.get_work_dir() / p.name).exists()
1473
1485
  ):
1474
1486
  yellow_warn(f"{p.name!r} not found!")
@@ -1492,8 +1504,7 @@ def common(
1492
1504
  is_eager=True,
1493
1505
  help="Show the version of this tool",
1494
1506
  ),
1495
- ) -> None:
1496
- pass
1507
+ ) -> None: ...
1497
1508
 
1498
1509
 
1499
1510
  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.0"
45
+ version = "0.18.2"
46
46
 
47
47
  [project.urls]
48
48
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -245,6 +245,19 @@ def test_lint_without_ruff_installed(mocker, mock_no_dmypy):
245
245
  with capture_stdout() as stream:
246
246
  lint(".", dry=True)
247
247
  output = stream.getvalue()
248
+ cmd = "pipx install ruff"
249
+ assert cmd in output
250
+ tip = "You may need to run the following command to install ruff"
251
+ assert tip in output
252
+ assert f"{tip}:\n\n {cmd}" in output
253
+
254
+
255
+ def test_lint_without_mypy_installed(mocker, mock_no_dmypy):
256
+ mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
257
+ mocker.patch("fast_dev_cli.cli.LintCode.missing_mypy_exec", return_value=True)
258
+ with capture_stdout() as stream:
259
+ lint(".", dry=True)
260
+ output = stream.getvalue()
248
261
  cmd = 'python -m pip install -U "fast-dev-cli"'
249
262
  assert cmd in output
250
263
  tip = "You may need to run the following command to install lint tools"
@@ -23,7 +23,7 @@ def test_pypi(tmp_work_dir, capsys):
23
23
  assert new_text != text
24
24
  assert "tsinghua" in text and "pypi.org" not in text
25
25
  assert "tsinghua" not in new_text and "pypi.org" in new_text
26
- shutil.copy(uv_qh, ".")
26
+ lock_file.write_text(text, encoding="utf-8")
27
27
  pypi(quiet=True)
28
28
  assert new_text == lock_file.read_text("utf-8")
29
29
  pypi()
@@ -37,7 +37,20 @@ def test_pypi(tmp_work_dir, capsys):
37
37
  assert new_text != text
38
38
  assert "tencent" in text and "pypi.org" not in text
39
39
  assert "tencent" not in new_text and "pypi.org" in new_text
40
+ lock_file.write_text(text, encoding="utf-8")
40
41
  pypi(quiet=True)
41
42
  assert new_text == lock_file.read_text("utf-8")
42
43
  pypi()
43
44
  assert new_text == lock_file.read_text("utf-8")
45
+
46
+ lock_file = Path(uv_tx.name)
47
+ shutil.copy(uv_tx, ".")
48
+ text = lock_file.read_text("utf-8")
49
+ with pytest.raises(Exit):
50
+ pypi(uv_tx.name)
51
+ new_text = lock_file.read_text("utf-8")
52
+ shutil.copy(uv_tx, ".")
53
+ pypi(uv_tx.name, quiet=True)
54
+ assert new_text == lock_file.read_text("utf-8")
55
+ pypi(uv_tx.name)
56
+ assert new_text == lock_file.read_text("utf-8")
@@ -1 +0,0 @@
1
- __version__ = "0.18.0"
File without changes
File without changes