fast-dev-cli 0.10.1__tar.gz → 0.11.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.
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/PKG-INFO +11 -3
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/README.md +10 -2
- fast_dev_cli-0.11.1/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/fast_dev_cli/cli.py +117 -35
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/pyproject.toml +1 -1
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/scripts/check.py +2 -1
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/scripts/format.py +1 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/scripts/test.py +1 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_lint.py +33 -1
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_poetry_version_plugin.py +27 -3
- fast_dev_cli-0.11.1/tests/test_sync.py +214 -0
- fast_dev_cli-0.11.1/tests/test_upload.py +43 -0
- fast_dev_cli-0.11.1/tests/test_version.py +56 -0
- fast_dev_cli-0.10.1/fast_dev_cli/__init__.py +0 -1
- fast_dev_cli-0.10.1/tests/test_sync.py +0 -64
- fast_dev_cli-0.10.1/tests/test_upload.py +0 -7
- fast_dev_cli-0.10.1/tests/test_version.py +0 -10
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/LICENSE +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/pdm_build.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/__init__.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/conftest.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_bump.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_fast_test.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_functions.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.1}/tests/test_upgrade.py +0 -0
- {fast_dev_cli-0.10.1 → fast_dev_cli-0.11.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.
|
|
3
|
+
Version: 0.11.1
|
|
4
4
|
Summary: Python project development tool.
|
|
5
5
|
Author-Email: Waket Zheng <waketzheng@gmail.com>>
|
|
6
6
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -96,7 +96,10 @@ fast check
|
|
|
96
96
|
```
|
|
97
97
|
- Bump up version in pyproject.toml
|
|
98
98
|
```bash
|
|
99
|
-
fast bump
|
|
99
|
+
fast bump patch # 0.1.0 -> 0.1.1
|
|
100
|
+
fast bump minor # 0.1.0 -> 0.2.0
|
|
101
|
+
fast bump major # 0.1.0 -> 1.0.0
|
|
102
|
+
fast bump <part> --commit # bump version and run `git commit`
|
|
100
103
|
```
|
|
101
104
|
- Run unittest and report coverage
|
|
102
105
|
```bash
|
|
@@ -106,7 +109,7 @@ fast test
|
|
|
106
109
|
```bash
|
|
107
110
|
fast sync
|
|
108
111
|
```
|
|
109
|
-
- Upgrade main/dev dependencies to latest version
|
|
112
|
+
- Upgrade main/dev dependencies to latest version (only for poetry project)
|
|
110
113
|
```bash
|
|
111
114
|
fast upgrade
|
|
112
115
|
```
|
|
@@ -114,3 +117,8 @@ fast upgrade
|
|
|
114
117
|
```bash
|
|
115
118
|
fast dev
|
|
116
119
|
```
|
|
120
|
+
- Publish to pypi
|
|
121
|
+
```bash
|
|
122
|
+
fast upload
|
|
123
|
+
```
|
|
124
|
+
*Note: all command support the `--dry` option*
|
|
@@ -61,7 +61,10 @@ fast check
|
|
|
61
61
|
```
|
|
62
62
|
- Bump up version in pyproject.toml
|
|
63
63
|
```bash
|
|
64
|
-
fast bump
|
|
64
|
+
fast bump patch # 0.1.0 -> 0.1.1
|
|
65
|
+
fast bump minor # 0.1.0 -> 0.2.0
|
|
66
|
+
fast bump major # 0.1.0 -> 1.0.0
|
|
67
|
+
fast bump <part> --commit # bump version and run `git commit`
|
|
65
68
|
```
|
|
66
69
|
- Run unittest and report coverage
|
|
67
70
|
```bash
|
|
@@ -71,7 +74,7 @@ fast test
|
|
|
71
74
|
```bash
|
|
72
75
|
fast sync
|
|
73
76
|
```
|
|
74
|
-
- Upgrade main/dev dependencies to latest version
|
|
77
|
+
- Upgrade main/dev dependencies to latest version (only for poetry project)
|
|
75
78
|
```bash
|
|
76
79
|
fast upgrade
|
|
77
80
|
```
|
|
@@ -79,3 +82,8 @@ fast upgrade
|
|
|
79
82
|
```bash
|
|
80
83
|
fast dev
|
|
81
84
|
```
|
|
85
|
+
- Publish to pypi
|
|
86
|
+
```bash
|
|
87
|
+
fast upload
|
|
88
|
+
```
|
|
89
|
+
*Note: all command support the `--dry` option*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.11.1"
|
|
@@ -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, get_args
|
|
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,56 @@ 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
|
+
if (
|
|
111
|
+
not (init_file := package_dir / "__init__.py").exists()
|
|
112
|
+
and not (init_file := work_dir / "src" / package_name / init_file.name).exists()
|
|
113
|
+
and not (init_file := work_dir / "app" / init_file.name).exists()
|
|
114
|
+
):
|
|
115
|
+
secho("WARNING: __init__.py file does not exist!")
|
|
116
|
+
return "0.0.0"
|
|
117
|
+
pattern = re.compile(r"__version__\s*=")
|
|
118
|
+
for line in init_file.read_text().splitlines():
|
|
119
|
+
if pattern.match(line):
|
|
120
|
+
return _parse_version(line, pattern)
|
|
121
|
+
secho(f"WARNING: can not find '__version__' var in {init_file}!")
|
|
122
|
+
return "0.0.0"
|
|
123
|
+
|
|
124
|
+
|
|
89
125
|
def get_current_version(
|
|
90
|
-
verbose=False,
|
|
91
|
-
is_poetry: bool | None = None,
|
|
92
|
-
package_name=Path(__file__).parent.name,
|
|
126
|
+
verbose=False, is_poetry: bool | None = None, package_name: str | None = None
|
|
93
127
|
) -> str:
|
|
94
128
|
if is_poetry is None:
|
|
95
129
|
is_poetry = Project.manage_by_poetry()
|
|
96
130
|
if not is_poetry:
|
|
97
|
-
|
|
131
|
+
work_dir = None
|
|
132
|
+
if package_name is None:
|
|
133
|
+
work_dir = Project.get_work_dir()
|
|
134
|
+
package_name = re.sub(r"[- ]", "_", work_dir.name)
|
|
135
|
+
try:
|
|
136
|
+
return importlib_metadata.version(package_name)
|
|
137
|
+
except importlib_metadata.PackageNotFoundError:
|
|
138
|
+
return read_version_from_file(package_name, work_dir)
|
|
139
|
+
|
|
98
140
|
cmd = ["poetry", "version", "-s"]
|
|
99
141
|
if verbose:
|
|
100
142
|
echo(f"--> {' '.join(cmd)}")
|
|
@@ -182,14 +224,14 @@ class BumpUp(DryRun):
|
|
|
182
224
|
version_value = context["tool"]["poetry"]["version"]
|
|
183
225
|
except KeyError:
|
|
184
226
|
return TOML_FILE
|
|
185
|
-
if version_value
|
|
227
|
+
if version_value in ("0", "0.0.0"):
|
|
186
228
|
try:
|
|
187
229
|
package_item = context["tool"]["poetry"]["packages"]
|
|
188
230
|
except KeyError:
|
|
189
231
|
packages = []
|
|
190
232
|
else:
|
|
191
233
|
packages = [j for i in package_item if (j := i.get("include"))]
|
|
192
|
-
# In case of managed by `poetry-version
|
|
234
|
+
# In case of managed by `poetry-plugin-version`
|
|
193
235
|
cwd = Path.cwd()
|
|
194
236
|
pattern = re.compile(r"__version__\s*=\s*['\"]")
|
|
195
237
|
ds = [cwd / i for i in packages] + [cwd / cwd.name.replace("-", "_"), cwd]
|
|
@@ -321,16 +363,16 @@ class Project:
|
|
|
321
363
|
return "[tool.poetry]" in cls.load_toml_text()
|
|
322
364
|
|
|
323
365
|
@classmethod
|
|
324
|
-
def get_manage_tool(cls: Type[Self]) ->
|
|
366
|
+
def get_manage_tool(cls: Type[Self]) -> ToolName:
|
|
325
367
|
try:
|
|
326
368
|
text = cls.load_toml_text()
|
|
327
369
|
except EnvError:
|
|
328
370
|
pass
|
|
329
371
|
else:
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
372
|
+
name: ToolName
|
|
373
|
+
for name in get_args(ToolName):
|
|
374
|
+
if f"[tool.{name}]" in text:
|
|
375
|
+
return name
|
|
334
376
|
return ""
|
|
335
377
|
|
|
336
378
|
@staticmethod
|
|
@@ -573,11 +615,18 @@ def tag(
|
|
|
573
615
|
|
|
574
616
|
class LintCode(DryRun):
|
|
575
617
|
def __init__(
|
|
576
|
-
self: Self,
|
|
618
|
+
self: Self,
|
|
619
|
+
args,
|
|
620
|
+
check_only=False,
|
|
621
|
+
_exit=False,
|
|
622
|
+
dry=False,
|
|
623
|
+
bandit=False,
|
|
624
|
+
skip_mypy=False,
|
|
577
625
|
) -> None:
|
|
578
626
|
self.args = args
|
|
579
627
|
self.check_only = check_only
|
|
580
628
|
self._bandit = bandit
|
|
629
|
+
self._skip_mypy = skip_mypy
|
|
581
630
|
super().__init__(_exit, dry)
|
|
582
631
|
|
|
583
632
|
@staticmethod
|
|
@@ -602,14 +651,16 @@ class LintCode(DryRun):
|
|
|
602
651
|
return "."
|
|
603
652
|
|
|
604
653
|
@classmethod
|
|
605
|
-
def to_cmd(
|
|
654
|
+
def to_cmd(
|
|
655
|
+
cls: Type[Self], paths=".", check_only=False, bandit=False, skip_mypy=False
|
|
656
|
+
) -> str:
|
|
606
657
|
cmd = ""
|
|
607
658
|
tools = ["ruff format", "ruff check --extend-select=I,B,SIM --fix", "mypy"]
|
|
608
659
|
if check_only:
|
|
609
660
|
tools[0] += " --check"
|
|
610
661
|
if check_only or load_bool("NO_FIX"):
|
|
611
662
|
tools[1] = tools[1].replace(" --fix", "")
|
|
612
|
-
if load_bool("SKIP_MYPY"):
|
|
663
|
+
if skip_mypy or load_bool("SKIP_MYPY"):
|
|
613
664
|
# Sometimes mypy is too slow
|
|
614
665
|
tools = tools[:-1]
|
|
615
666
|
elif load_bool("IGNORE_MISSING_IMPORTS"):
|
|
@@ -640,29 +691,32 @@ class LintCode(DryRun):
|
|
|
640
691
|
|
|
641
692
|
def gen(self: Self) -> str:
|
|
642
693
|
paths = " ".join(map(str, self.args)) if self.args else "."
|
|
643
|
-
return self.to_cmd(paths, self.check_only, self._bandit)
|
|
694
|
+
return self.to_cmd(paths, self.check_only, self._bandit, self._skip_mypy)
|
|
644
695
|
|
|
645
696
|
|
|
646
697
|
def parse_files(args: list[str] | tuple[str, ...]) -> list[str]:
|
|
647
698
|
return [i for i in args if not i.startswith("-")]
|
|
648
699
|
|
|
649
700
|
|
|
650
|
-
def lint(files=None, dry=False) -> None:
|
|
701
|
+
def lint(files=None, dry=False, skip_mypy=False) -> None:
|
|
651
702
|
if files is None:
|
|
652
703
|
files = parse_files(sys.argv[1:])
|
|
653
704
|
if files and files[0] == "lint":
|
|
654
705
|
files = files[1:]
|
|
655
|
-
LintCode(files, dry=dry).run()
|
|
706
|
+
LintCode(files, dry=dry, skip_mypy=skip_mypy).run()
|
|
656
707
|
|
|
657
708
|
|
|
658
|
-
def check(files=None, dry=False, bandit=False) -> None:
|
|
659
|
-
LintCode(
|
|
709
|
+
def check(files=None, dry=False, bandit=False, skip_mypy=False) -> None:
|
|
710
|
+
LintCode(
|
|
711
|
+
files, check_only=True, _exit=True, dry=dry, bandit=bandit, skip_mypy=skip_mypy
|
|
712
|
+
).run()
|
|
660
713
|
|
|
661
714
|
|
|
662
715
|
@cli.command(name="lint")
|
|
663
716
|
def make_style(
|
|
664
717
|
files: Annotated[Optional[list[Path]], typer.Argument()] = None,
|
|
665
718
|
check_only: bool = Option(False, "--check-only", "-c"),
|
|
719
|
+
skip_mypy: bool = Option(False, "--skip-mypy"),
|
|
666
720
|
dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
|
|
667
721
|
) -> None:
|
|
668
722
|
"""Run: ruff check/format to reformat code and then mypy to check"""
|
|
@@ -670,19 +724,21 @@ def make_style(
|
|
|
670
724
|
files = [Path(".")]
|
|
671
725
|
elif isinstance(files, str):
|
|
672
726
|
files = [files]
|
|
727
|
+
skip = _ensure_bool(skip_mypy)
|
|
673
728
|
if _ensure_bool(check_only):
|
|
674
|
-
check(files, dry=dry)
|
|
729
|
+
check(files, dry=dry, skip_mypy=skip)
|
|
675
730
|
else:
|
|
676
|
-
lint(files, dry=dry)
|
|
731
|
+
lint(files, dry=dry, skip_mypy=skip)
|
|
677
732
|
|
|
678
733
|
|
|
679
734
|
@cli.command(name="check")
|
|
680
735
|
def only_check(
|
|
681
736
|
bandit: bool = Option(False, "--bandit", help="Run `bandit -r <package_dir>`"),
|
|
737
|
+
skip_mypy: bool = Option(False, "--skip-mypy"),
|
|
682
738
|
dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
|
|
683
739
|
) -> None:
|
|
684
740
|
"""Check code style without reformat"""
|
|
685
|
-
check(dry=dry, bandit=bandit)
|
|
741
|
+
check(dry=dry, bandit=bandit, skip_mypy=_ensure_bool(skip_mypy))
|
|
686
742
|
|
|
687
743
|
|
|
688
744
|
class Sync(DryRun):
|
|
@@ -695,18 +751,30 @@ class Sync(DryRun):
|
|
|
695
751
|
def gen(self) -> str:
|
|
696
752
|
extras, save = self.extras, self._save
|
|
697
753
|
should_remove = not Path.cwd().joinpath(self.filename).exists()
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
"
|
|
702
|
-
)
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
754
|
+
if not (tool := Project.get_manage_tool()):
|
|
755
|
+
if should_remove or not is_venv():
|
|
756
|
+
raise EnvError("There project is not managed by uv/pdm/poetry!")
|
|
757
|
+
return f"python -m pip install -r {self.filename}"
|
|
758
|
+
prefix = "" if is_venv() else f"{tool} run "
|
|
759
|
+
ensurepip = " {1}python -m ensurepip && {1}python -m pip install -U pip &&"
|
|
760
|
+
if tool == "uv":
|
|
761
|
+
export_cmd = "uv export --no-hashes --all-extras --frozen"
|
|
762
|
+
if check_call(prefix + "python -m pip --version"):
|
|
763
|
+
ensurepip = ""
|
|
764
|
+
elif tool in ("poetry", "pdm"):
|
|
765
|
+
export_cmd = f"{tool} export --without-hashes --with=dev"
|
|
766
|
+
if tool == "poetry":
|
|
767
|
+
ensurepip = ""
|
|
768
|
+
if not UpgradeDependencies.should_with_dev():
|
|
769
|
+
export_cmd = export_cmd.replace(" --with=dev", "")
|
|
770
|
+
if extras and isinstance(extras, str | list):
|
|
771
|
+
export_cmd += f" --{extras=}".replace("'", '"')
|
|
772
|
+
elif check_call(prefix + "python -m pip --version"):
|
|
773
|
+
ensurepip = ""
|
|
774
|
+
install_cmd = "{2} -o {0} &&%s {1}python -m pip install -r {0}" % ensurepip
|
|
707
775
|
if should_remove and not save:
|
|
708
776
|
install_cmd += " && rm -f {0}"
|
|
709
|
-
return install_cmd.format(self.filename, prefix)
|
|
777
|
+
return install_cmd.format(self.filename, prefix, export_cmd)
|
|
710
778
|
|
|
711
779
|
|
|
712
780
|
@cli.command()
|
|
@@ -757,12 +825,26 @@ def coverage_test(
|
|
|
757
825
|
return test(dry, ignore_script)
|
|
758
826
|
|
|
759
827
|
|
|
828
|
+
class Publish:
|
|
829
|
+
class CommandEnum(StrEnum):
|
|
830
|
+
poetry = "poetry publish --build"
|
|
831
|
+
pdm = "pdm publish"
|
|
832
|
+
uv = "uv build && uv publish"
|
|
833
|
+
twine = "python -m build && twine upload"
|
|
834
|
+
|
|
835
|
+
@classmethod
|
|
836
|
+
def gen(cls) -> str:
|
|
837
|
+
if (tool := Project.get_manage_tool()) and tool in get_args(ToolName):
|
|
838
|
+
return cls.CommandEnum[tool]
|
|
839
|
+
return cls.CommandEnum.twine
|
|
840
|
+
|
|
841
|
+
|
|
760
842
|
@cli.command()
|
|
761
843
|
def upload(
|
|
762
844
|
dry: bool = Option(False, "--dry", help="Only print, not really run shell command"),
|
|
763
845
|
) -> None:
|
|
764
846
|
"""Shortcut for package publish"""
|
|
765
|
-
cmd =
|
|
847
|
+
cmd = Publish.gen()
|
|
766
848
|
exit_if_run_failed(cmd, dry=dry)
|
|
767
849
|
|
|
768
850
|
|
|
@@ -75,6 +75,7 @@ def test_check(mock_no_dmypy, monkeypatch, mocker):
|
|
|
75
75
|
def test_check_bandit(tmp_path):
|
|
76
76
|
package_path = tmp_path / "foo"
|
|
77
77
|
with chdir(tmp_path):
|
|
78
|
+
assert LintCode.get_package_name() == "."
|
|
78
79
|
run_and_echo(f"poetry new {package_path.name}")
|
|
79
80
|
shutil.rmtree(package_path / package_path.name)
|
|
80
81
|
with chdir(package_path):
|
|
@@ -82,6 +83,18 @@ def test_check_bandit(tmp_path):
|
|
|
82
83
|
assert "bandit -r ." in command
|
|
83
84
|
|
|
84
85
|
|
|
86
|
+
def test_check_skip_mypy(mock_skip_mypy_0, mocker, capsys):
|
|
87
|
+
cmd = "fast check --skip-mypy --dry"
|
|
88
|
+
cmd2 = "fast lint --check-only --skip-mypy --dry"
|
|
89
|
+
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
90
|
+
command = capture_cmd_output(cmd)
|
|
91
|
+
command2 = capture_cmd_output(cmd2)
|
|
92
|
+
expected = "--> " + SEP.join(
|
|
93
|
+
filter(lambda i: not i.startswith("mypy"), CHECK_CMD.split(SEP))
|
|
94
|
+
)
|
|
95
|
+
assert command == command2 == expected
|
|
96
|
+
|
|
97
|
+
|
|
85
98
|
def test_fast_check():
|
|
86
99
|
_fast_check()
|
|
87
100
|
|
|
@@ -128,7 +141,7 @@ def test_lint_with_prefix(mocker):
|
|
|
128
141
|
assert "pdm run" in stream.getvalue()
|
|
129
142
|
|
|
130
143
|
|
|
131
|
-
def test_make_style(mocker, mock_no_dmypy):
|
|
144
|
+
def test_make_style(mock_skip_mypy_0, mocker, mock_no_dmypy):
|
|
132
145
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
133
146
|
with capture_stdout() as stream:
|
|
134
147
|
make_style(check_only=False, dry=True)
|
|
@@ -196,6 +209,23 @@ def test_skip_mypy(mock_skip_mypy, mocker):
|
|
|
196
209
|
assert LintCode(".").gen() == SEP.join(i for i in cmds if not i.startswith("mypy"))
|
|
197
210
|
|
|
198
211
|
|
|
212
|
+
def test_skip_mypy_option(mock_skip_mypy_0, mocker):
|
|
213
|
+
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
214
|
+
cmds = LINT_CMD.split(SEP)
|
|
215
|
+
assert LintCode(".", skip_mypy=True).gen() == SEP.join(
|
|
216
|
+
i for i in cmds if not i.startswith("mypy")
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_skip_mypy_fast_lint(mock_skip_mypy_0, mocker, capsys):
|
|
221
|
+
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
222
|
+
command = capture_cmd_output("fast lint --skip-mypy --dry")
|
|
223
|
+
cmds = LINT_CMD.split(SEP)
|
|
224
|
+
assert command.replace("--> ", "") == SEP.join(
|
|
225
|
+
i for i in cmds if not i.startswith("mypy")
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
199
229
|
def test_skip_mypy_0(mock_skip_mypy_0, mocker, mock_no_dmypy):
|
|
200
230
|
mocker.patch("fast_dev_cli.cli.is_venv", return_value=True)
|
|
201
231
|
assert LintCode(".").gen() == LINT_CMD
|
|
@@ -238,3 +268,5 @@ def test_get_manage_tool(tmp_path):
|
|
|
238
268
|
assert Project.get_manage_tool() == "poetry"
|
|
239
269
|
Path(TOML_FILE).write_text("[tool.pdm]")
|
|
240
270
|
assert Project.get_manage_tool() == "pdm"
|
|
271
|
+
Path(TOML_FILE).write_text("[tool.uv]")
|
|
272
|
+
assert Project.get_manage_tool() == "uv"
|
|
@@ -18,19 +18,24 @@ from .utils import chdir
|
|
|
18
18
|
|
|
19
19
|
CONF = """
|
|
20
20
|
|
|
21
|
-
[tool.poetry-version
|
|
21
|
+
[tool.poetry-plugin-version]
|
|
22
22
|
source = "init"
|
|
23
23
|
"""
|
|
24
|
+
CONF_2 = """
|
|
25
|
+
|
|
26
|
+
[tool.poetry-dynamic-versioning]
|
|
27
|
+
enable = true
|
|
28
|
+
"""
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
@contextmanager
|
|
27
32
|
def _prepare_package(
|
|
28
|
-
package_path: Path, define_include=False
|
|
33
|
+
package_path: Path, define_include=False, mark="0"
|
|
29
34
|
) -> Generator[Path, None, None]:
|
|
30
35
|
toml_file = package_path / TOML_FILE
|
|
31
36
|
package_name = package_path.name.replace(" ", "_")
|
|
32
37
|
init_file = package_path / package_name / "__init__.py"
|
|
33
|
-
a, b = 'version = "0.1.0"', 'version = "
|
|
38
|
+
a, b = 'version = "0.1.0"', f'version = "{mark}"'
|
|
34
39
|
if define_include:
|
|
35
40
|
b += '\npackages = [{include = "%s"}]' % package_name
|
|
36
41
|
with chdir(package_path.parent):
|
|
@@ -62,9 +67,28 @@ def test_version_plugin(tmp_path: Path) -> None:
|
|
|
62
67
|
BumpUp(part="patch", commit=False, dry=True).gen()
|
|
63
68
|
|
|
64
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
|
+
|
|
65
81
|
def test_version_plugin_include_defined(tmp_path: Path) -> None:
|
|
66
82
|
with _prepare_package(tmp_path / "hello world", True) as init_file:
|
|
67
83
|
command = _build_bump_cmd(init_file)
|
|
68
84
|
assert BumpUp(part="patch", commit=False, dry=True).gen() == command
|
|
69
85
|
run_and_echo("poetry run fast bump patch")
|
|
70
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
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from fast_dev_cli.cli import TOML_FILE, run_and_echo, upload
|
|
4
|
+
|
|
5
|
+
from .utils import chdir
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_upload(capsys):
|
|
9
|
+
upload(dry=True)
|
|
10
|
+
out = capsys.readouterr().out.strip()
|
|
11
|
+
assert out.replace("--> ", "") == "pdm publish"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_upload_poetry(tmp_path, capsys):
|
|
15
|
+
with chdir(tmp_path):
|
|
16
|
+
run_and_echo("poetry new foo", verbose=False)
|
|
17
|
+
with chdir(tmp_path / "foo"):
|
|
18
|
+
upload(dry=True)
|
|
19
|
+
out = capsys.readouterr().out.strip()
|
|
20
|
+
assert out.replace("--> ", "") == "poetry publish --build"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_upload_uv(tmp_path, capsys):
|
|
24
|
+
project_dir = tmp_path / "uv_proj"
|
|
25
|
+
project_dir.mkdir()
|
|
26
|
+
toml_file = project_dir / TOML_FILE
|
|
27
|
+
with chdir(project_dir):
|
|
28
|
+
run_and_echo("uv init", verbose=False)
|
|
29
|
+
if (s := "[tool.uv]") not in (text := toml_file.read_text()):
|
|
30
|
+
toml_file.write_text(text + os.linesep + s)
|
|
31
|
+
upload(dry=True)
|
|
32
|
+
out = capsys.readouterr().out.strip()
|
|
33
|
+
assert out.replace("--> ", "") == "uv build && uv publish"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_upload_other(tmp_path, capsys):
|
|
37
|
+
project_dir = tmp_path / "proj"
|
|
38
|
+
project_dir.mkdir()
|
|
39
|
+
with chdir(project_dir):
|
|
40
|
+
run_and_echo("uv init", verbose=False)
|
|
41
|
+
upload(dry=True)
|
|
42
|
+
out = capsys.readouterr().out.strip()
|
|
43
|
+
assert out.replace("--> ", "") == "python -m build && twine upload"
|
|
@@ -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.1"
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|