fast-dev-cli 0.9.4__tar.gz → 0.9.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.9.4 → fast_dev_cli-0.9.6}/PKG-INFO +3 -2
  2. fast_dev_cli-0.9.6/fast_dev_cli/__init__.py +1 -0
  3. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/fast_dev_cli/cli.py +42 -16
  4. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/pdm_build.py +7 -7
  5. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/pyproject.toml +3 -2
  6. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/scripts/format.sh +1 -1
  7. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_bump.py +20 -1
  8. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_fast_test.py +2 -2
  9. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_lint.py +3 -2
  10. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_runserver.py +14 -14
  11. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_tag.py +2 -3
  12. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_upgrade.py +20 -0
  13. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_upload.py +1 -1
  14. fast_dev_cli-0.9.4/fast_dev_cli/__init__.py +0 -1
  15. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/LICENSE +0 -0
  16. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/README.md +0 -0
  17. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/fast_dev_cli/__main__.py +0 -0
  18. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/fast_dev_cli/py.typed +0 -0
  19. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/scripts/check.sh +0 -0
  20. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/scripts/test.sh +0 -0
  21. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/__init__.py +0 -0
  22. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/conftest.py +0 -0
  23. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_functions.py +0 -0
  24. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_poetry_version_plugin.py +0 -0
  25. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_sync.py +0 -0
  26. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.6}/tests/test_version.py +0 -0
  27. {fast_dev_cli-0.9.4 → fast_dev_cli-0.9.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.9.4
3
+ Version: 0.9.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,8 +25,9 @@ Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
25
25
  Requires-Dist: coverage<8,>=7.5.1
26
26
  Requires-Dist: ruff<0.7,>=0.4.4
27
27
  Requires-Dist: mypy<2,>=1.10.0
28
- Requires-Dist: bumpversion2<2,>=1.4.0
28
+ Requires-Dist: bumpversion2<2,>=1.4.2
29
29
  Requires-Dist: pytest<9,>=8.2.0
30
+ Requires-Dist: emoji<3,>=2.12.1
30
31
  Requires-Dist: ipython<9,>=8.24.0; extra == "all"
31
32
  Requires-Dist: pytest-mock<4,>=3.14.0; extra == "all"
32
33
  Provides-Extra: all
@@ -0,0 +1 @@
1
+ __version__ = "0.9.6"
@@ -3,12 +3,14 @@ from __future__ import annotations
3
3
  import importlib.metadata as importlib_metadata
4
4
  import os
5
5
  import re
6
+ import shlex
6
7
  import subprocess # nosec:B404
7
8
  import sys
8
9
  from functools import cached_property
9
10
  from pathlib import Path
10
11
  from typing import Literal, Optional, Type
11
12
 
13
+ import emoji
12
14
  import typer
13
15
  from typer import Exit, Option, echo, secho
14
16
  from typer.models import OptionInfo
@@ -74,7 +76,7 @@ def check_call(cmd: str) -> bool:
74
76
 
75
77
  def capture_cmd_output(command: list[str] | str, **kw) -> str:
76
78
  if isinstance(command, str) and not kw.get("shell"):
77
- command = command.split()
79
+ command = shlex.split(command)
78
80
  r = _run_shell(command, capture_output=True, **kw)
79
81
  return r.stdout.strip().decode()
80
82
 
@@ -146,6 +148,20 @@ class BumpUp(DryRun):
146
148
  self.filename = filename
147
149
  super().__init__(dry=dry)
148
150
 
151
+ @staticmethod
152
+ def get_last_commit_message() -> str:
153
+ cmd = 'git show --pretty=format:"%s" -s HEAD'
154
+ return capture_cmd_output(cmd)
155
+
156
+ @classmethod
157
+ def should_add_emoji(cls) -> bool:
158
+ """
159
+ If last commit message is startswith emoji,
160
+ add a ⬆️ flag at the prefix of bump up commit message.
161
+ """
162
+ out = cls.get_last_commit_message()
163
+ return emoji.is_emoji(out[0])
164
+
149
165
  @staticmethod
150
166
  def parse_filename() -> str:
151
167
  toml_text = Project.load_toml_text()
@@ -172,9 +188,10 @@ class BumpUp(DryRun):
172
188
  pattern = re.compile(r"__version__\s*=\s*['\"]")
173
189
  ds = [cwd / i for i in packages] + [cwd / cwd.name.replace("-", "_"), cwd]
174
190
  for d in ds:
175
- if (init_file := d / "__init__.py").exists():
176
- if pattern.search(init_file.read_text()):
177
- break
191
+ if (init_file := d / "__init__.py").exists() and pattern.search(
192
+ init_file.read_text()
193
+ ):
194
+ break
178
195
  else:
179
196
  raise ParseError("Version file not found! Where are you now?")
180
197
  return os.path.relpath(init_file, cwd)
@@ -199,18 +216,19 @@ class BumpUp(DryRun):
199
216
  if self.part:
200
217
  part = self.get_part(self.part)
201
218
  else:
202
- tip = "Which one?"
203
- if a := input(tip).strip():
219
+ part = "patch"
220
+ if a := input("Which one?").strip():
204
221
  part = self.get_part(a)
205
- else:
206
- part = "patch"
207
222
  self.part = part
208
223
  parse = r'--parse "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)"'
209
224
  cmd = f'bumpversion {parse} --current-version="{_version}" {part} {filename}'
210
225
  if self.commit:
211
226
  if part != "patch":
212
227
  cmd += " --tag"
213
- cmd += " --commit && git push && git push --tags && git log -1"
228
+ cmd += " --commit"
229
+ if self.should_add_emoji():
230
+ cmd += " --commit-emoji=1"
231
+ cmd += " && git push && git push --tags && git log -1"
214
232
  else:
215
233
  cmd += " --allow-dirty"
216
234
  return cmd
@@ -365,6 +383,9 @@ class UpgradeDependencies(Project, DryRun):
365
383
  if v == "*":
366
384
  echo(f"Skip wildcard line: {line}")
367
385
  return True
386
+ elif v == "[":
387
+ echo(f"Skip complex dependence: {line}")
388
+ return True
368
389
  elif v.startswith(">") or v.startswith("<") or v[0].isdigit():
369
390
  echo(f"Ignore bigger/smaller/equal: {line}")
370
391
  return True
@@ -376,13 +397,18 @@ class UpgradeDependencies(Project, DryRun):
376
397
  ) -> tuple[list[str], dict[str, list[str]]]:
377
398
  args: list[str] = [] # ['typer[all]', 'fastapi']
378
399
  specials: dict[str, list[str]] = {} # {'--platform linux': ['gunicorn']}
379
- for line in package_lines:
380
- if not (m := line.strip()) or m.startswith("#"):
400
+ for no, line in enumerate(package_lines, 1):
401
+ if (
402
+ not (m := line.strip())
403
+ or m.startswith("#")
404
+ or m == "]"
405
+ or (m.startswith("{") and m.strip(",").endswith("}"))
406
+ ):
381
407
  continue
382
408
  try:
383
409
  package, version_info = m.split("=", 1)
384
410
  except ValueError as e:
385
- raise ParseError(f"{m = }") from e
411
+ raise ParseError(f"Failed to separate by '='@line {no}: {m}") from e
386
412
  if (package := package.strip()).lower() == "python":
387
413
  continue
388
414
  if cls.no_need_upgrade(version_info := version_info.strip(' "'), line):
@@ -549,7 +575,7 @@ class LintCode(DryRun):
549
575
  @classmethod
550
576
  def to_cmd(cls: Type[Self], paths=".", check_only=False) -> str:
551
577
  cmd = ""
552
- tools = ["ruff format", "ruff check --extend-select=I --fix", "mypy"]
578
+ tools = ["ruff format", "ruff check --extend-select=I,B,SIM --fix", "mypy"]
553
579
  if check_only:
554
580
  tools[0] += " --check"
555
581
  if check_only or load_bool("NO_FIX"):
@@ -567,8 +593,8 @@ class LintCode(DryRun):
567
593
  should_run_by_tool = True
568
594
  if check_call("python -c 'import fast_dev_cli'"):
569
595
  command = 'python -m pip install -U "fast_dev_cli"'
570
- tip = "You may need to run following command to install lint tools"
571
- secho(f"{tip}:\n\n {command}\n", fg="yellow")
596
+ tip = "You may need to run following command to install lint tools:"
597
+ secho(f"{tip}\n\n {command}\n", fg="yellow")
572
598
  else:
573
599
  should_run_by_tool = True
574
600
  if should_run_by_tool:
@@ -713,7 +739,7 @@ def dev(
713
739
  cmd += f" {file}"
714
740
  else:
715
741
  if port != 8000:
716
- cmd += f" --{port=}"
742
+ cmd += f" --port={port}"
717
743
  no_port_yet = False
718
744
  if no_port_yet and (port := getattr(port, "default", port)) and str(port) != "8000":
719
745
  cmd += f" --port={port}"
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Any, Dict, List
2
+ from typing import Any
3
3
 
4
4
  from pdm.backend.hooks import Context
5
5
 
@@ -9,30 +9,30 @@ BUILD_PACKAGE = os.getenv("BUILD_PACKAGE", "fast-dev-cli")
9
9
  def pdm_build_initialize(context: Context) -> None:
10
10
  metadata = context.config.metadata
11
11
  # Get custom config for the current package, from the env var
12
- config: Dict[str, Any] = context.config.data["tool"]["waketzheng"][
12
+ config: dict[str, Any] = context.config.data["tool"]["waketzheng"][
13
13
  "_internal-slim-build"
14
14
  ]["packages"][BUILD_PACKAGE]
15
- project_config: Dict[str, Any] = config["project"]
15
+ project_config: dict[str, Any] = config["project"]
16
16
  # Get main optional dependencies, extras
17
- optional_dependencies: Dict[str, List[str]] = metadata.get(
17
+ optional_dependencies: dict[str, list[str]] = metadata.get(
18
18
  "optional-dependencies", {}
19
19
  )
20
20
  # Get custom optional dependencies name to always include in this (non-slim) package
21
- include_optional_dependencies: List[str] = config.get(
21
+ include_optional_dependencies: list[str] = config.get(
22
22
  "include-optional-dependencies", []
23
23
  )
24
24
  # Override main [project] configs with custom configs for this package
25
25
  for key, value in project_config.items():
26
26
  metadata[key] = value
27
27
  # Get custom build config for the current package
28
- build_config: Dict[str, Any] = (
28
+ build_config: dict[str, Any] = (
29
29
  config.get("tool", {}).get("pdm", {}).get("build", {})
30
30
  )
31
31
  # Override PDM build config with custom build config for this package
32
32
  for key, value in build_config.items():
33
33
  context.config.build_config[key] = value
34
34
  # Get main dependencies
35
- dependencies: List[str] = metadata.get("dependencies", [])
35
+ dependencies: list[str] = metadata.get("dependencies", [])
36
36
  # Add optional dependencies to the default dependencies for this (non-slim) package
37
37
  for include_optional in include_optional_dependencies:
38
38
  optional_dependencies_group = optional_dependencies.get(include_optional, [])
@@ -36,10 +36,11 @@ dependencies = [
36
36
  "coverage >=7.5.1,<8",
37
37
  "ruff >=0.4.4,<0.7",
38
38
  "mypy >=1.10.0,<2",
39
- "bumpversion2 >=1.4.0,<2",
39
+ "bumpversion2 >=1.4.2,<2",
40
40
  "pytest >=8.2.0,<9",
41
+ "emoji >=2.12.1,<3",
41
42
  ]
42
- version = "0.9.4"
43
+ version = "0.9.6"
43
44
 
44
45
  [project.urls]
45
46
  Homepage = "https://github.com/waketzheng/fast-dev-cli"
@@ -3,4 +3,4 @@ set -x
3
3
 
4
4
  [ -f ../pyproject.toml ] && cd ..
5
5
 
6
- pdm run fast lint
6
+ SKIP_MYPY=1 pdm run fast lint
@@ -34,9 +34,13 @@ def test_enum():
34
34
  assert A.ABCD == "ABCD"
35
35
 
36
36
 
37
- def _bump_commands(version: str, filename=TOML_FILE) -> tuple[str, str, str]:
37
+ def _bump_commands(
38
+ version: str, filename=TOML_FILE, emoji=False
39
+ ) -> tuple[str, str, str]:
38
40
  cmd = rf'bumpversion --parse "(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)" --current-version="{version}"'
39
41
  suffix = " --commit && git push && git push --tags && git log -1"
42
+ if emoji:
43
+ suffix = suffix.replace("--commit", "--commit --commit-emoji=1")
40
44
  patch_without_commit = cmd + f" patch {filename} --allow-dirty"
41
45
  patch_with_commit = cmd + f" patch {filename}" + suffix
42
46
  minor_with_commit = cmd + f" minor {filename} --tag" + suffix
@@ -130,3 +134,18 @@ def test_bump_with_poetry(mocker, tmp_poetry_project, tmp_path):
130
134
  Project.get_work_dir(TOML_FILE)
131
135
  assert Project.get_work_dir(allow_cwd=True) == Path.cwd()
132
136
  assert work_dir == work_dir2 == tmp_path
137
+
138
+
139
+ def test_bump_with_emoji(mocker):
140
+ mocker.patch("fast_dev_cli.cli.Project.manage_by_poetry", return_value=True)
141
+ version = get_current_version()
142
+ patch_without_commit, patch_with_commit, minor_with_commit = _bump_commands(
143
+ version, emoji=True
144
+ )
145
+ mocker.patch(
146
+ "fast_dev_cli.cli.BumpUp.get_last_commit_message",
147
+ return_value="📝 Update release notes",
148
+ )
149
+ assert BumpUp(part="patch", commit=False, dry=True).gen() == patch_without_commit
150
+ assert BumpUp(part="patch", commit=True, dry=True).gen() == patch_with_commit
151
+ assert BumpUp(part="minor", commit=True, dry=True).gen() == minor_with_commit
@@ -41,7 +41,7 @@ def test_test_with_poetry_or_pdm_run(mocker: MockerFixture, capsys):
41
41
  if tool := Project.get_manage_tool():
42
42
  command = tool + " run " + command
43
43
  assert (
44
- '--> {0} run -m pytest -s && {0} report --omit="tests/*" -m'.format(command)
44
+ f'--> {command} run -m pytest -s && {command} report --omit="tests/*" -m'
45
45
  in capsys.readouterr().out
46
46
  )
47
47
 
@@ -54,7 +54,7 @@ def test_test_not_in_venv(mocker: MockerFixture, capsys):
54
54
  if tool := Project.get_manage_tool():
55
55
  command = tool + " run " + command
56
56
  assert (
57
- '--> {0} run -m pytest -s && {0} report --omit="tests/*" -m'.format(command)
57
+ f'--> {command} run -m pytest -s && {command} report --omit="tests/*" -m'
58
58
  in capsys.readouterr().out
59
59
  )
60
60
 
@@ -41,8 +41,9 @@ def mock_ignore_missing_imports_0(monkeypatch):
41
41
 
42
42
 
43
43
  SEP = " && "
44
- LINT_CMD = "ruff format . && ruff check --extend-select=I --fix . && mypy ."
45
- CHECK_CMD = "ruff format --check . && ruff check --extend-select=I . && mypy ."
44
+ _CMD = "ruff format{} . && ruff check --extend-select=I,B,SIM{} . && mypy ."
45
+ LINT_CMD = _CMD.format("", " --fix")
46
+ CHECK_CMD = _CMD.format(" --check", "")
46
47
 
47
48
 
48
49
  def test_check():
@@ -4,49 +4,49 @@ from fast_dev_cli.cli import dev, run_and_echo, runserver
4
4
  def test_runserver(capsys):
5
5
  runserver(dry=True)
6
6
  out = capsys.readouterr().out.strip()
7
- assert "fastapi dev" == out.replace("--> ", "")
7
+ assert out.replace("--> ", "") == "fastapi dev"
8
8
  runserver(port=8000, dry=True)
9
9
  out = capsys.readouterr().out.strip()
10
- assert "fastapi dev" == out.replace("--> ", "")
10
+ assert out.replace("--> ", "") == "fastapi dev"
11
11
  runserver(port=9000, dry=True)
12
12
  out = capsys.readouterr().out.strip()
13
- assert "fastapi dev --port=9000" == out.replace("--> ", "")
13
+ assert out.replace("--> ", "") == "fastapi dev --port=9000"
14
14
  runserver(host="0.0.0.0", dry=True)
15
15
  out = capsys.readouterr().out.strip()
16
- assert "fastapi dev --host=0.0.0.0" == out.replace("--> ", "")
16
+ assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0"
17
17
  runserver(port=9000, host="0.0.0.0", dry=True)
18
18
  out = capsys.readouterr().out.strip()
19
- assert "fastapi dev --port=9000 --host=0.0.0.0" == out.replace("--> ", "")
19
+ assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
20
20
 
21
21
 
22
22
  def test_dev(capsys):
23
23
  dev(None, None, dry=True)
24
24
  out = capsys.readouterr().out.strip()
25
- assert "fastapi dev" == out.replace("--> ", "")
25
+ assert out.replace("--> ", "") == "fastapi dev"
26
26
  dev(port=8000, host="", dry=True)
27
27
  out = capsys.readouterr().out.strip()
28
- assert "fastapi dev" == out.replace("--> ", "")
28
+ assert out.replace("--> ", "") == "fastapi dev"
29
29
  dev(port=9000, host=None, dry=True)
30
30
  out = capsys.readouterr().out.strip()
31
- assert "fastapi dev --port=9000" == out.replace("--> ", "")
31
+ assert out.replace("--> ", "") == "fastapi dev --port=9000"
32
32
  dev(8000, host="0.0.0.0", dry=True)
33
33
  out = capsys.readouterr().out.strip()
34
- assert "fastapi dev --host=0.0.0.0" == out.replace("--> ", "")
34
+ assert out.replace("--> ", "") == "fastapi dev --host=0.0.0.0"
35
35
  dev(port=9000, host="0.0.0.0", dry=True)
36
36
  out = capsys.readouterr().out.strip()
37
- assert "fastapi dev --port=9000 --host=0.0.0.0" == out.replace("--> ", "")
37
+ assert out.replace("--> ", "") == "fastapi dev --port=9000 --host=0.0.0.0"
38
38
  dev(8001, host="0.0.0.0", dry=True)
39
39
  out = capsys.readouterr().out.strip()
40
- assert "fastapi dev --port=8001 --host=0.0.0.0" == out.replace("--> ", "")
40
+ assert out.replace("--> ", "") == "fastapi dev --port=8001 --host=0.0.0.0"
41
41
  dev(None, file="8001", host="0.0.0.0", dry=True)
42
42
  out = capsys.readouterr().out.strip()
43
- assert "fastapi dev --port=8001 --host=0.0.0.0" == out.replace("--> ", "")
43
+ assert out.replace("--> ", "") == "fastapi dev --port=8001 --host=0.0.0.0"
44
44
  dev(None, file="main.py", host="0.0.0.0", dry=True)
45
45
  out = capsys.readouterr().out.strip()
46
- assert "fastapi dev main.py --host=0.0.0.0" == out.replace("--> ", "")
46
+ assert out.replace("--> ", "") == "fastapi dev main.py --host=0.0.0.0"
47
47
  dev(8001, file="main.py", host="0.0.0.0", dry=True)
48
48
  out = capsys.readouterr().out.strip()
49
- assert "fastapi dev main.py --port=8001 --host=0.0.0.0" == out.replace("--> ", "")
49
+ assert out.replace("--> ", "") == "fastapi dev main.py --port=8001 --host=0.0.0.0"
50
50
 
51
51
 
52
52
  def test_fast_dev(tmp_path):
@@ -16,9 +16,8 @@ def test_tag():
16
16
  GitTag(message="", dry=True).run()
17
17
  assert "git tag -a" in stream.getvalue()
18
18
 
19
- with temp_file("foo.txt"):
20
- with capture_stdout() as stream:
21
- GitTag(message="", dry=True).run()
19
+ with temp_file("foo.txt"), capture_stdout() as stream:
20
+ GitTag(message="", dry=True).run()
22
21
 
23
22
  assert "git status" in stream.getvalue()
24
23
  assert "ERROR" in stream.getvalue()
@@ -258,3 +258,23 @@ def test_get_dir(mocker, tmp_path):
258
258
  assert UpgradeDependencies.get_root_dir() == parent
259
259
  mocker.patch.object(UpgradeDependencies, "python_exec_dir", return_value=me)
260
260
  assert UpgradeDependencies.get_root_dir() == root
261
+
262
+
263
+ def test_parse_complex_segment():
264
+ segment = """
265
+ [tool.poetry.dependencies]
266
+ torch = [
267
+ {version="*",platform="linux"},
268
+ {version="^1.2.0",platform=""},
269
+ {version=">=1.2.0",platform=""},
270
+ ]
271
+ fastapi = "^0.112.2"
272
+
273
+ [tool.isort]
274
+ """.strip()
275
+ assert UpgradeDependencies.get_args(segment) == (
276
+ ['"fastapi@latest"'],
277
+ [],
278
+ [],
279
+ "--dev",
280
+ )
@@ -4,4 +4,4 @@ from fast_dev_cli.cli import upload
4
4
  def test_upload(capsys):
5
5
  upload(dry=True)
6
6
  out = capsys.readouterr().out.strip()
7
- assert "poetry publish --build" == out.replace("--> ", "")
7
+ assert out.replace("--> ", "") == "poetry publish --build"
@@ -1 +0,0 @@
1
- __version__ = "0.9.4"
File without changes
File without changes