fast-dev-cli 0.11.5__tar.gz → 0.11.6__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 (27) hide show
  1. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/PKG-INFO +3 -2
  2. fast_dev_cli-0.11.6/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/fast_dev_cli/cli.py +51 -31
  4. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/pyproject.toml +3 -2
  5. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_bump.py +40 -34
  6. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_functions.py +8 -4
  7. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_poetry_version_plugin.py +8 -2
  8. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_upgrade.py +1 -0
  9. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_version.py +5 -1
  10. fast_dev_cli-0.11.5/fast_dev_cli/__init__.py +0 -1
  11. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/LICENSE +0 -0
  12. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/README.md +0 -0
  13. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/fast_dev_cli/__main__.py +0 -0
  14. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/fast_dev_cli/py.typed +0 -0
  15. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/pdm_build.py +0 -0
  16. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/scripts/check.py +0 -0
  17. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/scripts/format.py +0 -0
  18. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/scripts/test.py +0 -0
  19. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/__init__.py +0 -0
  20. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/conftest.py +0 -0
  21. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_fast_test.py +0 -0
  22. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_lint.py +0 -0
  23. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_runserver.py +0 -0
  24. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_sync.py +0 -0
  25. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_tag.py +0 -0
  26. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/test_upload.py +0 -0
  27. {fast_dev_cli-0.11.5 → fast_dev_cli-0.11.6}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fast-dev-cli
3
- Version: 0.11.5
3
+ Version: 0.11.6
4
4
  Summary: Python project development tool.
5
5
  Author-Email: Waket Zheng <waketzheng@gmail.com>>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -25,10 +25,11 @@ Requires-Dist: typer<0.16,>=0.12.3
25
25
  Requires-Dist: emoji<3,>=2.12.1
26
26
  Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
27
27
  Requires-Dist: coverage<8,>=7.5.1
28
- Requires-Dist: ruff<0.9,>=0.4.4
28
+ Requires-Dist: ruff<1,>=0.4.4
29
29
  Requires-Dist: mypy<2,>=1.10.0
30
30
  Requires-Dist: bumpversion2<2,>=1.4.3
31
31
  Requires-Dist: pytest<9,>=8.2.0
32
+ Requires-Dist: packaging>=20.5
32
33
  Provides-Extra: include-optional-dependencies
33
34
  Requires-Dist: all; extra == "include-optional-dependencies"
34
35
  Description-Content-Type: text/markdown
@@ -0,0 +1 @@
1
+ __version__ = "0.11.6"
@@ -39,7 +39,17 @@ else: # pragma: no cover
39
39
 
40
40
  cli = typer.Typer()
41
41
  TOML_FILE = "pyproject.toml"
42
- ToolName = Literal["poetry", "pdm", "uv", ""]
42
+ ToolName = Literal["poetry", "pdm", "uv"]
43
+
44
+
45
+ class ShellCommandError(Exception): ...
46
+
47
+
48
+ def poetry_module_name(name: str) -> str:
49
+ """Get module name that generated by `poetry new`"""
50
+ from packaging.utils import canonicalize_name
51
+
52
+ return canonicalize_name(name).replace("-", "_").replace(" ", "_")
43
53
 
44
54
 
45
55
  def load_bool(name: str, default=False) -> bool:
@@ -80,11 +90,13 @@ def check_call(cmd: str) -> bool:
80
90
  return r.returncode == 0
81
91
 
82
92
 
83
- def capture_cmd_output(command: list[str] | str, **kw) -> str:
93
+ def capture_cmd_output(command: list[str] | str, *, raises=False, **kw) -> str:
84
94
  if isinstance(command, str) and not kw.get("shell"):
85
95
  command = shlex.split(command)
86
- r = _run_shell(command, capture_output=True, **kw)
87
- return r.stdout.strip().decode()
96
+ r = _run_shell(command, capture_output=True, encoding="utf-8", **kw)
97
+ if raises and r.returncode != 0:
98
+ raise ShellCommandError(r.stderr)
99
+ return r.stdout.strip()
88
100
 
89
101
 
90
102
  def _parse_version(line: str, pattern: re.Pattern) -> str:
@@ -140,7 +152,7 @@ def get_current_version(
140
152
  cmd = ["poetry", "version", "-s"]
141
153
  if verbose:
142
154
  echo(f"--> {' '.join(cmd)}")
143
- if out := capture_cmd_output(cmd).strip():
155
+ if out := capture_cmd_output(cmd, raises=True):
144
156
  out = out.splitlines()[-1].strip().split()[-1]
145
157
  return out
146
158
 
@@ -214,29 +226,35 @@ class BumpUp(DryRun):
214
226
  def parse_filename() -> str:
215
227
  toml_text = Project.load_toml_text()
216
228
  context = tomllib.loads(toml_text)
229
+ by_version_plugin = False
217
230
  try:
218
231
  ver = context["project"]["version"]
219
232
  except KeyError:
220
233
  pass
221
234
  else:
222
- if isinstance(ver, str) and re.match(r"\d+\.\d+\.\d+", ver):
223
- return TOML_FILE
224
- try:
225
- version_value = context["tool"]["poetry"]["version"]
226
- except KeyError:
227
- if not Project.manage_by_poetry():
228
- # version = { source = "file", path = "fast_dev_cli/__init__.py" }
229
- v_key = "version = "
230
- p_key = 'path = "'
231
- for line in toml_text.splitlines():
232
- if not line.startswith(v_key):
233
- continue
234
- if p_key in (value := line.split(v_key, 1)[-1].split("#")[0]):
235
- filename = value.split(p_key, 1)[-1].split('"')[0]
236
- if Project.get_work_dir().joinpath(filename).exists():
237
- return filename
238
- return TOML_FILE
239
- if version_value in ("0", "0.0.0"):
235
+ if isinstance(ver, str):
236
+ if ver in ("0", "0.0.0"):
237
+ by_version_plugin = True
238
+ elif re.match(r"\d+\.\d+\.\d+", ver):
239
+ return TOML_FILE
240
+ if not by_version_plugin:
241
+ try:
242
+ version_value = context["tool"]["poetry"]["version"]
243
+ except KeyError:
244
+ if not Project.manage_by_poetry():
245
+ # version = { source = "file", path = "fast_dev_cli/__init__.py" }
246
+ v_key = "version = "
247
+ p_key = 'path = "'
248
+ for line in toml_text.splitlines():
249
+ if not line.startswith(v_key):
250
+ continue
251
+ if p_key in (value := line.split(v_key, 1)[-1].split("#")[0]):
252
+ filename = value.split(p_key, 1)[-1].split('"')[0]
253
+ if Project.get_work_dir().joinpath(filename).exists():
254
+ return filename
255
+ else:
256
+ by_version_plugin = version_value in ("0", "0.0.0", "init")
257
+ if by_version_plugin:
240
258
  try:
241
259
  package_item = context["tool"]["poetry"]["packages"]
242
260
  except KeyError:
@@ -246,7 +264,7 @@ class BumpUp(DryRun):
246
264
  # In case of managed by `poetry-plugin-version`
247
265
  cwd = Path.cwd()
248
266
  pattern = re.compile(r"__version__\s*=\s*['\"]")
249
- ds = [cwd / i for i in packages] + [cwd / cwd.name.replace("-", "_"), cwd]
267
+ ds = [cwd / i for i in packages] + [cwd / poetry_module_name(cwd.name), cwd]
250
268
  for d in ds:
251
269
  if (init_file := d / "__init__.py").exists() and pattern.search(
252
270
  init_file.read_text()
@@ -372,20 +390,22 @@ class Project:
372
390
 
373
391
  @classmethod
374
392
  def manage_by_poetry(cls: Type[Self]) -> bool:
375
- return "[tool.poetry]" in cls.load_toml_text()
393
+ return cls.get_manage_tool() == "poetry"
376
394
 
377
395
  @classmethod
378
- def get_manage_tool(cls: Type[Self]) -> ToolName:
396
+ def get_manage_tool(cls: Type[Self]) -> ToolName | None:
379
397
  try:
380
398
  text = cls.load_toml_text()
381
399
  except EnvError:
382
400
  pass
383
401
  else:
384
- name: ToolName
385
402
  for name in get_args(ToolName):
386
403
  if f"[tool.{name}]" in text:
387
404
  return name
388
- return ""
405
+ if 'build-backend = "poetry.core.masonry.api"' in text:
406
+ # Poetry 2.0 default to not include the '[tool.poetry]' section
407
+ return "poetry"
408
+ return None
389
409
 
390
410
  @staticmethod
391
411
  def python_exec_dir() -> Path:
@@ -823,8 +843,8 @@ def test(dry: bool, ignore_script=False) -> None:
823
843
  cmd = 'coverage run -m pytest -s && coverage report --omit="tests/*" -m'
824
844
  if not is_venv() or not check_call("coverage --version"):
825
845
  sep = " && "
826
- tool = Project.get_manage_tool()
827
- cmd = sep.join(f"{tool} run " + i for i in cmd.split(sep))
846
+ prefix = f"{tool} run " if (tool := Project.get_manage_tool()) else ""
847
+ cmd = sep.join(prefix + i for i in cmd.split(sep))
828
848
  exit_if_run_failed(cmd, dry=dry)
829
849
 
830
850
 
@@ -846,7 +866,7 @@ class Publish:
846
866
 
847
867
  @classmethod
848
868
  def gen(cls) -> str:
849
- if (tool := Project.get_manage_tool()) and tool in get_args(ToolName):
869
+ if tool := Project.get_manage_tool():
850
870
  return cls.CommandEnum[tool]
851
871
  return cls.CommandEnum.twine
852
872
 
@@ -36,12 +36,13 @@ dependencies = [
36
36
  "emoji >=2.12.1,<3",
37
37
  "tomli>=2.0.1,<3; python_version < '3.11'",
38
38
  "coverage >=7.5.1,<8",
39
- "ruff >=0.4.4,<0.9",
39
+ "ruff >=0.4.4,<1",
40
40
  "mypy >=1.10.0,<2",
41
41
  "bumpversion2 >=1.4.3,<2",
42
42
  "pytest >=8.2.0,<9",
43
+ "packaging>=20.5",
43
44
  ]
44
- version = "0.11.5"
45
+ version = "0.11.6"
45
46
 
46
47
  [project.urls]
47
48
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -13,10 +13,10 @@ from fast_dev_cli.cli import (
13
13
  EnvError,
14
14
  Exit,
15
15
  Project,
16
+ ShellCommandError,
16
17
  StrEnum,
17
18
  bump,
18
19
  bump_version,
19
- capture_cmd_output,
20
20
  get_current_version,
21
21
  )
22
22
 
@@ -51,11 +51,14 @@ def _bump_commands(
51
51
 
52
52
  def test_bump_dry(mocker):
53
53
  mocker.patch("fast_dev_cli.cli.Project.manage_by_poetry", return_value=True)
54
- version = get_current_version()
55
- patch_without_commit, patch_with_commit, minor_with_commit = _bump_commands(version)
56
- assert BumpUp(part="patch", commit=False, dry=True).gen() == patch_without_commit
57
- assert BumpUp(part="patch", commit=True, dry=True).gen() == patch_with_commit
58
- assert BumpUp(part="minor", commit=True, dry=True).gen() == minor_with_commit
54
+ with pytest.raises(ShellCommandError):
55
+ get_current_version()
56
+ # TODO:
57
+ # version = get_current_version()
58
+ # patch_without_commit, patch_with_commit, minor_with_commit = _bump_commands(version)
59
+ # assert BumpUp(part="patch", commit=False, dry=True).gen() == patch_without_commit
60
+ # assert BumpUp(part="patch", commit=True, dry=True).gen() == patch_with_commit
61
+ # assert BumpUp(part="minor", commit=True, dry=True).gen() == minor_with_commit
59
62
 
60
63
 
61
64
  def test_bump(
@@ -140,35 +143,38 @@ def test_bump_with_poetry(mocker, tmp_poetry_project, tmp_path):
140
143
 
141
144
  def test_bump_with_emoji(mocker, tmp_path, monkeypatch):
142
145
  mocker.patch("fast_dev_cli.cli.Project.manage_by_poetry", return_value=True)
143
- version = get_current_version()
144
- patch_without_commit, patch_with_commit, minor_with_commit = _bump_commands(
145
- version, emoji=True
146
- )
147
- last_commit = "📝 Update release notes"
148
- mocker.patch(
149
- "fast_dev_cli.cli.BumpUp.get_last_commit_message",
150
- return_value=last_commit,
151
- )
152
- assert BumpUp(part="patch", commit=False, dry=True).gen() == patch_without_commit
153
- assert BumpUp(part="patch", commit=True, dry=True).gen() == patch_with_commit
154
- assert BumpUp(part="minor", commit=True, dry=True).gen() == minor_with_commit
146
+ with pytest.raises(ShellCommandError):
147
+ get_current_version()
148
+ # TODO:
149
+ # version = get_current_version()
150
+ # patch_without_commit, patch_with_commit, minor_with_commit = _bump_commands(
151
+ # version, emoji=True
152
+ # )
153
+ # last_commit = "📝 Update release notes"
154
+ # mocker.patch(
155
+ # "fast_dev_cli.cli.BumpUp.get_last_commit_message",
156
+ # return_value=last_commit,
157
+ # )
158
+ # assert BumpUp(part="patch", commit=False, dry=True).gen() == patch_without_commit
159
+ # assert BumpUp(part="patch", commit=True, dry=True).gen() == patch_with_commit
160
+ # assert BumpUp(part="minor", commit=True, dry=True).gen() == minor_with_commit
155
161
  # real bump
156
- with chdir(tmp_path):
157
- project = "foo"
158
- subprocess.run(["poetry", "new", project])
159
- with chdir(tmp_path / project):
160
- subprocess.run(["git", "init"])
161
- subprocess.run(["git", "add", "."])
162
- subprocess.run(["git", "commit", "-m", last_commit])
163
- monkeypatch.setenv("DONT_GIT_PUSH", "1")
164
- command = BumpUp(part="patch", commit=True).gen()
165
- expected = patch_with_commit.split("&&")[0].strip().replace('""', '"0.1.0"')
166
- assert expected == command
167
- subprocess.run(["poetry", "run", "pip", "install", "bumpversion2"])
168
- subprocess.run(["fast", "bump", "patch", "--commit"])
169
- out = capture_cmd_output(["git", "log"])
170
- new_commit = "⬆️ Bump version: 0.1.0 → 0.1.1"
171
- assert new_commit in out
162
+ # with chdir(tmp_path):
163
+ # project = "foo"
164
+ # subprocess.run(["poetry", "new", project])
165
+ # with chdir(tmp_path / project):
166
+ # subprocess.run(["git", "init"])
167
+ # subprocess.run(["git", "add", "."])
168
+ # subprocess.run(["git", "commit", "-m", last_commit])
169
+ # monkeypatch.setenv("DONT_GIT_PUSH", "1")
170
+ # command = BumpUp(part="patch", commit=True).gen()
171
+ # expected = patch_with_commit.split("&&")[0].strip().replace('""', '"0.1.0"')
172
+ # assert expected == command
173
+ # subprocess.run(["poetry", "run", "pip", "install", "bumpversion2"])
174
+ # subprocess.run(["fast", "bump", "patch", "--commit"])
175
+ # out = capture_cmd_output(["git", "log"])
176
+ # new_commit = "⬆️ Bump version: 0.1.0 → 0.1.1"
177
+ # assert new_commit in out
172
178
 
173
179
 
174
180
  def test_bump_with_uv(tmp_path):
@@ -7,6 +7,7 @@ import typer
7
7
 
8
8
  from fast_dev_cli.cli import (
9
9
  DryRun,
10
+ ShellCommandError,
10
11
  _ensure_bool,
11
12
  exit_if_run_failed,
12
13
  get_current_version,
@@ -61,11 +62,14 @@ def test_utils(capsys):
61
62
 
62
63
  def test_run_shell():
63
64
  # current version
64
- stream = StringIO()
65
- write_to_stream = redirect_stdout(stream)
66
- with write_to_stream:
65
+ with pytest.raises(ShellCommandError):
67
66
  get_current_version(True, is_poetry=True)
68
- assert "poetry version -s" in stream.getvalue()
67
+ # TODO: add [tool.poetry] to pyproject.toml
68
+ # stream = StringIO()
69
+ # write_to_stream = redirect_stdout(stream)
70
+ # with write_to_stream:
71
+ # get_current_version(True, is_poetry=True)
72
+ # assert "poetry version -s" in stream.getvalue()
69
73
 
70
74
  name = "TEST_EXIT_IF_RUN_FAILED"
71
75
  value = "foo"
@@ -11,6 +11,7 @@ from fast_dev_cli.cli import (
11
11
  TOML_FILE,
12
12
  BumpUp,
13
13
  ParseError,
14
+ poetry_module_name,
14
15
  run_and_echo,
15
16
  )
16
17
 
@@ -33,7 +34,7 @@ def _prepare_package(
33
34
  package_path: Path, define_include=False, mark="0"
34
35
  ) -> Generator[Path, None, None]:
35
36
  toml_file = package_path / TOML_FILE
36
- package_name = package_path.name.replace(" ", "_")
37
+ package_name = poetry_module_name(package_path.name)
37
38
  init_file = package_path / package_name / "__init__.py"
38
39
  a, b = 'version = "0.1.0"', f'version = "{mark}"'
39
40
  if define_include:
@@ -45,8 +46,13 @@ def _prepare_package(
45
46
  with chdir(package_path):
46
47
  run_and_echo(f'poetry init --python="^{py_version}" --no-interaction')
47
48
  text = toml_file.read_text().replace(a, b)
49
+ if " " in package_path.name:
50
+ text = text.replace(
51
+ f'name = "{package_path.name}"', f'name = "{package_name}"'
52
+ )
48
53
  toml_file.write_text(text + CONF)
49
- shutil.move(package_path.name, package_name)
54
+ if package_path.name != package_name:
55
+ shutil.move(package_path.name, package_name)
50
56
  init_file.write_text('__version__ = "0.0.1"\n')
51
57
  yield init_file
52
58
 
@@ -162,6 +162,7 @@ anyio = "^4.0"
162
162
  run_and_echo(f"poetry new {project.name}")
163
163
  with chdir(project):
164
164
  with project.joinpath(TOML_FILE).open("a") as f:
165
+ f.write('\n[tool.poetry.dependencies]\nsix="*"')
165
166
  f.write(dev_text)
166
167
  assert UpgradeDependencies.get_args() == (
167
168
  [],
@@ -1,9 +1,12 @@
1
1
  import re
2
2
  from pathlib import Path
3
3
 
4
+ import pytest
5
+
4
6
  from fast_dev_cli import __version__
5
7
  from fast_dev_cli.cli import (
6
8
  TOML_FILE,
9
+ ShellCommandError,
7
10
  _parse_version,
8
11
  get_current_version,
9
12
  read_version_from_file,
@@ -17,8 +20,9 @@ def test_version(capsys):
17
20
  version()
18
21
  assert get_current_version(is_poetry=False) in capsys.readouterr().out
19
22
  assert get_current_version(is_poetry=False) == __version__
20
- assert get_current_version(is_poetry=True) == ""
21
23
  assert get_current_version() == __version__
24
+ with pytest.raises(ShellCommandError):
25
+ get_current_version(is_poetry=True)
22
26
 
23
27
 
24
28
  def test_read_version(tmp_path: Path, capsys):
@@ -1 +0,0 @@
1
- __version__ = "0.11.5"
File without changes
File without changes