fast-dev-cli 0.9.9__tar.gz → 0.10.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 (28) hide show
  1. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/PKG-INFO +3 -3
  2. fast_dev_cli-0.10.0/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/fast_dev_cli/cli.py +39 -12
  4. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/pyproject.toml +2 -2
  5. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/scripts/check.sh +1 -1
  6. fast_dev_cli-0.10.0/scripts/format.sh +6 -0
  7. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_bump.py +24 -5
  8. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_fast_test.py +7 -1
  9. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_functions.py +6 -1
  10. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_lint.py +19 -1
  11. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_runserver.py +14 -1
  12. fast_dev_cli-0.9.9/fast_dev_cli/__init__.py +0 -1
  13. fast_dev_cli-0.9.9/scripts/format.sh +0 -6
  14. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/LICENSE +0 -0
  15. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/README.md +0 -0
  16. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/fast_dev_cli/__main__.py +0 -0
  17. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/fast_dev_cli/py.typed +0 -0
  18. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/pdm_build.py +0 -0
  19. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/scripts/test.sh +0 -0
  20. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/__init__.py +0 -0
  21. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/conftest.py +0 -0
  22. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_poetry_version_plugin.py +0 -0
  23. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_sync.py +0 -0
  24. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_tag.py +0 -0
  25. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_upgrade.py +0 -0
  26. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_upload.py +0 -0
  27. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.0}/tests/test_version.py +0 -0
  28. {fast_dev_cli-0.9.9 → fast_dev_cli-0.10.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.9.9
3
+ Version: 0.10.0
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -24,13 +24,13 @@ Requires-Dist: typer<0.13,>=0.12.3
24
24
  Requires-Dist: emoji<3,>=2.12.1
25
25
  Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
26
26
  Requires-Dist: coverage<8,>=7.5.1
27
- Requires-Dist: ruff<0.7,>=0.4.4
27
+ Requires-Dist: ruff<0.8,>=0.4.4
28
28
  Requires-Dist: mypy<2,>=1.10.0
29
29
  Requires-Dist: bumpversion2<2,>=1.4.2
30
30
  Requires-Dist: pytest<9,>=8.2.0
31
+ Provides-Extra: all
31
32
  Requires-Dist: ipython<9,>=8.24.0; extra == "all"
32
33
  Requires-Dist: pytest-mock<4,>=3.14.0; extra == "all"
33
- Provides-Extra: all
34
34
  Description-Content-Type: text/markdown
35
35
 
36
36
  <p align="center">
@@ -0,0 +1 @@
1
+ __version__ = "0.10.0"
@@ -44,7 +44,12 @@ cli = typer.Typer()
44
44
  def load_bool(name: str, default=False) -> bool:
45
45
  if not (v := os.getenv(name)):
46
46
  return default
47
- return v.lower() not in ("0", "false", "off", "no", "n")
47
+ if (lower := v.lower()) in ("0", "false", "f", "off", "no", "n"):
48
+ return False
49
+ elif lower in ("1", "true", "t", "on", "yes", "y"):
50
+ return True
51
+ secho(f"WARNING: can not convert value({v!r}) of {name} to bool!")
52
+ return default
48
53
 
49
54
 
50
55
  def is_venv() -> bool:
@@ -228,8 +233,9 @@ class BumpUp(DryRun):
228
233
  cmd += " --tag"
229
234
  cmd += " --commit"
230
235
  if self.should_add_emoji():
231
- cmd += " --commit-emoji=1"
232
- cmd += " && git push && git push --tags && git log -1"
236
+ cmd += " --message-emoji=1"
237
+ if not load_bool("DONT_GIT_PUSH"):
238
+ cmd += " && git push && git push --tags && git log -1"
233
239
  else:
234
240
  cmd += " --allow-dirty"
235
241
  return cmd
@@ -543,7 +549,9 @@ class GitTag(DryRun):
543
549
  return capture_cmd_output("git status")
544
550
 
545
551
  def mark_tag(self: Self) -> bool:
546
- if not re.search(r"working (tree|directory) clean", self.git_status):
552
+ if not re.search(r"working (tree|directory) clean", self.git_status) and (
553
+ "无文件要提交,干净的工作区" not in self.git_status
554
+ ):
547
555
  run_and_echo("git status")
548
556
  echo("ERROR: Please run git commit to make sure working tree is clean!")
549
557
  return False
@@ -564,9 +572,12 @@ def tag(
564
572
 
565
573
 
566
574
  class LintCode(DryRun):
567
- def __init__(self: Self, args, check_only=False, _exit=False, dry=False) -> None:
575
+ def __init__(
576
+ self: Self, args, check_only=False, _exit=False, dry=False, bandit=False
577
+ ) -> None:
568
578
  self.args = args
569
579
  self.check_only = check_only
580
+ self._bandit = bandit
570
581
  super().__init__(_exit, dry)
571
582
 
572
583
  @staticmethod
@@ -576,11 +587,13 @@ class LintCode(DryRun):
576
587
  @staticmethod
577
588
  def prefer_dmypy(paths: str, tools: list[str]) -> bool:
578
589
  return (
579
- paths == "." and tools[-1].startswith("mypy") and not load_bool("NO_DMYPY")
590
+ paths == "."
591
+ and any(t.startswith("mypy") for t in tools)
592
+ and not load_bool("NO_DMYPY")
580
593
  )
581
594
 
582
595
  @classmethod
583
- def to_cmd(cls: Type[Self], paths=".", check_only=False) -> str:
596
+ def to_cmd(cls: Type[Self], paths=".", check_only=False, bandit=False) -> str:
584
597
  cmd = ""
585
598
  tools = ["ruff format", "ruff check --extend-select=I,B,SIM --fix", "mypy"]
586
599
  if check_only:
@@ -609,11 +622,24 @@ class LintCode(DryRun):
609
622
  if cls.prefer_dmypy(paths, tools):
610
623
  tools[-1] = "dmypy run"
611
624
  cmd += lint_them.format(prefix, paths, *tools)
625
+ if bandit or load_bool("FASTDEVCLI_BANDIT"):
626
+ command = prefix + "bandit"
627
+ if paths == ".": # fast check --bandit
628
+ command += " -r"
629
+ root = Project.get_work_dir(allow_cwd=True)
630
+ package_maybe = (root.name.replace("-", "_"), "src")
631
+ for name in package_maybe:
632
+ if root.joinpath(name).is_dir():
633
+ command += " " + name
634
+ break
635
+ else:
636
+ command += " ."
637
+ cmd += " && " + command
612
638
  return cmd
613
639
 
614
640
  def gen(self: Self) -> str:
615
641
  paths = " ".join(map(str, self.args)) if self.args else "."
616
- return self.to_cmd(paths, self.check_only)
642
+ return self.to_cmd(paths, self.check_only, self._bandit)
617
643
 
618
644
 
619
645
  def parse_files(args: list[str] | tuple[str, ...]) -> list[str]:
@@ -628,8 +654,8 @@ def lint(files=None, dry=False) -> None:
628
654
  LintCode(files, dry=dry).run()
629
655
 
630
656
 
631
- def check(files=None, dry=False) -> None:
632
- LintCode(files, check_only=True, _exit=True, dry=dry).run()
657
+ def check(files=None, dry=False, bandit=False) -> None:
658
+ LintCode(files, check_only=True, _exit=True, dry=dry, bandit=bandit).run()
633
659
 
634
660
 
635
661
  @cli.command(name="lint")
@@ -651,10 +677,11 @@ def make_style(
651
677
 
652
678
  @cli.command(name="check")
653
679
  def only_check(
680
+ bandit: bool = Option(False, "--bandit", help="Run `bandit -r <package_dir>`"),
654
681
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
655
682
  ) -> None:
656
683
  """Check code style without reformat"""
657
- check(dry=dry)
684
+ check(dry=dry, bandit=bandit)
658
685
 
659
686
 
660
687
  class Sync(DryRun):
@@ -694,7 +721,7 @@ def sync(
694
721
  Sync(filename, extras, save, dry=dry).run()
695
722
 
696
723
 
697
- def _should_run_test_script(path: Path) -> bool:
724
+ def _should_run_test_script(path: Path = Path("scripts/test.sh")) -> bool:
698
725
  return path.exists()
699
726
 
700
727
 
@@ -35,12 +35,12 @@ dependencies = [
35
35
  "emoji >=2.12.1,<3",
36
36
  "tomli>=2.0.1,<3; python_version < '3.11'",
37
37
  "coverage >=7.5.1,<8",
38
- "ruff >=0.4.4,<0.7",
38
+ "ruff >=0.4.4,<0.8",
39
39
  "mypy >=1.10.0,<2",
40
40
  "bumpversion2 >=1.4.2,<2",
41
41
  "pytest >=8.2.0,<9",
42
42
  ]
43
- version = "0.9.9"
43
+ version = "0.10.0"
44
44
 
45
45
  [project.urls]
46
46
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -3,7 +3,7 @@
3
3
  set -e
4
4
  set -x
5
5
 
6
- [ -f ../pyproject.toml ] && cd ..
6
+ [ -f pyproject.toml ] || ([ -f ../pyproject.toml ] && cd ..)
7
7
 
8
8
  pdm run fast check || \
9
9
  echo -e "\033[1m Please run './scripts/format.sh' to auto-fix style issues \033[0m"
@@ -0,0 +1,6 @@
1
+ #!/bin/sh -e
2
+ set -x
3
+
4
+ [ -f pyproject.toml ] || ([ -f ../pyproject.toml ] && cd ..)
5
+
6
+ SKIP_MYPY=1 pdm run fast lint
@@ -1,3 +1,4 @@
1
+ import subprocess
1
2
  from contextlib import redirect_stdout
2
3
  from io import StringIO
3
4
  from pathlib import Path
@@ -14,11 +15,11 @@ from fast_dev_cli.cli import (
14
15
  StrEnum,
15
16
  bump,
16
17
  bump_version,
18
+ capture_cmd_output,
17
19
  get_current_version,
18
20
  )
19
- from tests.utils import mock_sys_argv
20
21
 
21
- from .utils import chdir
22
+ from .utils import chdir, mock_sys_argv
22
23
 
23
24
 
24
25
  def test_enum():
@@ -40,7 +41,7 @@ def _bump_commands(
40
41
  cmd = rf'bumpversion --parse "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)" --current-version="{version}"'
41
42
  suffix = " --commit && git push && git push --tags && git log -1"
42
43
  if emoji:
43
- suffix = suffix.replace("--commit", "--commit --commit-emoji=1")
44
+ suffix = suffix.replace("--commit", "--commit --message-emoji=1")
44
45
  patch_without_commit = cmd + f" patch {filename} --allow-dirty"
45
46
  patch_with_commit = cmd + f" patch {filename}" + suffix
46
47
  minor_with_commit = cmd + f" minor {filename} --tag" + suffix
@@ -136,16 +137,34 @@ def test_bump_with_poetry(mocker, tmp_poetry_project, tmp_path):
136
137
  assert work_dir == work_dir2 == tmp_path
137
138
 
138
139
 
139
- def test_bump_with_emoji(mocker):
140
+ def test_bump_with_emoji(mocker, tmp_path, monkeypatch):
140
141
  mocker.patch("fast_dev_cli.cli.Project.manage_by_poetry", return_value=True)
141
142
  version = get_current_version()
142
143
  patch_without_commit, patch_with_commit, minor_with_commit = _bump_commands(
143
144
  version, emoji=True
144
145
  )
146
+ last_commit = "📝 Update release notes"
145
147
  mocker.patch(
146
148
  "fast_dev_cli.cli.BumpUp.get_last_commit_message",
147
- return_value="📝 Update release notes",
149
+ return_value=last_commit,
148
150
  )
149
151
  assert BumpUp(part="patch", commit=False, dry=True).gen() == patch_without_commit
150
152
  assert BumpUp(part="patch", commit=True, dry=True).gen() == patch_with_commit
151
153
  assert BumpUp(part="minor", commit=True, dry=True).gen() == minor_with_commit
154
+ # real bump
155
+ with chdir(tmp_path):
156
+ project = "foo"
157
+ subprocess.run(["poetry", "new", project])
158
+ with chdir(tmp_path / project):
159
+ subprocess.run(["git", "init"])
160
+ subprocess.run(["git", "add", "."])
161
+ subprocess.run(["git", "commit", "-m", last_commit])
162
+ monkeypatch.setenv("DONT_GIT_PUSH", "1")
163
+ command = BumpUp(part="patch", commit=True).gen()
164
+ expected = patch_with_commit.split("&&")[0].strip().replace('""', '"0.1.0"')
165
+ assert expected == command
166
+ subprocess.run(["poetry", "run", "pip", "install", "bumpversion2"])
167
+ subprocess.run(["fast", "bump", "patch", "--commit"])
168
+ out = capture_cmd_output(["git", "log"])
169
+ new_commit = "⬆️ Bump version: 0.1.0 → 0.1.1"
170
+ assert new_commit in out
@@ -2,7 +2,12 @@ import pathlib
2
2
 
3
3
  from pytest_mock import MockerFixture
4
4
 
5
- from fast_dev_cli.cli import Project, capture_cmd_output, coverage_test
5
+ from fast_dev_cli.cli import (
6
+ Project,
7
+ _should_run_test_script,
8
+ capture_cmd_output,
9
+ coverage_test,
10
+ )
6
11
  from fast_dev_cli.cli import test as unitcase
7
12
 
8
13
 
@@ -60,6 +65,7 @@ def test_test_not_in_venv(mocker: MockerFixture, capsys):
60
65
 
61
66
 
62
67
  def test_run_script(mocker: MockerFixture, capsys):
68
+ assert _should_run_test_script()
63
69
  mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=True)
64
70
  unitcase(dry=True)
65
71
  assert "sh scripts/test.sh" in capsys.readouterr().out
@@ -16,7 +16,7 @@ from fast_dev_cli.cli import (
16
16
  )
17
17
 
18
18
 
19
- def test_utils():
19
+ def test_utils(capsys):
20
20
  # parse files
21
21
  assert parse_files([]) == []
22
22
  assert parse_files(["-a", "--a"]) == []
@@ -52,6 +52,11 @@ def test_utils():
52
52
  assert load_bool(name) is True
53
53
  os.environ.pop(name)
54
54
  assert load_bool(name) is False
55
+ os.environ[name] = "yeah"
56
+ assert load_bool(name) is False
57
+ assert load_bool(name, True) is True
58
+ out = capsys.readouterr().out.strip()
59
+ assert "WARNING" in out
55
60
 
56
61
 
57
62
  def test_run_shell():
@@ -56,10 +56,18 @@ LINT_CMD = _CMD.format("", " --fix")
56
56
  CHECK_CMD = _CMD.format(" --check", "")
57
57
 
58
58
 
59
- def test_check(mock_no_dmypy):
59
+ def test_check(mock_no_dmypy, monkeypatch):
60
60
  command = capture_cmd_output("fast check --dry")
61
61
  for cmd in CHECK_CMD.split(SEP):
62
62
  assert cmd in command
63
+ command2 = capture_cmd_output("fast check --bandit --dry")
64
+ assert command2 == command + " && bandit -r fast_dev_cli"
65
+ monkeypatch.setenv("FASTDEVCLI_BANDIT", "1")
66
+ command3 = capture_cmd_output("fast check --dry")
67
+ assert command3 == command2
68
+ monkeypatch.setenv("FASTDEVCLI_BANDIT", "0")
69
+ command4 = capture_cmd_output("fast check --dry")
70
+ assert command4 == command
63
71
 
64
72
 
65
73
  def test_fast_check():
@@ -110,9 +118,15 @@ def test_lint_with_prefix(mocker):
110
118
 
111
119
  def test_make_style(mocker, mock_no_dmypy):
112
120
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
121
+ with capture_stdout() as stream:
122
+ make_style(check_only=False, dry=True)
123
+ assert LINT_CMD in stream.getvalue()
113
124
  with capture_stdout() as stream:
114
125
  make_style([Path(".")], check_only=False, dry=True)
115
126
  assert LINT_CMD in stream.getvalue()
127
+ with capture_stdout() as stream:
128
+ make_style(".", check_only=False, dry=True) # type:ignore[arg-type]
129
+ assert LINT_CMD in stream.getvalue()
116
130
  with capture_stdout() as stream:
117
131
  make_style([Path(".")], check_only=True, dry=True)
118
132
  assert CHECK_CMD in stream.getvalue()
@@ -138,6 +152,10 @@ def test_lint_func(mocker, mock_no_dmypy):
138
152
  with mock_sys_argv(["tests"]), capture_stdout() as stream:
139
153
  lint(dry=True)
140
154
  assert LINT_CMD.replace(" .", " tests") in stream.getvalue()
155
+ with capture_stdout() as stream:
156
+ lint(["lint"], dry=True)
157
+ assert LINT_CMD in stream.getvalue()
158
+ assert LINT_CMD in capture_cmd_output("python -m fast_dev_cli lint --dry")
141
159
 
142
160
 
143
161
  def test_lint_without_ruff_installed(mocker, mock_no_dmypy):
@@ -1,4 +1,5 @@
1
- from fast_dev_cli.cli import dev, run_and_echo, runserver
1
+ import fast_dev_cli
2
+ from fast_dev_cli.cli import dev, main, run_and_echo, runserver
2
3
 
3
4
 
4
5
  def test_runserver(capsys):
@@ -17,6 +18,12 @@ def test_runserver(capsys):
17
18
  runserver(port=9000, host="0.0.0.0", dry=True)
18
19
  out = capsys.readouterr().out.strip()
19
20
  assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
21
+ runserver("9000", host="0.0.0.0", dry=True)
22
+ out = capsys.readouterr().out.strip()
23
+ assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
24
+ runserver("app.py", host="0.0.0.0", dry=True)
25
+ out = capsys.readouterr().out.strip()
26
+ assert out.replace("--> ", "") == "fastapi dev app.py --host=0.0.0.0"
20
27
 
21
28
 
22
29
  def test_dev(capsys):
@@ -80,3 +87,9 @@ def test_run_by_module(tmp_path):
80
87
  assert "fastapi dev main.py --port=9000 --host=0.0.0.0" in out.read_text()
81
88
  run_and_echo(f"{fast} dev 9000 --host=0.0.0.0 --dry > {out}", verbose=False)
82
89
  assert "fastapi dev --port=9000 --host=0.0.0.0" in out.read_text()
90
+
91
+
92
+ def test_main(mocker):
93
+ mocker.patch("fast_dev_cli.cli.cli")
94
+ main()
95
+ fast_dev_cli.cli.cli.assert_called_once() # type:ignore[attr-defined]
@@ -1 +0,0 @@
1
- __version__ = "0.9.9"
@@ -1,6 +0,0 @@
1
- #!/bin/sh -e
2
- set -x
3
-
4
- [ -f ../pyproject.toml ] && cd ..
5
-
6
- SKIP_MYPY=1 pdm run fast lint
File without changes
File without changes
File without changes