fast-dev-cli 0.10.0__tar.gz → 0.11.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 (34) hide show
  1. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/PKG-INFO +3 -3
  2. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/README.md +2 -2
  3. fast_dev_cli-0.11.0/fast_dev_cli/__init__.py +1 -0
  4. fast_dev_cli-0.11.0/fast_dev_cli/__main__.py +3 -0
  5. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/fast_dev_cli/cli.py +97 -37
  6. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/pyproject.toml +1 -1
  7. fast_dev_cli-0.11.0/scripts/check.py +20 -0
  8. fast_dev_cli-0.11.0/scripts/format.py +15 -0
  9. fast_dev_cli-0.11.0/scripts/test.py +48 -0
  10. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_fast_test.py +28 -16
  11. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_lint.py +16 -2
  12. fast_dev_cli-0.11.0/tests/test_poetry_version_plugin.py +94 -0
  13. fast_dev_cli-0.11.0/tests/test_sync.py +214 -0
  14. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_tag.py +7 -1
  15. fast_dev_cli-0.11.0/tests/test_version.py +56 -0
  16. fast_dev_cli-0.10.0/fast_dev_cli/__init__.py +0 -1
  17. fast_dev_cli-0.10.0/fast_dev_cli/__main__.py +0 -3
  18. fast_dev_cli-0.10.0/scripts/check.sh +0 -11
  19. fast_dev_cli-0.10.0/scripts/format.sh +0 -6
  20. fast_dev_cli-0.10.0/scripts/test.sh +0 -6
  21. fast_dev_cli-0.10.0/tests/test_poetry_version_plugin.py +0 -62
  22. fast_dev_cli-0.10.0/tests/test_sync.py +0 -64
  23. fast_dev_cli-0.10.0/tests/test_version.py +0 -10
  24. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/LICENSE +0 -0
  25. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/fast_dev_cli/py.typed +0 -0
  26. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/pdm_build.py +0 -0
  27. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/__init__.py +0 -0
  28. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/conftest.py +0 -0
  29. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_bump.py +0 -0
  30. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_functions.py +0 -0
  31. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_runserver.py +0 -0
  32. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_upgrade.py +0 -0
  33. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_upload.py +0 -0
  34. {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.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.10.0
3
+ Version: 0.11.0
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -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.11.0"
@@ -0,0 +1,3 @@
1
+ from fast_dev_cli.cli import main
2
+
3
+ main()
@@ -8,7 +8,7 @@ import subprocess # nosec:B404
8
8
  import sys
9
9
  from functools import cached_property
10
10
  from pathlib import Path
11
- from typing import Literal, Optional, Type
11
+ from typing import Literal, Optional, Type, TypeAlias
12
12
 
13
13
  import emoji
14
14
  import typer
@@ -37,8 +37,9 @@ else: # pragma: no cover
37
37
  __str__ = str.__str__
38
38
 
39
39
 
40
- TOML_FILE = "pyproject.toml"
41
40
  cli = typer.Typer()
41
+ TOML_FILE = "pyproject.toml"
42
+ ToolName: TypeAlias = Literal["poetry", "pdm", "uv", ""]
42
43
 
43
44
 
44
45
  def load_bool(name: str, default=False) -> bool:
@@ -86,15 +87,55 @@ def capture_cmd_output(command: list[str] | str, **kw) -> str:
86
87
  return r.stdout.strip().decode()
87
88
 
88
89
 
90
+ def _parse_version(line: str, pattern: re.Pattern) -> str:
91
+ return pattern.sub("", line).split("#")[0].strip(" '\"")
92
+
93
+
94
+ def read_version_from_file(
95
+ package_name: str, work_dir=None, toml_text: str | None = None
96
+ ) -> str:
97
+ if toml_text is None:
98
+ toml_text = Project.load_toml_text()
99
+ pattern = re.compile(r"version\s*=")
100
+ invalid = ("0", "0.0.0")
101
+ for line in toml_text.splitlines():
102
+ if pattern.match(line):
103
+ version = _parse_version(line, pattern)
104
+ if version.startswith("{") or version in invalid:
105
+ break
106
+ return version
107
+ if work_dir is None:
108
+ work_dir = Project.get_work_dir()
109
+ package_dir = work_dir / package_name
110
+ init_file = package_dir / "__init__.py"
111
+ if not init_file.exists():
112
+ init_file = work_dir / "app" / init_file.name
113
+ if not init_file.exists():
114
+ secho("WARNING: __init__.py file does not exist!")
115
+ return "0.0.0"
116
+ pattern = re.compile(r"__version__\s*=")
117
+ for line in init_file.read_text().splitlines():
118
+ if pattern.match(line):
119
+ return _parse_version(line, pattern)
120
+ secho(f"WARNING: can not find '__version__' var in {init_file}!")
121
+ return "0.0.0"
122
+
123
+
89
124
  def get_current_version(
90
- verbose=False,
91
- is_poetry: bool | None = None,
92
- package_name=Path(__file__).parent.name,
125
+ verbose=False, is_poetry: bool | None = None, package_name: str | None = None
93
126
  ) -> str:
94
127
  if is_poetry is None:
95
128
  is_poetry = Project.manage_by_poetry()
96
129
  if not is_poetry:
97
- return importlib_metadata.version(package_name)
130
+ work_dir = None
131
+ if package_name is None:
132
+ work_dir = Project.get_work_dir()
133
+ package_name = re.sub(r"[- ]", "_", work_dir.name)
134
+ try:
135
+ return importlib_metadata.version(package_name)
136
+ except importlib_metadata.PackageNotFoundError:
137
+ return read_version_from_file(package_name, work_dir)
138
+
98
139
  cmd = ["poetry", "version", "-s"]
99
140
  if verbose:
100
141
  echo(f"--> {' '.join(cmd)}")
@@ -182,7 +223,7 @@ class BumpUp(DryRun):
182
223
  version_value = context["tool"]["poetry"]["version"]
183
224
  except KeyError:
184
225
  return TOML_FILE
185
- if version_value == "0":
226
+ if version_value in ("0", "0.0.0"):
186
227
  try:
187
228
  package_item = context["tool"]["poetry"]["packages"]
188
229
  except KeyError:
@@ -321,16 +362,16 @@ class Project:
321
362
  return "[tool.poetry]" in cls.load_toml_text()
322
363
 
323
364
  @classmethod
324
- def get_manage_tool(cls: Type[Self]) -> Literal["poetry", "pdm", ""]:
365
+ def get_manage_tool(cls: Type[Self]) -> ToolName:
325
366
  try:
326
367
  text = cls.load_toml_text()
327
368
  except EnvError:
328
369
  pass
329
370
  else:
330
- if "[tool.poetry]" in text:
331
- return "poetry"
332
- elif "[tool.pdm]" in text:
333
- return "pdm"
371
+ name: ToolName
372
+ for name in ("poetry", "pdm", "uv"):
373
+ if f"[tool.{name}]" in text:
374
+ return name
334
375
  return ""
335
376
 
336
377
  @staticmethod
@@ -592,6 +633,15 @@ class LintCode(DryRun):
592
633
  and not load_bool("NO_DMYPY")
593
634
  )
594
635
 
636
+ @staticmethod
637
+ def get_package_name() -> str:
638
+ root = Project.get_work_dir(allow_cwd=True)
639
+ package_maybe = (root.name.replace("-", "_"), "src")
640
+ for name in package_maybe:
641
+ if root.joinpath(name).is_dir():
642
+ return name
643
+ return "."
644
+
595
645
  @classmethod
596
646
  def to_cmd(cls: Type[Self], paths=".", check_only=False, bandit=False) -> str:
597
647
  cmd = ""
@@ -611,7 +661,7 @@ class LintCode(DryRun):
611
661
  if is_venv():
612
662
  if not cls.check_lint_tool_installed():
613
663
  should_run_by_tool = True
614
- if check_call("python -c 'import fast_dev_cli'"):
664
+ if check_call('python -c "import fast_dev_cli"'):
615
665
  command = 'python -m pip install -U "fast_dev_cli"'
616
666
  tip = "You may need to run following command to install lint tools:"
617
667
  secho(f"{tip}\n\n {command}\n", fg="yellow")
@@ -625,15 +675,7 @@ class LintCode(DryRun):
625
675
  if bandit or load_bool("FASTDEVCLI_BANDIT"):
626
676
  command = prefix + "bandit"
627
677
  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 += " ."
678
+ command += " -r " + cls.get_package_name()
637
679
  cmd += " && " + command
638
680
  return cmd
639
681
 
@@ -694,18 +736,31 @@ class Sync(DryRun):
694
736
  def gen(self) -> str:
695
737
  extras, save = self.extras, self._save
696
738
  should_remove = not Path.cwd().joinpath(self.filename).exists()
697
- prefix = "" if is_venv() else "poetry run "
698
- install_cmd = (
699
- "poetry export --with=dev --without-hashes -o {0}"
700
- " && {1}pip install -r {0}"
701
- )
702
- if not UpgradeDependencies.should_with_dev():
703
- install_cmd = install_cmd.replace(" --with=dev", "")
704
- if extras and isinstance(extras, str | list):
705
- install_cmd = install_cmd.replace("export", f"export --{extras=}")
739
+ if not (tool := Project.get_manage_tool()):
740
+ if should_remove or not is_venv():
741
+ raise EnvError("There project is not managed by uv/pdm/poetry!")
742
+ return f"python -m pip install -r {self.filename}"
743
+ prefix = "" if is_venv() else f"{tool} run "
744
+ ensurepip = " {1}python -m ensurepip && {1}python -m pip install -U pip &&"
745
+ match tool:
746
+ case "uv":
747
+ export_cmd = "uv export --no-hashes --all-extras --frozen"
748
+ if check_call(prefix + "python -m pip --version"):
749
+ ensurepip = ""
750
+ case "poetry" | "pdm":
751
+ export_cmd = f"{tool} export --without-hashes --with=dev"
752
+ if tool == "poetry":
753
+ ensurepip = ""
754
+ if not UpgradeDependencies.should_with_dev():
755
+ export_cmd = export_cmd.replace(" --with=dev", "")
756
+ if extras and isinstance(extras, str | list):
757
+ export_cmd += f" --{extras=}".replace("'", '"')
758
+ elif check_call(prefix + "python -m pip --version"):
759
+ ensurepip = ""
760
+ install_cmd = "{2} -o {0} &&%s {1}python -m pip install -r {0}" % ensurepip
706
761
  if should_remove and not save:
707
762
  install_cmd += " && rm -f {0}"
708
- return install_cmd.format(self.filename, prefix)
763
+ return install_cmd.format(self.filename, prefix, export_cmd)
709
764
 
710
765
 
711
766
  @cli.command()
@@ -721,16 +776,21 @@ def sync(
721
776
  Sync(filename, extras, save, dry=dry).run()
722
777
 
723
778
 
724
- def _should_run_test_script(path: Path = Path("scripts/test.sh")) -> bool:
725
- return path.exists()
779
+ def _should_run_test_script(path: Path = Path("scripts")) -> Path | None:
780
+ for name in ("test.sh", "test.py"):
781
+ if (file := path / name).exists():
782
+ return file
783
+ return None
726
784
 
727
785
 
728
786
  def test(dry: bool, ignore_script=False) -> None:
729
787
  cwd = Path.cwd()
730
788
  root = Project.get_work_dir(cwd=cwd, allow_cwd=True)
731
- test_script = root / "scripts" / "test.sh"
732
- if not _ensure_bool(ignore_script) and _should_run_test_script(test_script):
733
- cmd = f"sh {os.path.relpath(test_script, root)}"
789
+ script_dir = root / "scripts"
790
+ if not _ensure_bool(ignore_script) and (
791
+ test_script := _should_run_test_script(script_dir)
792
+ ):
793
+ cmd = f"{os.path.relpath(test_script, root)}"
734
794
  if cwd != root:
735
795
  cmd = f"cd {root} && " + cmd
736
796
  else:
@@ -40,7 +40,7 @@ dependencies = [
40
40
  "bumpversion2 >=1.4.2,<2",
41
41
  "pytest >=8.2.0,<9",
42
42
  ]
43
- version = "0.10.0"
43
+ version = "0.11.0"
44
44
 
45
45
  [project.urls]
46
46
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding:utf-8 -*-
3
+ import os
4
+ import sys
5
+
6
+ parent = os.path.abspath(os.path.dirname(__file__))
7
+ work_dir = os.path.dirname(parent)
8
+ if os.getcwd() != work_dir:
9
+ os.chdir(work_dir)
10
+
11
+ if os.system("pdm run fast check") != 0:
12
+ print("\033[1m Please run './scripts/format.py' to auto-fix style issues \033[0m")
13
+ sys.exit(1)
14
+
15
+ package_name = os.path.basename(work_dir).replace("-", "_")
16
+ cmd = "pdm run bandit -r {}".format(package_name)
17
+ print("-->", cmd)
18
+ if os.system(cmd) != 0:
19
+ sys.exit(1)
20
+ print("Done. ✨ 🍰 ✨")
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding:utf-8 -*-
3
+ import os
4
+ import shlex
5
+ import subprocess
6
+ import sys
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 fast lint"
14
+ r = subprocess.run(shlex.split(cmd), env=dict(os.environ, SKIP_MYPY="1"))
15
+ sys.exit(None if r.returncode == 0 else 1)
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding:utf-8 -*-
3
+ import os
4
+ import shlex
5
+ import subprocess
6
+ import sys
7
+ import time
8
+ from pathlib import Path
9
+
10
+ work_dir = Path(__file__).parent.resolve().parent
11
+ if Path.cwd() != work_dir:
12
+ os.chdir(str(work_dir))
13
+
14
+ CMD = "pdm run coverage run -m pytest"
15
+ COMBINE = "pdm run coverage combine .coverage*"
16
+ REPORT = "pdm run coverage report -m"
17
+
18
+
19
+ def remove_outdate_files(start_time: float) -> None:
20
+ for file in work_dir.glob(".coverage*"):
21
+ if file.stat().st_mtime < start_time:
22
+ file.unlink()
23
+ print(f"Removed outdate file: {file}")
24
+
25
+
26
+ def run_command(cmd: str, shell=False) -> None:
27
+ print("-->", cmd, flush=True)
28
+ r = subprocess.run(cmd if shell else shlex.split(cmd), shell=shell)
29
+ r.returncode and sys.exit(1)
30
+
31
+
32
+ def combine_result_files(shell=COMBINE) -> None:
33
+ to_be_combine = [i.name for i in work_dir.glob(".coverage.*")]
34
+ if to_be_combine:
35
+ if sys.platform == "win32":
36
+ if work_dir.joinpath(".coverage").exists():
37
+ shell = shell.replace("*", " ")
38
+ else:
39
+ shell = shell.replace(".coverage*", "")
40
+ shell += " ".join(to_be_combine)
41
+ run_command(shell, True)
42
+
43
+
44
+ started_at = time.time()
45
+ run_command(CMD)
46
+ remove_outdate_files(started_at)
47
+ combine_result_files()
48
+ run_command(REPORT)
@@ -1,5 +1,8 @@
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
8
  from fast_dev_cli.cli import (
@@ -10,6 +13,15 @@ from fast_dev_cli.cli import (
10
13
  )
11
14
  from fast_dev_cli.cli import test as unitcase
12
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
+
13
25
 
14
26
  def test_cli_test(mocker, capsys):
15
27
  output = capture_cmd_output("python fast_dev_cli/cli.py test --dry --ignore-script")
@@ -19,7 +31,7 @@ def test_cli_test(mocker, capsys):
19
31
  )
20
32
 
21
33
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
22
- 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)
23
35
  unitcase(dry=True)
24
36
  assert (
25
37
  'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
@@ -29,7 +41,7 @@ def test_cli_test(mocker, capsys):
29
41
 
30
42
  def test_test_with_pdm_run(mocker: MockerFixture, capsys):
31
43
  mocker.patch("fast_dev_cli.cli.check_call", return_value=False)
32
- 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)
33
45
  unitcase(dry=True)
34
46
  assert (
35
47
  '--> pdm run coverage run -m pytest -s && pdm run coverage report --omit="tests/*" -m'
@@ -39,7 +51,7 @@ def test_test_with_pdm_run(mocker: MockerFixture, capsys):
39
51
 
40
52
  def test_test_with_poetry_or_pdm_run(mocker: MockerFixture, capsys):
41
53
  mocker.patch("fast_dev_cli.cli.check_call", return_value=False)
42
- 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)
43
55
  mocker.patch("fast_dev_cli.cli.Project.manage_by_poetry", return_value=True)
44
56
  unitcase(dry=True)
45
57
  command = "coverage"
@@ -52,7 +64,7 @@ def test_test_with_poetry_or_pdm_run(mocker: MockerFixture, capsys):
52
64
 
53
65
 
54
66
  def test_test_not_in_venv(mocker: MockerFixture, capsys):
55
- 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)
56
68
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
57
69
  unitcase(dry=True)
58
70
  command = "coverage"
@@ -64,26 +76,26 @@ def test_test_not_in_venv(mocker: MockerFixture, capsys):
64
76
  )
65
77
 
66
78
 
67
- def test_run_script(mocker: MockerFixture, capsys):
79
+ def test_run_script(mocker: MockerFixture, capsys, script_path):
68
80
  assert _should_run_test_script()
69
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=True)
81
+ mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=script_path)
70
82
  unitcase(dry=True)
71
- 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
72
85
 
73
86
 
74
- def test_ignore_script(mocker: MockerFixture, capsys):
75
- 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)
76
89
  unitcase(dry=True, ignore_script=True)
77
- assert "sh scripts/test.sh" not in capsys.readouterr().out
90
+ assert TEST_SCRIPT not in capsys.readouterr().out
78
91
 
79
92
 
80
- def test_run_script_in_sub_directory(mocker: MockerFixture, capsys):
81
- parent = pathlib.Path(__file__).parent
82
- mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=True)
83
- 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)
84
96
  unitcase(dry=True)
85
97
  out = capsys.readouterr().out
86
- assert f"cd {parent.parent} && sh scripts/test.sh" in out
98
+ assert f"cd {script_path.parent.parent} && {TEST_SCRIPT}" in out
87
99
 
88
100
 
89
101
  def test_fast_test(mocker, capsys):
@@ -94,7 +106,7 @@ def test_fast_test(mocker, capsys):
94
106
  )
95
107
 
96
108
  mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
97
- 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)
98
110
  coverage_test(dry=True)
99
111
  assert (
100
112
  'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
@@ -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,7 +58,7 @@ LINT_CMD = _CMD.format("", " --fix")
56
58
  CHECK_CMD = _CMD.format(" --check", "")
57
59
 
58
60
 
59
- def test_check(mock_no_dmypy, monkeypatch):
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
@@ -70,6 +72,16 @@ def test_check(mock_no_dmypy, monkeypatch):
70
72
  assert command4 == command
71
73
 
72
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
83
+
84
+
73
85
  def test_fast_check():
74
86
  _fast_check()
75
87
 
@@ -155,7 +167,7 @@ def test_lint_func(mocker, mock_no_dmypy):
155
167
  with capture_stdout() as stream:
156
168
  lint(["lint"], dry=True)
157
169
  assert LINT_CMD in stream.getvalue()
158
- assert LINT_CMD in capture_cmd_output("python -m fast_dev_cli lint --dry")
170
+ assert LINT_CMD in capture_cmd_output("pdm run python -m fast_dev_cli lint --dry")
159
171
 
160
172
 
161
173
  def test_lint_without_ruff_installed(mocker, mock_no_dmypy):
@@ -226,3 +238,5 @@ def test_get_manage_tool(tmp_path):
226
238
  assert Project.get_manage_tool() == "poetry"
227
239
  Path(TOML_FILE).write_text("[tool.pdm]")
228
240
  assert Project.get_manage_tool() == "pdm"
241
+ Path(TOML_FILE).write_text("[tool.uv]")
242
+ assert Project.get_manage_tool() == "uv"
@@ -0,0 +1,94 @@
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
+ CONF_2 = """
25
+
26
+ [tool.poetry-dynamic-versioning]
27
+ enable = true
28
+ """
29
+
30
+
31
+ @contextmanager
32
+ def _prepare_package(
33
+ package_path: Path, define_include=False, mark="0"
34
+ ) -> Generator[Path, None, None]:
35
+ toml_file = package_path / TOML_FILE
36
+ package_name = package_path.name.replace(" ", "_")
37
+ init_file = package_path / package_name / "__init__.py"
38
+ a, b = 'version = "0.1.0"', f'version = "{mark}"'
39
+ if define_include:
40
+ b += '\npackages = [{include = "%s"}]' % package_name
41
+ with chdir(package_path.parent):
42
+ run_and_echo(f'poetry new "{package_path.name}"')
43
+ toml_file.unlink()
44
+ py_version = "{0}.{1}".format(*sys.version_info)
45
+ with chdir(package_path):
46
+ run_and_echo(f'poetry init --python="^{py_version}" --no-interaction')
47
+ text = toml_file.read_text().replace(a, b)
48
+ toml_file.write_text(text + CONF)
49
+ shutil.move(package_path.name, package_name)
50
+ init_file.write_text('__version__ = "0.0.1"\n')
51
+ yield init_file
52
+
53
+
54
+ def _build_bump_cmd(init_file: Path) -> str:
55
+ relative_path = os.path.join(init_file.parent.name, init_file.name)
56
+ return rf'bumpversion --parse "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)" --current-version="0.0.1" patch {relative_path} --allow-dirty'
57
+
58
+
59
+ def test_version_plugin(tmp_path: Path) -> None:
60
+ with _prepare_package(tmp_path / "helloworld") as init_file:
61
+ command = _build_bump_cmd(init_file)
62
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == command
63
+ run_and_echo("poetry run fast bump patch")
64
+ assert init_file.read_text() == '__version__ = "0.0.2"\n'
65
+ init_file.unlink()
66
+ with pytest.raises(ParseError, match=r"Version file not found!.*"):
67
+ BumpUp(part="patch", commit=False, dry=True).gen()
68
+
69
+
70
+ def test_version_plugin_2(tmp_path: Path) -> None:
71
+ with _prepare_package(tmp_path / "helloworld", mark="0.0.0") as init_file:
72
+ command = _build_bump_cmd(init_file)
73
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == command
74
+ run_and_echo("poetry run fast bump patch")
75
+ assert init_file.read_text() == '__version__ = "0.0.2"\n'
76
+ init_file.unlink()
77
+ with pytest.raises(ParseError, match=r"Version file not found!.*"):
78
+ BumpUp(part="patch", commit=False, dry=True).gen()
79
+
80
+
81
+ def test_version_plugin_include_defined(tmp_path: Path) -> None:
82
+ with _prepare_package(tmp_path / "hello world", True) as init_file:
83
+ command = _build_bump_cmd(init_file)
84
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == command
85
+ run_and_echo("poetry run fast bump patch")
86
+ assert init_file.read_text() == '__version__ = "0.0.2"\n'
87
+
88
+
89
+ def test_version_plugin_include_defined_2(tmp_path: Path) -> None:
90
+ with _prepare_package(tmp_path / "hello world", True, mark="0.0.0") as init_file:
91
+ command = _build_bump_cmd(init_file)
92
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == command
93
+ run_and_echo("poetry run fast bump patch")
94
+ assert init_file.read_text() == '__version__ = "0.0.2"\n'
@@ -0,0 +1,214 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from fast_dev_cli.cli import TOML_FILE, EnvError, Sync, run_and_echo, sync
6
+
7
+ from .utils import chdir, temp_file
8
+
9
+ TOML_TEXT = """
10
+ [tool.poetry]
11
+ name = "foo"
12
+ version = "0.1.0"
13
+ description = ""
14
+ authors = []
15
+ readme = ""
16
+
17
+ [tool.poetry.dependencies]
18
+ python = "^3.11"
19
+ click = ">=7.1.1"
20
+ anyio = {version = "^4.0", optional = true}
21
+
22
+ [tool.poetry.extras]
23
+ all = ["anyio"]
24
+
25
+ [build-system]
26
+ requires = ["poetry-core"]
27
+ build-backend = "poetry.core.masonry.api"
28
+ """
29
+
30
+
31
+ def test_sync_not_in_venv(mocker, capsys):
32
+ mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
33
+ test_dir = Path(__file__).parent
34
+ with temp_file(TOML_FILE, TOML_TEXT), chdir(test_dir):
35
+ cmd = Sync("req.txt", "all", save=False, dry=True).gen()
36
+ assert (
37
+ cmd
38
+ == 'poetry export --without-hashes --extras="all" -o req.txt && poetry run python -m pip install -r req.txt && rm -f req.txt'
39
+ )
40
+ sync(extras="all", save=False, dry=True)
41
+ assert "pip install -r" in capsys.readouterr().out
42
+ mocker.patch(
43
+ "fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
44
+ )
45
+ assert (
46
+ Sync("req.txt", "", True, dry=True).gen()
47
+ == "pdm export --without-hashes --with=dev -o req.txt && pdm run python -m pip install -r req.txt"
48
+ )
49
+
50
+
51
+ def test_sync(mocker):
52
+ mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
53
+ test_dir = Path(__file__).parent
54
+ with temp_file(TOML_FILE, TOML_TEXT), chdir(test_dir):
55
+ cmd = Sync("req.txt", "all", save=False, dry=True).gen()
56
+ assert (
57
+ cmd
58
+ == 'poetry export --without-hashes --extras="all" -o req.txt && python -m pip install -r req.txt && rm -f req.txt'
59
+ )
60
+ mocker.patch(
61
+ "fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
62
+ )
63
+ assert (
64
+ Sync("req.txt", "", True, dry=True).gen()
65
+ == "pdm export --without-hashes --with=dev -o req.txt && python -m pip install -r req.txt"
66
+ )
67
+
68
+
69
+ UV_TOML_EXAMPLE = """
70
+ [project]
71
+ name = "examples"
72
+ version = "0.1.0"
73
+ description = "Add your description here"
74
+ readme = "README.md"
75
+ requires-python = ">=3.10"
76
+ dependencies = [
77
+ "asynctor>=0.6.6",
78
+ ]
79
+
80
+ [tool.uv]
81
+ index-url = "https://mirrors.cloud.tencent.com/pypi/simple/"
82
+ """
83
+ UV_LOCK_EXAMPLE = """
84
+ version = 1
85
+ requires-python = ">=3.10"
86
+
87
+ [[package]]
88
+ name = "anyio"
89
+ version = "4.6.2.post1"
90
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
91
+ dependencies = [
92
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
93
+ { name = "idna" },
94
+ { name = "sniffio" },
95
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
96
+ ]
97
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c" }
98
+ wheels = [
99
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d" },
100
+ ]
101
+
102
+ [[package]]
103
+ name = "async-timeout"
104
+ version = "4.0.3"
105
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
106
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/87/d6/21b30a550dafea84b1b8eee21b5e23fa16d010ae006011221f33dcd8d7f8/async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f" }
107
+ wheels = [
108
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028" },
109
+ ]
110
+
111
+ [[package]]
112
+ name = "asynctor"
113
+ version = "0.6.6"
114
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
115
+ dependencies = [
116
+ { name = "anyio" },
117
+ { name = "redis" },
118
+ ]
119
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/38/d0/452d78491eda3b70bd7b8d401093e5e77999f0232a836b4465e0395223c5/asynctor-0.6.6.tar.gz", hash = "sha256:594c60a38483e399d5a6be513256657989fa2bbf069572074c6b529fa72219ef" }
120
+ wheels = [
121
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/77/01/31143ebf73a22b5e11a1c6dd3f3af53f1d045d9ab0f13a0159b32df71440/asynctor-0.6.6-py3-none-any.whl", hash = "sha256:54785f1cfd32bd9f9bcffe9445ffd02421aab9c3501c134dbd955901606cf1b4" },
122
+ ]
123
+
124
+ [[package]]
125
+ name = "examples"
126
+ version = "0.1.0"
127
+ source = { virtual = "." }
128
+ dependencies = [
129
+ { name = "asynctor" },
130
+ ]
131
+
132
+ [package.metadata]
133
+ requires-dist = [{ name = "asynctor", specifier = ">=0.6.6" }]
134
+
135
+ [[package]]
136
+ name = "exceptiongroup"
137
+ version = "1.2.2"
138
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
139
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc" }
140
+ wheels = [
141
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b" },
142
+ ]
143
+
144
+ [[package]]
145
+ name = "idna"
146
+ version = "3.10"
147
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
148
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9" }
149
+ wheels = [
150
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" },
151
+ ]
152
+
153
+ [[package]]
154
+ name = "redis"
155
+ version = "5.1.1"
156
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
157
+ dependencies = [
158
+ { name = "async-timeout", marker = "python_full_version < '3.11.3'" },
159
+ ]
160
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/e0/58/dcf97c3c09d429c3bb830d6075322256da3dba42df25359bd1c82b442d20/redis-5.1.1.tar.gz", hash = "sha256:f6c997521fedbae53387307c5d0bf784d9acc28d9f1d058abeac566ec4dbed72" }
161
+ wheels = [
162
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/15/f1/feeeaaaac0f589bcbc12c02da357cf635ee383c9128b77230a1e99118885/redis-5.1.1-py3-none-any.whl", hash = "sha256:f8ea06b7482a668c6475ae202ed8d9bcaa409f6e87fb77ed1043d912afd62e24" },
163
+ ]
164
+
165
+ [[package]]
166
+ name = "sniffio"
167
+ version = "1.3.1"
168
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
169
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc" }
170
+ wheels = [
171
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2" },
172
+ ]
173
+
174
+ [[package]]
175
+ name = "typing-extensions"
176
+ version = "4.12.2"
177
+ source = { registry = "https://mirrors.cloud.tencent.com/pypi/simple/" }
178
+ sdist = { url = "https://mirrors.cloud.tencent.com/pypi/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" }
179
+ wheels = [
180
+ { url = "https://mirrors.cloud.tencent.com/pypi/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d" },
181
+ ]
182
+ """
183
+
184
+
185
+ def test_sync_uv(mocker, tmp_path):
186
+ mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
187
+ with chdir(tmp_path):
188
+ toml = tmp_path / TOML_FILE
189
+ toml.write_text(UV_TOML_EXAMPLE)
190
+ toml.with_name("uv.lock").write_text(UV_LOCK_EXAMPLE)
191
+ assert (
192
+ Sync("req.txt", "", True, dry=True).gen()
193
+ == "uv export --no-hashes --all-extras --frozen -o req.txt && uv run python -m ensurepip && uv run python -m pip install -U pip && uv run python -m pip install -r req.txt"
194
+ )
195
+ run_and_echo("uv run python -m ensurepip")
196
+ assert (
197
+ Sync("req.txt", "", True, dry=True).gen()
198
+ == "uv export --no-hashes --all-extras --frozen -o req.txt && uv run python -m pip install -r req.txt"
199
+ )
200
+
201
+
202
+ def test_sync_no_tool(mocker, tmp_path):
203
+ mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
204
+ with chdir(tmp_path):
205
+ with pytest.raises(EnvError):
206
+ Sync("req.txt", "", True, dry=True).gen()
207
+ Path("req.txt").write_text("six")
208
+ with pytest.raises(EnvError):
209
+ Sync("req.txt", "", True, dry=True).gen()
210
+ mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
211
+ assert (
212
+ Sync("req.txt", "", True, dry=True).gen()
213
+ == "python -m pip install -r req.txt"
214
+ )
@@ -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
 
@@ -0,0 +1,56 @@
1
+ import re
2
+ from pathlib import Path
3
+
4
+ from fast_dev_cli import __version__
5
+ from fast_dev_cli.cli import (
6
+ TOML_FILE,
7
+ _parse_version,
8
+ get_current_version,
9
+ read_version_from_file,
10
+ version,
11
+ )
12
+
13
+ from .utils import chdir
14
+
15
+
16
+ def test_version(capsys):
17
+ version()
18
+ assert get_current_version(is_poetry=False) in capsys.readouterr().out
19
+ assert get_current_version(is_poetry=False) == __version__
20
+ assert get_current_version(is_poetry=True) == ""
21
+ assert get_current_version() == __version__
22
+
23
+
24
+ def test_read_version(tmp_path: Path, capsys):
25
+ assert read_version_from_file("fast_dev_cli") == __version__
26
+ toml_text = 'version = "0.1.0"'
27
+ assert read_version_from_file("", toml_text=toml_text) == "0.1.0"
28
+ with chdir(tmp_path):
29
+ tmp_path.joinpath(TOML_FILE).write_text("")
30
+ assert read_version_from_file("") == "0.0.0"
31
+ assert "WARNING" in capsys.readouterr().out
32
+ init_file = tmp_path / "app" / "__init__.py"
33
+ init_file.parent.mkdir()
34
+ init_file.write_text("")
35
+ assert read_version_from_file("") == "0.0.0"
36
+ assert "WARNING" in capsys.readouterr().out
37
+ assert get_current_version() == "0.0.0"
38
+
39
+
40
+ def test_parse_version():
41
+ pattern = re.compile(r"version\s*=")
42
+ line = 'version="0.0.1" # adsfasf version="0.0.2"'
43
+ assert _parse_version(line, pattern) == "0.0.1"
44
+ line = 'version = "0.0.1" # adsfasf version="0.0.2"'
45
+ assert _parse_version(line, pattern) == "0.0.1"
46
+ line = "version = '0.0.1' # adsfasf version=\"0.0.2\""
47
+ assert _parse_version(line, pattern) == "0.0.1"
48
+ line = "version='0.0.1' # adsfasf version=\"0.0.2\""
49
+ assert _parse_version(line, pattern) == "0.0.1"
50
+ line = 'version="0.0.1"'
51
+ assert _parse_version(line, pattern) == "0.0.1"
52
+ pattern = re.compile(r"__version__\s*=")
53
+ line = '__version__ = "0.0.1"'
54
+ assert _parse_version(line, pattern) == "0.0.1"
55
+ line = '__version__ = "0.0.1" # 0.0.2'
56
+ assert _parse_version(line, pattern) == "0.0.1"
@@ -1 +0,0 @@
1
- __version__ = "0.10.0"
@@ -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 ] || ([ -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 ] || ([ -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'
@@ -1,64 +0,0 @@
1
- from pathlib import Path
2
-
3
- from fast_dev_cli.cli import TOML_FILE, Sync, sync
4
-
5
- from .utils import chdir, temp_file
6
-
7
- TOML_TEXT = """
8
- [tool.poetry]
9
- name = "foo"
10
- version = "0.1.0"
11
- description = ""
12
- authors = []
13
- readme = ""
14
-
15
- [tool.poetry.dependencies]
16
- python = "^3.11"
17
- click = ">=7.1.1"
18
- anyio = {version = "^4.0", optional = true}
19
-
20
- [tool.poetry.extras]
21
- all = ["anyio"]
22
-
23
- [build-system]
24
- requires = ["poetry-core"]
25
- build-backend = "poetry.core.masonry.api"
26
- """
27
-
28
-
29
- def test_sync_not_in_venv(mocker, capsys):
30
- mocker.patch("fast_dev_cli.cli.is_venv", return_value=False)
31
- test_dir = Path(__file__).parent
32
- with temp_file(TOML_FILE, TOML_TEXT), chdir(test_dir):
33
- cmd = Sync("req.txt", "all", save=False, dry=True).gen()
34
- assert (
35
- cmd
36
- == "poetry export --extras='all' --without-hashes -o req.txt && poetry run pip install -r req.txt && rm -f req.txt"
37
- )
38
- sync(extras="all", save=False, dry=True)
39
- assert "pip install -r" in capsys.readouterr().out
40
- mocker.patch(
41
- "fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
42
- )
43
- assert (
44
- Sync("req.txt", "", True, dry=True).gen()
45
- == "poetry export --with=dev --without-hashes -o req.txt && poetry run pip install -r req.txt"
46
- )
47
-
48
-
49
- def test_sync(mocker):
50
- mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
51
- test_dir = Path(__file__).parent
52
- with temp_file(TOML_FILE, TOML_TEXT), chdir(test_dir):
53
- cmd = Sync("req.txt", "all", save=False, dry=True).gen()
54
- assert (
55
- cmd
56
- == "poetry export --extras='all' --without-hashes -o req.txt && pip install -r req.txt && rm -f req.txt"
57
- )
58
- mocker.patch(
59
- "fast_dev_cli.cli.UpgradeDependencies.should_with_dev", return_value=True
60
- )
61
- assert (
62
- Sync("req.txt", "", True, dry=True).gen()
63
- == "poetry export --with=dev --without-hashes -o req.txt && pip install -r req.txt"
64
- )
@@ -1,10 +0,0 @@
1
- from fast_dev_cli import __version__
2
- from fast_dev_cli.cli import get_current_version, version
3
-
4
-
5
- def test_version(capsys):
6
- version()
7
- assert get_current_version(is_poetry=False) in capsys.readouterr().out
8
- assert get_current_version(is_poetry=False) == __version__
9
- assert get_current_version(is_poetry=True) == ""
10
- assert get_current_version() == __version__
File without changes