fast-dev-cli 0.16.2__tar.gz → 0.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/PKG-INFO +1 -1
  2. fast_dev_cli-0.17.0/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/fast_dev_cli/cli.py +117 -36
  4. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/pyproject.toml +19 -23
  5. fast_dev_cli-0.17.0/scripts/deps.py +156 -0
  6. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_bump.py +32 -48
  7. fast_dev_cli-0.17.0/tests/test_exec.py +41 -0
  8. fast_dev_cli-0.16.2/fast_dev_cli/__init__.py +0 -1
  9. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/LICENSE +0 -0
  10. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/README.md +0 -0
  11. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/fast_dev_cli/__main__.py +0 -0
  12. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/fast_dev_cli/py.typed +0 -0
  13. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/pdm_build.py +0 -0
  14. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/scripts/check.py +0 -0
  15. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/scripts/format.py +0 -0
  16. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/scripts/test.py +0 -0
  17. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/__init__.py +0 -0
  18. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/conftest.py +0 -0
  19. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_fast_test.py +0 -0
  20. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_functions.py +0 -0
  21. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_lint.py +0 -0
  22. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_poetry_version_plugin.py +0 -0
  23. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_runserver.py +0 -0
  24. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_sync.py +0 -0
  25. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_tag.py +0 -0
  26. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_upgrade.py +0 -0
  27. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_upload.py +0 -0
  28. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/test_version.py +0 -0
  29. {fast_dev_cli-0.16.2 → fast_dev_cli-0.17.0}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.16.2
3
+ Version: 0.17.0
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -0,0 +1 @@
1
+ __version__ = "0.17.0"
@@ -16,6 +16,7 @@ from typing import (
16
16
  Optional,
17
17
  cast,
18
18
  get_args,
19
+ overload,
19
20
  )
20
21
 
21
22
  import typer
@@ -64,7 +65,10 @@ ToolOption = Option(
64
65
  )
65
66
 
66
67
 
67
- class ShellCommandError(Exception): ...
68
+ class FastDevCliError(Exception): ...
69
+
70
+
71
+ class ShellCommandError(FastDevCliError): ...
68
72
 
69
73
 
70
74
  def poetry_module_name(name: str) -> str:
@@ -111,7 +115,10 @@ def run_and_echo(
111
115
  echo(f"--> {cmd}")
112
116
  if dry:
113
117
  return 0
114
- return _run_shell(cmd, **kw).returncode
118
+ command: list[str] | str = cmd
119
+ if "shell" not in kw and not (set(cmd) & {"|", ">"}):
120
+ command = shlex.split(cmd)
121
+ return _run_shell(command, **kw).returncode
115
122
 
116
123
 
117
124
  def check_call(cmd: str) -> bool:
@@ -177,33 +184,61 @@ def read_version_from_file(
177
184
  return "0.0.0"
178
185
 
179
186
 
187
+ @overload
180
188
  def get_current_version(
181
189
  verbose: bool = False,
182
190
  is_poetry: bool | None = None,
183
191
  package_name: str | None = None,
184
- ) -> str:
185
- if is_poetry is None:
186
- is_poetry = Project.manage_by_poetry()
187
- if not is_poetry:
188
- work_dir = None
189
- if package_name is None:
190
- work_dir = Project.get_work_dir()
191
- package_name = re.sub(r"[- ]", "_", work_dir.name)
192
- try:
193
- installed_version = importlib_metadata.version(package_name)
194
- except importlib_metadata.PackageNotFoundError:
195
- ...
196
- else:
197
- if installed_version != "0.0.0":
198
- return installed_version
199
- return read_version_from_file(package_name, work_dir)
192
+ *,
193
+ check_version: Literal[False] = False,
194
+ ) -> str: ...
200
195
 
201
- cmd = ["poetry", "version", "-s"]
202
- if verbose:
203
- echo(f"--> {' '.join(cmd)}")
204
- if out := capture_cmd_output(cmd, raises=True):
205
- out = out.splitlines()[-1].strip().split()[-1]
206
- return out
196
+
197
+ @overload
198
+ def get_current_version(
199
+ verbose: bool = False,
200
+ is_poetry: bool | None = None,
201
+ package_name: str | None = None,
202
+ *,
203
+ check_version: Literal[True] = True,
204
+ ) -> tuple[bool, str]: ...
205
+
206
+
207
+ def get_current_version(
208
+ verbose: bool = False,
209
+ is_poetry: bool | None = None,
210
+ package_name: str | None = None,
211
+ *,
212
+ check_version: bool = False,
213
+ ) -> str | tuple[bool, str]:
214
+ if is_poetry is True or Project.manage_by_poetry():
215
+ cmd = ["poetry", "version", "-s"]
216
+ if verbose:
217
+ echo(f"--> {' '.join(cmd)}")
218
+ if out := capture_cmd_output(cmd, raises=True):
219
+ out = out.splitlines()[-1].strip().split()[-1]
220
+ if check_version:
221
+ return True, out
222
+ return out
223
+ toml_text = work_dir = None
224
+ if package_name is None:
225
+ work_dir = Project.get_work_dir()
226
+ toml_text = Project.load_toml_text()
227
+ doc = tomllib.loads(toml_text)
228
+ project_name = doc.get("project", {}).get("name", work_dir.name)
229
+ package_name = re.sub(r"[- ]", "_", project_name)
230
+ local_version = read_version_from_file(package_name, work_dir, toml_text)
231
+ try:
232
+ installed_version = importlib_metadata.version(package_name)
233
+ except importlib_metadata.PackageNotFoundError:
234
+ installed_version = ""
235
+ current_version = local_version or installed_version
236
+ if not current_version:
237
+ raise FastDevCliError(f"Failed to get current version of {package_name!r}")
238
+ if check_version:
239
+ is_conflict = bool(local_version) and local_version != installed_version
240
+ return is_conflict, current_version
241
+ return current_version
207
242
 
208
243
 
209
244
  def _ensure_bool(value: bool | OptionInfo) -> bool:
@@ -391,7 +426,9 @@ class BumpUp(DryRun):
391
426
  raise Exit(1) from e
392
427
 
393
428
  def gen(self: Self) -> str:
394
- _version = get_current_version()
429
+ should_sync, _version = get_current_version(check_version=True)
430
+ if should_sync:
431
+ Project.sync_dependencies()
395
432
  filename = self.filename
396
433
  echo(f"Current version(@{filename}): {_version}")
397
434
  if self.part:
@@ -413,8 +450,6 @@ class BumpUp(DryRun):
413
450
  cmd += " && git push && git push --tags && git log -1"
414
451
  else:
415
452
  cmd += " --allow-dirty"
416
- if Project.is_pdm_project():
417
- cmd = "pdm sync --prod && " + cmd
418
453
  return cmd
419
454
 
420
455
  def run(self: Self) -> None:
@@ -462,6 +497,7 @@ class EnvError(Exception):
462
497
 
463
498
  class Project:
464
499
  path_depth = 5
500
+ _tool: ToolName | None = None
465
501
 
466
502
  @staticmethod
467
503
  def is_poetry_v2(text: str) -> bool:
@@ -492,7 +528,7 @@ class Project:
492
528
  return d
493
529
  if allow_cwd:
494
530
  return cls.get_root_dir(cwd)
495
- raise EnvError(f"{name} not found! Make sure this is a poetry project.")
531
+ raise EnvError(f"{name} not found! Make sure this is a python project.")
496
532
 
497
533
  @classmethod
498
534
  def load_toml_text(cls: type[Self], name: str = TOML_FILE) -> str:
@@ -500,22 +536,40 @@ class Project:
500
536
  return toml_file.read_text("utf8")
501
537
 
502
538
  @classmethod
503
- def manage_by_poetry(cls: type[Self]) -> bool:
504
- return cls.get_manage_tool() == "poetry"
539
+ def manage_by_poetry(cls: type[Self], cache: bool = False) -> bool:
540
+ return cls.get_manage_tool(cache=cache) == "poetry"
505
541
 
506
542
  @classmethod
507
- def get_manage_tool(cls: type[Self]) -> ToolName | None:
543
+ def get_manage_tool(cls: type[Self], cache: bool = False) -> ToolName | None:
544
+ if cache and cls._tool:
545
+ return cls._tool
508
546
  try:
509
547
  text = cls.load_toml_text()
510
548
  except EnvError:
511
549
  pass
512
550
  else:
551
+ with contextlib.suppress(KeyError, tomllib.TOMLDecodeError):
552
+ doc = tomllib.loads(text)
553
+ backend = doc["build-system"]["build-backend"]
554
+ if "poetry" in backend:
555
+ cls._tool = "poetry"
556
+ return cls._tool
557
+ elif "pdm" in backend:
558
+ cls._tool = "pdm"
559
+ work_dir = cls.get_work_dir(allow_cwd=True)
560
+ if not Path(work_dir, "pdm.lock").exists() and (
561
+ "[tool.uv]" in text or Path(work_dir, "uv.lock").exists()
562
+ ):
563
+ cls._tool = "uv"
564
+ return cls._tool
513
565
  for name in get_args(ToolName):
514
566
  if f"[tool.{name}]" in text:
515
- return cast(ToolName, name)
567
+ cls._tool = cast(ToolName, name)
568
+ return cls._tool
516
569
  # Poetry 2.0 default to not include the '[tool.poetry]' section
517
570
  if cls.is_poetry_v2(text):
518
- return "poetry"
571
+ cls._tool = "poetry"
572
+ return cls._tool
519
573
  return None
520
574
 
521
575
  @staticmethod
@@ -531,14 +585,24 @@ class Project:
531
585
  return root
532
586
 
533
587
  @classmethod
534
- def is_pdm_project(cls, strict: bool = True) -> bool:
535
- if cls.get_manage_tool() != "pdm":
588
+ def is_pdm_project(cls, strict: bool = True, cache: bool = False) -> bool:
589
+ if cls.get_manage_tool(cache=cache) != "pdm":
536
590
  return False
537
591
  if strict:
538
592
  lock_file = cls.get_work_dir() / "pdm.lock"
539
593
  return lock_file.exists()
540
594
  return True
541
595
 
596
+ @classmethod
597
+ def sync_dependencies(cls) -> None:
598
+ if cls.is_pdm_project():
599
+ cmd = "pdm sync --prod"
600
+ run_and_echo(cmd)
601
+ elif cls.manage_by_poetry(cache=True):
602
+ run_and_echo("poetry install --only=main")
603
+ elif cls.get_manage_tool(cache=True) == "uv":
604
+ run_and_echo("uv sync")
605
+
542
606
 
543
607
  class ParseError(Exception):
544
608
  """Raise this if parse dependence line error"""
@@ -749,7 +813,9 @@ class GitTag(DryRun):
749
813
  return "git push" in self.git_status
750
814
 
751
815
  def gen(self: Self) -> str:
752
- _version = get_current_version(verbose=False)
816
+ should_sync, _version = get_current_version(verbose=False, check_version=True)
817
+ if should_sync:
818
+ Project.sync_dependencies()
753
819
  if self.has_v_prefix():
754
820
  # Add `v` at prefix to compare with bumpversion tool
755
821
  _version = "v" + _version
@@ -1124,6 +1190,21 @@ def runserver(
1124
1190
  dev(port, host, dry=dry)
1125
1191
 
1126
1192
 
1193
+ @cli.command(name="exec")
1194
+ def run_by_subprocess(cmd: str, dry: bool = DryOption) -> None:
1195
+ """Run cmd by subprocess, auto set shell=True when cmd contains '|'"""
1196
+ try:
1197
+ rc = run_and_echo(cmd, verbose=True, dry=_ensure_bool(dry))
1198
+ except FileNotFoundError as e:
1199
+ if e.filename == cmd.split()[0]:
1200
+ echo(f"Command not found: {e.filename}")
1201
+ raise Exit(1) from None
1202
+ raise e
1203
+ else:
1204
+ if rc:
1205
+ raise Exit(rc)
1206
+
1207
+
1127
1208
  def main() -> None:
1128
1209
  cli()
1129
1210
 
@@ -42,7 +42,7 @@ dependencies = [
42
42
  "bumpversion2 >=1.4.3,<2",
43
43
  "pytest >=8.2.0,<9",
44
44
  ]
45
- version = "0.16.2"
45
+ version = "0.17.0"
46
46
 
47
47
  [project.urls]
48
48
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -78,30 +78,27 @@ dev = [
78
78
  ]
79
79
 
80
80
  [tool.pdm.scripts]
81
- test = "pdm run fast test"
82
- check = "pdm run fast check {args}"
81
+ up = "pdm update -G :all"
82
+ tree = "pdm list --tree {args}"
83
+ deps = "pdm run python scripts/deps.py {args}"
84
+ prod = "pdm install --prod --frozen {args}"
85
+ test = "pdm run fast test {args}"
83
86
  lint = "pdm run fast lint {args}"
84
- deps = "pdm sync -G :all {args}"
85
- prod = "pdm install --prod --frozen"
86
- tree = "pdm list --tree"
87
- update_self_version = "pdm sync"
88
-
89
- [tool.pdm.scripts.style]
90
- composite = [
91
- "ruff format",
92
- "ruff check --fix {args}",
93
- ]
87
+ check = "pdm run fast check {args}"
88
+ tag = "pdm run fast tag {args}"
89
+ bump = "pdm run fast bump patch --commit {args}"
94
90
 
95
- [tool.pdm.scripts.bump]
91
+ [tool.pdm.scripts.fresh]
96
92
  composite = [
97
- "update_self_version",
98
- "pdm run fast bump patch --commit {args}",
93
+ "up",
94
+ "deps",
99
95
  ]
100
96
 
101
- [tool.pdm.scripts.tag]
97
+ [tool.pdm.scripts.ci]
102
98
  composite = [
103
- "update_self_version",
104
- "pdm run fast tag {args}",
99
+ "deps",
100
+ "check",
101
+ "test",
105
102
  ]
106
103
 
107
104
  [tool.pdm.scripts.start]
@@ -110,11 +107,10 @@ composite = [
110
107
  "deps",
111
108
  ]
112
109
 
113
- [tool.pdm.scripts.ci]
110
+ [tool.pdm.scripts.style]
114
111
  composite = [
115
- "deps",
116
- "check",
117
- "test",
112
+ "ruff format",
113
+ "ruff check --fix --unsafe-fixes {args}",
118
114
  ]
119
115
 
120
116
  [tool.waketzheng._internal-slim-build.packages.fastdevcli-slim.project]
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Install deps by `pdm install -G :all --frozen`
4
+ if value of `pdm config use_uv` is True,
5
+ otherwise by the following shells:
6
+
7
+ pdm run python -m ensurepip
8
+ pdm run python -m pip install --upgrade pip
9
+ pdm run python -m pip install --group dev -e .
10
+
11
+ Usage::
12
+ pdm run python scripts/deps.py
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import os
18
+ import platform
19
+ import shlex
20
+ import subprocess
21
+ import sys
22
+ from pathlib import Path
23
+
24
+ __version__ = "0.1.3"
25
+ __updated_at__ = "2025.07.21"
26
+ SHELL = """
27
+ pdm run python -m ensurepip
28
+ pdm run python -m pip install --upgrade pip
29
+ pdm run python -m pip install --group dev -e .
30
+ """
31
+
32
+
33
+ def run_and_echo(
34
+ cmd: str,
35
+ env: dict[str, str] | None = None,
36
+ dry: bool = False,
37
+ verbose: bool = True,
38
+ **kw,
39
+ ) -> int:
40
+ if verbose:
41
+ print("-->", cmd)
42
+ if dry:
43
+ return 0
44
+ if env is not None:
45
+ env = dict(os.environ, **env)
46
+ return subprocess.run(shlex.split(cmd), env=env, **kw).returncode
47
+
48
+
49
+ def capture_output(cmd: str) -> str:
50
+ r = subprocess.run(
51
+ shlex.split(cmd), capture_output=True, text=True, encoding="utf-8"
52
+ )
53
+ return r.stdout.strip()
54
+
55
+
56
+ def is_using_uv() -> bool:
57
+ return capture_output("pdm config use_uv").lower() == "true"
58
+
59
+
60
+ def pop_if_contains(args: list[str], flag: str) -> bool:
61
+ try:
62
+ index = args.index(flag)
63
+ except ValueError:
64
+ return False
65
+ else:
66
+ args.pop(index)
67
+ return True
68
+
69
+
70
+ def prefer_pdm(args: list[str]) -> bool:
71
+ if pop_if_contains(args, "--pip"):
72
+ return False
73
+ return platform.system() != "Windows" or is_using_uv()
74
+
75
+
76
+ def not_distribution() -> bool:
77
+ toml = Path(__file__).parent / "pyproject.toml"
78
+ if not toml.exists():
79
+ toml = toml.parent.parent / toml.name
80
+ if not toml.exists():
81
+ return False
82
+ if sys.version_info >= (3, 11):
83
+ import tomllib
84
+ else:
85
+ try:
86
+ import tomli as tomllib
87
+ except ImportError:
88
+ return False
89
+ doc = tomllib.loads(toml.read_text("utf8"))
90
+ try:
91
+ return not doc["tool"]["pdm"]["distribution"]
92
+ except KeyError:
93
+ ...
94
+ return False
95
+
96
+
97
+ def run_shell(command: str, args: list[str], dry: bool) -> int | None:
98
+ if args:
99
+ command += " " + " ".join(i if i.startswith("-") else repr(i) for i in args)
100
+ if run_and_echo(command, dry=dry) == 0:
101
+ return None
102
+ return 1
103
+
104
+
105
+ def pip_available() -> bool:
106
+ rc = run_and_echo(
107
+ "pdm run python -m pip --version", capture_output=True, verbose=False
108
+ )
109
+ return rc == 0
110
+
111
+
112
+ def main() -> int | None:
113
+ args = sys.argv[1:]
114
+ if len(args) == 1:
115
+ a1 = args[0]
116
+ if a1 == "-h" or "--help" in args:
117
+ print(__doc__)
118
+ return None
119
+ elif a1 in ("-v", "--version"):
120
+ print(__version__)
121
+ return None
122
+ dry = pop_if_contains(args, "--dry") or pop_if_contains(args, "--dry-run")
123
+ if pop_if_contains(args, "--uv"):
124
+ if os.path.exists("uv.lock"):
125
+ return run_shell("uv sync --all-extras --all-groups", args, dry)
126
+ command = (
127
+ "uv install --all-extras -r pyproject.toml --group dev --no-verify-hashes"
128
+ )
129
+ return run_shell(command, args, dry)
130
+ if prefer_pdm(args):
131
+ command = "pdm install --frozen -G :all"
132
+ return run_shell(command, args, dry)
133
+ shell = SHELL.strip()
134
+ if pop_if_contains(args, "--no-dev"):
135
+ shell = shell.replace(" --group dev", "")
136
+ if args:
137
+ extras = " ".join(i if i.startswith("-") else repr(i) for i in args)
138
+ if extras.startswith("-"):
139
+ shell += " " + extras
140
+ else:
141
+ shell += extras # e.g.: '[xls,fastapi]'
142
+ elif not_distribution():
143
+ shell = shell.replace(" -e .", "")
144
+ cmds = shell.splitlines()
145
+ if pip_available(): # If pip exists, skip installing it by ensurepip
146
+ cmds = cmds[1:]
147
+ for cmd in cmds:
148
+ if run_and_echo(cmd, dry=dry) != 0:
149
+ return 1
150
+ if not dry:
151
+ print("Done.")
152
+ return 0
153
+
154
+
155
+ if __name__ == "__main__":
156
+ sys.exit(main())
@@ -1,6 +1,8 @@
1
1
  import os
2
+ import re
2
3
  import shutil
3
4
  import subprocess
5
+ import sys
4
6
  from contextlib import redirect_stdout
5
7
  from io import StringIO
6
8
  from pathlib import Path
@@ -39,7 +41,7 @@ def test_enum():
39
41
 
40
42
 
41
43
  def _bump_commands(
42
- version: str, filename=TOML_FILE, emoji=False, add_sync=True
44
+ version: str, filename=TOML_FILE, emoji=False, add_sync=False
43
45
  ) -> tuple[str, str, str]:
44
46
  cmd = rf'bumpversion --parse "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)" --current-version="{version}"'
45
47
  suffix = " --commit && git push && git push --tags && git log -1"
@@ -243,23 +245,41 @@ path = "app/__init__.py"
243
245
  """
244
246
 
245
247
 
246
- def test_pdm_project(tmp_work_dir):
247
- capture_cmd_output("pdm new my-project --non-interactive")
248
- with chdir("my-project"):
248
+ @pytest.fixture
249
+ def dynamic_pdm_project(tmp_work_dir):
250
+ py = "{}.{}".format(*sys.version_info)
251
+ project = "my-project"
252
+ capture_cmd_output(f"pdm new {project} --non-interactive --python={py} --no-git")
253
+ with chdir(project):
249
254
  toml_file = Path("pyproject.toml")
250
255
  content = toml_file.read_text().replace(
251
256
  'version = "0.1.0"', 'dynamic = ["version"]'
252
257
  )
253
- toml_file.write_text(content)
254
- with toml_file.open("a+") as f:
255
- f.write(PDM_DYNAMIC_VERSION)
258
+ toml_file.write_text(content + PDM_DYNAMIC_VERSION)
256
259
  app = Path("app")
257
260
  app.mkdir()
258
- init_file = app.joinpath("__init__.py")
259
- init_file.write_text('__version__ = "0.2.0"')
260
- out = capture_cmd_output("fast bump patch")
261
- assert str(init_file) in out
262
- assert "0.2.1" in init_file.read_text()
261
+ yield app
262
+
263
+
264
+ def test_pdm_project(dynamic_pdm_project):
265
+ init_file = dynamic_pdm_project / "__init__.py"
266
+ init_file.write_text('__version__ = "0.2.0"')
267
+ out = capture_cmd_output("fast bump patch")
268
+ assert str(init_file) in out
269
+ assert "0.2.1" in init_file.read_text()
270
+
271
+
272
+ def test_installed_version_is_0_0_0(dynamic_pdm_project):
273
+ version_file = dynamic_pdm_project / "__init__.py"
274
+ version_file.write_text('__version__ = "0.0.0"')
275
+ toml_file = Path("pyproject.toml")
276
+ text = toml_file.read_text()
277
+ toml_file.write_text(re.sub(r"(distribution = )false", r"\1true", text))
278
+ capture_cmd_output("pdm install")
279
+ version_file.write_text('__version__ = "0.3.0"')
280
+ out = capture_cmd_output("fast bump patch")
281
+ assert str(version_file) in out
282
+ assert "0.3.1" in version_file.read_text()
263
283
 
264
284
 
265
285
  def test_hatch_project(tmp_work_dir):
@@ -369,39 +389,3 @@ build-backend = "pdm.backend"
369
389
  out = capture_cmd_output("fast bump patch")
370
390
  assert str(version_file) in out
371
391
  assert "0.3.1" in version_file.read_text()
372
-
373
-
374
- def test_installed_version_is_0_0_0(tmp_work_dir):
375
- project_name = "my-project"
376
- package_name = "app"
377
- Path(project_name).mkdir()
378
- with chdir(project_name):
379
- Path(package_name).mkdir()
380
- version_file = Path(package_name, "__init__.py")
381
- version_file.write_text('__version__ = "0.3.0"')
382
- toml_file = Path("pyproject.toml")
383
- toml_file.write_text("""
384
- [project]
385
- name = "my-project"
386
- description = ""
387
- authors = [{name="Waket Zheng", email="waketzheng@gmail.com"}]
388
- readme = "README.md"
389
- dynamic = ["version"]
390
- keywords = []
391
- requires-python = ">=3.9"
392
- dependencies = []
393
-
394
- [tool.pdm]
395
- distribution = false
396
-
397
- [tool.pdm.version]
398
- source = "file"
399
- path = "app/__init__.py"
400
-
401
- [build-system]
402
- requires = ["pdm-backend"]
403
- build-backend = "pdm.backend"
404
- """)
405
- out = capture_cmd_output("fast bump patch")
406
- assert str(version_file) in out
407
- assert "0.3.1" in version_file.read_text()
@@ -0,0 +1,41 @@
1
+ import pytest
2
+
3
+ from fast_dev_cli.cli import Exit, capture_cmd_output, run_by_subprocess
4
+
5
+
6
+ def test_exec_dry():
7
+ out = capture_cmd_output('fast exec "echo hello" --dry')
8
+ assert "--> echo hello" in out
9
+ assert out.count("hello") == 1
10
+ out = capture_cmd_output('fast exec "echo hello|grep h" --dry')
11
+ assert "--> echo hello|grep h" in out
12
+ assert out.count("hello") == 1
13
+ out = capture_cmd_output(
14
+ 'fast exec "invalid command" --dry && echo success || echo failed', shell=True
15
+ )
16
+ assert "success" in out
17
+ assert "failed" not in out
18
+
19
+
20
+ def test_exec():
21
+ out = capture_cmd_output('fast exec "echo hello"')
22
+ assert "--> echo hello" in out
23
+ assert out.count("hello") == 2
24
+ out = capture_cmd_output('fast exec "echo hello|grep h"')
25
+ assert "--> echo hello|grep h" in out
26
+ assert out.count("hello") == 2
27
+ out = capture_cmd_output(
28
+ 'fast exec "invalid command" && echo success || echo failed', shell=True
29
+ )
30
+ assert "failed" in out
31
+ assert "success" not in out
32
+
33
+
34
+ def test_run_by_subprocess(capsys):
35
+ with pytest.raises(Exit):
36
+ run_by_subprocess("python -c 'import sys;sys.exit(1)'")
37
+ with pytest.raises(Exit, match="1"):
38
+ run_by_subprocess("not-exit-command")
39
+ out = capsys.readouterr().out
40
+ assert "Command not found: not-exit-command" in out
41
+ assert not run_by_subprocess(f"cat {__file__}|grep xxx")
@@ -1 +0,0 @@
1
- __version__ = "0.16.2"
File without changes
File without changes