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.
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/PKG-INFO +3 -3
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/README.md +2 -2
- fast_dev_cli-0.11.0/fast_dev_cli/__init__.py +1 -0
- fast_dev_cli-0.11.0/fast_dev_cli/__main__.py +3 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/fast_dev_cli/cli.py +97 -37
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/pyproject.toml +1 -1
- fast_dev_cli-0.11.0/scripts/check.py +20 -0
- fast_dev_cli-0.11.0/scripts/format.py +15 -0
- fast_dev_cli-0.11.0/scripts/test.py +48 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_fast_test.py +28 -16
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_lint.py +16 -2
- fast_dev_cli-0.11.0/tests/test_poetry_version_plugin.py +94 -0
- fast_dev_cli-0.11.0/tests/test_sync.py +214 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_tag.py +7 -1
- fast_dev_cli-0.11.0/tests/test_version.py +56 -0
- fast_dev_cli-0.10.0/fast_dev_cli/__init__.py +0 -1
- fast_dev_cli-0.10.0/fast_dev_cli/__main__.py +0 -3
- fast_dev_cli-0.10.0/scripts/check.sh +0 -11
- fast_dev_cli-0.10.0/scripts/format.sh +0 -6
- fast_dev_cli-0.10.0/scripts/test.sh +0 -6
- fast_dev_cli-0.10.0/tests/test_poetry_version_plugin.py +0 -62
- fast_dev_cli-0.10.0/tests/test_sync.py +0 -64
- fast_dev_cli-0.10.0/tests/test_version.py +0 -10
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/LICENSE +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/pdm_build.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/__init__.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/conftest.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.10.0 → fast_dev_cli-0.11.0}/tests/test_upload.py +0 -0
- {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.
|
|
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
|
|
81
|
+
pip install fast-dev-cli
|
|
82
82
|
```
|
|
83
|
-
*Will install: fast-dev-cli typer ruff mypy
|
|
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
|
|
46
|
+
pip install fast-dev-cli
|
|
47
47
|
```
|
|
48
|
-
*Will install: fast-dev-cli typer ruff mypy
|
|
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"
|
|
@@ -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
|
-
|
|
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
|
|
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]) ->
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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(
|
|
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
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
"
|
|
701
|
-
)
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
|
725
|
-
|
|
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
|
-
|
|
732
|
-
if not _ensure_bool(ignore_script) and
|
|
733
|
-
|
|
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:
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
81
|
+
mocker.patch("fast_dev_cli.cli._should_run_test_script", return_value=script_path)
|
|
70
82
|
unitcase(dry=True)
|
|
71
|
-
assert
|
|
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=
|
|
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
|
|
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
|
-
|
|
82
|
-
mocker.patch("
|
|
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} &&
|
|
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=
|
|
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
|
-
|
|
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,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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|