fast-dev-cli 0.18.4__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.4 → 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.4 → fast_dev_cli-0.18.5}/fast_dev_cli/cli.py +114 -52
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/pyproject.toml +1 -1
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_bump.py +8 -8
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_deps.py +3 -3
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_fast_test.py +1 -1
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_functions.py +8 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_poetry_version_plugin.py +7 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_upgrade.py +14 -19
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/utils.py +5 -3
- fast_dev_cli-0.18.4/fast_dev_cli/__init__.py +0 -1
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/LICENSE +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/README.md +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/fast_dev_cli/__main__.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/fast_dev_cli/py.typed +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/pdm_build.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/scripts/check.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/scripts/deps.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/scripts/format.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/scripts/test.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/__init__.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/assets/uv-tx.lock +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/assets/uv.lock +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/conftest.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_exec.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_help.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_lint.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_pypi.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_runserver.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_sync.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_tag.py +0 -0
- {fast_dev_cli-0.18.4 → fast_dev_cli-0.18.5}/tests/test_upload.py +0 -0
- {fast_dev_cli-0.18.4 → 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)
|
|
@@ -561,6 +599,19 @@ class Project:
|
|
|
561
599
|
def is_poetry_v2(text: str) -> bool:
|
|
562
600
|
return 'build-backend = "poetry' in text
|
|
563
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
|
+
|
|
564
615
|
@staticmethod
|
|
565
616
|
def work_dir(
|
|
566
617
|
name: str, parent: Path, depth: int, be_file: bool = False
|
|
@@ -657,7 +708,7 @@ class Project:
|
|
|
657
708
|
@classmethod
|
|
658
709
|
def get_sync_command(cls, prod: bool = True, doc: dict | None = None) -> str:
|
|
659
710
|
if cls.is_pdm_project():
|
|
660
|
-
return "pdm
|
|
711
|
+
return "pdm install --frozen" + " --prod" * prod
|
|
661
712
|
elif cls.manage_by_poetry(cache=True):
|
|
662
713
|
cmd = "poetry install"
|
|
663
714
|
if prod:
|
|
@@ -854,7 +905,7 @@ class UpgradeDependencies(Project, DryRun):
|
|
|
854
905
|
deps = "uv sync --inexact --frozen --all-groups --all-extras"
|
|
855
906
|
return f"{up} && {deps}"
|
|
856
907
|
elif self._tool == "pdm":
|
|
857
|
-
return "pdm update --verbose && pdm
|
|
908
|
+
return "pdm update --verbose && pdm install -G :all --frozen"
|
|
858
909
|
return self.gen_cmd() + " && poetry lock && poetry update"
|
|
859
910
|
|
|
860
911
|
|
|
@@ -1044,11 +1095,12 @@ class LintCode(DryRun):
|
|
|
1044
1095
|
if tool == ToolOption.default:
|
|
1045
1096
|
tool = Project.get_manage_tool() or ""
|
|
1046
1097
|
if tool:
|
|
1047
|
-
prefix =
|
|
1048
|
-
|
|
1049
|
-
if
|
|
1050
|
-
|
|
1051
|
-
|
|
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
|
|
1052
1104
|
if cls.prefer_dmypy(paths, tools, use_dmypy=use_dmypy):
|
|
1053
1105
|
tools[-1] = "dmypy run"
|
|
1054
1106
|
cmd += lint_them.format(prefix, paths, *tools)
|
|
@@ -1070,7 +1122,12 @@ class LintCode(DryRun):
|
|
|
1070
1122
|
ps = args.split() if isinstance(args, str) else [str(i) for i in args]
|
|
1071
1123
|
if len(ps) == 1:
|
|
1072
1124
|
paths = ps[0]
|
|
1073
|
-
if
|
|
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
|
+
):
|
|
1074
1131
|
# e.g.:
|
|
1075
1132
|
# stem -> stem.py
|
|
1076
1133
|
# me. -> me.py
|
|
@@ -1252,7 +1309,9 @@ def test(dry: bool, ignore_script: bool = False) -> None:
|
|
|
1252
1309
|
if not _ensure_bool(ignore_script) and (
|
|
1253
1310
|
test_script := _should_run_test_script(script_dir)
|
|
1254
1311
|
):
|
|
1255
|
-
cmd =
|
|
1312
|
+
cmd = test_script.relative_to(root).as_posix()
|
|
1313
|
+
if test_script.suffix == ".py":
|
|
1314
|
+
cmd = "python " + cmd
|
|
1256
1315
|
if cwd != root:
|
|
1257
1316
|
cmd = f"cd {root} && " + cmd
|
|
1258
1317
|
else:
|
|
@@ -1343,8 +1402,11 @@ def run_by_subprocess(cmd: str, dry: bool = DryOption) -> None:
|
|
|
1343
1402
|
try:
|
|
1344
1403
|
rc = run_and_echo(cmd, verbose=True, dry=_ensure_bool(dry))
|
|
1345
1404
|
except FileNotFoundError as e:
|
|
1346
|
-
|
|
1347
|
-
|
|
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}")
|
|
1348
1410
|
raise Exit(1) from None
|
|
1349
1411
|
raise e
|
|
1350
1412
|
else:
|
|
@@ -1380,7 +1442,7 @@ class MakeDeps(DryRun):
|
|
|
1380
1442
|
|
|
1381
1443
|
def gen(self) -> str:
|
|
1382
1444
|
if self._tool == "pdm":
|
|
1383
|
-
return "pdm
|
|
1445
|
+
return "pdm install --frozen " + ("--prod" if self._prod else "-G :all")
|
|
1384
1446
|
elif self._tool == "uv":
|
|
1385
1447
|
uv_sync = "uv sync" + " --inexact" * self._inexact
|
|
1386
1448
|
if self._active:
|
|
@@ -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()
|
|
@@ -12,8 +12,8 @@ def test_make_deps_class():
|
|
|
12
12
|
)
|
|
13
13
|
assert MakeDeps("uv", prod=True).gen() == "uv sync --inexact --active"
|
|
14
14
|
assert MakeDeps("uv", prod=True, active=False).gen() == "uv sync --inexact"
|
|
15
|
-
assert MakeDeps("pdm", prod=False).gen() == "pdm
|
|
16
|
-
assert MakeDeps("pdm", prod=True).gen() == "pdm
|
|
15
|
+
assert MakeDeps("pdm", prod=False).gen() == "pdm install --frozen -G :all"
|
|
16
|
+
assert MakeDeps("pdm", prod=True).gen() == "pdm install --frozen --prod"
|
|
17
17
|
assert (
|
|
18
18
|
MakeDeps("poetry", prod=False).gen()
|
|
19
19
|
== "poetry install --all-extras --all-groups"
|
|
@@ -31,7 +31,7 @@ def test_make_deps_class():
|
|
|
31
31
|
|
|
32
32
|
def test_fast_deps():
|
|
33
33
|
out = capture_cmd_output("fast deps --dry")
|
|
34
|
-
assert out == "--> pdm
|
|
34
|
+
assert out == "--> pdm install --frozen -G :all"
|
|
35
35
|
out = capture_cmd_output("fast deps --uv --prod --dry")
|
|
36
36
|
assert out == "--> uv sync --inexact --active"
|
|
37
37
|
out = capture_cmd_output("fast deps --uv --prod --dry --no-active")
|
|
@@ -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.4"
|
|
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
|