fast-dev-cli 0.9.10__tar.gz → 0.10.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.
Files changed (32) hide show
  1. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/PKG-INFO +5 -5
  2. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/README.md +2 -2
  3. fast_dev_cli-0.10.1/fast_dev_cli/__init__.py +1 -0
  4. fast_dev_cli-0.10.1/fast_dev_cli/__main__.py +3 -0
  5. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/fast_dev_cli/cli.py +44 -14
  6. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/pyproject.toml +2 -2
  7. fast_dev_cli-0.10.1/scripts/check.py +19 -0
  8. fast_dev_cli-0.10.1/scripts/format.py +14 -0
  9. fast_dev_cli-0.10.1/scripts/test.py +47 -0
  10. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_fast_test.py +35 -17
  11. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_functions.py +6 -1
  12. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_lint.py +31 -1
  13. fast_dev_cli-0.10.1/tests/test_poetry_version_plugin.py +70 -0
  14. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_runserver.py +14 -1
  15. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_tag.py +7 -1
  16. fast_dev_cli-0.9.10/fast_dev_cli/__init__.py +0 -1
  17. fast_dev_cli-0.9.10/fast_dev_cli/__main__.py +0 -3
  18. fast_dev_cli-0.9.10/scripts/check.sh +0 -11
  19. fast_dev_cli-0.9.10/scripts/format.sh +0 -6
  20. fast_dev_cli-0.9.10/scripts/test.sh +0 -6
  21. fast_dev_cli-0.9.10/tests/test_poetry_version_plugin.py +0 -62
  22. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/LICENSE +0 -0
  23. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/fast_dev_cli/py.typed +0 -0
  24. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/pdm_build.py +0 -0
  25. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/__init__.py +0 -0
  26. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/conftest.py +0 -0
  27. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_bump.py +0 -0
  28. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_sync.py +0 -0
  29. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_upgrade.py +0 -0
  30. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_upload.py +0 -0
  31. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/tests/test_version.py +0 -0
  32. {fast_dev_cli-0.9.10 → fast_dev_cli-0.10.1}/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.10
3
+ Version: 0.10.1
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">
@@ -78,9 +78,9 @@ Python 3.10+
78
78
  <div class="termy">
79
79
 
80
80
  ```bash
81
- pip install "fast-dev-cli"
81
+ pip install fast-dev-cli
82
82
  ```
83
- *Will install: fast-dev-cli typer ruff mypy bumpversion pytest coverage*
83
+ *Will install: fast-dev-cli emoji typer ruff mypy bumpversion2 pytest coverage*
84
84
 
85
85
  </div>
86
86
 
@@ -43,9 +43,9 @@ Python 3.10+
43
43
  <div class="termy">
44
44
 
45
45
  ```bash
46
- pip install "fast-dev-cli"
46
+ pip install fast-dev-cli
47
47
  ```
48
- *Will install: fast-dev-cli typer ruff mypy bumpversion pytest coverage*
48
+ *Will install: fast-dev-cli emoji typer ruff mypy bumpversion2 pytest coverage*
49
49
 
50
50
  </div>
51
51
 
@@ -0,0 +1 @@
1
+ __version__ = "0.10.1"
@@ -0,0 +1,3 @@
1
+ from fast_dev_cli.cli import main
2
+
3
+ main()
@@ -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:
@@ -567,9 +572,12 @@ def tag(
567
572
 
568
573
 
569
574
  class LintCode(DryRun):
570
- 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:
571
578
  self.args = args
572
579
  self.check_only = check_only
580
+ self._bandit = bandit
573
581
  super().__init__(_exit, dry)
574
582
 
575
583
  @staticmethod
@@ -579,11 +587,22 @@ class LintCode(DryRun):
579
587
  @staticmethod
580
588
  def prefer_dmypy(paths: str, tools: list[str]) -> bool:
581
589
  return (
582
- 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")
583
593
  )
584
594
 
595
+ @staticmethod
596
+ def get_package_name() -> str:
597
+ root = Project.get_work_dir(allow_cwd=True)
598
+ package_maybe = (root.name.replace("-", "_"), "src")
599
+ for name in package_maybe:
600
+ if root.joinpath(name).is_dir():
601
+ return name
602
+ return "."
603
+
585
604
  @classmethod
586
- def to_cmd(cls: Type[Self], paths=".", check_only=False) -> str:
605
+ def to_cmd(cls: Type[Self], paths=".", check_only=False, bandit=False) -> str:
587
606
  cmd = ""
588
607
  tools = ["ruff format", "ruff check --extend-select=I,B,SIM --fix", "mypy"]
589
608
  if check_only:
@@ -601,7 +620,7 @@ class LintCode(DryRun):
601
620
  if is_venv():
602
621
  if not cls.check_lint_tool_installed():
603
622
  should_run_by_tool = True
604
- if check_call("python -c 'import fast_dev_cli'"):
623
+ if check_call('python -c "import fast_dev_cli"'):
605
624
  command = 'python -m pip install -U "fast_dev_cli"'
606
625
  tip = "You may need to run following command to install lint tools:"
607
626
  secho(f"{tip}\n\n {command}\n", fg="yellow")
@@ -612,11 +631,16 @@ class LintCode(DryRun):
612
631
  if cls.prefer_dmypy(paths, tools):
613
632
  tools[-1] = "dmypy run"
614
633
  cmd += lint_them.format(prefix, paths, *tools)
634
+ if bandit or load_bool("FASTDEVCLI_BANDIT"):
635
+ command = prefix + "bandit"
636
+ if paths == ".": # fast check --bandit
637
+ command += " -r " + cls.get_package_name()
638
+ cmd += " && " + command
615
639
  return cmd
616
640
 
617
641
  def gen(self: Self) -> str:
618
642
  paths = " ".join(map(str, self.args)) if self.args else "."
619
- return self.to_cmd(paths, self.check_only)
643
+ return self.to_cmd(paths, self.check_only, self._bandit)
620
644
 
621
645
 
622
646
  def parse_files(args: list[str] | tuple[str, ...]) -> list[str]:
@@ -631,8 +655,8 @@ def lint(files=None, dry=False) -> None:
631
655
  LintCode(files, dry=dry).run()
632
656
 
633
657
 
634
- def check(files=None, dry=False) -> None:
635
- LintCode(files, check_only=True, _exit=True, dry=dry).run()
658
+ def check(files=None, dry=False, bandit=False) -> None:
659
+ LintCode(files, check_only=True, _exit=True, dry=dry, bandit=bandit).run()
636
660
 
637
661
 
638
662
  @cli.command(name="lint")
@@ -654,10 +678,11 @@ def make_style(
654
678
 
655
679
  @cli.command(name="check")
656
680
  def only_check(
681
+ bandit: bool = Option(False, "--bandit", help="Run `bandit -r <package_dir>`"),
657
682
  dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
658
683
  ) -> None:
659
684
  """Check code style without reformat"""
660
- check(dry=dry)
685
+ check(dry=dry, bandit=bandit)
661
686
 
662
687
 
663
688
  class Sync(DryRun):
@@ -697,16 +722,21 @@ def sync(
697
722
  Sync(filename, extras, save, dry=dry).run()
698
723
 
699
724
 
700
- def _should_run_test_script(path: Path) -> bool:
701
- return path.exists()
725
+ def _should_run_test_script(path: Path = Path("scripts")) -> Path | None:
726
+ for name in ("test.sh", "test.py"):
727
+ if (file := path / name).exists():
728
+ return file
729
+ return None
702
730
 
703
731
 
704
732
  def test(dry: bool, ignore_script=False) -> None:
705
733
  cwd = Path.cwd()
706
734
  root = Project.get_work_dir(cwd=cwd, allow_cwd=True)
707
- test_script = root / "scripts" / "test.sh"
708
- if not _ensure_bool(ignore_script) and _should_run_test_script(test_script):
709
- cmd = f"sh {os.path.relpath(test_script, root)}"
735
+ script_dir = root / "scripts"
736
+ if not _ensure_bool(ignore_script) and (
737
+ test_script := _should_run_test_script(script_dir)
738
+ ):
739
+ cmd = f"{os.path.relpath(test_script, root)}"
710
740
  if cwd != root:
711
741
  cmd = f"cd {root} && " + cmd
712
742
  else:
@@ -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.10"
43
+ version = "0.10.1"
44
44
 
45
45
  [project.urls]
46
46
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python
2
+ import os
3
+ import sys
4
+
5
+ parent = os.path.abspath(os.path.dirname(__file__))
6
+ work_dir = os.path.dirname(parent)
7
+ if os.getcwd() != work_dir:
8
+ os.chdir(work_dir)
9
+
10
+ if os.system("pdm run fast check") != 0:
11
+ print("\033[1m Please run './scripts/format.py' to auto-fix style issues \033[0m")
12
+ sys.exit(1)
13
+
14
+ package_name = os.path.basename(work_dir).replace("-", "_")
15
+ cmd = "pdm run bandit -r {}".format(package_name)
16
+ print("-->", cmd)
17
+ if os.system(cmd) != 0:
18
+ sys.exit(1)
19
+ print("Done.")
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python
2
+ import os
3
+ import shlex
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ work_dir = Path(__file__).parent.resolve().parent
9
+ if Path.cwd() != work_dir:
10
+ os.chdir(str(work_dir))
11
+
12
+ cmd = "pdm run fast lint"
13
+ r = subprocess.run(shlex.split(cmd), env=dict(os.environ, SKIP_MYPY="1"))
14
+ sys.exit(None if r.returncode == 0 else 1)
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python
2
+ import os
3
+ import shlex
4
+ import subprocess
5
+ import sys
6
+ import time
7
+ from pathlib import Path
8
+
9
+ work_dir = Path(__file__).parent.resolve().parent
10
+ if Path.cwd() != work_dir:
11
+ os.chdir(str(work_dir))
12
+
13
+ CMD = "pdm run coverage run -m pytest"
14
+ COMBINE = "pdm run coverage combine .coverage*"
15
+ REPORT = "pdm run coverage report -m"
16
+
17
+
18
+ def remove_outdate_files(start_time: float) -> None:
19
+ for file in work_dir.glob(".coverage*"):
20
+ if file.stat().st_mtime < start_time:
21
+ file.unlink()
22
+ print(f"Removed outdate file: {file}")
23
+
24
+
25
+ def run_command(cmd: str, shell=False) -> None:
26
+ print("-->", cmd, flush=True)
27
+ r = subprocess.run(cmd if shell else shlex.split(cmd), shell=shell)
28
+ r.returncode and sys.exit(1)
29
+
30
+
31
+ def combine_result_files(shell=COMBINE) -> None:
32
+ to_be_combine = [i.name for i in work_dir.glob(".coverage.*")]
33
+ if to_be_combine:
34
+ if sys.platform == "win32":
35
+ if work_dir.joinpath(".coverage").exists():
36
+ shell = shell.replace("*", " ")
37
+ else:
38
+ shell = shell.replace(".coverage*", "")
39
+ shell += " ".join(to_be_combine)
40
+ run_command(shell, True)
41
+
42
+
43
+ started_at = time.time()
44
+ run_command(CMD)
45
+ remove_outdate_files(started_at)
46
+ combine_result_files()
47
+ run_command(REPORT)
@@ -1,10 +1,27 @@
1
+ import os
1
2
  import pathlib
3
+ from typing import Generator
2
4
 
5
+ import pytest
3
6
  from pytest_mock import MockerFixture
4
7
 
5
- from fast_dev_cli.cli import Project, capture_cmd_output, coverage_test
8
+ from fast_dev_cli.cli import (
9
+ Project,
10
+ _should_run_test_script,
11
+ capture_cmd_output,
12
+ coverage_test,
13
+ )
6
14
  from fast_dev_cli.cli import test as unitcase
7
15
 
16
+ TEST_SCRIPT = os.path.join("scripts", "test.py")
17
+
18
+
19
+ @pytest.fixture
20
+ def script_path() -> Generator[pathlib.Path, None, None]:
21
+ parent = pathlib.Path(__file__).parent
22
+ test_script = parent.resolve().parent / "scripts" / "test.py"
23
+ yield test_script
24
+
8
25
 
9
26
  def test_cli_test(mocker, capsys):
10
27
  output = capture_cmd_output("python fast_dev_cli/cli.py test --dry --ignore-script")
@@ -14,7 +31,7 @@ def test_cli_test(mocker, capsys):
14
31
  )
15
32
 
16
33
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
17
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=False)
34
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
18
35
  unitcase(dry=True)
19
36
  assert (
20
37
  'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
@@ -24,7 +41,7 @@ def test_cli_test(mocker, capsys):
24
41
 
25
42
  def test_test_with_pdm_run(mocker: MockerFixture, capsys):
26
43
  mocker.patch("fast_dev_cli.cli.check_call", return_value=False)
27
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=False)
44
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
28
45
  unitcase(dry=True)
29
46
  assert (
30
47
  '--> pdm run coverage run -m pytest -s && pdm run coverage report --omit="tests/*" -m'
@@ -34,7 +51,7 @@ def test_test_with_pdm_run(mocker: MockerFixture, capsys):
34
51
 
35
52
  def test_test_with_poetry_or_pdm_run(mocker: MockerFixture, capsys):
36
53
  mocker.patch("fast_dev_cli.cli.check_call", return_value=False)
37
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=False)
54
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
38
55
  mocker.patch("fast_dev_cli.cli.Project.manage_by_poetry", return_value=True)
39
56
  unitcase(dry=True)
40
57
  command = "coverage"
@@ -47,7 +64,7 @@ def test_test_with_poetry_or_pdm_run(mocker: MockerFixture, capsys):
47
64
 
48
65
 
49
66
  def test_test_not_in_venv(mocker: MockerFixture, capsys):
50
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=False)
67
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
51
68
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
52
69
  unitcase(dry=True)
53
70
  command = "coverage"
@@ -59,25 +76,26 @@ def test_test_not_in_venv(mocker: MockerFixture, capsys):
59
76
  )
60
77
 
61
78
 
62
- def test_run_script(mocker: MockerFixture, capsys):
63
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=True)
79
+ def test_run_script(mocker: MockerFixture, capsys, script_path):
80
+ assert _should_run_test_script()
81
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=script_path)
64
82
  unitcase(dry=True)
65
- assert "sh scripts/test.sh" in capsys.readouterr().out
83
+ assert TEST_SCRIPT in capsys.readouterr().out
84
+ assert _should_run_test_script(pathlib.Path("not-exist")) is None
66
85
 
67
86
 
68
- def test_ignore_script(mocker: MockerFixture, capsys):
69
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=True)
87
+ def test_ignore_script(mocker: MockerFixture, capsys, script_path):
88
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=script_path)
70
89
  unitcase(dry=True, ignore_script=True)
71
- assert "sh scripts/test.sh" not in capsys.readouterr().out
90
+ assert TEST_SCRIPT not in capsys.readouterr().out
72
91
 
73
92
 
74
- def test_run_script_in_sub_directory(mocker: MockerFixture, capsys):
75
- parent = pathlib.Path(__file__).parent
76
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=True)
77
- mocker.patch("pathlib.Path.cwd", return_value=parent)
93
+ def test_run_script_in_sub_directory(mocker: MockerFixture, capsys, script_path):
94
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=script_path)
95
+ mocker.patch("pathlib.Path.cwd", return_value=script_path.parent)
78
96
  unitcase(dry=True)
79
97
  out = capsys.readouterr().out
80
- assert f"cd {parent.parent} && sh scripts/test.sh" in out
98
+ assert f"cd {script_path.parent.parent} && {TEST_SCRIPT}" in out
81
99
 
82
100
 
83
101
  def test_fast_test(mocker, capsys):
@@ -88,7 +106,7 @@ def test_fast_test(mocker, capsys):
88
106
  )
89
107
 
90
108
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
91
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=False)
109
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=None)
92
110
  coverage_test(dry=True)
93
111
  assert (
94
112
  'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
@@ -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():
@@ -1,3 +1,4 @@
1
+ import shutil
1
2
  from pathlib import Path
2
3
 
3
4
  import pytest
@@ -10,6 +11,7 @@ from fast_dev_cli.cli import (
10
11
  lint,
11
12
  make_style,
12
13
  only_check,
14
+ run_and_echo,
13
15
  )
14
16
 
15
17
  from .utils import capture_stdout, chdir, mock_sys_argv
@@ -56,10 +58,28 @@ LINT_CMD = _CMD.format("", " --fix")
56
58
  CHECK_CMD = _CMD.format(" --check", "")
57
59
 
58
60
 
59
- def test_check(mock_no_dmypy):
61
+ def test_check(mock_no_dmypy, monkeypatch, mocker):
60
62
  command = capture_cmd_output("fast check --dry")
61
63
  for cmd in CHECK_CMD.split(SEP):
62
64
  assert cmd in command
65
+ command2 = capture_cmd_output("fast check --bandit --dry")
66
+ assert command2 == command + " && bandit -r fast_dev_cli"
67
+ monkeypatch.setenv("FASTDEVCLI_BANDIT", "1")
68
+ command3 = capture_cmd_output("fast check --dry")
69
+ assert command3 == command2
70
+ monkeypatch.setenv("FASTDEVCLI_BANDIT", "0")
71
+ command4 = capture_cmd_output("fast check --dry")
72
+ assert command4 == command
73
+
74
+
75
+ def test_check_bandit(tmp_path):
76
+ package_path = tmp_path / "foo"
77
+ with chdir(tmp_path):
78
+ run_and_echo(f"poetry new {package_path.name}")
79
+ shutil.rmtree(package_path / package_path.name)
80
+ with chdir(package_path):
81
+ command = capture_cmd_output("fast check --bandit --dry")
82
+ assert "bandit -r ." in command
63
83
 
64
84
 
65
85
  def test_fast_check():
@@ -110,9 +130,15 @@ def test_lint_with_prefix(mocker):
110
130
 
111
131
  def test_make_style(mocker, mock_no_dmypy):
112
132
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
133
+ with capture_stdout() as stream:
134
+ make_style(check_only=False, dry=True)
135
+ assert LINT_CMD in stream.getvalue()
113
136
  with capture_stdout() as stream:
114
137
  make_style([Path(".")], check_only=False, dry=True)
115
138
  assert LINT_CMD in stream.getvalue()
139
+ with capture_stdout() as stream:
140
+ make_style(".", check_only=False, dry=True) # type:ignore[arg-type]
141
+ assert LINT_CMD in stream.getvalue()
116
142
  with capture_stdout() as stream:
117
143
  make_style([Path(".")], check_only=True, dry=True)
118
144
  assert CHECK_CMD in stream.getvalue()
@@ -138,6 +164,10 @@ def test_lint_func(mocker, mock_no_dmypy):
138
164
  with mock_sys_argv(["tests"]), capture_stdout() as stream:
139
165
  lint(dry=True)
140
166
  assert LINT_CMD.replace(" .", " tests") in stream.getvalue()
167
+ with capture_stdout() as stream:
168
+ lint(["lint"], dry=True)
169
+ assert LINT_CMD in stream.getvalue()
170
+ assert LINT_CMD in capture_cmd_output("pdm run python -m fast_dev_cli lint --dry")
141
171
 
142
172
 
143
173
  def test_lint_without_ruff_installed(mocker, mock_no_dmypy):
@@ -0,0 +1,70 @@
1
+ import os
2
+ import shutil
3
+ import sys
4
+ from contextlib import contextmanager
5
+ from pathlib import Path
6
+ from typing import Generator
7
+
8
+ import pytest
9
+
10
+ from fast_dev_cli.cli import (
11
+ TOML_FILE,
12
+ BumpUp,
13
+ ParseError,
14
+ run_and_echo,
15
+ )
16
+
17
+ from .utils import chdir
18
+
19
+ CONF = """
20
+
21
+ [tool.poetry-version-plugin]
22
+ source = "init"
23
+ """
24
+
25
+
26
+ @contextmanager
27
+ def _prepare_package(
28
+ package_path: Path, define_include=False
29
+ ) -> Generator[Path, None, None]:
30
+ toml_file = package_path / TOML_FILE
31
+ package_name = package_path.name.replace(" ", "_")
32
+ init_file = package_path / package_name / "__init__.py"
33
+ a, b = 'version = "0.1.0"', 'version = "0"'
34
+ if define_include:
35
+ b += '\npackages = [{include = "%s"}]' % package_name
36
+ with chdir(package_path.parent):
37
+ run_and_echo(f'poetry new "{package_path.name}"')
38
+ toml_file.unlink()
39
+ py_version = "{0}.{1}".format(*sys.version_info)
40
+ with chdir(package_path):
41
+ run_and_echo(f'poetry init --python="^{py_version}" --no-interaction')
42
+ text = toml_file.read_text().replace(a, b)
43
+ toml_file.write_text(text + CONF)
44
+ shutil.move(package_path.name, package_name)
45
+ init_file.write_text('__version__ = "0.0.1"\n')
46
+ yield init_file
47
+
48
+
49
+ def _build_bump_cmd(init_file: Path) -> str:
50
+ relative_path = os.path.join(init_file.parent.name, init_file.name)
51
+ return rf'bumpversion --parse "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)" --current-version="0.0.1" patch {relative_path} --allow-dirty'
52
+
53
+
54
+ def test_version_plugin(tmp_path: Path) -> None:
55
+ with _prepare_package(tmp_path / "helloworld") as init_file:
56
+ command = _build_bump_cmd(init_file)
57
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == command
58
+ run_and_echo("poetry run fast bump patch")
59
+ assert init_file.read_text() == '__version__ = "0.0.2"\n'
60
+ init_file.unlink()
61
+ with pytest.raises(ParseError, match=r"Version file not found!.*"):
62
+ BumpUp(part="patch", commit=False, dry=True).gen()
63
+
64
+
65
+ def test_version_plugin_include_defined(tmp_path: Path) -> None:
66
+ with _prepare_package(tmp_path / "hello world", True) as init_file:
67
+ command = _build_bump_cmd(init_file)
68
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == command
69
+ run_and_echo("poetry run fast bump patch")
70
+ assert init_file.read_text() == '__version__ = "0.0.2"\n'
@@ -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,3 +1,4 @@
1
+ import sys
1
2
  from contextlib import contextmanager
2
3
 
3
4
  from fast_dev_cli.cli import (
@@ -36,7 +37,12 @@ def test_echo_when_not_dry(mocker, capsys):
36
37
 
37
38
  @contextmanager
38
39
  def _clear_tags():
39
- run_and_echo("git tag | xargs git tag -d")
40
+ if sys.platform == "win32":
41
+ for t in capture_cmd_output("git tag").splitlines():
42
+ if "v" in (tag := t.strip()):
43
+ run_and_echo(f"git tag -d {tag}")
44
+ else:
45
+ run_and_echo("git tag | xargs git tag -d")
40
46
  yield
41
47
  run_and_echo("git pull --tags")
42
48
 
@@ -1 +0,0 @@
1
- __version__ = "0.9.10"
@@ -1,3 +0,0 @@
1
- from .cli import main
2
-
3
- main()
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -e
4
- set -x
5
-
6
- [ -f ../pyproject.toml ] && cd ..
7
-
8
- pdm run fast check || \
9
- echo -e "\033[1m Please run './scripts/format.sh' to auto-fix style issues \033[0m"
10
-
11
- pdm run bandit -r fast_dev_cli/
@@ -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
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -e
4
- pdm run coverage run -m pytest
5
- pdm run coverage combine .coverage*
6
- pdm run coverage report -m
@@ -1,62 +0,0 @@
1
- from pathlib import Path
2
-
3
- import pytest
4
-
5
- from fast_dev_cli.cli import (
6
- TOML_FILE,
7
- BumpUp,
8
- ParseError,
9
- run_and_echo,
10
- )
11
-
12
- from .utils import chdir
13
-
14
- CONF = """
15
-
16
- [tool.poetry-version-plugin]
17
- source = "init"
18
- """
19
-
20
-
21
- def test_version_plugin(tmp_path: Path) -> None:
22
- package_path = tmp_path / "helloworld"
23
- toml_file = package_path / TOML_FILE
24
- init_file = package_path / package_path.name / "__init__.py"
25
- a, b = 'version = "0.1.0"', 'version = "0"'
26
- with chdir(tmp_path):
27
- run_and_echo(f"poetry new {package_path.name}")
28
- with chdir(package_path):
29
- text = toml_file.read_text().replace(a, b)
30
- toml_file.write_text(text + CONF)
31
- init_file.write_text('__version__ = "0.0.1"\n')
32
- assert (
33
- BumpUp(part="patch", commit=False, dry=True).gen()
34
- == 'bumpversion --parse "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)" --current-version="0.0.1" patch helloworld/__init__.py --allow-dirty'
35
- )
36
- run_and_echo("poetry run fast bump patch")
37
- assert init_file.read_text() == '__version__ = "0.0.2"\n'
38
- init_file.unlink()
39
- with pytest.raises(ParseError, match=r"Version file not found!.*"):
40
- BumpUp(part="patch", commit=False, dry=True).gen()
41
-
42
-
43
- def test_version_plugin_include_defined(tmp_path: Path) -> None:
44
- package_path = tmp_path / "hello world"
45
- toml_file = package_path / TOML_FILE
46
- package_name = package_path.name.replace(" ", "_")
47
- init_file = package_path / package_name / "__init__.py"
48
- a, b = 'version = "0.1.0"', 'version = "0"'
49
- b += '\npackages = [{include = "%s"}]' % package_name
50
- with chdir(tmp_path):
51
- run_and_echo(f"poetry new '{package_path.name}'")
52
- with chdir(package_path):
53
- text = toml_file.read_text().replace(a, b)
54
- toml_file.write_text(text + CONF)
55
- run_and_echo(f"mv '{package_path.name}' {package_name}")
56
- init_file.write_text('__version__ = "0.0.1"\n')
57
- assert (
58
- BumpUp(part="patch", commit=False, dry=True).gen()
59
- == f'bumpversion --parse "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)" --current-version="0.0.1" patch {package_name}/__init__.py --allow-dirty'
60
- )
61
- run_and_echo("poetry run fast bump patch")
62
- assert init_file.read_text() == '__version__ = "0.0.2"\n'
File without changes