fast-dev-cli 0.18.3__tar.gz → 0.18.5__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.18.3 → fast_dev_cli-0.18.5}/PKG-INFO +1 -1
- fast_dev_cli-0.18.5/fast_dev_cli/__init__.py +1 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/fast_dev_cli/cli.py +151 -61
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/pyproject.toml +1 -1
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_bump.py +8 -8
- fast_dev_cli-0.18.5/tests/test_deps.py +50 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_fast_test.py +1 -1
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_functions.py +8 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_poetry_version_plugin.py +7 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_upgrade.py +14 -19
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/utils.py +5 -3
- fast_dev_cli-0.18.3/fast_dev_cli/__init__.py +0 -1
- fast_dev_cli-0.18.3/tests/test_deps.py +0 -33
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/LICENSE +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/README.md +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/pdm_build.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/scripts/check.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/scripts/deps.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/scripts/format.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/scripts/test.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/__init__.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/assets/uv-tx.lock +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/assets/uv.lock +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/conftest.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_exec.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_help.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_lint.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_pypi.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_sync.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_upload.py +0 -0
- {fast_dev_cli-0.18.3 → fast_dev_cli-0.18.5}/tests/test_version.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.18.5"
|
|
@@ -4,6 +4,7 @@ import contextlib
|
|
|
4
4
|
import functools
|
|
5
5
|
import importlib.metadata as importlib_metadata
|
|
6
6
|
import os
|
|
7
|
+
import platform
|
|
7
8
|
import re
|
|
8
9
|
import shlex
|
|
9
10
|
import shutil
|
|
@@ -102,6 +103,11 @@ def is_emoji(char: str) -> bool:
|
|
|
102
103
|
return not "\u4e00" <= char <= "\u9fff" # Chinese character
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
@functools.cache
|
|
107
|
+
def is_windows() -> bool:
|
|
108
|
+
return platform.system() == "Windows"
|
|
109
|
+
|
|
110
|
+
|
|
105
111
|
def yellow_warn(msg: str) -> None:
|
|
106
112
|
secho(msg, fg="yellow")
|
|
107
113
|
|
|
@@ -124,40 +130,92 @@ def is_venv() -> bool:
|
|
|
124
130
|
)
|
|
125
131
|
|
|
126
132
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
133
|
+
class Shell:
|
|
134
|
+
def __init__(self, cmd: list[str] | str, **kw: Any) -> None:
|
|
135
|
+
self._cmd = cmd
|
|
136
|
+
self._kw = kw
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def run_by_subprocess(
|
|
140
|
+
cmd: list[str] | str, **kw: Any
|
|
141
|
+
) -> subprocess.CompletedProcess[str]:
|
|
142
|
+
if isinstance(cmd, str):
|
|
143
|
+
kw.setdefault("shell", True)
|
|
144
|
+
return subprocess.run(cmd, **kw) # nosec:B603
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def command(self) -> list[str] | str:
|
|
148
|
+
command: list[str] | str = self._cmd
|
|
149
|
+
if (
|
|
150
|
+
isinstance(command, str)
|
|
151
|
+
and "shell" not in self._kw
|
|
152
|
+
and not (set(self._cmd) & {"|", ">", "&"})
|
|
153
|
+
):
|
|
154
|
+
command = shlex.split(command)
|
|
155
|
+
return command
|
|
156
|
+
|
|
157
|
+
def _run(self) -> subprocess.CompletedProcess[str]:
|
|
158
|
+
return self.run_by_subprocess(self.command, **self._kw)
|
|
159
|
+
|
|
160
|
+
def run(self, verbose: bool = False, dry: bool = False) -> int:
|
|
161
|
+
if verbose:
|
|
162
|
+
echo(f"--> {self._cmd}")
|
|
163
|
+
if dry:
|
|
164
|
+
return 0
|
|
165
|
+
return self._run().returncode
|
|
166
|
+
|
|
167
|
+
def check_call(self) -> bool:
|
|
168
|
+
self._kw.update(stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
169
|
+
return self.run() == 0
|
|
170
|
+
|
|
171
|
+
def capture_output(self, raises: bool = False) -> str:
|
|
172
|
+
self._kw.update(capture_output=True, encoding="utf-8")
|
|
173
|
+
r = self._run()
|
|
174
|
+
if raises and r.returncode != 0:
|
|
175
|
+
raise ShellCommandError(r.stderr)
|
|
176
|
+
return (r.stdout or r.stderr or "").strip()
|
|
177
|
+
|
|
178
|
+
def finish(
|
|
179
|
+
self, env: dict[str, str] | None = None, _exit: bool = False, dry=False
|
|
180
|
+
) -> subprocess.CompletedProcess[str]:
|
|
181
|
+
self.run(verbose=True, dry=True)
|
|
182
|
+
if _ensure_bool(dry):
|
|
183
|
+
return subprocess.CompletedProcess("", 0)
|
|
184
|
+
if env is not None:
|
|
185
|
+
self._kw["env"] = {**os.environ, **env}
|
|
186
|
+
r = self._run()
|
|
187
|
+
if rc := r.returncode:
|
|
188
|
+
if _exit:
|
|
189
|
+
sys.exit(rc)
|
|
190
|
+
raise Exit(rc)
|
|
191
|
+
return r
|
|
131
192
|
|
|
132
193
|
|
|
133
194
|
def run_and_echo(
|
|
134
195
|
cmd: str, *, dry: bool = False, verbose: bool = True, **kw: Any
|
|
135
196
|
) -> int:
|
|
136
197
|
"""Run shell command with subprocess and print it"""
|
|
137
|
-
|
|
138
|
-
echo(f"--> {cmd}")
|
|
139
|
-
if dry:
|
|
140
|
-
return 0
|
|
141
|
-
command: list[str] | str = cmd
|
|
142
|
-
if "shell" not in kw and not (set(cmd) & {"|", ">", "&"}):
|
|
143
|
-
command = shlex.split(cmd)
|
|
144
|
-
return _run_shell(command, **kw).returncode
|
|
198
|
+
return Shell(cmd, **kw).run(verbose=verbose, dry=dry)
|
|
145
199
|
|
|
146
200
|
|
|
147
201
|
def check_call(cmd: str) -> bool:
|
|
148
|
-
|
|
149
|
-
return r.returncode == 0
|
|
202
|
+
return Shell(cmd).check_call()
|
|
150
203
|
|
|
151
204
|
|
|
152
205
|
def capture_cmd_output(
|
|
153
206
|
command: list[str] | str, *, raises: bool = False, **kw: Any
|
|
154
207
|
) -> str:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
208
|
+
return Shell(command, **kw).capture_output(raises=raises)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def exit_if_run_failed(
|
|
212
|
+
cmd: str,
|
|
213
|
+
env: dict[str, str] | None = None,
|
|
214
|
+
_exit: bool = False,
|
|
215
|
+
dry: bool = False,
|
|
216
|
+
**kw: Any,
|
|
217
|
+
) -> subprocess.CompletedProcess[str]:
|
|
218
|
+
return Shell(cmd, **kw).finish(env=env, _exit=_exit, dry=dry)
|
|
161
219
|
|
|
162
220
|
|
|
163
221
|
def _parse_version(line: str, pattern: re.Pattern[str]) -> str:
|
|
@@ -276,26 +334,6 @@ def _ensure_str(value: str | OptionInfo | None) -> str:
|
|
|
276
334
|
return value
|
|
277
335
|
|
|
278
336
|
|
|
279
|
-
def exit_if_run_failed(
|
|
280
|
-
cmd: str,
|
|
281
|
-
env: dict[str, str] | None = None,
|
|
282
|
-
_exit: bool = False,
|
|
283
|
-
dry: bool = False,
|
|
284
|
-
**kw: Any,
|
|
285
|
-
) -> subprocess.CompletedProcess[str]:
|
|
286
|
-
run_and_echo(cmd, dry=True)
|
|
287
|
-
if _ensure_bool(dry):
|
|
288
|
-
return subprocess.CompletedProcess("", 0)
|
|
289
|
-
if env is not None:
|
|
290
|
-
env = {**os.environ, **env}
|
|
291
|
-
r = _run_shell(cmd, env=env, **kw)
|
|
292
|
-
if rc := r.returncode:
|
|
293
|
-
if _exit:
|
|
294
|
-
sys.exit(rc)
|
|
295
|
-
raise Exit(rc)
|
|
296
|
-
return r
|
|
297
|
-
|
|
298
|
-
|
|
299
337
|
class DryRun:
|
|
300
338
|
def __init__(self, _exit: bool = False, dry: bool = False) -> None:
|
|
301
339
|
self.dry = _ensure_bool(dry)
|
|
@@ -497,6 +535,13 @@ def version() -> None:
|
|
|
497
535
|
with contextlib.suppress(FileNotFoundError, KeyError):
|
|
498
536
|
toml_text = Project.load_toml_text()
|
|
499
537
|
doc = tomllib.loads(toml_text)
|
|
538
|
+
if value := doc.get("project", {}).get("version", ""):
|
|
539
|
+
styled = typer.style(value, bold=True, fg=typer.colors.CYAN)
|
|
540
|
+
if project_name := doc["project"].get("name", ""):
|
|
541
|
+
echo(f"{project_name} version: " + styled)
|
|
542
|
+
else:
|
|
543
|
+
echo(f"Got Version from {TOML_FILE}: " + styled)
|
|
544
|
+
return
|
|
500
545
|
version_file = doc["tool"]["pdm"]["version"]["path"]
|
|
501
546
|
text = Project.get_work_dir().joinpath(version_file).read_text()
|
|
502
547
|
varname = "__version__"
|
|
@@ -554,6 +599,19 @@ class Project:
|
|
|
554
599
|
def is_poetry_v2(text: str) -> bool:
|
|
555
600
|
return 'build-backend = "poetry' in text
|
|
556
601
|
|
|
602
|
+
@staticmethod
|
|
603
|
+
def get_poetry_version(command: str = "poetry") -> str:
|
|
604
|
+
pattern = r"(\d+\.\d+\.\d+)"
|
|
605
|
+
text = capture_cmd_output(f"{command} --version")
|
|
606
|
+
for expr in (
|
|
607
|
+
rf"Poetry \(version {pattern}\)",
|
|
608
|
+
rf"Poetry.*version.*{pattern}.*\)",
|
|
609
|
+
rf"{pattern}",
|
|
610
|
+
):
|
|
611
|
+
if m := re.search(expr, text):
|
|
612
|
+
return m.group(1)
|
|
613
|
+
return ""
|
|
614
|
+
|
|
557
615
|
@staticmethod
|
|
558
616
|
def work_dir(
|
|
559
617
|
name: str, parent: Path, depth: int, be_file: bool = False
|
|
@@ -650,7 +708,7 @@ class Project:
|
|
|
650
708
|
@classmethod
|
|
651
709
|
def get_sync_command(cls, prod: bool = True, doc: dict | None = None) -> str:
|
|
652
710
|
if cls.is_pdm_project():
|
|
653
|
-
return "pdm
|
|
711
|
+
return "pdm install --frozen" + " --prod" * prod
|
|
654
712
|
elif cls.manage_by_poetry(cache=True):
|
|
655
713
|
cmd = "poetry install"
|
|
656
714
|
if prod:
|
|
@@ -847,7 +905,7 @@ class UpgradeDependencies(Project, DryRun):
|
|
|
847
905
|
deps = "uv sync --inexact --frozen --all-groups --all-extras"
|
|
848
906
|
return f"{up} && {deps}"
|
|
849
907
|
elif self._tool == "pdm":
|
|
850
|
-
return "pdm update --verbose && pdm
|
|
908
|
+
return "pdm update --verbose && pdm install -G :all --frozen"
|
|
851
909
|
return self.gen_cmd() + " && poetry lock && poetry update"
|
|
852
910
|
|
|
853
911
|
|
|
@@ -1037,11 +1095,12 @@ class LintCode(DryRun):
|
|
|
1037
1095
|
if tool == ToolOption.default:
|
|
1038
1096
|
tool = Project.get_manage_tool() or ""
|
|
1039
1097
|
if tool:
|
|
1040
|
-
prefix =
|
|
1041
|
-
|
|
1042
|
-
if
|
|
1043
|
-
|
|
1044
|
-
|
|
1098
|
+
prefix = tool + " run "
|
|
1099
|
+
if tool == "uv":
|
|
1100
|
+
if is_windows():
|
|
1101
|
+
prefix += "--no-sync "
|
|
1102
|
+
elif Path(bin_dir := ".venv/bin/").exists():
|
|
1103
|
+
prefix = bin_dir
|
|
1045
1104
|
if cls.prefer_dmypy(paths, tools, use_dmypy=use_dmypy):
|
|
1046
1105
|
tools[-1] = "dmypy run"
|
|
1047
1106
|
cmd += lint_them.format(prefix, paths, *tools)
|
|
@@ -1063,11 +1122,21 @@ class LintCode(DryRun):
|
|
|
1063
1122
|
ps = args.split() if isinstance(args, str) else [str(i) for i in args]
|
|
1064
1123
|
if len(ps) == 1:
|
|
1065
1124
|
paths = ps[0]
|
|
1066
|
-
if
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1125
|
+
if (
|
|
1126
|
+
paths != "."
|
|
1127
|
+
# `Path("a.").suffix` got "." in py3.14 and got "" with py<3.14
|
|
1128
|
+
and (p := Path(paths)).suffix in ("", ".")
|
|
1129
|
+
and not p.exists()
|
|
1130
|
+
):
|
|
1131
|
+
# e.g.:
|
|
1132
|
+
# stem -> stem.py
|
|
1133
|
+
# me. -> me.py
|
|
1134
|
+
if paths.endswith("."):
|
|
1135
|
+
p = p.with_name(paths[:-1])
|
|
1136
|
+
for suffix in (".py", ".html"):
|
|
1137
|
+
p = p.with_suffix(suffix)
|
|
1138
|
+
if p.exists():
|
|
1139
|
+
paths = p.name
|
|
1071
1140
|
break
|
|
1072
1141
|
else:
|
|
1073
1142
|
paths = " ".join(ps)
|
|
@@ -1240,7 +1309,9 @@ def test(dry: bool, ignore_script: bool = False) -> None:
|
|
|
1240
1309
|
if not _ensure_bool(ignore_script) and (
|
|
1241
1310
|
test_script := _should_run_test_script(script_dir)
|
|
1242
1311
|
):
|
|
1243
|
-
cmd =
|
|
1312
|
+
cmd = test_script.relative_to(root).as_posix()
|
|
1313
|
+
if test_script.suffix == ".py":
|
|
1314
|
+
cmd = "python " + cmd
|
|
1244
1315
|
if cwd != root:
|
|
1245
1316
|
cmd = f"cd {root} && " + cmd
|
|
1246
1317
|
else:
|
|
@@ -1331,8 +1402,11 @@ def run_by_subprocess(cmd: str, dry: bool = DryOption) -> None:
|
|
|
1331
1402
|
try:
|
|
1332
1403
|
rc = run_and_echo(cmd, verbose=True, dry=_ensure_bool(dry))
|
|
1333
1404
|
except FileNotFoundError as e:
|
|
1334
|
-
|
|
1335
|
-
|
|
1405
|
+
command = cmd.split()[0]
|
|
1406
|
+
if e.filename == command or (
|
|
1407
|
+
e.filename is None and "系统找不到指定的文件" in str(e)
|
|
1408
|
+
):
|
|
1409
|
+
echo(f"Command not found: {command}")
|
|
1336
1410
|
raise Exit(1) from None
|
|
1337
1411
|
raise e
|
|
1338
1412
|
else:
|
|
@@ -1341,9 +1415,18 @@ def run_by_subprocess(cmd: str, dry: bool = DryOption) -> None:
|
|
|
1341
1415
|
|
|
1342
1416
|
|
|
1343
1417
|
class MakeDeps(DryRun):
|
|
1344
|
-
def __init__(
|
|
1418
|
+
def __init__(
|
|
1419
|
+
self,
|
|
1420
|
+
tool: str,
|
|
1421
|
+
prod: bool = False,
|
|
1422
|
+
dry: bool = False,
|
|
1423
|
+
active: bool = True,
|
|
1424
|
+
inexact: bool = True,
|
|
1425
|
+
) -> None:
|
|
1345
1426
|
self._tool = tool
|
|
1346
1427
|
self._prod = prod
|
|
1428
|
+
self._active = active
|
|
1429
|
+
self._inexact = inexact
|
|
1347
1430
|
super().__init__(dry=dry)
|
|
1348
1431
|
|
|
1349
1432
|
def should_ensure_pip(self) -> bool:
|
|
@@ -1359,11 +1442,12 @@ class MakeDeps(DryRun):
|
|
|
1359
1442
|
|
|
1360
1443
|
def gen(self) -> str:
|
|
1361
1444
|
if self._tool == "pdm":
|
|
1362
|
-
return "pdm
|
|
1445
|
+
return "pdm install --frozen " + ("--prod" if self._prod else "-G :all")
|
|
1363
1446
|
elif self._tool == "uv":
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1447
|
+
uv_sync = "uv sync" + " --inexact" * self._inexact
|
|
1448
|
+
if self._active:
|
|
1449
|
+
uv_sync += " --active"
|
|
1450
|
+
return uv_sync + ("" if self._prod else " --all-extras --all-groups")
|
|
1367
1451
|
elif self._tool == "poetry":
|
|
1368
1452
|
return "poetry install " + (
|
|
1369
1453
|
"--only=main" if self._prod else "--all-extras --all-groups"
|
|
@@ -1392,6 +1476,12 @@ def make_deps(
|
|
|
1392
1476
|
use_pdm: bool = Option(False, "--pdm", help="Use `pdm` to install deps"),
|
|
1393
1477
|
use_pip: bool = Option(False, "--pip", help="Use `pip` to install deps"),
|
|
1394
1478
|
use_poetry: bool = Option(False, "--poetry", help="Use `poetry` to install deps"),
|
|
1479
|
+
active: bool = Option(
|
|
1480
|
+
True, help="Add `--active` to uv sync command(Only work for uv project)"
|
|
1481
|
+
),
|
|
1482
|
+
inexact: bool = Option(
|
|
1483
|
+
True, help="Add `--inexact` to uv sync command(Only work for uv project)"
|
|
1484
|
+
),
|
|
1395
1485
|
dry: bool = DryOption,
|
|
1396
1486
|
) -> None:
|
|
1397
1487
|
"""Run: ruff check/format to reformat code and then mypy to check"""
|
|
@@ -1407,7 +1497,7 @@ def make_deps(
|
|
|
1407
1497
|
tool = "poetry"
|
|
1408
1498
|
elif tool == ToolOption.default:
|
|
1409
1499
|
tool = Project.get_manage_tool(cache=True) or "pip"
|
|
1410
|
-
MakeDeps(tool, prod, dry=dry).run()
|
|
1500
|
+
MakeDeps(tool, prod, active=active, inexact=inexact, dry=dry).run()
|
|
1411
1501
|
|
|
1412
1502
|
|
|
1413
1503
|
class UvPypi(DryRun):
|
|
@@ -230,16 +230,16 @@ version = "0"
|
|
|
230
230
|
another_dir = project_dir / "hello"
|
|
231
231
|
another_dir.mkdir()
|
|
232
232
|
shutil.copy(init_file, another_dir / init_file.name)
|
|
233
|
-
assert BumpUp.parse_filename() == "helloworld
|
|
233
|
+
assert BumpUp.parse_filename() == os.path.join("helloworld", "__init__.py")
|
|
234
234
|
toml_file.write_text(pyproject.strip() + '\npackages=[{include="hello"}]')
|
|
235
|
-
assert BumpUp.parse_filename() == "hello
|
|
235
|
+
assert BumpUp.parse_filename() == os.path.join("hello", "__init__.py")
|
|
236
236
|
toml_file.write_text(
|
|
237
237
|
pyproject.strip() + '\npackages=[{include="hello",from="py"}]'
|
|
238
238
|
)
|
|
239
239
|
from_dir = project_dir / "py"
|
|
240
240
|
from_dir.mkdir()
|
|
241
241
|
shutil.move(another_dir, from_dir)
|
|
242
|
-
assert BumpUp.parse_filename() == "py
|
|
242
|
+
assert BumpUp.parse_filename() == os.path.join("py", "hello", "__init__.py")
|
|
243
243
|
|
|
244
244
|
|
|
245
245
|
PDM_DYNAMIC_VERSION = """
|
|
@@ -269,7 +269,7 @@ def test_pdm_project(dynamic_pdm_project):
|
|
|
269
269
|
init_file = dynamic_pdm_project / "__init__.py"
|
|
270
270
|
init_file.write_text('__version__ = "0.2.0"')
|
|
271
271
|
out = capture_cmd_output("fast bump patch")
|
|
272
|
-
assert
|
|
272
|
+
assert init_file.as_posix() in out
|
|
273
273
|
assert "0.2.1" in init_file.read_text()
|
|
274
274
|
run_and_echo("git init")
|
|
275
275
|
shutil.copy(Path(__file__).parent.parent / ".gitignore", ".")
|
|
@@ -291,7 +291,7 @@ def test_installed_version_is_0_0_0(dynamic_pdm_project):
|
|
|
291
291
|
capture_cmd_output("pdm install")
|
|
292
292
|
version_file.write_text('__version__ = "0.3.0"')
|
|
293
293
|
out = capture_cmd_output("fast bump patch")
|
|
294
|
-
assert
|
|
294
|
+
assert version_file.as_posix() in out
|
|
295
295
|
assert "0.3.1" in version_file.read_text()
|
|
296
296
|
|
|
297
297
|
|
|
@@ -332,7 +332,7 @@ __description__ = "A next generation HTTP client, for Python 3."
|
|
|
332
332
|
__version__ = "0.28.1"
|
|
333
333
|
""")
|
|
334
334
|
out = capture_cmd_output("fast bump patch")
|
|
335
|
-
assert
|
|
335
|
+
assert version_file.as_posix() in out
|
|
336
336
|
assert "0.28.2" in version_file.read_text()
|
|
337
337
|
|
|
338
338
|
|
|
@@ -365,7 +365,7 @@ requires = ["pdm-backend"]
|
|
|
365
365
|
build-backend = "pdm.backend"
|
|
366
366
|
""")
|
|
367
367
|
out = capture_cmd_output("fast bump patch")
|
|
368
|
-
assert
|
|
368
|
+
assert version_file.as_posix() in out
|
|
369
369
|
assert "0.3.1" in version_file.read_text()
|
|
370
370
|
|
|
371
371
|
|
|
@@ -400,5 +400,5 @@ requires = ["pdm-backend"]
|
|
|
400
400
|
build-backend = "pdm.backend"
|
|
401
401
|
""")
|
|
402
402
|
out = capture_cmd_output("fast bump patch")
|
|
403
|
-
assert
|
|
403
|
+
assert version_file.as_posix() in out
|
|
404
404
|
assert "0.3.1" in version_file.read_text()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from fast_dev_cli.cli import MakeDeps, capture_cmd_output
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_make_deps_class():
|
|
5
|
+
assert (
|
|
6
|
+
MakeDeps("uv", prod=False).gen()
|
|
7
|
+
== "uv sync --inexact --active --all-extras --all-groups"
|
|
8
|
+
)
|
|
9
|
+
assert (
|
|
10
|
+
MakeDeps("uv", prod=False, active=False).gen()
|
|
11
|
+
== "uv sync --inexact --all-extras --all-groups"
|
|
12
|
+
)
|
|
13
|
+
assert MakeDeps("uv", prod=True).gen() == "uv sync --inexact --active"
|
|
14
|
+
assert MakeDeps("uv", prod=True, active=False).gen() == "uv sync --inexact"
|
|
15
|
+
assert MakeDeps("pdm", prod=False).gen() == "pdm install --frozen -G :all"
|
|
16
|
+
assert MakeDeps("pdm", prod=True).gen() == "pdm install --frozen --prod"
|
|
17
|
+
assert (
|
|
18
|
+
MakeDeps("poetry", prod=False).gen()
|
|
19
|
+
== "poetry install --all-extras --all-groups"
|
|
20
|
+
)
|
|
21
|
+
assert MakeDeps("poetry", prod=True).gen() == "poetry install --only=main"
|
|
22
|
+
assert (
|
|
23
|
+
MakeDeps("pip", prod=False).gen()
|
|
24
|
+
== "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e . --group dev"
|
|
25
|
+
)
|
|
26
|
+
assert (
|
|
27
|
+
MakeDeps("pip", prod=True).gen()
|
|
28
|
+
== "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e ."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_fast_deps():
|
|
33
|
+
out = capture_cmd_output("fast deps --dry")
|
|
34
|
+
assert out == "--> pdm install --frozen -G :all"
|
|
35
|
+
out = capture_cmd_output("fast deps --uv --prod --dry")
|
|
36
|
+
assert out == "--> uv sync --inexact --active"
|
|
37
|
+
out = capture_cmd_output("fast deps --uv --prod --dry --no-active")
|
|
38
|
+
assert out == "--> uv sync --inexact"
|
|
39
|
+
out = capture_cmd_output("fast deps --uv --prod --dry --no-inexact")
|
|
40
|
+
assert out == "--> uv sync --active"
|
|
41
|
+
out = capture_cmd_output("fast deps --uv --prod --dry --no-active --no-inexact")
|
|
42
|
+
assert out == "--> uv sync"
|
|
43
|
+
out = capture_cmd_output("fast deps --uv --dry")
|
|
44
|
+
assert out == "--> uv sync --inexact --active --all-extras --all-groups"
|
|
45
|
+
out = capture_cmd_output("fast deps --uv --dry --no-active")
|
|
46
|
+
assert out == "--> uv sync --inexact --all-extras --all-groups"
|
|
47
|
+
out = capture_cmd_output("fast deps --uv --dry --no-inexact")
|
|
48
|
+
assert out == "--> uv sync --active --all-extras --all-groups"
|
|
49
|
+
out = capture_cmd_output("fast deps --uv --dry --no-active --no-inexact")
|
|
50
|
+
assert out == "--> uv sync --all-extras --all-groups"
|
|
@@ -95,7 +95,7 @@ def test_run_script_in_sub_directory(mocker: MockerFixture, capsys, script_path)
|
|
|
95
95
|
mocker.patch("pathlib.Path.cwd", return_value=script_path.parent)
|
|
96
96
|
unitcase(dry=True)
|
|
97
97
|
out = capsys.readouterr().out
|
|
98
|
-
assert f"cd {script_path.parent.parent} && {TEST_SCRIPT}" in out
|
|
98
|
+
assert f"cd {script_path.parent.parent} && python {TEST_SCRIPT}" in out
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def test_fast_test(mocker, capsys):
|
|
@@ -80,8 +80,16 @@ def test_run_shell():
|
|
|
80
80
|
assert run_and_echo("echo foo", capture_output=True) == 0
|
|
81
81
|
|
|
82
82
|
with pytest.raises(SystemExit):
|
|
83
|
+
exit_if_run_failed(
|
|
84
|
+
"in_valid_command", _exit=True, capture_output=True, shell=True
|
|
85
|
+
)
|
|
86
|
+
with pytest.raises(FileNotFoundError):
|
|
83
87
|
exit_if_run_failed("in_valid_command", _exit=True, capture_output=True)
|
|
84
88
|
with pytest.raises(typer.Exit):
|
|
89
|
+
exit_if_run_failed(
|
|
90
|
+
"in_valid_command", _exit=False, capture_output=True, shell=True
|
|
91
|
+
)
|
|
92
|
+
with pytest.raises(FileNotFoundError):
|
|
85
93
|
exit_if_run_failed("in_valid_command", _exit=False, capture_output=True)
|
|
86
94
|
with pytest.raises(NotImplementedError):
|
|
87
95
|
|
|
@@ -10,6 +10,7 @@ from fast_dev_cli.cli import (
|
|
|
10
10
|
TOML_FILE,
|
|
11
11
|
BumpUp,
|
|
12
12
|
ParseError,
|
|
13
|
+
Project,
|
|
13
14
|
poetry_module_name,
|
|
14
15
|
run_and_echo,
|
|
15
16
|
)
|
|
@@ -93,3 +94,9 @@ def test_version_plugin_include_defined(mark, tmp_path: Path) -> None:
|
|
|
93
94
|
assert BumpUp(part="patch", commit=False, dry=True).gen() == command
|
|
94
95
|
run_and_echo("poetry run fast bump patch")
|
|
95
96
|
assert init_file.read_text() == '__version__ = "0.0.2"\n'
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@pytest.mark.parametrize("command", ["poetry", "uvx poetry"])
|
|
100
|
+
def test_poetry_version(command) -> None:
|
|
101
|
+
v = Project.get_poetry_version(command)
|
|
102
|
+
assert v >= "2.2.0"
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import re
|
|
4
|
-
import sys
|
|
5
3
|
from contextlib import redirect_stdout
|
|
6
4
|
from io import StringIO
|
|
7
5
|
from pathlib import Path
|
|
@@ -12,13 +10,14 @@ import typer
|
|
|
12
10
|
import fast_dev_cli
|
|
13
11
|
from fast_dev_cli.cli import (
|
|
14
12
|
TOML_FILE,
|
|
13
|
+
Project,
|
|
15
14
|
UpgradeDependencies,
|
|
16
15
|
capture_cmd_output,
|
|
17
16
|
run_and_echo,
|
|
18
17
|
upgrade,
|
|
19
18
|
)
|
|
20
19
|
|
|
21
|
-
from .utils import chdir
|
|
20
|
+
from .utils import chdir, prepare_poetry_project
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
def test_parse_value():
|
|
@@ -85,28 +84,24 @@ uvicorn = {version = "^0.23.2", platform = "linux", optional = true}
|
|
|
85
84
|
|
|
86
85
|
def test_dev_flag(tmp_path: Path):
|
|
87
86
|
assert UpgradeDependencies.should_with_dev() is False
|
|
88
|
-
with
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
pattern = r'(python = "\^3)\.\d+"'
|
|
96
|
-
new_content = re.sub(pattern, r'\1.9"', content)
|
|
97
|
-
p.write_text(new_content)
|
|
98
|
-
assert not UpgradeDependencies.should_with_dev()
|
|
99
|
-
run_and_echo("poetry add pytest")
|
|
87
|
+
with prepare_poetry_project(tmp_path) as poetry:
|
|
88
|
+
is_newer_poetry = Project.get_poetry_version(poetry) >= "2.2.0"
|
|
89
|
+
assert not UpgradeDependencies.should_with_dev()
|
|
90
|
+
run_and_echo(f"{poetry} add pytest")
|
|
91
|
+
assert not UpgradeDependencies.should_with_dev()
|
|
92
|
+
run_and_echo(f"{poetry} add --group=dev typer")
|
|
93
|
+
if is_newer_poetry:
|
|
100
94
|
assert not UpgradeDependencies.should_with_dev()
|
|
101
|
-
|
|
95
|
+
else:
|
|
96
|
+
toml_file = Path(TOML_FILE)
|
|
102
97
|
assert UpgradeDependencies.should_with_dev()
|
|
103
|
-
text =
|
|
98
|
+
text = toml_file.read_text()
|
|
104
99
|
DevFlag = UpgradeDependencies.DevFlag
|
|
105
100
|
if DevFlag.new in text:
|
|
106
101
|
new_text = text.replace(DevFlag.new, DevFlag.old)
|
|
107
102
|
else:
|
|
108
103
|
new_text = text.replace(DevFlag.old, DevFlag.new)
|
|
109
|
-
|
|
104
|
+
toml_file.write_text(new_text)
|
|
110
105
|
assert UpgradeDependencies.should_with_dev()
|
|
111
106
|
|
|
112
107
|
|
|
@@ -300,7 +295,7 @@ def test_upgrade_uv_project():
|
|
|
300
295
|
|
|
301
296
|
def test_upgrade_pdm_project():
|
|
302
297
|
cmd = "fast upgrade --tool=pdm --dry"
|
|
303
|
-
expected = "pdm update --verbose && pdm
|
|
298
|
+
expected = "pdm update --verbose && pdm install -G :all --frozen"
|
|
304
299
|
assert expected in capture_cmd_output(cmd)
|
|
305
300
|
assert expected in capture_cmd_output("pdm run " + cmd)
|
|
306
301
|
assert UpgradeDependencies(tool="pdm").gen() == expected
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import shutil
|
|
2
2
|
import sys
|
|
3
|
+
from collections.abc import Generator
|
|
3
4
|
from contextlib import contextmanager, redirect_stdout
|
|
4
5
|
from io import StringIO
|
|
5
6
|
from pathlib import Path
|
|
@@ -8,8 +9,9 @@ from asynctor import Shell
|
|
|
8
9
|
from asynctor.compat import chdir
|
|
9
10
|
|
|
10
11
|
__all__ = (
|
|
11
|
-
"mock_sys_argv",
|
|
12
12
|
"capture_stdout",
|
|
13
|
+
"mock_sys_argv",
|
|
14
|
+
"prepare_poetry_project",
|
|
13
15
|
"temp_file",
|
|
14
16
|
)
|
|
15
17
|
|
|
@@ -51,7 +53,7 @@ def temp_file(name: str, text=""):
|
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
@contextmanager
|
|
54
|
-
def prepare_poetry_project(tmp_path: Path):
|
|
56
|
+
def prepare_poetry_project(tmp_path: Path) -> Generator[str]:
|
|
55
57
|
py = "{}.{}".format(*sys.version_info)
|
|
56
58
|
poetry = "poetry"
|
|
57
59
|
if shutil.which(poetry) is None:
|
|
@@ -64,4 +66,4 @@ def prepare_poetry_project(tmp_path: Path):
|
|
|
64
66
|
f"{poetry} config --local virtualenvs.in-project true"
|
|
65
67
|
)
|
|
66
68
|
Shell.run_by_subprocess(f"{poetry} env use {py}")
|
|
67
|
-
yield
|
|
69
|
+
yield poetry
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.18.3"
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from fast_dev_cli.cli import MakeDeps, capture_cmd_output
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_make_deps_class():
|
|
5
|
-
assert (
|
|
6
|
-
MakeDeps("uv", prod=False).gen()
|
|
7
|
-
== "uv sync --inexact --active --all-extras --all-groups"
|
|
8
|
-
)
|
|
9
|
-
assert MakeDeps("uv", prod=True).gen() == "uv sync --inexact --active"
|
|
10
|
-
assert MakeDeps("pdm", prod=False).gen() == "pdm sync -G :all"
|
|
11
|
-
assert MakeDeps("pdm", prod=True).gen() == "pdm sync --prod"
|
|
12
|
-
assert (
|
|
13
|
-
MakeDeps("poetry", prod=False).gen()
|
|
14
|
-
== "poetry install --all-extras --all-groups"
|
|
15
|
-
)
|
|
16
|
-
assert MakeDeps("poetry", prod=True).gen() == "poetry install --only=main"
|
|
17
|
-
assert (
|
|
18
|
-
MakeDeps("pip", prod=False).gen()
|
|
19
|
-
== "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e . --group dev"
|
|
20
|
-
)
|
|
21
|
-
assert (
|
|
22
|
-
MakeDeps("pip", prod=True).gen()
|
|
23
|
-
== "python -m ensurepip && python -m pip install --upgrade pip && python -m pip install -e ."
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def test_fast_deps():
|
|
28
|
-
out = capture_cmd_output("fast deps --dry")
|
|
29
|
-
assert out == "--> pdm sync -G :all"
|
|
30
|
-
out = capture_cmd_output("fast deps --uv --prod --dry")
|
|
31
|
-
assert out == "--> uv sync --inexact --active"
|
|
32
|
-
out = capture_cmd_output("fast deps --uv --dry")
|
|
33
|
-
assert out == "--> uv sync --inexact --active --all-extras --all-groups"
|
|
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
|
|
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
|